diff options
| author | ivar <i@oiee.no> | 2026-04-07 00:23:24 +0200 |
|---|---|---|
| committer | ivar <i@oiee.no> | 2026-04-07 00:23:24 +0200 |
| commit | 85920b8c7a2696115d1f77c046f48f6f00d639f1 (patch) | |
| tree | 14ed2043796eadd6ed5b0a95c55e38e48713d638 | |
| download | iblog-85920b8c7a2696115d1f77c046f48f6f00d639f1.tar.xz iblog-85920b8c7a2696115d1f77c046f48f6f00d639f1.zip | |
Init
48 files changed, 4210 insertions, 0 deletions
diff --git a/.air.toml b/.air.toml new file mode 100644 index 0000000..dc625de --- /dev/null +++ b/.air.toml @@ -0,0 +1,58 @@ +#:schema https://json.schemastore.org/any.json + +env_files = [] +root = "." +testdata_dir = "testdata" +tmp_dir = "tmp" + +[build] + args_bin = ["serve", "--port", "8081"] + bin = "./tmp/main" + cmd = "go build -o ./tmp/main ./cmd/iblog" + delay = 200 + entrypoint = ["./tmp/main"] + exclude_dir = [ "tmp", "vendor", "testdata", "public", "data", "content"] + exclude_file = [] + exclude_regex = ["_test.go"] + exclude_unchanged = true + follow_symlink = false + full_bin = "" + ignore_dangerous_root_dir = false + include_dir = [] + include_ext = ["go", "tpl", "tmpl", "html"] + include_file = [] + kill_delay = "200ms" + log = "build-errors.log" + poll = false + poll_interval = 0 + post_cmd = [] + pre_cmd = [] + rerun = false + rerun_delay = 500 + send_interrupt = true + stop_on_error = false + +[color] + app = "" + build = "yellow" + main = "magenta" + runner = "green" + watcher = "cyan" + +[log] + main_only = false + silent = false + time = false + +[misc] + clean_on_exit = false + +[proxy] + app_port = 0 + app_start_timeout = 0 + enabled = false + proxy_port = 0 + +[screen] + clear_on_rebuild = false + keep_scroll = true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1021f46 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +public/ +data/ +.passwords +node_modules/ +assets/media/ +tmp/**
\ No newline at end of file diff --git a/assets/admin/lib/build.js b/assets/admin/lib/build.js new file mode 100644 index 0000000..a135e8d --- /dev/null +++ b/assets/admin/lib/build.js @@ -0,0 +1,10 @@ +// Entry point for bundling EditorJS and plugins into standalone ESM files. +// Run: bun run build + +export { default as EditorJS } from '@editorjs/editorjs'; +export { default as Header } from '@editorjs/header'; +export { default as Paragraph } from '@editorjs/paragraph'; +export { default as List } from '@editorjs/list'; +export { default as Code } from '@editorjs/code'; +export { default as Quote } from '@editorjs/quote'; +export { default as ImageTool } from '@editorjs/image'; diff --git a/assets/admin/lib/bun.lock b/assets/admin/lib/bun.lock new file mode 100644 index 0000000..dbeae70 --- /dev/null +++ b/assets/admin/lib/bun.lock @@ -0,0 +1,52 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "dependencies": { + "@editorjs/code": "^2.9.0", + "@editorjs/editorjs": "^2.30.0", + "@editorjs/header": "^2.8.0", + "@editorjs/image": "^2.10.0", + "@editorjs/list": "^2.0.0", + "@editorjs/paragraph": "^2.11.0", + "@editorjs/quote": "^2.7.0", + }, + }, + }, + "packages": { + "@codexteam/icons": ["@codexteam/icons@0.3.3", "", {}, "sha512-cp7mkZPgmBuSxigTm3Vb+DtVHYeX7qXfQd7o05vcLD8Ag5WvRlol2QSn5P10k0CDAJwmkH9nQGQLBycErS9lsQ=="], + + "@editorjs/caret": ["@editorjs/caret@1.0.3", "", { "dependencies": { "@editorjs/dom": "^1.0.1" } }, "sha512-VmgwQJZgL/LQjk049JunzRV1YCa0vDi+BNEpbDmr5cp3lGZllq9QQFO1eI71ZPzvFVn3vvhb+eOif4sAEyGgbw=="], + + "@editorjs/code": ["@editorjs/code@2.9.4", "", { "dependencies": { "@codexteam/icons": "^0.3.2" } }, "sha512-c0zyWodNqjL/0WI67sZvACIOFU9IAHG0UeeIpjss8pZGGNBum+UWkh7nKULK0SYvaOrdPdlWWqjuFU1TFA5jUA=="], + + "@editorjs/dom": ["@editorjs/dom@0.0.5", "", { "dependencies": { "@editorjs/helpers": "^0.0.4" } }, "sha512-SZ78Gwpkp3EUhjBIp0lSojeQ35V9acF8SubJsMeOH/vlOUE40GOnvvwWZnF05lO7bIB0dOHhhJy4N7IIAWxP2w=="], + + "@editorjs/editorjs": ["@editorjs/editorjs@2.31.5", "", { "dependencies": { "@editorjs/caret": "^1.0.1", "codex-notifier": "^1.1.2", "codex-tooltip": "^1.0.5" } }, "sha512-pEwYE4HzE63DlSSCErV2foTak7Wp9fd7SGkG+WcwiYD0cPmuCowhEsqL+9MF4/ZIjc/KJzDEvhB3NC1B8gQkpQ=="], + + "@editorjs/header": ["@editorjs/header@2.8.8", "", { "dependencies": { "@codexteam/icons": "^0.0.5", "@editorjs/editorjs": "^2.29.1" } }, "sha512-bsMSs34u2hoi0UBuRoc5EGWXIFzJiwYgkFUYQGVm63y5FU+s8zPBmVx5Ip2sw1xgs0fqfDROqmteMvvmbCy62w=="], + + "@editorjs/helpers": ["@editorjs/helpers@0.0.4", "", {}, "sha512-ieg3dzo2m1/ELze/RMNADiAiC5amXxIlVXoJ5vvXITOu/p/dPsrF+Oi3h5gBYvtGk9vg5LJUSG5YWU0tBUO1tw=="], + + "@editorjs/image": ["@editorjs/image@2.10.3", "", { "dependencies": { "@codexteam/icons": "^0.3.0" } }, "sha512-ekCsGICZOIdghF/U2T34H7CItqaWAoJDXbkRD+x8l/LIo/7Ozf7KovYm21qz+CluArgV4RurVFHqwlz+O0vfJA=="], + + "@editorjs/list": ["@editorjs/list@2.0.9", "", { "dependencies": { "@codexteam/icons": "^0.3.2" } }, "sha512-rUTgDSt5wygD3Dp24bNyp6vvye/Xf4UWju0ZuvWeP13Z4cu2z1Jb5JFSTEhCou72XUGuf4xVhtsd8cm/bwUS1g=="], + + "@editorjs/paragraph": ["@editorjs/paragraph@2.11.7", "", { "dependencies": { "@codexteam/icons": "^0.0.4" } }, "sha512-qD6bbWvRc4VvP0mXDOm+hOhzzhUYR9ZjcAvgCuKWcCbUMpCvhVF1s8NX40zdjekPi6JEnuHTamCncTrSzVsVhw=="], + + "@editorjs/quote": ["@editorjs/quote@2.7.6", "", { "dependencies": { "@codexteam/icons": "^0.3.2", "@editorjs/dom": "^0.0.5" } }, "sha512-D01KUMSDj2r+6Z+xjDkQqI+y6URpeHCvj0+P4pah+GtkG040lWjFb2H4pgHFXuol2cbfyAoraYSw85fuPheCvw=="], + + "codex-notifier": ["codex-notifier@1.1.2", "", {}, "sha512-DCp6xe/LGueJ1N5sXEwcBc3r3PyVkEEDNWCVigfvywAkeXcZMk9K41a31tkEFBW0Ptlwji6/JlAb49E3Yrxbtg=="], + + "codex-tooltip": ["codex-tooltip@1.0.5", "", {}, "sha512-IuA8LeyLU5p1B+HyhOsqR6oxyFQ11k3i9e9aXw40CrHFTRO2Y1npNBVU3W1SvhKAbUU7R/YikUBdcYFP0RcJag=="], + + "@editorjs/caret/@editorjs/dom": ["@editorjs/dom@1.0.1", "", { "dependencies": { "@editorjs/helpers": "^1.0.1" } }, "sha512-yLO+86MYOIUr1Jl7SQw23SYT84ggv6aJW0EIRsI3NTHYgnQzmK7Bt2n5ZFupQlB0GJqmKqA5tCue3NKQb+o7Pw=="], + + "@editorjs/header/@codexteam/icons": ["@codexteam/icons@0.0.5", "", {}, "sha512-s6H2KXhLz2rgbMZSkRm8dsMJvyUNZsEjxobBEg9ztdrb1B2H3pEzY6iTwI4XUPJWJ3c3qRKwV4TrO3J5jUdoQA=="], + + "@editorjs/paragraph/@codexteam/icons": ["@codexteam/icons@0.0.4", "", {}, "sha512-V8N/TY2TGyas4wLrPIFq7bcow68b3gu8DfDt1+rrHPtXxcexadKauRJL6eQgfG7Z0LCrN4boLRawR4S9gjIh/Q=="], + + "@editorjs/caret/@editorjs/dom/@editorjs/helpers": ["@editorjs/helpers@1.0.1", "", {}, "sha512-Lmr8ImoQvoROXtzhsIJsA1ZtXzH46DmE6O8hMjn9/AvQq62UfjREjn+Ewi6KxjIZMay2PsgDEbLlsVyNJGEaxw=="], + } +} diff --git a/assets/admin/lib/component-tool.js b/assets/admin/lib/component-tool.js new file mode 100644 index 0000000..8dbc330 --- /dev/null +++ b/assets/admin/lib/component-tool.js @@ -0,0 +1,166 @@ +/** @import { BlockTool, BlockToolConstructorOptions, BlockToolData, API, ToolboxConfig } from '@editorjs/editorjs' */ + +/** + * EditorJS block tool for embedding web components with props. + * + * Saved data format: + * { + * "name": "site-greeting", + * "props": { "name": "visitor", "theme": "dark" } + * } + * + * Renders in HTML as: <site-greeting name="visitor" theme="dark"></site-greeting> + * + * @implements {BlockTool} + */ +export default class ComponentTool { + /** @returns {ToolboxConfig} */ + static get toolbox() { + return { + title: 'Component', + icon: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-width="2" stroke-linecap="round" d="M7 8l-4 4 4 4M17 8l4 4-4 4M14 4l-4 16"/></svg>', + } + } + + static get isReadOnlySupported() { + return true + } + + /** + * @param {BlockToolConstructorOptions} options + * @param {BlockToolData} options.data + * @param {API} options.api + * @param {boolean} options.readOnly + */ + constructor({ data, api, readOnly }) { + this.api = api + this.readOnly = readOnly + this.data = { + name: data.name || '', + props: data.props || {}, + } + this.wrapper = null + } + + /** @returns {HTMLElement} */ + render() { + this.wrapper = document.createElement('div') + this.wrapper.classList.add('component-tool') + if (this.readOnly) { + this._renderPreview() + } else { + this._renderEditor() + } + return this.wrapper + } + + _renderPreview() { + const tag = document.createElement('code') + tag.style.cssText = 'display:block;padding:6px 8px;background:#f5f5f5;border-radius:4px;font-size:0.85em;color:#555;' + tag.textContent = this._previewText() + this.wrapper.append(tag) + } + + _previewText() { + const attrs = Object.entries(this.data.props) + .map(([k, v]) => ` ${k}="${v}"`) + .join('') + return `<${this.data.name || '?'}${attrs}>` + } + + _renderEditor() { + this.wrapper.innerHTML = '' + + const nameLabel = document.createElement('label') + nameLabel.textContent = 'Component tag name' + nameLabel.style.cssText = 'display:block;font-size:12px;color:#888;margin-bottom:2px;' + + const nameInput = document.createElement('input') + nameInput.type = 'text' + nameInput.placeholder = 'e.g. site-greeting' + nameInput.value = this.data.name + nameInput.style.cssText = 'width:100%;padding:6px 8px;border:1px solid #ccc;border-radius:4px;font-family:monospace;margin-bottom:8px;' + nameInput.addEventListener('input', () => { this.data.name = nameInput.value }) + + const propsLabel = document.createElement('label') + propsLabel.textContent = 'Props' + propsLabel.style.cssText = 'display:block;font-size:12px;color:#888;margin-bottom:2px;' + + const propsContainer = document.createElement('div') + propsContainer.classList.add('props-container') + + const addBtn = document.createElement('button') + addBtn.type = 'button' + addBtn.textContent = '+ Add prop' + addBtn.style.cssText = 'padding:4px 10px;font-size:12px;border:1px solid #ccc;border-radius:4px;background:#f5f5f5;cursor:pointer;margin-top:4px;' + addBtn.addEventListener('click', () => { + this.data.props[''] = '' + this._renderProps(propsContainer) + }) + + this.wrapper.append(nameLabel, nameInput, propsLabel, propsContainer, addBtn) + this._renderProps(propsContainer) + } + + _renderProps(container) { + container.innerHTML = '' + Object.entries(this.data.props).forEach(([key, value]) => { + const row = document.createElement('div') + row.style.cssText = 'display:flex;gap:4px;margin-bottom:4px;' + + const kInput = document.createElement('input') + kInput.type = 'text' + kInput.placeholder = 'key' + kInput.value = key + kInput.style.cssText = 'flex:1;padding:4px 6px;border:1px solid #ccc;border-radius:4px;font-family:monospace;' + + const vInput = document.createElement('input') + vInput.type = 'text' + vInput.placeholder = 'value' + vInput.value = value + vInput.style.cssText = 'flex:2;padding:4px 6px;border:1px solid #ccc;border-radius:4px;' + + const removeBtn = document.createElement('button') + removeBtn.type = 'button' + removeBtn.textContent = '×' + removeBtn.style.cssText = 'padding:4px 8px;border:1px solid #ccc;border-radius:4px;background:#f5f5f5;cursor:pointer;' + + const updateProps = () => { + const newProps = {} + container.querySelectorAll('div').forEach((r) => { + const [k, v] = r.querySelectorAll('input') + if (k && k.value) newProps[k.value] = v ? v.value : '' + }) + this.data.props = newProps + } + + kInput.addEventListener('input', updateProps) + vInput.addEventListener('input', updateProps) + removeBtn.addEventListener('click', () => { row.remove(); updateProps() }) + + row.append(kInput, vInput, removeBtn) + container.append(row) + }) + } + + /** + * @param {HTMLElement} _blockContent + * @returns {BlockToolData} + */ + save(_blockContent) { + const props = {} + for (const [k, v] of Object.entries(this.data.props)) { + if (k.trim()) props[k.trim()] = v + } + return { name: this.data.name.trim(), props } + } + + /** @param {BlockToolData} savedData */ + validate(savedData) { + return savedData.name.trim() !== '' + } + + destroyed() { + this.wrapper = null + } +} diff --git a/assets/admin/lib/dist/build.js b/assets/admin/lib/dist/build.js new file mode 100644 index 0000000..8fb2f6a --- /dev/null +++ b/assets/admin/lib/dist/build.js @@ -0,0 +1,41 @@ +(function(){try{if(typeof document<"u"){var J=document.createElement("style");J.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(J)}}catch(q){console.error("vite-plugin-css-injected-by-js",q)}})();var d0=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function H1(J){return J&&J.__esModule&&Object.prototype.hasOwnProperty.call(J,"default")?J.default:J}function a7(J){if(J.__esModule)return J;var q=J.default;if(typeof q=="function"){var Z=function X(){return this instanceof X?Reflect.construct(q,arguments,this.constructor):q.apply(this,arguments)};Z.prototype=q.prototype}else Z={};return Object.defineProperty(Z,"__esModule",{value:!0}),Object.keys(J).forEach(function(X){var K=Object.getOwnPropertyDescriptor(J,X);Object.defineProperty(Z,X,K.get?K:{enumerable:!0,get:function(){return J[X]}})}),Z}function n1(){}Object.assign(n1,{default:n1,register:n1,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(J){let q=(this.document||this.ownerDocument).querySelectorAll(J),Z=q.length;for(;--Z>=0&&q.item(Z)!==this;);return Z>-1});Element.prototype.closest||(Element.prototype.closest=function(J){let q=this;if(!document.documentElement.contains(q))return null;do{if(q.matches(J))return q;q=q.parentElement||q.parentNode}while(q!==null);return null});Element.prototype.prepend||(Element.prototype.prepend=function(J){let q=document.createDocumentFragment();Array.isArray(J)||(J=[J]),J.forEach((Z)=>{let X=Z instanceof Node;q.appendChild(X?Z:document.createTextNode(Z))}),this.insertBefore(q,this.firstChild)});Element.prototype.scrollIntoViewIfNeeded||(Element.prototype.scrollIntoViewIfNeeded=function(J){J=arguments.length===0?!0:!!J;let q=this.parentNode,Z=window.getComputedStyle(q,null),X=parseInt(Z.getPropertyValue("border-top-width")),K=parseInt(Z.getPropertyValue("border-left-width")),Q=this.offsetTop-q.offsetTop<q.scrollTop,V=this.offsetTop-q.offsetTop+this.clientHeight-X>q.scrollTop+q.clientHeight,G=this.offsetLeft-q.offsetLeft<q.scrollLeft,Y=this.offsetLeft-q.offsetLeft+this.clientWidth-K>q.scrollLeft+q.clientWidth,U=Q&&!V;(Q||V)&&J&&(q.scrollTop=this.offsetTop-q.offsetTop-q.clientHeight/2-X+this.clientHeight/2),(G||Y)&&J&&(q.scrollLeft=this.offsetLeft-q.offsetLeft-q.clientWidth/2-K+this.clientWidth/2),(Q||V||G||Y)&&!J&&this.scrollIntoView(U)});window.requestIdleCallback=window.requestIdleCallback||function(J){let q=Date.now();return setTimeout(function(){J({didTimeout:!1,timeRemaining:function(){return Math.max(0,50-(Date.now()-q))}})},1)};window.cancelIdleCallback=window.cancelIdleCallback||function(J){clearTimeout(J)};var s7=(J=21)=>crypto.getRandomValues(new Uint8Array(J)).reduce((q,Z)=>(Z&=63,Z<36?q+=Z.toString(36):Z<62?q+=(Z-26).toString(36).toUpperCase():Z>62?q+="-":q+="_",q),""),X5=((J)=>(J.VERBOSE="VERBOSE",J.INFO="INFO",J.WARN="WARN",J.ERROR="ERROR",J))(X5||{}),x={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},r7={LEFT:0,WHEEL:1,RIGHT:2,BACKWARD:3,FORWARD:4};function u0(J,q,Z="log",X,K="color: inherit"){if(!("console"in window)||!window.console[Z])return;let Q=["info","log","warn","error"].includes(Z),V=[];switch(u0.logLevel){case"ERROR":if(Z!=="error")return;break;case"WARN":if(!["error","warn"].includes(Z))return;break;case"INFO":if(!Q||J)return;break}X&&V.push(X);let G="Editor.js 2.31.5",Y=`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;`;J&&(Q?(V.unshift(Y,K),q=`%c${G}%c ${q}`):q=`( ${G} )${q}`);try{Q?X?console[Z](`${q} %o`,...V):console[Z](q,...V):console[Z](q)}catch{}}u0.logLevel="VERBOSE";function i7(J){u0.logLevel=J}var y=u0.bind(window,!1),e=u0.bind(window,!0);function N0(J){return Object.prototype.toString.call(J).match(/\s([a-zA-Z]+)/)[1].toLowerCase()}function m(J){return N0(J)==="function"||N0(J)==="asyncfunction"}function u(J){return N0(J)==="object"}function Y0(J){return N0(J)==="string"}function o7(J){return N0(J)==="boolean"}function u4(J){return N0(J)==="number"}function l4(J){return N0(J)==="undefined"}function J0(J){return J?Object.keys(J).length===0&&J.constructor===Object:!0}function K5(J){return J>47&&J<58||J===32||J===13||J===229||J>64&&J<91||J>95&&J<112||J>185&&J<193||J>218&&J<223}async function n7(J,q=()=>{},Z=()=>{}){async function X(K,Q,V){try{await K.function(K.data),await Q(l4(K.data)?{}:K.data)}catch{V(l4(K.data)?{}:K.data)}}return J.reduce(async(K,Q)=>(await K,X(Q,q,Z)),Promise.resolve())}function Q5(J){return Array.prototype.slice.call(J)}function Z1(J,q){return function(){let Z=this,X=arguments;window.setTimeout(()=>J.apply(Z,X),q)}}function t7(J){return J.name.split(".").pop()}function e7(J){return/^[-\w]+\/([-+\w]+|\*)$/.test(J)}function a4(J,q,Z){let X;return(...K)=>{let Q=this,V=()=>{X=null,Z||J.apply(Q,K)},G=Z&&!X;window.clearTimeout(X),X=window.setTimeout(V,q),G&&J.apply(Q,K)}}function K2(J,q,Z=void 0){let X,K,Q,V=null,G=0;Z||(Z={});let Y=function(){G=Z.leading===!1?0:Date.now(),V=null,Q=J.apply(X,K),V||(X=K=null)};return function(){let U=Date.now();!G&&Z.leading===!1&&(G=U);let H=q-(U-G);return X=this,K=arguments,H<=0||H>q?(V&&(clearTimeout(V),V=null),G=U,Q=J.apply(X,K),V||(X=K=null)):!V&&Z.trailing!==!1&&(V=setTimeout(Y,H)),Q}}function J8(){let J={win:!1,mac:!1,x11:!1,linux:!1},q=Object.keys(J).find((Z)=>window.navigator.appVersion.toLowerCase().indexOf(Z)!==-1);return q&&(J[q]=!0),J}function X1(J){return J[0].toUpperCase()+J.slice(1)}function Q2(J,...q){if(!q.length)return J;let Z=q.shift();if(u(J)&&u(Z))for(let X in Z)u(Z[X])?(J[X]||Object.assign(J,{[X]:{}}),Q2(J[X],Z[X])):Object.assign(J,{[X]:Z[X]});return Q2(J,...q)}function z2(J){let q=J8();return J=J.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," + "),q.mac?J=J.replace(/ctrl|cmd/gi,"⌘").replace(/alt/gi,"⌥"):J=J.replace(/cmd/gi,"Ctrl").replace(/windows/gi,"WIN"),J}function q8(J){try{return new URL(J).href}catch{}return J.substring(0,2)==="//"?window.location.protocol+J:window.location.origin+J}function Z8(){return s7(10)}function X8(J){window.open(J,"_blank")}function K8(J=""){return`${J}${Math.floor(Math.random()*1e8).toString(16)}`}function V2(J,q,Z){let X=`«${q}» is deprecated and will be removed in the next major release. Please use the «${Z}» instead.`;J&&e(X,"warn")}function B0(J,q,Z){let X=Z.value?"value":"get",K=Z[X],Q=`#${q}Cache`;if(Z[X]=function(...V){return this[Q]===void 0&&(this[Q]=K.apply(this,...V)),this[Q]},X==="get"&&Z.set){let V=Z.set;Z.set=function(G){delete J[Q],V.apply(this,G)}}return Z}var V5=650;function C0(){return window.matchMedia(`(max-width: ${V5}px)`).matches}var G2=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 Q8(J,q){let Z=Array.isArray(J)||u(J),X=Array.isArray(q)||u(q);return Z||X?JSON.stringify(J)===JSON.stringify(q):J===q}class F{static isSingleTag(J){return J.tagName&&["AREA","BASE","BR","COL","COMMAND","EMBED","HR","IMG","INPUT","KEYGEN","LINK","META","PARAM","SOURCE","TRACK","WBR"].includes(J.tagName)}static isLineBreakTag(J){return J&&J.tagName&&["BR","WBR"].includes(J.tagName)}static make(J,q=null,Z={}){let X=document.createElement(J);if(Array.isArray(q)){let K=q.filter((Q)=>Q!==void 0);X.classList.add(...K)}else q&&X.classList.add(q);for(let K in Z)Object.prototype.hasOwnProperty.call(Z,K)&&(X[K]=Z[K]);return X}static text(J){return document.createTextNode(J)}static append(J,q){Array.isArray(q)?q.forEach((Z)=>J.appendChild(Z)):J.appendChild(q)}static prepend(J,q){Array.isArray(q)?(q=q.reverse(),q.forEach((Z)=>J.prepend(Z))):J.prepend(q)}static swap(J,q){let Z=document.createElement("div"),X=J.parentNode;X.insertBefore(Z,J),X.insertBefore(J,q),X.insertBefore(q,Z),X.removeChild(Z)}static find(J=document,q){return J.querySelector(q)}static get(J){return document.getElementById(J)}static findAll(J=document,q){return J.querySelectorAll(q)}static get allInputsSelector(){return"[contenteditable=true], textarea, input:not([type]), "+["text","password","email","number","search","tel","url"].map((J)=>`input[type="${J}"]`).join(", ")}static findAllInputs(J){return Q5(J.querySelectorAll(F.allInputsSelector)).reduce((q,Z)=>F.isNativeInput(Z)||F.containsOnlyInlineElements(Z)?[...q,Z]:[...q,...F.getDeepestBlockElements(Z)],[])}static getDeepestNode(J,q=!1){let Z=q?"lastChild":"firstChild",X=q?"previousSibling":"nextSibling";if(J&&J.nodeType===Node.ELEMENT_NODE&&J[Z]){let K=J[Z];if(F.isSingleTag(K)&&!F.isNativeInput(K)&&!F.isLineBreakTag(K))if(K[X])K=K[X];else if(K.parentNode[X])K=K.parentNode[X];else return K.parentNode;return this.getDeepestNode(K,q)}return J}static isElement(J){return u4(J)?!1:J&&J.nodeType&&J.nodeType===Node.ELEMENT_NODE}static isFragment(J){return u4(J)?!1:J&&J.nodeType&&J.nodeType===Node.DOCUMENT_FRAGMENT_NODE}static isContentEditable(J){return J.contentEditable==="true"}static isNativeInput(J){let q=["INPUT","TEXTAREA"];return J&&J.tagName?q.includes(J.tagName):!1}static canSetCaret(J){let q=!0;if(F.isNativeInput(J))switch(J.type){case"file":case"checkbox":case"radio":case"hidden":case"submit":case"button":case"image":case"reset":q=!1;break}else q=F.isContentEditable(J);return q}static isNodeEmpty(J,q){let Z;return this.isSingleTag(J)&&!this.isLineBreakTag(J)?!1:(this.isElement(J)&&this.isNativeInput(J)?Z=J.value:Z=J.textContent.replace("",""),q&&(Z=Z.replace(new RegExp(q,"g"),"")),Z.length===0)}static isLeaf(J){return J?J.childNodes.length===0:!1}static isEmpty(J,q){let Z=[J];for(;Z.length>0;)if(J=Z.shift(),!!J){if(this.isLeaf(J)&&!this.isNodeEmpty(J,q))return!1;J.childNodes&&Z.push(...Array.from(J.childNodes))}return!0}static isHTMLString(J){let q=F.make("div");return q.innerHTML=J,q.childElementCount>0}static getContentLength(J){return F.isNativeInput(J)?J.value.length:J.nodeType===Node.TEXT_NODE?J.length:J.textContent.length}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"]}static containsOnlyInlineElements(J){let q;Y0(J)?(q=document.createElement("div"),q.innerHTML=J):q=J;let Z=(X)=>!F.blockElements.includes(X.tagName.toLowerCase())&&Array.from(X.children).every(Z);return Array.from(q.children).every(Z)}static getDeepestBlockElements(J){return F.containsOnlyInlineElements(J)?[J]:Array.from(J.children).reduce((q,Z)=>[...q,...F.getDeepestBlockElements(Z)],[])}static getHolder(J){return Y0(J)?document.getElementById(J):J}static isAnchor(J){return J.tagName.toLowerCase()==="a"}static getClosestAnchor(J){return J.closest("a")}static offset(J){let q=J.getBoundingClientRect(),Z=window.pageXOffset||document.documentElement.scrollLeft,X=window.pageYOffset||document.documentElement.scrollTop,K=q.top+X,Q=q.left+Z;return{top:K,left:Q,bottom:K+q.height,right:Q+q.width}}static getNodeByOffset(J,q){let Z=0,X=null,K=document.createTreeWalker(J,NodeFilter.SHOW_TEXT,null),Q=K.nextNode();for(;Q;){let Y=Q.textContent,U=Y===null?0:Y.length;if(X=Q,Z+U>=q)break;Z+=U,Q=K.nextNode()}if(!X)return{node:null,offset:0};let V=X.textContent;if(V===null||V.length===0)return{node:null,offset:0};let G=Math.min(q-Z,V.length);return{node:X,offset:G}}}function V8(J){return!/[^\t\n\r ]/.test(J)}function G8(J){let q=window.getComputedStyle(J),Z=parseFloat(q.fontSize),X=parseFloat(q.lineHeight)||Z*1.2,K=parseFloat(q.paddingTop),Q=parseFloat(q.borderTopWidth),V=parseFloat(q.marginTop),G=Z*0.8,Y=(X-Z)/2;return V+Q+K+Y+G}function G5(J){J.dataset.empty=F.isEmpty(J)?"true":"false"}var Y8={blockTunes:{toggler:{"Click to tune":"","or drag to move":""}},inlineToolbar:{converter:{"Convert to":""}},toolbar:{toolbox:{Add:""}},popover:{Filter:"","Nothing found":"","Convert to":""}},U8={Text:"",Link:"",Bold:"",Italic:""},H8={link:{"Add a link":""},stub:{"The block can not be displayed correctly.":""}},$8={delete:{Delete:"","Click to delete":""},moveUp:{"Move up":""},moveDown:{"Move down":""}},Y5={ui:Y8,toolNames:U8,tools:H8,blockTunes:$8},U5=class J{static ui(q,Z){return J._t(q,Z)}static t(q,Z){return J._t(q,Z)}static setDictionary(q){J.currentDictionary=q}static _t(q,Z){let X=J.getNamespace(q);return!X||!X[Z]?Z:X[Z]}static getNamespace(q){return q.split(".").reduce((Z,X)=>!Z||!Object.keys(Z).length?{}:Z[X],J.currentDictionary)}};U5.currentDictionary=Y5;var r=U5;class D2 extends Error{}class E0{constructor(){this.subscribers={}}on(J,q){J in this.subscribers||(this.subscribers[J]=[]),this.subscribers[J].push(q)}once(J,q){J in this.subscribers||(this.subscribers[J]=[]);let Z=(X)=>{let K=q(X),Q=this.subscribers[J].indexOf(Z);return Q!==-1&&this.subscribers[J].splice(Q,1),K};this.subscribers[J].push(Z)}emit(J,q){J0(this.subscribers)||!this.subscribers[J]||this.subscribers[J].reduce((Z,X)=>{let K=X(Z);return K!==void 0?K:Z},q)}off(J,q){if(this.subscribers[J]===void 0){console.warn(`EventDispatcher .off(): there is no subscribers for event "${J.toString()}". Probably, .off() called before .on()`);return}for(let Z=0;Z<this.subscribers[J].length;Z++)if(this.subscribers[J][Z]===q){delete this.subscribers[J][Z];break}}destroy(){this.subscribers={}}}function V0(J){Object.setPrototypeOf(this,{get id(){return J.id},get name(){return J.name},get config(){return J.config},get holder(){return J.holder},get isEmpty(){return J.isEmpty},get selected(){return J.selected},set stretched(q){J.stretched=q},get stretched(){return J.stretched},get focusable(){return J.focusable},call(q,Z){return J.call(q,Z)},save(){return J.save()},validate(q){return J.validate(q)},dispatchChange(){J.dispatchChange()},getActiveToolboxEntry(){return J.getActiveToolboxEntry()}})}class w0{constructor(){this.allListeners=[]}on(J,q,Z,X=!1){let K=K8("l"),Q={id:K,element:J,eventType:q,handler:Z,options:X};if(!this.findOne(J,q,Z))return this.allListeners.push(Q),J.addEventListener(q,Z,X),K}off(J,q,Z,X){let K=this.findAll(J,q,Z);K.forEach((Q,V)=>{let G=this.allListeners.indexOf(K[V]);G>-1&&(this.allListeners.splice(G,1),Q.element.removeEventListener(Q.eventType,Q.handler,Q.options))})}offById(J){let q=this.findById(J);q&&q.element.removeEventListener(q.eventType,q.handler,q.options)}findOne(J,q,Z){let X=this.findAll(J,q,Z);return X.length>0?X[0]:null}findAll(J,q,Z){let X,K=J?this.findByEventTarget(J):[];return J&&q&&Z?X=K.filter((Q)=>Q.eventType===q&&Q.handler===Z):J&&q?X=K.filter((Q)=>Q.eventType===q):X=K,X}removeAll(){this.allListeners.map((J)=>{J.element.removeEventListener(J.eventType,J.handler,J.options)}),this.allListeners=[]}destroy(){this.removeAll()}findByEventTarget(J){return this.allListeners.filter((q)=>{if(q.element===J)return q})}findByType(J){return this.allListeners.filter((q)=>{if(q.eventType===J)return q})}findByHandler(J){return this.allListeners.filter((q)=>{if(q.handler===J)return q})}findById(J){return this.allListeners.find((q)=>q.id===J)}}class B{constructor({config:J,eventsDispatcher:q}){if(this.nodes={},this.listeners=new w0,this.readOnlyMutableListeners={on:(Z,X,K,Q=!1)=>{this.mutableListenerIds.push(this.listeners.on(Z,X,K,Q))},clearAll:()=>{for(let Z of this.mutableListenerIds)this.listeners.offById(Z);this.mutableListenerIds=[]}},this.mutableListenerIds=[],new.target===B)throw TypeError("Constructors for abstract class Module are not allowed.");this.config=J,this.eventsDispatcher=q}set state(J){this.Editor=J}removeAllNodes(){for(let J in this.nodes){let q=this.nodes[J];q instanceof HTMLElement&&q.remove()}}get isRtl(){return this.config.i18n.direction==="rtl"}}class I{constructor(){this.instance=null,this.selection=null,this.savedSelectionRange=null,this.isFakeBackgroundEnabled=!1,this.commandBackground="backColor"}static get CSS(){return{editorWrapper:"codex-editor",editorZone:"codex-editor__redactor"}}static get anchorNode(){let J=window.getSelection();return J?J.anchorNode:null}static get anchorElement(){let J=window.getSelection();if(!J)return null;let q=J.anchorNode;return q?F.isElement(q)?q:q.parentElement:null}static get anchorOffset(){let J=window.getSelection();return J?J.anchorOffset:null}static get isCollapsed(){let J=window.getSelection();return J?J.isCollapsed:null}static get isAtEditor(){return this.isSelectionAtEditor(I.get())}static isSelectionAtEditor(J){if(!J)return!1;let q=J.anchorNode||J.focusNode;q&&q.nodeType===Node.TEXT_NODE&&(q=q.parentNode);let Z=null;return q&&q instanceof Element&&(Z=q.closest(`.${I.CSS.editorZone}`)),Z?Z.nodeType===Node.ELEMENT_NODE:!1}static isRangeAtEditor(J){if(!J)return;let q=J.startContainer;q&&q.nodeType===Node.TEXT_NODE&&(q=q.parentNode);let Z=null;return q&&q instanceof Element&&(Z=q.closest(`.${I.CSS.editorZone}`)),Z?Z.nodeType===Node.ELEMENT_NODE:!1}static get isSelectionExists(){return!!I.get().anchorNode}static get range(){return this.getRangeFromSelection(this.get())}static getRangeFromSelection(J){return J&&J.rangeCount?J.getRangeAt(0):null}static get rect(){let J=document.selection,q,Z={x:0,y:0,width:0,height:0};if(J&&J.type!=="Control")return J=J,q=J.createRange(),Z.x=q.boundingLeft,Z.y=q.boundingTop,Z.width=q.boundingWidth,Z.height=q.boundingHeight,Z;if(!window.getSelection)return y("Method window.getSelection is not supported","warn"),Z;if(J=window.getSelection(),J.rangeCount===null||isNaN(J.rangeCount))return y("Method SelectionUtils.rangeCount is not supported","warn"),Z;if(J.rangeCount===0)return Z;if(q=J.getRangeAt(0).cloneRange(),q.getBoundingClientRect&&(Z=q.getBoundingClientRect()),Z.x===0&&Z.y===0){let X=document.createElement("span");if(X.getBoundingClientRect){X.appendChild(document.createTextNode("")),q.insertNode(X),Z=X.getBoundingClientRect();let K=X.parentNode;K.removeChild(X),K.normalize()}}return Z}static get text(){return window.getSelection?window.getSelection().toString():""}static get(){return window.getSelection()}static setCursor(J,q=0){let Z=document.createRange(),X=window.getSelection();return F.isNativeInput(J)?F.canSetCaret(J)?(J.focus(),J.selectionStart=J.selectionEnd=q,J.getBoundingClientRect()):void 0:(Z.setStart(J,q),Z.setEnd(J,q),X.removeAllRanges(),X.addRange(Z),Z.getBoundingClientRect())}static isRangeInsideContainer(J){let q=I.range;return q===null?!1:J.contains(q.startContainer)}static addFakeCursor(){let J=I.range;if(J===null)return;let q=F.make("span","codex-editor__fake-cursor");q.dataset.mutationFree="true",J.collapse(),J.insertNode(q)}static isFakeCursorInsideContainer(J){return F.find(J,".codex-editor__fake-cursor")!==null}static removeFakeCursor(J=document.body){let q=F.find(J,".codex-editor__fake-cursor");q&&q.remove()}removeFakeBackground(){this.isFakeBackgroundEnabled&&(document.execCommand(this.commandBackground,!1,"transparent"),this.isFakeBackgroundEnabled=!1)}setFakeBackground(){document.execCommand(this.commandBackground,!1,"#a8d6ff"),this.isFakeBackgroundEnabled=!0}save(){this.savedSelectionRange=I.range}restore(){if(!this.savedSelectionRange)return;let J=window.getSelection();J.removeAllRanges(),J.addRange(this.savedSelectionRange)}clearSaved(){this.savedSelectionRange=null}collapseToEnd(){let J=window.getSelection(),q=document.createRange();q.selectNodeContents(J.focusNode),q.collapse(!1),J.removeAllRanges(),J.addRange(q)}findParentTag(J,q,Z=10){let X=window.getSelection(),K=null;return!X||!X.anchorNode||!X.focusNode?null:([X.anchorNode,X.focusNode].forEach((Q)=>{let V=Z;for(;V>0&&Q.parentNode&&!(Q.tagName===J&&(K=Q,q&&Q.classList&&!Q.classList.contains(q)&&(K=null),K));)Q=Q.parentNode,V--}),K)}expandToTag(J){let q=window.getSelection();q.removeAllRanges();let Z=document.createRange();Z.selectNodeContents(J),q.addRange(Z)}}function z8(J,q){let{type:Z,target:X,addedNodes:K,removedNodes:Q}=J;return J.type==="attributes"&&J.attributeName==="data-empty"?!1:!!(q.contains(X)||Z==="childList"&&(Array.from(K).some((V)=>V===q)||Array.from(Q).some((V)=>V===q)))}var Y2="redactor dom changed",H5="block changed",$5="fake cursor is about to be toggled",z5="fake cursor have been set",c0="editor mobile layout toggled";function U2(J,q){if(!J.conversionConfig)return!1;let Z=J.conversionConfig[q];return m(Z)||Y0(Z)}function K1(J,q){return U2(J.tool,q)}function D5(J,q){return Object.entries(J).some(([Z,X])=>q[Z]&&Q8(q[Z],X))}async function F5(J,q){let Z=(await J.save()).data,X=q.find((K)=>K.name===J.name);return X!==void 0&&!U2(X,"export")?[]:q.reduce((K,Q)=>{if(!U2(Q,"import")||Q.toolbox===void 0)return K;let V=Q.toolbox.filter((G)=>{if(J0(G)||G.icon===void 0)return!1;if(G.data!==void 0){if(D5(G.data,Z))return!1}else if(Q.name===J.name)return!1;return!0});return K.push({...Q,toolbox:V}),K},[])}function s4(J,q){return J.mergeable?J.name===q.name?!0:K1(q,"export")&&K1(J,"import"):!1}function D8(J,q){let Z=q==null?void 0:q.export;return m(Z)?Z(J):Y0(Z)?J[Z]:(Z!==void 0&&y("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 r4(J,q,Z){let X=q==null?void 0:q.import;return m(X)?X(J,Z):Y0(X)?{[X]:J}:(X!==void 0&&y("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 g=((J)=>(J.Default="default",J.Separator="separator",J.Html="html",J))(g||{}),G0=((J)=>(J.APPEND_CALLBACK="appendCallback",J.RENDERED="rendered",J.MOVED="moved",J.UPDATED="updated",J.REMOVED="removed",J.ON_PASTE="onPaste",J))(G0||{});class c extends E0{constructor({id:J=Z8(),data:q,tool:Z,readOnly:X,tunesData:K},Q){super(),this.cachedInputs=[],this.toolRenderedElement=null,this.tunesInstances=new Map,this.defaultTunesInstances=new Map,this.unavailableTunesData={},this.inputIndex=0,this.editorEventBus=null,this.handleFocus=()=>{this.dropInputsCache(),this.updateCurrentInput()},this.didMutated=(V=void 0)=>{let G=V===void 0,Y=V instanceof InputEvent;!G&&!Y&&this.detectToolRootChange(V);let U;G||Y?U=!0:U=!(V.length>0&&V.every((H)=>{let{addedNodes:$,removedNodes:D,target:A}=H;return[...Array.from($),...Array.from(D),A].some((W)=>(F.isElement(W)||(W=W.parentElement),W&&W.closest('[data-mutation-free="true"]')!==null))})),U&&(this.dropInputsCache(),this.updateCurrentInput(),this.toggleInputsEmptyMark(),this.call("updated"),this.emit("didMutated",this))},this.name=Z.name,this.id=J,this.settings=Z.settings,this.config=Z.settings.config||{},this.editorEventBus=Q||null,this.blockAPI=new V0(this),this.tool=Z,this.toolInstance=Z.create(q,this.blockAPI,X),this.tunes=Z.tunes,this.composeTunes(K),this.holder=this.compose(),window.requestIdleCallback(()=>{this.watchBlockMutations(),this.addInputEvents(),this.toggleInputsEmptyMark()})}static get CSS(){return{wrapper:"ce-block",wrapperStretched:"ce-block--stretched",content:"ce-block__content",selected:"ce-block--selected",dropTarget:"ce-block--drop-target"}}get inputs(){if(this.cachedInputs.length!==0)return this.cachedInputs;let J=F.findAllInputs(this.holder);return this.inputIndex>J.length-1&&(this.inputIndex=J.length-1),this.cachedInputs=J,J}get currentInput(){return this.inputs[this.inputIndex]}set currentInput(J){let q=this.inputs.findIndex((Z)=>Z===J||Z.contains(J));q!==-1&&(this.inputIndex=q)}get firstInput(){return this.inputs[0]}get lastInput(){let J=this.inputs;return J[J.length-1]}get nextInput(){return this.inputs[this.inputIndex+1]}get previousInput(){return this.inputs[this.inputIndex-1]}get data(){return this.save().then((J)=>J&&!J0(J.data)?J.data:{})}get sanitize(){return this.tool.sanitizeConfig}get mergeable(){return m(this.toolInstance.merge)}get focusable(){return this.inputs.length!==0}get isEmpty(){let J=F.isEmpty(this.pluginsContent,"/"),q=!this.hasMedia;return J&&q}get hasMedia(){let J=["img","iframe","video","audio","source","input","textarea","twitterwidget"];return!!this.holder.querySelector(J.join(","))}set selected(J){var q,Z;this.holder.classList.toggle(c.CSS.selected,J);let X=J===!0&&I.isRangeInsideContainer(this.holder),K=J===!1&&I.isFakeCursorInsideContainer(this.holder);(X||K)&&((q=this.editorEventBus)==null||q.emit($5,{state:J}),X?I.addFakeCursor():I.removeFakeCursor(this.holder),(Z=this.editorEventBus)==null||Z.emit(z5,{state:J}))}get selected(){return this.holder.classList.contains(c.CSS.selected)}set stretched(J){this.holder.classList.toggle(c.CSS.wrapperStretched,J)}get stretched(){return this.holder.classList.contains(c.CSS.wrapperStretched)}set dropTarget(J){this.holder.classList.toggle(c.CSS.dropTarget,J)}get pluginsContent(){return this.toolRenderedElement}call(J,q){if(m(this.toolInstance[J])){J==="appendCallback"&&y("`appendCallback` hook is deprecated and will be removed in the next major release. Use `rendered` hook instead","warn");try{this.toolInstance[J].call(this.toolInstance,q)}catch(Z){y(`Error during '${J}' call: ${Z.message}`,"error")}}}async mergeWith(J){await this.toolInstance.merge(J)}async save(){let J=await this.toolInstance.save(this.pluginsContent),q=this.unavailableTunesData;[...this.tunesInstances.entries(),...this.defaultTunesInstances.entries()].forEach(([K,Q])=>{if(m(Q.save))try{q[K]=Q.save()}catch(V){y(`Tune ${Q.constructor.name} save method throws an Error %o`,"warn",V)}});let Z=window.performance.now(),X;return Promise.resolve(J).then((K)=>(X=window.performance.now(),{id:this.id,tool:this.name,data:K,tunes:q,time:X-Z})).catch((K)=>{y(`Saving process for ${this.name} tool failed due to the ${K}`,"log","red")})}async validate(J){let q=!0;return this.toolInstance.validate instanceof Function&&(q=await this.toolInstance.validate(J)),q}getTunes(){let J=[],q=[],Z=typeof this.toolInstance.renderSettings=="function"?this.toolInstance.renderSettings():[];return F.isElement(Z)?J.push({type:g.Html,element:Z}):Array.isArray(Z)?J.push(...Z):J.push(Z),[...this.tunesInstances.values(),...this.defaultTunesInstances.values()].map((X)=>X.render()).forEach((X)=>{F.isElement(X)?q.push({type:g.Html,element:X}):Array.isArray(X)?q.push(...X):q.push(X)}),{toolTunes:J,commonTunes:q}}updateCurrentInput(){this.currentInput=F.isNativeInput(document.activeElement)||!I.anchorNode?document.activeElement:I.anchorNode}dispatchChange(){this.didMutated()}destroy(){this.unwatchBlockMutations(),this.removeInputEvents(),super.destroy(),m(this.toolInstance.destroy)&&this.toolInstance.destroy()}async getActiveToolboxEntry(){let J=this.tool.toolbox;if(J.length===1)return Promise.resolve(this.tool.toolbox[0]);let q=await this.data,Z=J;return Z==null?void 0:Z.find((X)=>D5(X.data,q))}async exportDataAsString(){let J=await this.data;return D8(J,this.tool.conversionConfig)}compose(){let J=F.make("div",c.CSS.wrapper),q=F.make("div",c.CSS.content),Z=this.toolInstance.render();J.dataset.id=this.id,this.toolRenderedElement=Z,q.appendChild(this.toolRenderedElement);let X=q;return[...this.tunesInstances.values(),...this.defaultTunesInstances.values()].forEach((K)=>{if(m(K.wrap))try{X=K.wrap(X)}catch(Q){y(`Tune ${K.constructor.name} wrap method throws an Error %o`,"warn",Q)}}),J.appendChild(X),J}composeTunes(J){Array.from(this.tunes.values()).forEach((q)=>{(q.isInternal?this.defaultTunesInstances:this.tunesInstances).set(q.name,q.create(J[q.name],this.blockAPI))}),Object.entries(J).forEach(([q,Z])=>{this.tunesInstances.has(q)||(this.unavailableTunesData[q]=Z)})}addInputEvents(){this.inputs.forEach((J)=>{J.addEventListener("focus",this.handleFocus),F.isNativeInput(J)&&J.addEventListener("input",this.didMutated)})}removeInputEvents(){this.inputs.forEach((J)=>{J.removeEventListener("focus",this.handleFocus),F.isNativeInput(J)&&J.removeEventListener("input",this.didMutated)})}watchBlockMutations(){var J;this.redactorDomChangedCallback=(q)=>{let{mutations:Z}=q;Z.some((X)=>z8(X,this.toolRenderedElement))&&this.didMutated(Z)},(J=this.editorEventBus)==null||J.on(Y2,this.redactorDomChangedCallback)}unwatchBlockMutations(){var J;(J=this.editorEventBus)==null||J.off(Y2,this.redactorDomChangedCallback)}detectToolRootChange(J){J.forEach((q)=>{if(Array.from(q.removedNodes).includes(this.toolRenderedElement)){let Z=q.addedNodes[q.addedNodes.length-1];this.toolRenderedElement=Z}})}dropInputsCache(){this.cachedInputs=[]}toggleInputsEmptyMark(){this.inputs.forEach(G5)}}class W5 extends B{constructor(){super(...arguments),this.insert=(J=this.config.defaultBlock,q={},Z={},X,K,Q,V)=>{let G=this.Editor.BlockManager.insert({id:V,tool:J,data:q,index:X,needToFocus:K,replace:Q});return new V0(G)},this.composeBlockData=async(J)=>{let q=this.Editor.Tools.blockTools.get(J);return new c({tool:q,api:this.Editor.API,readOnly:!0,data:{},tunesData:{}}).data},this.update=async(J,q,Z)=>{let{BlockManager:X}=this.Editor,K=X.getBlockById(J);if(K===void 0)throw Error(`Block with id "${J}" not found`);let Q=await X.update(K,q,Z);return new V0(Q)},this.convert=async(J,q,Z)=>{var X,K;let{BlockManager:Q,Tools:V}=this.Editor,G=Q.getBlockById(J);if(!G)throw Error(`Block with id "${J}" not found`);let Y=V.blockTools.get(G.name),U=V.blockTools.get(q);if(!U)throw Error(`Block Tool with type "${q}" not found`);let H=((X=Y==null?void 0:Y.conversionConfig)==null?void 0:X.export)!==void 0,$=((K=U.conversionConfig)==null?void 0:K.import)!==void 0;if(H&&$){let D=await Q.convert(G,q,Z);return new V0(D)}else{let D=[H?!1:X1(G.name),$?!1:X1(q)].filter(Boolean).join(" and ");throw Error(`Conversion from "${G.name}" to "${q}" is not possible. ${D} tool(s) should provide a "conversionConfig"`)}},this.insertMany=(J,q=this.Editor.BlockManager.blocks.length-1)=>{this.validateIndex(q);let Z=J.map(({id:X,type:K,data:Q})=>this.Editor.BlockManager.composeBlock({id:X,tool:K||this.config.defaultBlock,data:Q}));return this.Editor.BlockManager.insertMany(Z,q),Z.map((X)=>new V0(X))}}get methods(){return{clear:()=>this.clear(),render:(J)=>this.render(J),renderFromHTML:(J)=>this.renderFromHTML(J),delete:(J)=>this.delete(J),swap:(J,q)=>this.swap(J,q),move:(J,q)=>this.move(J,q),getBlockByIndex:(J)=>this.getBlockByIndex(J),getById:(J)=>this.getById(J),getCurrentBlockIndex:()=>this.getCurrentBlockIndex(),getBlockIndex:(J)=>this.getBlockIndex(J),getBlocksCount:()=>this.getBlocksCount(),getBlockByElement:(J)=>this.getBlockByElement(J),stretchBlock:(J,q=!0)=>this.stretchBlock(J,q),insertNewBlock:()=>this.insertNewBlock(),insert:this.insert,insertMany:this.insertMany,update:this.update,composeBlockData:this.composeBlockData,convert:this.convert}}getBlocksCount(){return this.Editor.BlockManager.blocks.length}getCurrentBlockIndex(){return this.Editor.BlockManager.currentBlockIndex}getBlockIndex(J){let q=this.Editor.BlockManager.getBlockById(J);if(!q){e("There is no block with id `"+J+"`","warn");return}return this.Editor.BlockManager.getBlockIndex(q)}getBlockByIndex(J){let q=this.Editor.BlockManager.getBlockByIndex(J);if(q===void 0){e("There is no block at index `"+J+"`","warn");return}return new V0(q)}getById(J){let q=this.Editor.BlockManager.getBlockById(J);return q===void 0?(e("There is no block with id `"+J+"`","warn"),null):new V0(q)}getBlockByElement(J){let q=this.Editor.BlockManager.getBlock(J);if(q===void 0){e("There is no block corresponding to element `"+J+"`","warn");return}return new V0(q)}swap(J,q){y("`blocks.swap()` method is deprecated and will be removed in the next major release. Use `block.move()` method instead","info"),this.Editor.BlockManager.swap(J,q)}move(J,q){this.Editor.BlockManager.move(J,q)}delete(J=this.Editor.BlockManager.currentBlockIndex){try{let q=this.Editor.BlockManager.getBlockByIndex(J);this.Editor.BlockManager.removeBlock(q)}catch(q){e(q,"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()}async clear(){await this.Editor.BlockManager.clear(!0),this.Editor.InlineToolbar.close()}async render(J){if(J===void 0||J.blocks===void 0)throw Error("Incorrect data passed to the render() method");this.Editor.ModificationsObserver.disable(),await this.Editor.BlockManager.clear(),await this.Editor.Renderer.render(J.blocks),this.Editor.ModificationsObserver.enable()}async renderFromHTML(J){return await this.Editor.BlockManager.clear(),this.Editor.Paste.processText(J,!0)}stretchBlock(J,q=!0){V2(!0,"blocks.stretchBlock()","BlockAPI");let Z=this.Editor.BlockManager.getBlockByIndex(J);Z&&(Z.stretched=q)}insertNewBlock(){y("Method blocks.insertNewBlock() is deprecated and it will be removed in the next major release. Use blocks.insert() instead.","warn"),this.insert()}validateIndex(J){if(typeof J!="number")throw Error("Index should be a number");if(J<0)throw Error("Index should be greater than or equal to 0");if(J===null)throw Error("Index should be greater than or equal to 0")}}function F8(J,q){return typeof J=="number"?q.BlockManager.getBlockByIndex(J):typeof J=="string"?q.BlockManager.getBlockById(J):q.BlockManager.getBlockById(J.id)}class L5 extends B{constructor(){super(...arguments),this.setToFirstBlock=(J=this.Editor.Caret.positions.DEFAULT,q=0)=>this.Editor.BlockManager.firstBlock?(this.Editor.Caret.setToBlock(this.Editor.BlockManager.firstBlock,J,q),!0):!1,this.setToLastBlock=(J=this.Editor.Caret.positions.DEFAULT,q=0)=>this.Editor.BlockManager.lastBlock?(this.Editor.Caret.setToBlock(this.Editor.BlockManager.lastBlock,J,q),!0):!1,this.setToPreviousBlock=(J=this.Editor.Caret.positions.DEFAULT,q=0)=>this.Editor.BlockManager.previousBlock?(this.Editor.Caret.setToBlock(this.Editor.BlockManager.previousBlock,J,q),!0):!1,this.setToNextBlock=(J=this.Editor.Caret.positions.DEFAULT,q=0)=>this.Editor.BlockManager.nextBlock?(this.Editor.Caret.setToBlock(this.Editor.BlockManager.nextBlock,J,q),!0):!1,this.setToBlock=(J,q=this.Editor.Caret.positions.DEFAULT,Z=0)=>{let X=F8(J,this.Editor);return X===void 0?!1:(this.Editor.Caret.setToBlock(X,q,Z),!0)},this.focus=(J=!1)=>J?this.setToLastBlock(this.Editor.Caret.positions.END):this.setToFirstBlock(this.Editor.Caret.positions.START)}get methods(){return{setToFirstBlock:this.setToFirstBlock,setToLastBlock:this.setToLastBlock,setToPreviousBlock:this.setToPreviousBlock,setToNextBlock:this.setToNextBlock,setToBlock:this.setToBlock,focus:this.focus}}}class A5 extends B{get methods(){return{emit:(J,q)=>this.emit(J,q),off:(J,q)=>this.off(J,q),on:(J,q)=>this.on(J,q)}}on(J,q){this.eventsDispatcher.on(J,q)}emit(J,q){this.eventsDispatcher.emit(J,q)}off(J,q){this.eventsDispatcher.off(J,q)}}class F2 extends B{static getNamespace(J,q){return q?`blockTunes.${J}`:`tools.${J}`}get methods(){return{t:()=>{e("I18n.t() method can be accessed only from Tools","warn")}}}getMethodsForTool(J,q){return Object.assign(this.methods,{t:(Z)=>r.t(F2.getNamespace(J,q),Z)})}}class N5 extends B{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}}getMethodsForTool(J,q){return Object.assign(this.methods,{i18n:this.Editor.I18nAPI.getMethodsForTool(J,q)})}}class P5 extends B{get methods(){return{close:()=>this.close(),open:()=>this.open()}}open(){this.Editor.InlineToolbar.tryToShow()}close(){this.Editor.InlineToolbar.close()}}class M5 extends B{get methods(){return{on:(J,q,Z,X)=>this.on(J,q,Z,X),off:(J,q,Z,X)=>this.off(J,q,Z,X),offById:(J)=>this.offById(J)}}on(J,q,Z,X){return this.listeners.on(J,q,Z,X)}off(J,q,Z,X){this.listeners.off(J,q,Z,X)}offById(J){this.listeners.offById(J)}}var R5={exports:{}};(function(J,q){(function(Z,X){J.exports=X()})(window,function(){return function(Z){var X={};function K(Q){if(X[Q])return X[Q].exports;var V=X[Q]={i:Q,l:!1,exports:{}};return Z[Q].call(V.exports,V,V.exports,K),V.l=!0,V.exports}return K.m=Z,K.c=X,K.d=function(Q,V,G){K.o(Q,V)||Object.defineProperty(Q,V,{enumerable:!0,get:G})},K.r=function(Q){typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(Q,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(Q,"__esModule",{value:!0})},K.t=function(Q,V){if(1&V&&(Q=K(Q)),8&V||4&V&&typeof Q=="object"&&Q&&Q.__esModule)return Q;var G=Object.create(null);if(K.r(G),Object.defineProperty(G,"default",{enumerable:!0,value:Q}),2&V&&typeof Q!="string")for(var Y in Q)K.d(G,Y,function(U){return Q[U]}.bind(null,Y));return G},K.n=function(Q){var V=Q&&Q.__esModule?function(){return Q.default}:function(){return Q};return K.d(V,"a",V),V},K.o=function(Q,V){return Object.prototype.hasOwnProperty.call(Q,V)},K.p="/",K(K.s=0)}([function(Z,X,K){K(1),Z.exports=function(){/*! + * Codex JavaScript Notification module + * https://github.com/codex-team/js-notifier + */var Q=K(6),V="cdx-notify--bounce-in",G=null;return{show:function(Y){if(Y.message){(function(){if(G)return!0;G=Q.getWrapper(),document.body.appendChild(G)})();var U=null,H=Y.time||8000;switch(Y.type){case"confirm":U=Q.confirm(Y);break;case"prompt":U=Q.prompt(Y);break;default:U=Q.alert(Y),window.setTimeout(function(){U.remove()},H)}G.appendChild(U),U.classList.add(V)}}}}()},function(Z,X,K){var Q=K(2);typeof Q=="string"&&(Q=[[Z.i,Q,""]]);var V={hmr:!0,transform:void 0,insertInto:void 0};K(4)(Q,V),Q.locals&&(Z.exports=Q.locals)},function(Z,X,K){(Z.exports=K(3)(!1)).push([Z.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(Z,X){Z.exports=function(K){var Q=[];return Q.toString=function(){return this.map(function(V){var G=function(Y,U){var H=Y[1]||"",$=Y[3];if(!$)return H;if(U&&typeof btoa=="function"){var D=(W=$,"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(W))))+" */"),A=$.sources.map(function(z){return"/*# sourceURL="+$.sourceRoot+z+" */"});return[H].concat(A).concat([D]).join(` +`)}var W;return[H].join(` +`)}(V,K);return V[2]?"@media "+V[2]+"{"+G+"}":G}).join("")},Q.i=function(V,G){typeof V=="string"&&(V=[[null,V,""]]);for(var Y={},U=0;U<this.length;U++){var H=this[U][0];typeof H=="number"&&(Y[H]=!0)}for(U=0;U<V.length;U++){var $=V[U];typeof $[0]=="number"&&Y[$[0]]||(G&&!$[2]?$[2]=G:G&&($[2]="("+$[2]+") and ("+G+")"),Q.push($))}},Q}},function(Z,X,K){var Q,V,G={},Y=(Q=function(){return window&&document&&document.all&&!window.atob},function(){return V===void 0&&(V=Q.apply(this,arguments)),V}),U=function(R){var M={};return function(S){if(typeof S=="function")return S();if(M[S]===void 0){var C=function(k){return document.querySelector(k)}.call(this,S);if(window.HTMLIFrameElement&&C instanceof window.HTMLIFrameElement)try{C=C.contentDocument.head}catch{C=null}M[S]=C}return M[S]}}(),H=null,$=0,D=[],A=K(5);function W(R,M){for(var S=0;S<R.length;S++){var C=R[S],k=G[C.id];if(k){k.refs++;for(var w=0;w<k.parts.length;w++)k.parts[w](C.parts[w]);for(;w<C.parts.length;w++)k.parts.push(j(C.parts[w],M))}else{var d=[];for(w=0;w<C.parts.length;w++)d.push(j(C.parts[w],M));G[C.id]={id:C.id,refs:1,parts:d}}}}function z(R,M){for(var S=[],C={},k=0;k<R.length;k++){var w=R[k],d=M.base?w[0]+M.base:w[0],E={css:w[1],media:w[2],sourceMap:w[3]};C[d]?C[d].parts.push(E):S.push(C[d]={id:d,parts:[E]})}return S}function L(R,M){var S=U(R.insertInto);if(!S)throw Error("Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid.");var C=D[D.length-1];if(R.insertAt==="top")C?C.nextSibling?S.insertBefore(M,C.nextSibling):S.appendChild(M):S.insertBefore(M,S.firstChild),D.push(M);else if(R.insertAt==="bottom")S.appendChild(M);else{if(typeof R.insertAt!="object"||!R.insertAt.before)throw 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 k=U(R.insertInto+" "+R.insertAt.before);S.insertBefore(M,k)}}function N(R){if(R.parentNode===null)return!1;R.parentNode.removeChild(R);var M=D.indexOf(R);M>=0&&D.splice(M,1)}function P(R){var M=document.createElement("style");return R.attrs.type===void 0&&(R.attrs.type="text/css"),_(M,R.attrs),L(R,M),M}function _(R,M){Object.keys(M).forEach(function(S){R.setAttribute(S,M[S])})}function j(R,M){var S,C,k,w;if(M.transform&&R.css){if(!(w=M.transform(R.css)))return function(){};R.css=w}if(M.singleton){var d=$++;S=H||(H=P(M)),C=b.bind(null,S,d,!1),k=b.bind(null,S,d,!0)}else R.sourceMap&&typeof URL=="function"&&typeof URL.createObjectURL=="function"&&typeof URL.revokeObjectURL=="function"&&typeof Blob=="function"&&typeof btoa=="function"?(S=function(E){var o=document.createElement("link");return E.attrs.type===void 0&&(E.attrs.type="text/css"),E.attrs.rel="stylesheet",_(o,E.attrs),L(E,o),o}(M),C=function(E,o,m0){var{css:L0,sourceMap:o1}=m0,u7=o.convertToAbsoluteUrls===void 0&&o1;(o.convertToAbsoluteUrls||u7)&&(L0=A(L0)),o1&&(L0+=` +/*# sourceMappingURL=data:application/json;base64,`+btoa(unescape(encodeURIComponent(JSON.stringify(o1))))+" */");var l7=new Blob([L0],{type:"text/css"}),c4=E.href;E.href=URL.createObjectURL(l7),c4&&URL.revokeObjectURL(c4)}.bind(null,S,M),k=function(){N(S),S.href&&URL.revokeObjectURL(S.href)}):(S=P(M),C=function(E,o){var{css:m0,media:L0}=o;if(L0&&E.setAttribute("media",L0),E.styleSheet)E.styleSheet.cssText=m0;else{for(;E.firstChild;)E.removeChild(E.firstChild);E.appendChild(document.createTextNode(m0))}}.bind(null,S),k=function(){N(S)});return C(R),function(E){if(E){if(E.css===R.css&&E.media===R.media&&E.sourceMap===R.sourceMap)return;C(R=E)}else k()}}Z.exports=function(R,M){if(typeof DEBUG<"u"&&DEBUG&&typeof document!="object")throw 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=Y()),M.insertInto||(M.insertInto="head"),M.insertAt||(M.insertAt="bottom");var S=z(R,M);return W(S,M),function(C){for(var k=[],w=0;w<S.length;w++){var d=S[w];(E=G[d.id]).refs--,k.push(E)}for(C&&W(z(C,M),M),w=0;w<k.length;w++){var E;if((E=k[w]).refs===0){for(var o=0;o<E.parts.length;o++)E.parts[o]();delete G[E.id]}}}};var O,T=(O=[],function(R,M){return O[R]=M,O.filter(Boolean).join(` +`)});function b(R,M,S,C){var k=S?"":C.css;if(R.styleSheet)R.styleSheet.cssText=T(M,k);else{var w=document.createTextNode(k),d=R.childNodes;d[M]&&R.removeChild(d[M]),d.length?R.insertBefore(w,d[M]):R.appendChild(w)}}},function(Z,X){Z.exports=function(K){var Q=typeof window<"u"&&window.location;if(!Q)throw Error("fixUrls requires window.location");if(!K||typeof K!="string")return K;var V=Q.protocol+"//"+Q.host,G=V+Q.pathname.replace(/\/[^\/]*$/,"/");return K.replace(/url\s*\(((?:[^)(]|\((?:[^)(]+|\([^)(]*\))*\))*)\)/gi,function(Y,U){var H,$=U.trim().replace(/^"(.*)"$/,function(D,A){return A}).replace(/^'(.*)'$/,function(D,A){return A});return/^(#|data:|http:\/\/|https:\/\/|file:\/\/\/|\s*$)/i.test($)?Y:(H=$.indexOf("//")===0?$:$.indexOf("/")===0?V+$:G+$.replace(/^\.\//,""),"url("+JSON.stringify(H)+")")})}},function(Z,X,K){var Q,V,G,Y,U,H,$,D,A;Z.exports=(Q="cdx-notifies",V="cdx-notify",G="cdx-notify__cross",Y="cdx-notify__button--confirm",U="cdx-notify__button--cancel",H="cdx-notify__input",$="cdx-notify__button",D="cdx-notify__btns-wrapper",{alert:A=function(W){var z=document.createElement("DIV"),L=document.createElement("DIV"),N=W.message,P=W.style;return z.classList.add(V),P&&z.classList.add(V+"--"+P),z.innerHTML=N,L.classList.add(G),L.addEventListener("click",z.remove.bind(z)),z.appendChild(L),z},confirm:function(W){var z=A(W),L=document.createElement("div"),N=document.createElement("button"),P=document.createElement("button"),_=z.querySelector("."+G),j=W.cancelHandler,O=W.okHandler;return L.classList.add(D),N.innerHTML=W.okText||"Confirm",P.innerHTML=W.cancelText||"Cancel",N.classList.add($),P.classList.add($),N.classList.add(Y),P.classList.add(U),j&&typeof j=="function"&&(P.addEventListener("click",j),_.addEventListener("click",j)),O&&typeof O=="function"&&N.addEventListener("click",O),N.addEventListener("click",z.remove.bind(z)),P.addEventListener("click",z.remove.bind(z)),L.appendChild(N),L.appendChild(P),z.appendChild(L),z},prompt:function(W){var z=A(W),L=document.createElement("div"),N=document.createElement("button"),P=document.createElement("input"),_=z.querySelector("."+G),j=W.cancelHandler,O=W.okHandler;return L.classList.add(D),N.innerHTML=W.okText||"Ok",N.classList.add($),N.classList.add(Y),P.classList.add(H),W.placeholder&&P.setAttribute("placeholder",W.placeholder),W.default&&(P.value=W.default),W.inputType&&(P.type=W.inputType),j&&typeof j=="function"&&_.addEventListener("click",j),O&&typeof O=="function"&&N.addEventListener("click",function(){O(P.value)}),N.addEventListener("click",z.remove.bind(z)),L.appendChild(P),L.appendChild(N),z.appendChild(L),z},getWrapper:function(){var W=document.createElement("DIV");return W.classList.add(Q),W}})}])})})(R5);var W8=R5.exports,L8=H1(W8);class O5{show(J){L8.show(J)}}class j5 extends B{constructor({config:J,eventsDispatcher:q}){super({config:J,eventsDispatcher:q}),this.notifier=new O5}get methods(){return{show:(J)=>this.show(J)}}show(J){return this.notifier.show(J)}}class I5 extends B{get methods(){let J=()=>this.isEnabled;return{toggle:(q)=>this.toggle(q),get isEnabled(){return J()}}}toggle(J){return this.Editor.ReadOnly.toggle(J)}get isEnabled(){return this.Editor.ReadOnly.isEnabled}}var _5={exports:{}};(function(J,q){(function(Z,X){J.exports=X()})(d0,function(){function Z($){var D=$.tags,A=Object.keys(D),W=A.map(function(z){return typeof D[z]}).every(function(z){return z==="object"||z==="boolean"||z==="function"});if(!W)throw Error("The configuration was invalid");this.config=$}var X=["P","LI","TD","TH","DIV","H1","H2","H3","H4","H5","H6","PRE"];function K($){return X.indexOf($.nodeName)!==-1}var Q=["A","B","STRONG","I","EM","SUB","SUP","U","STRIKE"];function V($){return Q.indexOf($.nodeName)!==-1}Z.prototype.clean=function($){let D=document.implementation.createHTMLDocument(),A=D.createElement("div");return A.innerHTML=$,this._sanitize(D,A),A.innerHTML},Z.prototype._sanitize=function($,D){var A=G($,D),W=A.firstChild();if(W)do{if(W.nodeType===Node.TEXT_NODE)if(W.data.trim()===""&&(W.previousElementSibling&&K(W.previousElementSibling)||W.nextElementSibling&&K(W.nextElementSibling))){D.removeChild(W),this._sanitize($,D);break}else continue;if(W.nodeType===Node.COMMENT_NODE){D.removeChild(W),this._sanitize($,D);break}var z=V(W),L;z&&(L=Array.prototype.some.call(W.childNodes,K));var N=!!D.parentNode,P=K(D)&&K(W)&&N,_=W.nodeName.toLowerCase(),j=Y(this.config,_,W),O=z&&L;if(O||U(W,j)||!this.config.keepNestedBlockElements&&P){if(!(W.nodeName==="SCRIPT"||W.nodeName==="STYLE"))for(;W.childNodes.length>0;)D.insertBefore(W.childNodes[0],W);D.removeChild(W),this._sanitize($,D);break}for(var T=0;T<W.attributes.length;T+=1){var b=W.attributes[T];H(b,j,W)&&(W.removeAttribute(b.name),T=T-1)}this._sanitize($,W)}while(W=A.nextSibling())};function G($,D){return $.createTreeWalker(D,NodeFilter.SHOW_TEXT|NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_COMMENT,null,!1)}function Y($,D,A){return typeof $.tags[D]=="function"?$.tags[D](A):$.tags[D]}function U($,D){return typeof D>"u"?!0:typeof D=="boolean"?!D:!1}function H($,D,A){var W=$.name.toLowerCase();return D===!0?!1:typeof D[W]=="function"?!D[W]($.value,A):typeof D[W]>"u"||D[W]===!1?!0:typeof D[W]=="string"?D[W]!==$.value:!1}return Z})})(_5);var A8=_5.exports,N8=H1(A8);function W2(J,q){return J.map((Z)=>{let X=m(q)?q(Z.tool):q;return J0(X)||(Z.data=L2(Z.data,X)),Z})}function Z0(J,q={}){return new N8({tags:q}).clean(J)}function L2(J,q){return Array.isArray(J)?P8(J,q):u(J)?M8(J,q):Y0(J)?R8(J,q):J}function P8(J,q){return J.map((Z)=>L2(Z,q))}function M8(J,q){let Z={};for(let X in J){if(!Object.prototype.hasOwnProperty.call(J,X))continue;let K=J[X],Q=O8(q[X])?q[X]:q;Z[X]=L2(K,Q)}return Z}function R8(J,q){return u(q)?Z0(J,q):q===!1?Z0(J,{}):J}function O8(J){return u(J)||o7(J)||m(J)}class S5 extends B{get methods(){return{clean:(J,q)=>this.clean(J,q)}}clean(J,q){return Z0(J,q)}}class x5 extends B{get methods(){return{save:()=>this.save()}}save(){return this.Editor.ReadOnly.isEnabled?(e("Editor's content can not be saved in read-only mode","warn"),Promise.reject(Error("Editor's content can not be saved in read-only mode"))):this.Editor.Saver.save()}}class T5 extends B{constructor(){super(...arguments),this.selectionUtils=new I}get methods(){return{findParentTag:(J,q)=>this.findParentTag(J,q),expandToTag:(J)=>this.expandToTag(J),save:()=>this.selectionUtils.save(),restore:()=>this.selectionUtils.restore(),setFakeBackground:()=>this.selectionUtils.setFakeBackground(),removeFakeBackground:()=>this.selectionUtils.removeFakeBackground()}}findParentTag(J,q){return this.selectionUtils.findParentTag(J,q)}expandToTag(J){this.selectionUtils.expandToTag(J)}}class B5 extends B{get methods(){return{getBlockTools:()=>Array.from(this.Editor.Tools.blockTools.values())}}}class C5 extends B{get classes(){return{block:"cdx-block",inlineToolButton:"ce-inline-tool",inlineToolButtonActive:"ce-inline-tool--active",input:"cdx-input",loader:"cdx-loader",button:"cdx-button",settingsButton:"cdx-settings-button",settingsButtonActive:"cdx-settings-button--active"}}}class E5 extends B{get methods(){return{close:()=>this.close(),open:()=>this.open(),toggleBlockSettings:(J)=>this.toggleBlockSettings(J),toggleToolbox:(J)=>this.toggleToolbox(J)}}open(){this.Editor.Toolbar.moveAndOpen()}close(){this.Editor.Toolbar.close()}toggleBlockSettings(J){if(this.Editor.BlockManager.currentBlockIndex===-1){e("Could't toggle the Toolbar because there is no block selected ","warn");return}J??!this.Editor.BlockSettings.opened?(this.Editor.Toolbar.moveAndOpen(),this.Editor.BlockSettings.open()):this.Editor.BlockSettings.close()}toggleToolbox(J){if(this.Editor.BlockManager.currentBlockIndex===-1){e("Could't toggle the Toolbox because there is no block selected ","warn");return}J??!this.Editor.Toolbar.toolbox.opened?(this.Editor.Toolbar.moveAndOpen(),this.Editor.Toolbar.toolbox.open()):this.Editor.Toolbar.toolbox.close()}}var w5={exports:{}};/*! + * CodeX.Tooltips + * + * @version 1.0.5 + * + * @licence MIT + * @author CodeX <https://codex.so> + * + * + */(function(J,q){(function(Z,X){J.exports=X()})(window,function(){return function(Z){var X={};function K(Q){if(X[Q])return X[Q].exports;var V=X[Q]={i:Q,l:!1,exports:{}};return Z[Q].call(V.exports,V,V.exports,K),V.l=!0,V.exports}return K.m=Z,K.c=X,K.d=function(Q,V,G){K.o(Q,V)||Object.defineProperty(Q,V,{enumerable:!0,get:G})},K.r=function(Q){typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(Q,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(Q,"__esModule",{value:!0})},K.t=function(Q,V){if(1&V&&(Q=K(Q)),8&V||4&V&&typeof Q=="object"&&Q&&Q.__esModule)return Q;var G=Object.create(null);if(K.r(G),Object.defineProperty(G,"default",{enumerable:!0,value:Q}),2&V&&typeof Q!="string")for(var Y in Q)K.d(G,Y,function(U){return Q[U]}.bind(null,Y));return G},K.n=function(Q){var V=Q&&Q.__esModule?function(){return Q.default}:function(){return Q};return K.d(V,"a",V),V},K.o=function(Q,V){return Object.prototype.hasOwnProperty.call(Q,V)},K.p="",K(K.s=0)}([function(Z,X,K){Z.exports=K(1)},function(Z,X,K){K.r(X),K.d(X,"default",function(){return Q});class Q{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(V,G,Y){this.nodes.wrapper||this.prepare(),this.hidingTimeout&&clearTimeout(this.hidingTimeout);let U=Object.assign({placement:"bottom",marginTop:0,marginLeft:0,marginRight:0,marginBottom:0,delay:70,hidingDelay:0},Y);if(U.hidingDelay&&(this.hidingDelay=U.hidingDelay),this.nodes.content.innerHTML="",typeof G=="string")this.nodes.content.appendChild(document.createTextNode(G));else{if(!(G instanceof Node))throw Error("[CodeX Tooltip] Wrong type of «content» passed. It should be an instance of Node or String. But "+typeof G+" given.");this.nodes.content.appendChild(G)}switch(this.nodes.wrapper.classList.remove(...Object.values(this.CSS.placement)),U.placement){case"top":this.placeTop(V,U);break;case"left":this.placeLeft(V,U);break;case"right":this.placeRight(V,U);break;case"bottom":default:this.placeBottom(V,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(V=!1){if(this.hidingDelay&&!V)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(V,G,Y){V.addEventListener("mouseenter",()=>{this.show(V,G,Y)}),V.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(){if(document.getElementById("codex-tooltips-style"))return;let G=K(2),Y=this.make("style",null,{textContent:G.toString(),id:"codex-tooltips-style"});this.prepend(document.head,Y)}placeBottom(V,G){let Y=V.getBoundingClientRect(),U=Y.left+V.clientWidth/2-this.nodes.wrapper.offsetWidth/2,H=Y.bottom+window.pageYOffset+this.offsetTop+G.marginTop;this.applyPlacement("bottom",U,H)}placeTop(V,G){let Y=V.getBoundingClientRect(),U=Y.left+V.clientWidth/2-this.nodes.wrapper.offsetWidth/2,H=Y.top+window.pageYOffset-this.nodes.wrapper.clientHeight-this.offsetTop;this.applyPlacement("top",U,H)}placeLeft(V,G){let Y=V.getBoundingClientRect(),U=Y.left-this.nodes.wrapper.offsetWidth-this.offsetLeft-G.marginLeft,H=Y.top+window.pageYOffset+V.clientHeight/2-this.nodes.wrapper.offsetHeight/2;this.applyPlacement("left",U,H)}placeRight(V,G){let Y=V.getBoundingClientRect(),U=Y.right+this.offsetRight+G.marginRight,H=Y.top+window.pageYOffset+V.clientHeight/2-this.nodes.wrapper.offsetHeight/2;this.applyPlacement("right",U,H)}applyPlacement(V,G,Y){this.nodes.wrapper.classList.add(this.CSS.placement[V]),this.nodes.wrapper.style.left=G+"px",this.nodes.wrapper.style.top=Y+"px"}make(V,G=null,Y={}){let U=document.createElement(V);Array.isArray(G)?U.classList.add(...G):G&&U.classList.add(G);for(let H in Y)Y.hasOwnProperty(H)&&(U[H]=Y[H]);return U}append(V,G){Array.isArray(G)?G.forEach((Y)=>V.appendChild(Y)):V.appendChild(G)}prepend(V,G){Array.isArray(G)?(G=G.reverse()).forEach((Y)=>V.prepend(Y)):V.prepend(G)}}},function(Z,X){Z.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,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path d="M10.71 0h2.58c3.02 0 4.64.42 6.1 1.2a8.18 8.18 0 013.4 3.4C23.6 6.07 24 7.7 24 10.71v2.58c0 3.02-.42 4.64-1.2 6.1a8.18 8.18 0 01-3.4 3.4c-1.47.8-3.1 1.21-6.11 1.21H10.7c-3.02 0-4.64-.42-6.1-1.2a8.18 8.18 0 01-3.4-3.4C.4 17.93 0 16.3 0 13.29V10.7c0-3.02.42-4.64 1.2-6.1a8.18 8.18 0 013.4-3.4C6.07.4 7.7 0 10.71 0z"/></svg>') 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})})(w5);var j8=w5.exports,I8=H1(j8),q0=null;function A2(){q0||(q0=new I8)}function _8(J,q,Z){A2(),q0==null||q0.show(J,q,Z)}function Q1(J=!1){A2(),q0==null||q0.hide(J)}function V1(J,q,Z){A2(),q0==null||q0.onHover(J,q,Z)}function S8(){q0==null||q0.destroy(),q0=null}class y5 extends B{constructor({config:J,eventsDispatcher:q}){super({config:J,eventsDispatcher:q})}get methods(){return{show:(J,q,Z)=>this.show(J,q,Z),hide:()=>this.hide(),onHover:(J,q,Z)=>this.onHover(J,q,Z)}}show(J,q,Z){_8(J,q,Z)}hide(){Q1()}onHover(J,q,Z){V1(J,q,Z)}}class v5 extends B{get methods(){return{nodes:this.editorNodes}}get editorNodes(){return{wrapper:this.Editor.UI.nodes.wrapper,redactor:this.Editor.UI.nodes.redactor}}}function k5(J,q){let Z={};return Object.entries(J).forEach(([X,K])=>{if(u(K)){let Q=q?`${q}.${X}`:X;Object.values(K).every((V)=>Y0(V))?Z[X]=Q:Z[X]=k5(K,Q);return}Z[X]=K}),Z}var t=k5(Y5);function x8(J,q){let Z={};return Object.keys(J).forEach((X)=>{let K=q[X];K!==void 0?Z[K]=J[X]:Z[X]=J[X]}),Z}var g5=class J{constructor(q,Z){this.cursor=-1,this.items=[],this.items=q||[],this.focusedCssClass=Z}get currentItem(){return this.cursor===-1?null:this.items[this.cursor]}setCursor(q){q<this.items.length&&q>=-1&&(this.dropCursor(),this.cursor=q,this.items[this.cursor].classList.add(this.focusedCssClass))}setItems(q){this.items=q}next(){this.cursor=this.leafNodesAndReturnIndex(J.directions.RIGHT)}previous(){this.cursor=this.leafNodesAndReturnIndex(J.directions.LEFT)}dropCursor(){this.cursor!==-1&&(this.items[this.cursor].classList.remove(this.focusedCssClass),this.cursor=-1)}leafNodesAndReturnIndex(q){if(this.items.length===0)return this.cursor;let Z=this.cursor;return Z===-1?Z=q===J.directions.RIGHT?-1:0:this.items[Z].classList.remove(this.focusedCssClass),q===J.directions.RIGHT?Z=(Z+1)%this.items.length:Z=(this.items.length+Z-1)%this.items.length,F.canSetCaret(this.items[Z])&&Z1(()=>I.setCursor(this.items[Z]),50)(),this.items[Z].classList.add(this.focusedCssClass),Z}};g5.directions={RIGHT:"right",LEFT:"left"};var b0=g5;class P0{constructor(J){this.iterator=null,this.activated=!1,this.flipCallbacks=[],this.onKeyDown=(q)=>{if(!(!this.isEventReadyForHandling(q)||q.shiftKey===!0))switch(P0.usedKeys.includes(q.keyCode)&&q.preventDefault(),q.keyCode){case x.TAB:this.handleTabPress(q);break;case x.LEFT:case x.UP:this.flipLeft();break;case x.RIGHT:case x.DOWN:this.flipRight();break;case x.ENTER:this.handleEnterPress(q);break}},this.iterator=new b0(J.items,J.focusedItemClass),this.activateCallback=J.activateCallback,this.allowedKeys=J.allowedKeys||P0.usedKeys}get isActivated(){return this.activated}static get usedKeys(){return[x.TAB,x.LEFT,x.RIGHT,x.ENTER,x.UP,x.DOWN]}activate(J,q){this.activated=!0,J&&this.iterator.setItems(J),q!==void 0&&this.iterator.setCursor(q),document.addEventListener("keydown",this.onKeyDown,!0)}deactivate(){this.activated=!1,this.dropCursor(),document.removeEventListener("keydown",this.onKeyDown)}focusFirst(){this.dropCursor(),this.flipRight()}flipLeft(){this.iterator.previous(),this.flipCallback()}flipRight(){this.iterator.next(),this.flipCallback()}hasFocus(){return!!this.iterator.currentItem}onFlip(J){this.flipCallbacks.push(J)}removeOnFlip(J){this.flipCallbacks=this.flipCallbacks.filter((q)=>q!==J)}dropCursor(){this.iterator.dropCursor()}isEventReadyForHandling(J){return this.activated&&this.allowedKeys.includes(J.keyCode)}handleTabPress(J){switch(J.shiftKey?b0.directions.LEFT:b0.directions.RIGHT){case b0.directions.RIGHT:this.flipRight();break;case b0.directions.LEFT:this.flipLeft();break}}handleEnterPress(J){this.activated&&(this.iterator.currentItem&&(J.stopPropagation(),J.preventDefault(),this.iterator.currentItem.click()),m(this.activateCallback)&&this.activateCallback(this.iterator.currentItem))}flipCallback(){this.iterator.currentItem&&this.iterator.currentItem.scrollIntoViewIfNeeded(),this.flipCallbacks.forEach((J)=>J())}}var T8='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M9 12L9 7.1C9 7.04477 9.04477 7 9.1 7H10.4C11.5 7 14 7.1 14 9.5C14 9.5 14 12 11 12M9 12V16.8C9 16.9105 9.08954 17 9.2 17H12.5C14 17 15 16 15 14.5C15 11.7046 11 12 11 12M9 12H11"/></svg>',B8='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M7 10L11.8586 14.8586C11.9367 14.9367 12.0633 14.9367 12.1414 14.8586L17 10"/></svg>',C8='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M14.5 17.5L9.64142 12.6414C9.56331 12.5633 9.56331 12.4367 9.64142 12.3586L14.5 7.5"/></svg>',E8='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M9.58284 17.5L14.4414 12.6414C14.5195 12.5633 14.5195 12.4367 14.4414 12.3586L9.58284 7.5"/></svg>',w8='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M7 15L11.8586 10.1414C11.9367 10.0633 12.0633 10.0633 12.1414 10.1414L17 15"/></svg>',y8='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M8 8L12 12M12 12L16 16M12 12L16 8M12 12L8 16"/></svg>',v8='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><circle cx="12" cy="12" r="4" stroke="currentColor" stroke-width="2"/></svg>',k8='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M13.34 10C12.4223 12.7337 11 17 11 17"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M14.21 7H14.2"/></svg>',i4='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M7.69998 12.6L7.67896 12.62C6.53993 13.7048 6.52012 15.5155 7.63516 16.625V16.625C8.72293 17.7073 10.4799 17.7102 11.5712 16.6314L13.0263 15.193C14.0703 14.1609 14.2141 12.525 13.3662 11.3266L13.22 11.12"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M16.22 11.12L16.3564 10.9805C17.2895 10.0265 17.3478 8.5207 16.4914 7.49733V7.49733C15.5691 6.39509 13.9269 6.25143 12.8271 7.17675L11.3901 8.38588C10.0935 9.47674 9.95706 11.4241 11.0888 12.6852L11.12 12.72"/></svg>',g8='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2.6" d="M9.40999 7.29999H9.4"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2.6" d="M14.6 7.29999H14.59"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2.6" d="M9.30999 12H9.3"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2.6" d="M14.6 12H14.59"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2.6" d="M9.40999 16.7H9.4"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2.6" d="M14.6 16.7H14.59"/></svg>',m8='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M12 7V12M12 17V12M17 12H12M12 12H7"/></svg>',m5='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M11.5 17.5L5 11M5 11V15.5M5 11H9.5"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M12.5 6.5L19 13M19 13V8.5M19 13H14.5"/></svg>',b8='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><circle cx="10.5" cy="10.5" r="5.5" stroke="currentColor" stroke-width="2"/><line x1="15.4142" x2="19" y1="15" y2="18.5858" stroke="currentColor" stroke-linecap="round" stroke-width="2"/></svg>',f8='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M15.7795 11.5C15.7795 11.5 16.053 11.1962 16.5497 10.6722C17.4442 9.72856 17.4701 8.2475 16.5781 7.30145V7.30145C15.6482 6.31522 14.0873 6.29227 13.1288 7.25073L11.8796 8.49999"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M8.24517 12.3883C8.24517 12.3883 7.97171 12.6922 7.47504 13.2161C6.58051 14.1598 6.55467 15.6408 7.44666 16.5869V16.5869C8.37653 17.5731 9.93744 17.5961 10.8959 16.6376L12.1452 15.3883"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M17.7802 15.1032L16.597 14.9422C16.0109 14.8624 15.4841 15.3059 15.4627 15.8969L15.4199 17.0818"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M6.39064 9.03238L7.58432 9.06668C8.17551 9.08366 8.6522 8.58665 8.61056 7.99669L8.5271 6.81397"/><line x1="12.1142" x2="11.7" y1="12.2" y2="11.7858" stroke="currentColor" stroke-linecap="round" stroke-width="2"/></svg>',p8='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><rect width="14" height="14" x="5" y="5" stroke="currentColor" stroke-width="2" rx="4"/><line x1="12" x2="12" y1="9" y2="12" stroke="currentColor" stroke-linecap="round" stroke-width="2"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M12 15.02V15.01"/></svg>',h8="__",d8="--";function z0(J){return(q,Z)=>[[J,q].filter((X)=>!!X).join(h8),Z].filter((X)=>!!X).join(d8)}var f0=z0("ce-hint"),p0={root:f0(),alignedStart:f0(null,"align-left"),alignedCenter:f0(null,"align-center"),title:f0("title"),description:f0("description")};class b5{constructor(J){this.nodes={root:F.make("div",[p0.root,J.alignment==="center"?p0.alignedCenter:p0.alignedStart]),title:F.make("div",p0.title,{textContent:J.title})},this.nodes.root.appendChild(this.nodes.title),J.description!==void 0&&(this.nodes.description=F.make("div",p0.description,{textContent:J.description}),this.nodes.root.appendChild(this.nodes.description))}getElement(){return this.nodes.root}}class $1{constructor(J){this.params=J}get name(){if(this.params!==void 0&&"name"in this.params)return this.params.name}destroy(){Q1()}onChildrenOpen(){var J;this.params!==void 0&&"children"in this.params&&typeof((J=this.params.children)==null?void 0:J.onOpen)=="function"&&this.params.children.onOpen()}onChildrenClose(){var J;this.params!==void 0&&"children"in this.params&&typeof((J=this.params.children)==null?void 0:J.onClose)=="function"&&this.params.children.onClose()}handleClick(){var J,q;this.params!==void 0&&"onActivate"in this.params&&((q=(J=this.params).onActivate)==null||q.call(J,this.params))}addHint(J,q){let Z=new b5(q);V1(J,Z.getElement(),{placement:q.position,hidingDelay:100})}get children(){var J;return this.params!==void 0&&"children"in this.params&&((J=this.params.children)==null?void 0:J.items)!==void 0?this.params.children.items:[]}get hasChildren(){return this.children.length>0}get isChildrenOpen(){var J;return this.params!==void 0&&"children"in this.params&&((J=this.params.children)==null?void 0:J.isOpen)===!0}get isChildrenFlippable(){var J;return!(this.params===void 0||!("children"in this.params)||((J=this.params.children)==null?void 0:J.isFlippable)===!1)}get isChildrenSearchable(){var J;return this.params!==void 0&&"children"in this.params&&((J=this.params.children)==null?void 0:J.searchable)===!0}get closeOnActivate(){return this.params!==void 0&&"closeOnActivate"in this.params&&this.params.closeOnActivate}get isActive(){return this.params===void 0||!("isActive"in this.params)?!1:typeof this.params.isActive=="function"?this.params.isActive():this.params.isActive===!0}}var n=z0("ce-popover-item"),f={container:n(),active:n(null,"active"),disabled:n(null,"disabled"),focused:n(null,"focused"),hidden:n(null,"hidden"),confirmationState:n(null,"confirmation"),noHover:n(null,"no-hover"),noFocus:n(null,"no-focus"),title:n("title"),secondaryTitle:n("secondary-title"),icon:n("icon"),iconTool:n("icon","tool"),iconChevronRight:n("icon","chevron-right"),wobbleAnimation:z0("wobble")()};class H0 extends $1{constructor(J,q){super(J),this.params=J,this.nodes={root:null,icon:null},this.confirmationState=null,this.removeSpecialFocusBehavior=()=>{var Z;(Z=this.nodes.root)==null||Z.classList.remove(f.noFocus)},this.removeSpecialHoverBehavior=()=>{var Z;(Z=this.nodes.root)==null||Z.classList.remove(f.noHover)},this.onErrorAnimationEnd=()=>{var Z,X;(Z=this.nodes.icon)==null||Z.classList.remove(f.wobbleAnimation),(X=this.nodes.icon)==null||X.removeEventListener("animationend",this.onErrorAnimationEnd)},this.nodes.root=this.make(J,q)}get isDisabled(){return this.params.isDisabled===!0}get toggle(){return this.params.toggle}get title(){return this.params.title}get isConfirmationStateEnabled(){return this.confirmationState!==null}get isFocused(){return this.nodes.root===null?!1:this.nodes.root.classList.contains(f.focused)}getElement(){return this.nodes.root}handleClick(){if(this.isConfirmationStateEnabled&&this.confirmationState!==null){this.activateOrEnableConfirmationMode(this.confirmationState);return}this.activateOrEnableConfirmationMode(this.params)}toggleActive(J){var q;(q=this.nodes.root)==null||q.classList.toggle(f.active,J)}toggleHidden(J){var q;(q=this.nodes.root)==null||q.classList.toggle(f.hidden,J)}reset(){this.isConfirmationStateEnabled&&this.disableConfirmationMode()}onFocus(){this.disableSpecialHoverAndFocusBehavior()}make(J,q){var Z,X;let K=(q==null?void 0:q.wrapperTag)||"div",Q=F.make(K,f.container,{type:K==="button"?"button":void 0});return J.name&&(Q.dataset.itemName=J.name),this.nodes.icon=F.make("div",[f.icon,f.iconTool],{innerHTML:J.icon||v8}),Q.appendChild(this.nodes.icon),J.title!==void 0&&Q.appendChild(F.make("div",f.title,{innerHTML:J.title||""})),J.secondaryLabel&&Q.appendChild(F.make("div",f.secondaryTitle,{textContent:J.secondaryLabel})),this.hasChildren&&Q.appendChild(F.make("div",[f.icon,f.iconChevronRight],{innerHTML:E8})),this.isActive&&Q.classList.add(f.active),J.isDisabled&&Q.classList.add(f.disabled),J.hint!==void 0&&((Z=q==null?void 0:q.hint)==null?void 0:Z.enabled)!==!1&&this.addHint(Q,{...J.hint,position:((X=q==null?void 0:q.hint)==null?void 0:X.position)||"right"}),Q}enableConfirmationMode(J){if(this.nodes.root===null)return;let q={...this.params,...J,confirmation:"confirmation"in J?J.confirmation:void 0},Z=this.make(q);this.nodes.root.innerHTML=Z.innerHTML,this.nodes.root.classList.add(f.confirmationState),this.confirmationState=J,this.enableSpecialHoverAndFocusBehavior()}disableConfirmationMode(){if(this.nodes.root===null)return;let J=this.make(this.params);this.nodes.root.innerHTML=J.innerHTML,this.nodes.root.classList.remove(f.confirmationState),this.confirmationState=null,this.disableSpecialHoverAndFocusBehavior()}enableSpecialHoverAndFocusBehavior(){var J,q,Z;(J=this.nodes.root)==null||J.classList.add(f.noHover),(q=this.nodes.root)==null||q.classList.add(f.noFocus),(Z=this.nodes.root)==null||Z.addEventListener("mouseleave",this.removeSpecialHoverBehavior,{once:!0})}disableSpecialHoverAndFocusBehavior(){var J;this.removeSpecialFocusBehavior(),this.removeSpecialHoverBehavior(),(J=this.nodes.root)==null||J.removeEventListener("mouseleave",this.removeSpecialHoverBehavior)}activateOrEnableConfirmationMode(J){var q;if(!("confirmation"in J)||J.confirmation===void 0)try{(q=J.onActivate)==null||q.call(J,J),this.disableConfirmationMode()}catch{this.animateError()}else this.enableConfirmationMode(J.confirmation)}animateError(){var J,q,Z;(J=this.nodes.icon)!=null&&J.classList.contains(f.wobbleAnimation)||((q=this.nodes.icon)==null||q.classList.add(f.wobbleAnimation),(Z=this.nodes.icon)==null||Z.addEventListener("animationend",this.onErrorAnimationEnd))}}var t1=z0("ce-popover-item-separator"),e1={container:t1(),line:t1("line"),hidden:t1(null,"hidden")};class N2 extends $1{constructor(){super(),this.nodes={root:F.make("div",e1.container),line:F.make("div",e1.line)},this.nodes.root.appendChild(this.nodes.line)}getElement(){return this.nodes.root}toggleHidden(J){var q;(q=this.nodes.root)==null||q.classList.toggle(e1.hidden,J)}}var X0=((J)=>(J.Closed="closed",J.ClosedOnActivate="closed-on-activate",J))(X0||{}),s=z0("ce-popover"),p={popover:s(),popoverContainer:s("container"),popoverOpenTop:s(null,"open-top"),popoverOpenLeft:s(null,"open-left"),popoverOpened:s(null,"opened"),search:s("search"),nothingFoundMessage:s("nothing-found-message"),nothingFoundMessageDisplayed:s("nothing-found-message","displayed"),items:s("items"),overlay:s("overlay"),overlayHidden:s("overlay","hidden"),popoverNested:s(null,"nested"),getPopoverNestedClass:(J)=>s(null,`nested-level-${J.toString()}`),popoverInline:s(null,"inline"),popoverHeader:s("header")},S0=((J)=>(J.NestingLevel="--nesting-level",J.PopoverHeight="--popover-height",J.InlinePopoverWidth="--inline-popover-width",J.TriggerItemLeft="--trigger-item-left",J.TriggerItemTop="--trigger-item-top",J))(S0||{}),o4=z0("ce-popover-item-html"),n4={root:o4(),hidden:o4(null,"hidden")};class x0 extends $1{constructor(J,q){var Z,X;super(J),this.nodes={root:F.make("div",n4.root)},this.nodes.root.appendChild(J.element),J.name&&(this.nodes.root.dataset.itemName=J.name),J.hint!==void 0&&((Z=q==null?void 0:q.hint)==null?void 0:Z.enabled)!==!1&&this.addHint(this.nodes.root,{...J.hint,position:((X=q==null?void 0:q.hint)==null?void 0:X.position)||"right"})}getElement(){return this.nodes.root}toggleHidden(J){var q;(q=this.nodes.root)==null||q.classList.toggle(n4.hidden,J)}getControls(){let J=this.nodes.root.querySelectorAll(`button, ${F.allInputsSelector}`);return Array.from(J)}}class P2 extends E0{constructor(J,q={}){super(),this.params=J,this.itemsRenderParams=q,this.listeners=new w0,this.messages={nothingFound:"Nothing found",search:"Search"},this.items=this.buildItems(J.items),J.messages&&(this.messages={...this.messages,...J.messages}),this.nodes={},this.nodes.popoverContainer=F.make("div",[p.popoverContainer]),this.nodes.nothingFoundMessage=F.make("div",[p.nothingFoundMessage],{textContent:this.messages.nothingFound}),this.nodes.popoverContainer.appendChild(this.nodes.nothingFoundMessage),this.nodes.items=F.make("div",[p.items]),this.items.forEach((Z)=>{let X=Z.getElement();X!==null&&this.nodes.items.appendChild(X)}),this.nodes.popoverContainer.appendChild(this.nodes.items),this.listeners.on(this.nodes.popoverContainer,"click",(Z)=>this.handleClick(Z)),this.nodes.popover=F.make("div",[p.popover,this.params.class]),this.nodes.popover.appendChild(this.nodes.popoverContainer)}get itemsDefault(){return this.items.filter((J)=>J instanceof H0)}getElement(){return this.nodes.popover}show(){this.nodes.popover.classList.add(p.popoverOpened),this.search!==void 0&&this.search.focus()}hide(){this.nodes.popover.classList.remove(p.popoverOpened),this.nodes.popover.classList.remove(p.popoverOpenTop),this.itemsDefault.forEach((J)=>J.reset()),this.search!==void 0&&this.search.clear(),this.emit(X0.Closed)}destroy(){var J;this.items.forEach((q)=>q.destroy()),this.nodes.popover.remove(),this.listeners.removeAll(),(J=this.search)==null||J.destroy()}activateItemByName(J){let q=this.items.find((Z)=>Z.name===J);this.handleItemClick(q)}buildItems(J){return J.map((q)=>{switch(q.type){case g.Separator:return new N2;case g.Html:return new x0(q,this.itemsRenderParams[g.Html]);default:return new H0(q,this.itemsRenderParams[g.Default])}})}getTargetItem(J){return this.items.filter((q)=>q instanceof H0||q instanceof x0).find((q)=>{let Z=q.getElement();return Z===null?!1:J.composedPath().includes(Z)})}handleItemClick(J){if(!(("isDisabled"in J)&&J.isDisabled)){if(J.hasChildren){this.showNestedItems(J),"handleClick"in J&&typeof J.handleClick=="function"&&J.handleClick();return}this.itemsDefault.filter((q)=>q!==J).forEach((q)=>q.reset()),"handleClick"in J&&typeof J.handleClick=="function"&&J.handleClick(),this.toggleItemActivenessIfNeeded(J),J.closeOnActivate&&(this.hide(),this.emit(X0.ClosedOnActivate))}}handleClick(J){let q=this.getTargetItem(J);q!==void 0&&this.handleItemClick(q)}toggleItemActivenessIfNeeded(J){if(J instanceof H0&&(J.toggle===!0&&J.toggleActive(),typeof J.toggle=="string")){let q=this.itemsDefault.filter((Z)=>Z.toggle===J.toggle);if(q.length===1){J.toggleActive();return}q.forEach((Z)=>{Z.toggleActive(Z===J)})}}}var G1=((J)=>(J.Search="search",J))(G1||{}),J2=z0("cdx-search-field"),q2={wrapper:J2(),icon:J2("icon"),input:J2("input")};class f5 extends E0{constructor({items:J,placeholder:q}){super(),this.listeners=new w0,this.items=J,this.wrapper=F.make("div",q2.wrapper);let Z=F.make("div",q2.icon,{innerHTML:b8});this.input=F.make("input",q2.input,{placeholder:q,tabIndex:-1}),this.wrapper.appendChild(Z),this.wrapper.appendChild(this.input),this.listeners.on(this.input,"input",()=>{this.searchQuery=this.input.value,this.emit(G1.Search,{query:this.searchQuery,items:this.foundItems})})}getElement(){return this.wrapper}focus(){this.input.focus()}clear(){this.input.value="",this.searchQuery="",this.emit(G1.Search,{query:"",items:this.foundItems})}destroy(){this.listeners.removeAll()}get foundItems(){return this.items.filter((J)=>this.checkItem(J))}checkItem(J){var q,Z;let X=((q=J.title)==null?void 0:q.toLowerCase())||"",K=(Z=this.searchQuery)==null?void 0:Z.toLowerCase();return K!==void 0?X.includes(K):!1}}var{defineProperty:c8,getOwnPropertyDescriptor:u8}=Object,l8=(J,q,Z,X)=>{for(var K=X>1?void 0:X?u8(q,Z):q,Q=J.length-1,V;Q>=0;Q--)(V=J[Q])&&(K=(X?V(q,Z,K):V(K))||K);return X&&K&&c8(q,Z,K),K},p5=class J extends P2{constructor(q,Z){super(q,Z),this.nestingLevel=0,this.nestedPopoverTriggerItem=null,this.previouslyHoveredItem=null,this.scopeElement=document.body,this.hide=()=>{var X;super.hide(),this.destroyNestedPopoverIfExists(),(X=this.flipper)==null||X.deactivate(),this.previouslyHoveredItem=null},this.onFlip=()=>{let X=this.itemsDefault.find((K)=>K.isFocused);X==null||X.onFocus()},this.onSearch=(X)=>{var K;let Q=X.query==="",V=X.items.length===0;this.items.forEach((Y)=>{let U=!1;Y instanceof H0?U=!X.items.includes(Y):(Y instanceof N2||Y instanceof x0)&&(U=V||!Q),Y.toggleHidden(U)}),this.toggleNothingFoundMessage(V);let G=X.query===""?this.flippableElements:X.items.map((Y)=>Y.getElement());(K=this.flipper)!=null&&K.isActivated&&(this.flipper.deactivate(),this.flipper.activate(G))},q.nestingLevel!==void 0&&(this.nestingLevel=q.nestingLevel),this.nestingLevel>0&&this.nodes.popover.classList.add(p.popoverNested),q.scopeElement!==void 0&&(this.scopeElement=q.scopeElement),this.nodes.popoverContainer!==null&&this.listeners.on(this.nodes.popoverContainer,"mouseover",(X)=>this.handleHover(X)),q.searchable&&this.addSearch(),q.flippable!==!1&&(this.flipper=new P0({items:this.flippableElements,focusedItemClass:f.focused,allowedKeys:[x.TAB,x.UP,x.DOWN,x.ENTER]}),this.flipper.onFlip(this.onFlip))}hasFocus(){return this.flipper===void 0?!1:this.flipper.hasFocus()}get scrollTop(){return this.nodes.items===null?0:this.nodes.items.scrollTop}get offsetTop(){return this.nodes.popoverContainer===null?0:this.nodes.popoverContainer.offsetTop}show(){var q;this.nodes.popover.style.setProperty(S0.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(),(q=this.flipper)==null||q.activate(this.flippableElements)}destroy(){this.hide(),super.destroy()}showNestedItems(q){this.nestedPopover!==null&&this.nestedPopover!==void 0||(this.nestedPopoverTriggerItem=q,this.showNestedPopoverForItem(q))}handleHover(q){let Z=this.getTargetItem(q);Z!==void 0&&this.previouslyHoveredItem!==Z&&(this.destroyNestedPopoverIfExists(),this.previouslyHoveredItem=Z,Z.hasChildren&&this.showNestedPopoverForItem(Z))}setTriggerItemPosition(q,Z){let X=Z.getElement(),K=(X?X.offsetTop:0)-this.scrollTop,Q=this.offsetTop+K;q.style.setProperty(S0.TriggerItemTop,Q+"px")}destroyNestedPopoverIfExists(){var q,Z;this.nestedPopover===void 0||this.nestedPopover===null||(this.nestedPopover.off(X0.ClosedOnActivate,this.hide),this.nestedPopover.hide(),this.nestedPopover.destroy(),this.nestedPopover.getElement().remove(),this.nestedPopover=null,(q=this.flipper)==null||q.activate(this.flippableElements),(Z=this.nestedPopoverTriggerItem)==null||Z.onChildrenClose())}showNestedPopoverForItem(q){var Z;this.nestedPopover=new J({searchable:q.isChildrenSearchable,items:q.children,nestingLevel:this.nestingLevel+1,flippable:q.isChildrenFlippable,messages:this.messages}),q.onChildrenOpen(),this.nestedPopover.on(X0.ClosedOnActivate,this.hide);let X=this.nestedPopover.getElement();return this.nodes.popover.appendChild(X),this.setTriggerItemPosition(X,q),X.style.setProperty(S0.NestingLevel,this.nestedPopover.nestingLevel.toString()),this.nestedPopover.show(),(Z=this.flipper)==null||Z.deactivate(),this.nestedPopover}get shouldOpenBottom(){if(this.nodes.popover===void 0||this.nodes.popover===null)return!1;let q=this.nodes.popoverContainer.getBoundingClientRect(),Z=this.scopeElement.getBoundingClientRect(),X=this.size.height,K=q.top+X,Q=q.top-X,V=Math.min(window.innerHeight,Z.bottom);return Q<Z.top||K<=V}get shouldOpenRight(){if(this.nodes.popover===void 0||this.nodes.popover===null)return!1;let q=this.nodes.popover.getBoundingClientRect(),Z=this.scopeElement.getBoundingClientRect(),X=this.size.width,K=q.right+X,Q=q.left-X,V=Math.min(window.innerWidth,Z.right);return Q<Z.left||K<=V}get size(){var q;let Z={height:0,width:0};if(this.nodes.popover===null)return Z;let X=this.nodes.popover.cloneNode(!0);X.style.visibility="hidden",X.style.position="absolute",X.style.top="-1000px",X.classList.add(p.popoverOpened),(q=X.querySelector("."+p.popoverNested))==null||q.remove(),document.body.appendChild(X);let K=X.querySelector("."+p.popoverContainer);return Z.height=K.offsetHeight,Z.width=K.offsetWidth,X.remove(),Z}get flippableElements(){return this.items.map((q)=>{if(q instanceof H0)return q.getElement();if(q instanceof x0)return q.getControls()}).flat().filter((q)=>q!=null)}addSearch(){this.search=new f5({items:this.itemsDefault,placeholder:this.messages.search}),this.search.on(G1.Search,this.onSearch);let q=this.search.getElement();q.classList.add(p.search),this.nodes.popoverContainer.insertBefore(q,this.nodes.popoverContainer.firstChild)}toggleNothingFoundMessage(q){this.nodes.nothingFoundMessage.classList.toggle(p.nothingFoundMessageDisplayed,q)}};l8([B0],p5.prototype,"size",1);var M2=p5;class h5 extends M2{constructor(J){let q=!C0();super({...J,class:p.popoverInline},{[g.Default]:{wrapperTag:"button",hint:{position:"top",alignment:"center",enabled:q}},[g.Html]:{hint:{position:"top",alignment:"center",enabled:q}}}),this.items.forEach((Z)=>{!(Z instanceof H0)&&!(Z instanceof x0)||Z.hasChildren&&Z.isChildrenOpen&&this.showNestedItems(Z)})}get offsetLeft(){return this.nodes.popoverContainer===null?0:this.nodes.popoverContainer.offsetLeft}show(){this.nestingLevel===0&&this.nodes.popover.style.setProperty(S0.InlinePopoverWidth,this.size.width+"px"),super.show()}handleHover(){}setTriggerItemPosition(J,q){let Z=q.getElement(),X=Z?Z.offsetLeft:0,K=this.offsetLeft+X;J.style.setProperty(S0.TriggerItemLeft,K+"px")}showNestedItems(J){if(this.nestedPopoverTriggerItem===J){this.destroyNestedPopoverIfExists(),this.nestedPopoverTriggerItem=null;return}super.showNestedItems(J)}showNestedPopoverForItem(J){let q=super.showNestedPopoverForItem(J);return q.getElement().classList.add(p.getPopoverNestedClass(q.nestingLevel)),q}handleItemClick(J){var q;J!==this.nestedPopoverTriggerItem&&((q=this.nestedPopoverTriggerItem)==null||q.handleClick(),super.destroyNestedPopoverIfExists()),super.handleItemClick(J)}}var d5=class J{constructor(){this.scrollPosition=null}lock(){G2?this.lockHard():document.body.classList.add(J.CSS.scrollLocked)}unlock(){G2?this.unlockHard():document.body.classList.remove(J.CSS.scrollLocked)}lockHard(){this.scrollPosition=window.pageYOffset,document.documentElement.style.setProperty("--window-scroll-offset",`${this.scrollPosition}px`),document.body.classList.add(J.CSS.scrollLockedHard)}unlockHard(){document.body.classList.remove(J.CSS.scrollLockedHard),this.scrollPosition!==null&&window.scrollTo(0,this.scrollPosition),this.scrollPosition=null}};d5.CSS={scrollLocked:"ce-scroll-locked",scrollLockedHard:"ce-scroll-locked--hard"};var a8=d5,Z2=z0("ce-popover-header"),X2={root:Z2(),text:Z2("text"),backButton:Z2("back-button")};class c5{constructor({text:J,onBackButtonClick:q}){this.listeners=new w0,this.text=J,this.onBackButtonClick=q,this.nodes={root:F.make("div",[X2.root]),backButton:F.make("button",[X2.backButton]),text:F.make("div",[X2.text])},this.nodes.backButton.innerHTML=C8,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)}getElement(){return this.nodes.root}destroy(){this.nodes.root.remove(),this.listeners.destroy()}}class u5{constructor(){this.history=[]}push(J){this.history.push(J)}pop(){return this.history.pop()}get currentTitle(){return this.history.length===0?"":this.history[this.history.length-1].title}get currentItems(){return this.history.length===0?[]:this.history[this.history.length-1].items}reset(){for(;this.history.length>1;)this.pop()}}class R2 extends P2{constructor(J){super(J,{[g.Default]:{hint:{enabled:!1}},[g.Html]:{hint:{enabled:!1}}}),this.scrollLocker=new a8,this.history=new u5,this.isHidden=!0,this.nodes.overlay=F.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:J.items})}show(){this.nodes.overlay.classList.remove(p.overlayHidden),super.show(),this.scrollLocker.lock(),this.isHidden=!1}hide(){this.isHidden||(super.hide(),this.nodes.overlay.classList.add(p.overlayHidden),this.scrollLocker.unlock(),this.history.reset(),this.isHidden=!0)}destroy(){super.destroy(),this.scrollLocker.unlock()}showNestedItems(J){this.updateItemsAndHeader(J.children,J.title),this.history.push({title:J.title,items:J.children})}updateItemsAndHeader(J,q){if(this.header!==null&&this.header!==void 0&&(this.header.destroy(),this.header=null),q!==void 0){this.header=new c5({text:q,onBackButtonClick:()=>{this.history.pop(),this.updateItemsAndHeader(this.history.currentItems,this.history.currentTitle)}});let Z=this.header.getElement();Z!==null&&this.nodes.popoverContainer.insertBefore(Z,this.nodes.popoverContainer.firstChild)}this.items.forEach((Z)=>{var X;return(X=Z.getElement())==null?void 0:X.remove()}),this.items=this.buildItems(J),this.items.forEach((Z)=>{var X;let K=Z.getElement();K!==null&&((X=this.nodes.items)==null||X.appendChild(K))})}}class l5 extends B{constructor(){super(...arguments),this.opened=!1,this.hasMobileLayoutToggleListener=!1,this.selection=new I,this.popover=null,this.close=()=>{this.opened&&(this.opened=!1,I.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(X0.Closed,this.onPopoverClose),this.popover.destroy(),this.popover.getElement().remove(),this.popover=null))},this.onPopoverClose=()=>{this.close()}}get events(){return{opened:"block-settings-opened",closed:"block-settings-closed"}}get CSS(){return{settings:"ce-settings"}}get flipper(){var J;if(this.popover!==null)return"flipper"in this.popover?(J=this.popover)==null?void 0:J.flipper:void 0}make(){this.nodes.wrapper=F.make("div",[this.CSS.settings]),this.eventsDispatcher.on(c0,this.close),this.hasMobileLayoutToggleListener=!0}destroy(){this.removeAllNodes(),this.listeners.destroy(),this.hasMobileLayoutToggleListener&&(this.eventsDispatcher.off(c0,this.close),this.hasMobileLayoutToggleListener=!1)}async open(J=this.Editor.BlockManager.currentBlock){var q;this.opened=!0,this.selection.save(),this.Editor.BlockSelection.selectBlock(J),this.Editor.BlockSelection.clearCache();let{toolTunes:Z,commonTunes:X}=J.getTunes();this.eventsDispatcher.emit(this.events.opened);let K=C0()?R2:M2;this.popover=new K({searchable:!0,items:await this.getTunesItems(J,X,Z),scopeElement:this.Editor.API.methods.ui.nodes.redactor,messages:{nothingFound:r.ui(t.ui.popover,"Nothing found"),search:r.ui(t.ui.popover,"Filter")}}),this.popover.on(X0.Closed,this.onPopoverClose),(q=this.nodes.wrapper)==null||q.append(this.popover.getElement()),this.popover.show()}getElement(){return this.nodes.wrapper}async getTunesItems(J,q,Z){let X=[];Z!==void 0&&Z.length>0&&(X.push(...Z),X.push({type:g.Separator}));let K=Array.from(this.Editor.Tools.blockTools.values()),Q=(await F5(J,K)).reduce((V,G)=>(G.toolbox.forEach((Y)=>{V.push({icon:Y.icon,title:r.t(t.toolNames,Y.title),name:G.name,closeOnActivate:!0,onActivate:async()=>{let{BlockManager:U,Caret:H,Toolbar:$}=this.Editor,D=await U.convert(J,G.name,Y.data);$.close(),H.setToBlock(D,H.positions.END)}})}),V),[]);return Q.length>0&&(X.push({icon:m5,name:"convert-to",title:r.ui(t.ui.popover,"Convert to"),children:{searchable:!0,items:Q}}),X.push({type:g.Separator})),X.push(...q),X.map((V)=>this.resolveTuneAliases(V))}resolveTuneAliases(J){if(J.type===g.Separator||J.type===g.Html)return J;let q=x8(J,{label:"title"});return J.confirmation&&(q.confirmation=this.resolveTuneAliases(J.confirmation)),q}}var a5={exports:{}};/*! + * Library for handling keyboard shortcuts + * @copyright CodeX (https://codex.so) + * @license MIT + * @author CodeX (https://codex.so) + * @version 1.2.0 + */(function(J,q){(function(Z,X){J.exports=X()})(window,function(){return function(Z){var X={};function K(Q){if(X[Q])return X[Q].exports;var V=X[Q]={i:Q,l:!1,exports:{}};return Z[Q].call(V.exports,V,V.exports,K),V.l=!0,V.exports}return K.m=Z,K.c=X,K.d=function(Q,V,G){K.o(Q,V)||Object.defineProperty(Q,V,{enumerable:!0,get:G})},K.r=function(Q){typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(Q,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(Q,"__esModule",{value:!0})},K.t=function(Q,V){if(1&V&&(Q=K(Q)),8&V||4&V&&typeof Q=="object"&&Q&&Q.__esModule)return Q;var G=Object.create(null);if(K.r(G),Object.defineProperty(G,"default",{enumerable:!0,value:Q}),2&V&&typeof Q!="string")for(var Y in Q)K.d(G,Y,function(U){return Q[U]}.bind(null,Y));return G},K.n=function(Q){var V=Q&&Q.__esModule?function(){return Q.default}:function(){return Q};return K.d(V,"a",V),V},K.o=function(Q,V){return Object.prototype.hasOwnProperty.call(Q,V)},K.p="",K(K.s=0)}([function(Z,X,K){function Q(Y,U){for(var H=0;H<U.length;H++){var $=U[H];$.enumerable=$.enumerable||!1,$.configurable=!0,"value"in $&&($.writable=!0),Object.defineProperty(Y,$.key,$)}}function V(Y,U,H){return U&&Q(Y.prototype,U),H&&Q(Y,H),Y}K.r(X);var G=function(){function Y(U){var H=this;(function($,D){if(!($ instanceof D))throw TypeError("Cannot call a class as a function")})(this,Y),this.commands={},this.keys={},this.name=U.name,this.parseShortcutName(U.name),this.element=U.on,this.callback=U.callback,this.executeShortcut=function($){H.execute($)},this.element.addEventListener("keydown",this.executeShortcut,!1)}return V(Y,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}}}]),V(Y,[{key:"parseShortcutName",value:function(U){U=U.split("+");for(var H=0;H<U.length;H++){U[H]=U[H].toUpperCase();var $=!1;for(var D in Y.supportedCommands)if(Y.supportedCommands[D].includes(U[H])){$=this.commands[D]=!0;break}$||(this.keys[U[H]]=!0)}for(var A in Y.supportedCommands)this.commands[A]||(this.commands[A]=!1)}},{key:"execute",value:function(U){var H,$={CMD:U.ctrlKey||U.metaKey,SHIFT:U.shiftKey,ALT:U.altKey},D=!0;for(H in this.commands)this.commands[H]!==$[H]&&(D=!1);var A,W=!0;for(A in this.keys)W=W&&U.keyCode===Y.keyCodes[A];D&&W&&this.callback(U)}},{key:"remove",value:function(){this.element.removeEventListener("keydown",this.executeShortcut)}}]),Y}();X.default=G}]).default})})(a5);var s8=a5.exports,r8=H1(s8);class s5{constructor(){this.registeredShortcuts=new Map}add(J){if(this.findShortcut(J.on,J.name))throw Error(`Shortcut ${J.name} is already registered for ${J.on}. Please remove it before add a new handler.`);let q=new r8({name:J.name,on:J.on,callback:J.handler}),Z=this.registeredShortcuts.get(J.on)||[];this.registeredShortcuts.set(J.on,[...Z,q])}remove(J,q){let Z=this.findShortcut(J,q);if(!Z)return;Z.remove();let X=this.registeredShortcuts.get(J).filter((K)=>K!==Z);if(X.length===0){this.registeredShortcuts.delete(J);return}this.registeredShortcuts.set(J,X)}findShortcut(J,q){return(this.registeredShortcuts.get(J)||[]).find(({name:Z})=>Z===q)}}var T0=new s5,i8=Object.defineProperty,o8=Object.getOwnPropertyDescriptor,r5=(J,q,Z,X)=>{for(var K=X>1?void 0:X?o8(q,Z):q,Q=J.length-1,V;Q>=0;Q--)(V=J[Q])&&(K=(X?V(q,Z,K):V(K))||K);return X&&K&&i8(q,Z,K),K},o0=((J)=>(J.Opened="toolbox-opened",J.Closed="toolbox-closed",J.BlockAdded="toolbox-block-added",J))(o0||{}),O2=class J extends E0{constructor({api:q,tools:Z,i18nLabels:X}){super(),this.opened=!1,this.listeners=new w0,this.popover=null,this.handleMobileLayoutToggle=()=>{this.destroyPopover(),this.initPopover()},this.onPopoverClose=()=>{this.opened=!1,this.emit("toolbox-closed")},this.api=q,this.tools=Z,this.i18nLabels=X,this.enableShortcuts(),this.nodes={toolbox:F.make("div",J.CSS.toolbox)},this.initPopover(),this.api.events.on(c0,this.handleMobileLayoutToggle)}get isEmpty(){return this.toolsToBeDisplayed.length===0}static get CSS(){return{toolbox:"ce-toolbox"}}getElement(){return this.nodes.toolbox}hasFocus(){if(this.popover!==null)return"hasFocus"in this.popover?this.popover.hasFocus():void 0}destroy(){var q;super.destroy(),this.nodes&&this.nodes.toolbox&&this.nodes.toolbox.remove(),this.removeAllShortcuts(),(q=this.popover)==null||q.off(X0.Closed,this.onPopoverClose),this.listeners.destroy(),this.api.events.off(c0,this.handleMobileLayoutToggle)}toolButtonActivated(q,Z){this.insertNewBlock(q,Z)}open(){var q;this.isEmpty||((q=this.popover)==null||q.show(),this.opened=!0,this.emit("toolbox-opened"))}close(){var q;(q=this.popover)==null||q.hide(),this.opened=!1,this.emit("toolbox-closed")}toggle(){this.opened?this.close():this.open()}initPopover(){var q;let Z=C0()?R2:M2;this.popover=new Z({scopeElement:this.api.ui.nodes.redactor,searchable:!0,messages:{nothingFound:this.i18nLabels.nothingFound,search:this.i18nLabels.filter},items:this.toolboxItemsToBeDisplayed}),this.popover.on(X0.Closed,this.onPopoverClose),(q=this.nodes.toolbox)==null||q.append(this.popover.getElement())}destroyPopover(){this.popover!==null&&(this.popover.hide(),this.popover.off(X0.Closed,this.onPopoverClose),this.popover.destroy(),this.popover=null),this.nodes.toolbox!==null&&(this.nodes.toolbox.innerHTML="")}get toolsToBeDisplayed(){let q=[];return this.tools.forEach((Z)=>{Z.toolbox&&q.push(Z)}),q}get toolboxItemsToBeDisplayed(){let q=(Z,X,K=!0)=>({icon:Z.icon,title:r.t(t.toolNames,Z.title||X1(X.name)),name:X.name,onActivate:()=>{this.toolButtonActivated(X.name,Z.data)},secondaryLabel:X.shortcut&&K?z2(X.shortcut):""});return this.toolsToBeDisplayed.reduce((Z,X)=>(Array.isArray(X.toolbox)?X.toolbox.forEach((K,Q)=>{Z.push(q(K,X,Q===0))}):X.toolbox!==void 0&&Z.push(q(X.toolbox,X)),Z),[])}enableShortcuts(){this.toolsToBeDisplayed.forEach((q)=>{let Z=q.shortcut;Z&&this.enableShortcutForTool(q.name,Z)})}enableShortcutForTool(q,Z){T0.add({name:Z,on:this.api.ui.nodes.redactor,handler:async(X)=>{X.preventDefault();let K=this.api.blocks.getCurrentBlockIndex(),Q=this.api.blocks.getBlockByIndex(K);if(Q)try{let V=await this.api.blocks.convert(Q.id,q);this.api.caret.setToBlock(V,"end");return}catch{}this.insertNewBlock(q)}})}removeAllShortcuts(){this.toolsToBeDisplayed.forEach((q)=>{let Z=q.shortcut;Z&&T0.remove(this.api.ui.nodes.redactor,Z)})}async insertNewBlock(q,Z){let X=this.api.blocks.getCurrentBlockIndex(),K=this.api.blocks.getBlockByIndex(X);if(!K)return;let Q=K.isEmpty?X:X+1,V;if(Z){let Y=await this.api.blocks.composeBlockData(q);V=Object.assign(Y,Z)}let G=this.api.blocks.insert(q,V,void 0,Q,void 0,K.isEmpty);G.call(G0.APPEND_CALLBACK),this.api.caret.setToBlock(Q),this.emit("toolbox-block-added",{block:G}),this.api.toolbar.close()}};r5([B0],O2.prototype,"toolsToBeDisplayed",1);r5([B0],O2.prototype,"toolboxItemsToBeDisplayed",1);var n8=O2,i5="block hovered";async function t8(J,q){let Z=navigator.keyboard;if(!Z)return q;try{return(await Z.getLayoutMap()).get(J)||q}catch(X){return console.error(X),q}}class o5 extends B{constructor({config:J,eventsDispatcher:q}){super({config:J,eventsDispatcher:q}),this.toolboxInstance=null}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"}}get opened(){return this.nodes.wrapper.classList.contains(this.CSS.toolbarOpened)}get toolbox(){var J;return{opened:(J=this.toolboxInstance)==null?void 0:J.opened,close:()=>{var q;(q=this.toolboxInstance)==null||q.close()},open:()=>{if(this.toolboxInstance===null){y("toolbox.open() called before initialization is finished","warn");return}this.Editor.BlockManager.currentBlock=this.hoveredBlock,this.toolboxInstance.open()},toggle:()=>{if(this.toolboxInstance===null){y("toolbox.toggle() called before initialization is finished","warn");return}this.toolboxInstance.toggle()},hasFocus:()=>{var q;return(q=this.toolboxInstance)==null?void 0:q.hasFocus()}}}get blockActions(){return{hide:()=>{this.nodes.actions.classList.remove(this.CSS.actionsOpened)},show:()=>{this.nodes.actions.classList.add(this.CSS.actionsOpened)}}}get blockTunesToggler(){return{hide:()=>this.nodes.settingsToggler.classList.add(this.CSS.settingsTogglerHidden),show:()=>this.nodes.settingsToggler.classList.remove(this.CSS.settingsTogglerHidden)}}toggleReadOnly(J){J?(this.destroy(),this.Editor.BlockSettings.destroy(),this.disableModuleBindings()):window.requestIdleCallback(()=>{this.drawUI(),this.enableModuleBindings()},{timeout:2000})}moveAndOpen(J=this.Editor.BlockManager.currentBlock){if(this.toolboxInstance===null){y("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(),!J)return;this.hoveredBlock=J;let q=J.holder,{isMobile:Z}=this.Editor.UI,X,K=20,Q=J.firstInput,V=q.getBoundingClientRect(),G=Q!==void 0?Q.getBoundingClientRect():null,Y=G!==null?G.top-V.top:null,U=Y!==null?Y>K:void 0;if(Z)X=q.offsetTop+q.offsetHeight;else if(Q===void 0||U){let H=parseInt(window.getComputedStyle(J.pluginsContent).paddingTop);X=q.offsetTop+H}else{let H=G8(Q),$=parseInt(window.getComputedStyle(this.nodes.plusButton).height,10);X=q.offsetTop+H-$+8+Y}this.nodes.wrapper.style.top=`${Math.floor(X)}px`,this.Editor.BlockManager.blocks.length===1&&J.isEmpty?this.blockTunesToggler.hide():this.blockTunesToggler.show(),this.open()}close(){var J,q;this.Editor.ReadOnly.isEnabled||((J=this.nodes.wrapper)==null||J.classList.remove(this.CSS.toolbarOpened),this.blockActions.hide(),(q=this.toolboxInstance)==null||q.close(),this.Editor.BlockSettings.close(),this.reset())}reset(){this.nodes.wrapper.style.top="unset"}open(J=!0){this.nodes.wrapper.classList.add(this.CSS.toolbarOpened),J?this.blockActions.show():this.blockActions.hide()}async make(){this.nodes.wrapper=F.make("div",this.CSS.toolbar),["content","actions"].forEach((K)=>{this.nodes[K]=F.make("div",this.CSS[K])}),F.append(this.nodes.wrapper,this.nodes.content),F.append(this.nodes.content,this.nodes.actions),this.nodes.plusButton=F.make("div",this.CSS.plusButton,{innerHTML:m8}),F.append(this.nodes.actions,this.nodes.plusButton),this.readOnlyMutableListeners.on(this.nodes.plusButton,"click",()=>{Q1(!0),this.plusButtonClicked()},!1);let J=F.make("div");J.appendChild(document.createTextNode(r.ui(t.ui.toolbar.toolbox,"Add"))),J.appendChild(F.make("div",this.CSS.plusButtonShortcut,{textContent:"/"})),V1(this.nodes.plusButton,J,{hidingDelay:400}),this.nodes.settingsToggler=F.make("span",this.CSS.settingsToggler,{innerHTML:g8}),F.append(this.nodes.actions,this.nodes.settingsToggler);let q=F.make("div"),Z=F.text(r.ui(t.ui.blockTunes.toggler,"Click to tune")),X=await t8("Slash","/");q.appendChild(Z),q.appendChild(F.make("div",this.CSS.plusButtonShortcut,{textContent:z2(`CMD + ${X}`)})),V1(this.nodes.settingsToggler,q,{hidingDelay:400}),F.append(this.nodes.actions,this.makeToolbox()),F.append(this.nodes.actions,this.Editor.BlockSettings.getElement()),F.append(this.Editor.UI.nodes.wrapper,this.nodes.wrapper)}makeToolbox(){return this.toolboxInstance=new n8({api:this.Editor.API.methods,tools:this.Editor.Tools.blockTools,i18nLabels:{filter:r.ui(t.ui.popover,"Filter"),nothingFound:r.ui(t.ui.popover,"Nothing found")}}),this.toolboxInstance.on(o0.Opened,()=>{this.Editor.UI.nodes.wrapper.classList.add(this.CSS.openedToolboxHolderModifier)}),this.toolboxInstance.on(o0.Closed,()=>{this.Editor.UI.nodes.wrapper.classList.remove(this.CSS.openedToolboxHolderModifier)}),this.toolboxInstance.on(o0.BlockAdded,({block:J})=>{let{BlockManager:q,Caret:Z}=this.Editor,X=q.getBlockById(J.id);X.inputs.length===0&&(X===q.lastBlock?(q.insertAtEnd(),Z.setToBlock(q.lastBlock)):Z.setToBlock(q.nextBlock))}),this.toolboxInstance.getElement()}plusButtonClicked(){var J;this.Editor.BlockManager.currentBlock=this.hoveredBlock,(J=this.toolboxInstance)==null||J.toggle()}enableModuleBindings(){this.readOnlyMutableListeners.on(this.nodes.settingsToggler,"mousedown",(J)=>{var q;J.stopPropagation(),this.settingsTogglerClicked(),(q=this.toolboxInstance)!=null&&q.opened&&this.toolboxInstance.close(),Q1(!0)},!0),C0()||this.eventsDispatcher.on(i5,(J)=>{var q;this.Editor.BlockSettings.opened||(q=this.toolboxInstance)!=null&&q.opened||this.moveAndOpen(J.block)})}disableModuleBindings(){this.readOnlyMutableListeners.clearAll()}settingsTogglerClicked(){this.Editor.BlockManager.currentBlock=this.hoveredBlock,this.Editor.BlockSettings.opened?this.Editor.BlockSettings.close():this.Editor.BlockSettings.open(this.hoveredBlock)}drawUI(){this.Editor.BlockSettings.make(),this.make()}destroy(){this.removeAllNodes(),this.toolboxInstance&&this.toolboxInstance.destroy()}}var A0=((J)=>(J[J.Block=0]="Block",J[J.Inline=1]="Inline",J[J.Tune=2]="Tune",J))(A0||{}),n0=((J)=>(J.Shortcut="shortcut",J.Toolbox="toolbox",J.EnabledInlineTools="inlineToolbar",J.EnabledBlockTunes="tunes",J.Config="config",J))(n0||{}),n5=((J)=>(J.Shortcut="shortcut",J.SanitizeConfig="sanitize",J))(n5||{}),_0=((J)=>(J.IsEnabledLineBreaks="enableLineBreaks",J.Toolbox="toolbox",J.ConversionConfig="conversionConfig",J.IsReadOnlySupported="isReadOnlySupported",J.PasteConfig="pasteConfig",J))(_0||{}),Y1=((J)=>(J.IsInline="isInline",J.Title="title",J.IsReadOnlySupported="isReadOnlySupported",J))(Y1||{}),H2=((J)=>(J.IsTune="isTune",J))(H2||{});class z1{constructor({name:J,constructable:q,config:Z,api:X,isDefault:K,isInternal:Q=!1,defaultPlaceholder:V}){this.api=X,this.name=J,this.constructable=q,this.config=Z,this.isDefault=K,this.isInternal=Q,this.defaultPlaceholder=V}get settings(){let J=this.config.config||{};return this.isDefault&&!("placeholder"in J)&&this.defaultPlaceholder&&(J.placeholder=this.defaultPlaceholder),J}reset(){if(m(this.constructable.reset))return this.constructable.reset()}prepare(){if(m(this.constructable.prepare))return this.constructable.prepare({toolName:this.name,config:this.settings})}get shortcut(){let J=this.constructable.shortcut;return this.config.shortcut||J}get sanitizeConfig(){return this.constructable.sanitize||{}}isInline(){return this.type===A0.Inline}isBlock(){return this.type===A0.Block}isTune(){return this.type===A0.Tune}}class t5 extends B{constructor({config:J,eventsDispatcher:q}){super({config:J,eventsDispatcher:q}),this.CSS={inlineToolbar:"ce-inline-toolbar"},this.opened=!1,this.popover=null,this.toolbarVerticalMargin=C0()?20:6,this.tools=new Map,window.requestIdleCallback(()=>{this.make()},{timeout:2000})}async tryToShow(J=!1){J&&this.close(),this.allowedToShow()&&(await this.open(),this.Editor.Toolbar.close())}close(){var J,q;if(this.opened){for(let[Z,X]of this.tools){let K=this.getToolShortcut(Z.name);K!==void 0&&T0.remove(this.Editor.UI.nodes.redactor,K),m(X.clear)&&X.clear()}this.tools=new Map,this.reset(),this.opened=!1,(J=this.popover)==null||J.hide(),(q=this.popover)==null||q.destroy(),this.popover=null}}containsNode(J){return this.nodes.wrapper===void 0?!1:this.nodes.wrapper.contains(J)}destroy(){var J;this.removeAllNodes(),(J=this.popover)==null||J.destroy(),this.popover=null}make(){this.nodes.wrapper=F.make("div",[this.CSS.inlineToolbar,...this.isRtl?[this.Editor.UI.CSS.editorRtlFix]:[]]),F.append(this.Editor.UI.nodes.wrapper,this.nodes.wrapper)}async open(){var J;if(this.opened)return;this.opened=!0,this.popover!==null&&this.popover.destroy(),this.createToolsInstances();let q=await this.getPopoverItems();this.popover=new h5({items:q,scopeElement:this.Editor.API.methods.ui.nodes.redactor,messages:{nothingFound:r.ui(t.ui.popover,"Nothing found"),search:r.ui(t.ui.popover,"Filter")}}),this.move(this.popover.size.width),(J=this.nodes.wrapper)==null||J.append(this.popover.getElement()),this.popover.show()}move(J){let q=I.rect,Z=this.Editor.UI.nodes.wrapper.getBoundingClientRect(),X={x:q.x-Z.x,y:q.y+q.height-Z.top+this.toolbarVerticalMargin};X.x+J+Z.x>this.Editor.UI.contentRect.right&&(X.x=this.Editor.UI.contentRect.right-J-Z.x),this.nodes.wrapper.style.left=Math.floor(X.x)+"px",this.nodes.wrapper.style.top=Math.floor(X.y)+"px"}reset(){this.nodes.wrapper.style.left="0",this.nodes.wrapper.style.top="0"}allowedToShow(){let J=["IMG","INPUT"],q=I.get(),Z=I.text;if(!q||!q.anchorNode||q.isCollapsed||Z.length<1)return!1;let X=F.isElement(q.anchorNode)?q.anchorNode:q.anchorNode.parentElement;if(X===null||q!==null&&J.includes(X.tagName))return!1;let K=this.Editor.BlockManager.getBlock(q.anchorNode);return!K||this.getTools().some((Q)=>K.tool.inlineTools.has(Q.name))===!1?!1:X.closest("[contenteditable]")!==null}getTools(){let J=this.Editor.BlockManager.currentBlock;return J?Array.from(J.tool.inlineTools.values()).filter((q)=>!(this.Editor.ReadOnly.isEnabled&&q.isReadOnlySupported!==!0)):[]}createToolsInstances(){this.tools=new Map,this.getTools().forEach((J)=>{let q=J.create();this.tools.set(J,q)})}async getPopoverItems(){let J=[],q=0;for(let[Z,X]of this.tools){let K=await X.render(),Q=this.getToolShortcut(Z.name);if(Q!==void 0)try{this.enableShortcuts(Z.name,Q)}catch{}let V=Q!==void 0?z2(Q):void 0,G=r.t(t.toolNames,Z.title||X1(Z.name));[K].flat().forEach((Y)=>{var U,H;let $={name:Z.name,onActivate:()=>{this.toolClicked(X)},hint:{title:G,description:V}};if(F.isElement(Y)){let D={...$,element:Y,type:g.Html};if(m(X.renderActions)){let A=X.renderActions();D.children={isOpen:(U=X.checkState)==null?void 0:U.call(X,I.get()),isFlippable:!1,items:[{type:g.Html,element:A}]}}else(H=X.checkState)==null||H.call(X,I.get());J.push(D)}else if(Y.type===g.Html)J.push({...$,...Y,type:g.Html});else if(Y.type===g.Separator)J.push({type:g.Separator});else{let D={...$,...Y,type:g.Default};"children"in D&&q!==0&&J.push({type:g.Separator}),J.push(D),"children"in D&&q<this.tools.size-1&&J.push({type:g.Separator})}}),q++}return J}getToolShortcut(J){let{Tools:q}=this.Editor,Z=q.inlineTools.get(J),X=q.internal.inlineTools;return Array.from(X.keys()).includes(J)?this.inlineTools[J][n5.Shortcut]:Z==null?void 0:Z.shortcut}enableShortcuts(J,q){T0.add({name:q,handler:(Z)=>{var X;let{currentBlock:K}=this.Editor.BlockManager;K&&K.tool.enabledInlineTools&&(Z.preventDefault(),(X=this.popover)==null||X.activateItemByName(J))},on:document})}toolClicked(J){var q;let Z=I.range;(q=J.surround)==null||q.call(J,Z),this.checkToolsState()}checkToolsState(){var J;(J=this.tools)==null||J.forEach((q)=>{var Z;(Z=q.checkState)==null||Z.call(q,I.get())})}get inlineTools(){let J={};return Array.from(this.Editor.Tools.inlineTools.entries()).forEach(([q,Z])=>{J[q]=Z.create()}),J}}function e5(){let J=window.getSelection();if(J===null)return[null,0];let{focusNode:q,focusOffset:Z}=J;return q===null?[null,0]:(q.nodeType!==Node.TEXT_NODE&&q.childNodes.length>0&&(q.childNodes[Z]?(q=q.childNodes[Z],Z=0):(q=q.childNodes[Z-1],Z=q.textContent.length)),[q,Z])}function J6(J,q,Z,X){let K=document.createRange();X==="left"?(K.setStart(J,0),K.setEnd(q,Z)):(K.setStart(q,Z),K.setEnd(J,J.childNodes.length));let Q=K.cloneContents(),V=document.createElement("div");V.appendChild(Q);let G=V.textContent||"";return V8(G)}function t0(J){let q=F.getDeepestNode(J);if(q===null||F.isEmpty(J))return!0;if(F.isNativeInput(q))return q.selectionEnd===0;if(F.isEmpty(J))return!0;let[Z,X]=e5();return Z===null?!1:J6(J,Z,X,"left")}function e0(J){let q=F.getDeepestNode(J,!0);if(q===null)return!0;if(F.isNativeInput(q))return q.selectionEnd===q.value.length;let[Z,X]=e5();return Z===null?!1:J6(J,Z,X,"right")}var q6={},j2={},D1={},M0={},I2={},_2={};Object.defineProperty(_2,"__esModule",{value:!0});_2.allInputsSelector=e8;function e8(){var J=["text","password","email","number","search","tel","url"];return"[contenteditable=true], textarea, input:not([type]), "+J.map(function(q){return'input[type="'.concat(q,'"]')}).join(", ")}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.allInputsSelector=void 0;var q=_2;Object.defineProperty(J,"allInputsSelector",{enumerable:!0,get:function(){return q.allInputsSelector}})})(I2);var R0={},S2={};Object.defineProperty(S2,"__esModule",{value:!0});S2.isNativeInput=J9;function J9(J){var q=["INPUT","TEXTAREA"];return J&&J.tagName?q.includes(J.tagName):!1}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isNativeInput=void 0;var q=S2;Object.defineProperty(J,"isNativeInput",{enumerable:!0,get:function(){return q.isNativeInput}})})(R0);var Z6={},x2={};Object.defineProperty(x2,"__esModule",{value:!0});x2.append=q9;function q9(J,q){Array.isArray(q)?q.forEach(function(Z){J.appendChild(Z)}):J.appendChild(q)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.append=void 0;var q=x2;Object.defineProperty(J,"append",{enumerable:!0,get:function(){return q.append}})})(Z6);var T2={},B2={};Object.defineProperty(B2,"__esModule",{value:!0});B2.blockElements=Z9;function Z9(){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(J){Object.defineProperty(J,"__esModule",{value:!0}),J.blockElements=void 0;var q=B2;Object.defineProperty(J,"blockElements",{enumerable:!0,get:function(){return q.blockElements}})})(T2);var X6={},C2={};Object.defineProperty(C2,"__esModule",{value:!0});C2.calculateBaseline=X9;function X9(J){var q=window.getComputedStyle(J),Z=parseFloat(q.fontSize),X=parseFloat(q.lineHeight)||Z*1.2,K=parseFloat(q.paddingTop),Q=parseFloat(q.borderTopWidth),V=parseFloat(q.marginTop),G=Z*0.8,Y=(X-Z)/2,U=V+Q+K+Y+G;return U}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.calculateBaseline=void 0;var q=C2;Object.defineProperty(J,"calculateBaseline",{enumerable:!0,get:function(){return q.calculateBaseline}})})(X6);var K6={},E2={},w2={},y2={};Object.defineProperty(y2,"__esModule",{value:!0});y2.isContentEditable=K9;function K9(J){return J.contentEditable==="true"}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isContentEditable=void 0;var q=y2;Object.defineProperty(J,"isContentEditable",{enumerable:!0,get:function(){return q.isContentEditable}})})(w2);Object.defineProperty(E2,"__esModule",{value:!0});E2.canSetCaret=G9;var Q9=R0,V9=w2;function G9(J){var q=!0;if((0,Q9.isNativeInput)(J))switch(J.type){case"file":case"checkbox":case"radio":case"hidden":case"submit":case"button":case"image":case"reset":q=!1;break}else q=(0,V9.isContentEditable)(J);return q}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.canSetCaret=void 0;var q=E2;Object.defineProperty(J,"canSetCaret",{enumerable:!0,get:function(){return q.canSetCaret}})})(K6);var F1={},v2={};function Y9(J,q,Z){let X=Z.value!==void 0?"value":"get",K=Z[X],Q=`#${q}Cache`;if(Z[X]=function(...V){return this[Q]===void 0&&(this[Q]=K.apply(this,V)),this[Q]},X==="get"&&Z.set){let V=Z.set;Z.set=function(G){delete J[Q],V.apply(this,G)}}return Z}function Q6(){let J={win:!1,mac:!1,x11:!1,linux:!1},q=Object.keys(J).find((Z)=>window.navigator.appVersion.toLowerCase().indexOf(Z)!==-1);return q!==void 0&&(J[q]=!0),J}function k2(J){return J!=null&&J!==""&&(typeof J!="object"||Object.keys(J).length>0)}function U9(J){return!k2(J)}var H9=()=>typeof window<"u"&&window.navigator!==null&&k2(window.navigator.platform)&&(/iP(ad|hone|od)/.test(window.navigator.platform)||window.navigator.platform==="MacIntel"&&window.navigator.maxTouchPoints>1);function $9(J){let q=Q6();return J=J.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,"+"),q.mac?J=J.replace(/ctrl|cmd/gi,"⌘").replace(/alt/gi,"⌥"):J=J.replace(/cmd/gi,"Ctrl").replace(/windows/gi,"WIN"),J}function z9(J){return J[0].toUpperCase()+J.slice(1)}function D9(J){let q=document.createElement("div");q.style.position="absolute",q.style.left="-999px",q.style.bottom="-999px",q.innerHTML=J,document.body.appendChild(q);let Z=window.getSelection(),X=document.createRange();if(X.selectNode(q),Z===null)throw Error("Cannot copy text to clipboard");Z.removeAllRanges(),Z.addRange(X),document.execCommand("copy"),document.body.removeChild(q)}function F9(J,q,Z){let X;return(...K)=>{let Q=this,V=()=>{X=void 0,Z!==!0&&J.apply(Q,K)},G=Z===!0&&X!==void 0;window.clearTimeout(X),X=window.setTimeout(V,q),G&&J.apply(Q,K)}}function $0(J){return Object.prototype.toString.call(J).match(/\s([a-zA-Z]+)/)[1].toLowerCase()}function W9(J){return $0(J)==="boolean"}function V6(J){return $0(J)==="function"||$0(J)==="asyncfunction"}function L9(J){return V6(J)&&/^\s*class\s+/.test(J.toString())}function A9(J){return $0(J)==="number"}function J1(J){return $0(J)==="object"}function N9(J){return Promise.resolve(J)===J}function P9(J){return $0(J)==="string"}function M9(J){return $0(J)==="undefined"}function $2(J,...q){if(!q.length)return J;let Z=q.shift();if(J1(J)&&J1(Z))for(let X in Z)J1(Z[X])?(J[X]===void 0&&Object.assign(J,{[X]:{}}),$2(J[X],Z[X])):Object.assign(J,{[X]:Z[X]});return $2(J,...q)}function R9(J,q,Z){let X=`«${q}» is deprecated and will be removed in the next major release. Please use the «${Z}» instead.`;J&&console.warn(X)}function O9(J){try{return new URL(J).href}catch{}return J.substring(0,2)==="//"?window.location.protocol+J:window.location.origin+J}function j9(J){return J>47&&J<58||J===32||J===13||J===229||J>64&&J<91||J>95&&J<112||J>185&&J<193||J>218&&J<223}var I9={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},_9={LEFT:0,WHEEL:1,RIGHT:2,BACKWARD:3,FORWARD:4},S9=class{constructor(){this.completed=Promise.resolve()}add(J){return new Promise((q,Z)=>{this.completed=this.completed.then(J).then(q).catch(Z)})}};function x9(J,q,Z=void 0){let X,K,Q,V=null,G=0;Z||(Z={});let Y=function(){G=Z.leading===!1?0:Date.now(),V=null,Q=J.apply(X,K),V===null&&(X=K=null)};return function(){let U=Date.now();!G&&Z.leading===!1&&(G=U);let H=q-(U-G);return X=this,K=arguments,H<=0||H>q?(V&&(clearTimeout(V),V=null),G=U,Q=J.apply(X,K),V===null&&(X=K=null)):!V&&Z.trailing!==!1&&(V=setTimeout(Y,H)),Q}}var T9=Object.freeze(Object.defineProperty({__proto__:null,PromiseQueue:S9,beautifyShortcut:$9,cacheable:Y9,capitalize:z9,copyTextToClipboard:D9,debounce:F9,deepMerge:$2,deprecationAssert:R9,getUserOS:Q6,getValidUrl:O9,isBoolean:W9,isClass:L9,isEmpty:U9,isFunction:V6,isIosDevice:H9,isNumber:A9,isObject:J1,isPrintableKey:j9,isPromise:N9,isString:P9,isUndefined:M9,keyCodes:I9,mouseButtons:_9,notEmpty:k2,throttle:x9,typeOf:$0},Symbol.toStringTag,{value:"Module"})),g2=a7(T9);Object.defineProperty(v2,"__esModule",{value:!0});v2.containsOnlyInlineElements=E9;var B9=g2,C9=T2;function E9(J){var q;(0,B9.isString)(J)?(q=document.createElement("div"),q.innerHTML=J):q=J;var Z=function(X){return!(0,C9.blockElements)().includes(X.tagName.toLowerCase())&&Array.from(X.children).every(Z)};return Array.from(q.children).every(Z)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.containsOnlyInlineElements=void 0;var q=v2;Object.defineProperty(J,"containsOnlyInlineElements",{enumerable:!0,get:function(){return q.containsOnlyInlineElements}})})(F1);var G6={},m2={},W1={},b2={};Object.defineProperty(b2,"__esModule",{value:!0});b2.make=w9;function w9(J,q,Z){var X;q===void 0&&(q=null),Z===void 0&&(Z={});var K=document.createElement(J);if(Array.isArray(q)){var Q=q.filter(function(G){return G!==void 0});(X=K.classList).add.apply(X,Q)}else q!==null&&K.classList.add(q);for(var V in Z)Object.prototype.hasOwnProperty.call(Z,V)&&(K[V]=Z[V]);return K}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.make=void 0;var q=b2;Object.defineProperty(J,"make",{enumerable:!0,get:function(){return q.make}})})(W1);Object.defineProperty(m2,"__esModule",{value:!0});m2.fragmentToString=v9;var y9=W1;function v9(J){var q=(0,y9.make)("div");return q.appendChild(J),q.innerHTML}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.fragmentToString=void 0;var q=m2;Object.defineProperty(J,"fragmentToString",{enumerable:!0,get:function(){return q.fragmentToString}})})(G6);var Y6={},f2={};Object.defineProperty(f2,"__esModule",{value:!0});f2.getContentLength=g9;var k9=R0;function g9(J){var q,Z;return(0,k9.isNativeInput)(J)?J.value.length:J.nodeType===Node.TEXT_NODE?J.length:(Z=(q=J.textContent)===null||q===void 0?void 0:q.length)!==null&&Z!==void 0?Z:0}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.getContentLength=void 0;var q=f2;Object.defineProperty(J,"getContentLength",{enumerable:!0,get:function(){return q.getContentLength}})})(Y6);var p2={},h2={},t4=d0&&d0.__spreadArray||function(J,q,Z){if(Z||arguments.length===2)for(var X=0,K=q.length,Q;X<K;X++)(Q||!(X in q))&&(Q||(Q=Array.prototype.slice.call(q,0,X)),Q[X]=q[X]);return J.concat(Q||Array.prototype.slice.call(q))};Object.defineProperty(h2,"__esModule",{value:!0});h2.getDeepestBlockElements=U6;var m9=F1;function U6(J){return(0,m9.containsOnlyInlineElements)(J)?[J]:Array.from(J.children).reduce(function(q,Z){return t4(t4([],q,!0),U6(Z),!0)},[])}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.getDeepestBlockElements=void 0;var q=h2;Object.defineProperty(J,"getDeepestBlockElements",{enumerable:!0,get:function(){return q.getDeepestBlockElements}})})(p2);var H6={},d2={},L1={},c2={};Object.defineProperty(c2,"__esModule",{value:!0});c2.isLineBreakTag=b9;function b9(J){return["BR","WBR"].includes(J.tagName)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isLineBreakTag=void 0;var q=c2;Object.defineProperty(J,"isLineBreakTag",{enumerable:!0,get:function(){return q.isLineBreakTag}})})(L1);var A1={},u2={};Object.defineProperty(u2,"__esModule",{value:!0});u2.isSingleTag=f9;function f9(J){return["AREA","BASE","BR","COL","COMMAND","EMBED","HR","IMG","INPUT","KEYGEN","LINK","META","PARAM","SOURCE","TRACK","WBR"].includes(J.tagName)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isSingleTag=void 0;var q=u2;Object.defineProperty(J,"isSingleTag",{enumerable:!0,get:function(){return q.isSingleTag}})})(A1);Object.defineProperty(d2,"__esModule",{value:!0});d2.getDeepestNode=$6;var p9=R0,h9=L1,d9=A1;function $6(J,q){q===void 0&&(q=!1);var Z=q?"lastChild":"firstChild",X=q?"previousSibling":"nextSibling";if(J.nodeType===Node.ELEMENT_NODE&&J[Z]){var K=J[Z];if((0,d9.isSingleTag)(K)&&!(0,p9.isNativeInput)(K)&&!(0,h9.isLineBreakTag)(K))if(K[X])K=K[X];else if(K.parentNode!==null&&K.parentNode[X])K=K.parentNode[X];else return K.parentNode;return $6(K,q)}return J}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.getDeepestNode=void 0;var q=d2;Object.defineProperty(J,"getDeepestNode",{enumerable:!0,get:function(){return q.getDeepestNode}})})(H6);var z6={},l2={},r0=d0&&d0.__spreadArray||function(J,q,Z){if(Z||arguments.length===2)for(var X=0,K=q.length,Q;X<K;X++)(Q||!(X in q))&&(Q||(Q=Array.prototype.slice.call(q,0,X)),Q[X]=q[X]);return J.concat(Q||Array.prototype.slice.call(q))};Object.defineProperty(l2,"__esModule",{value:!0});l2.findAllInputs=s9;var c9=F1,u9=p2,l9=I2,a9=R0;function s9(J){return Array.from(J.querySelectorAll((0,l9.allInputsSelector)())).reduce(function(q,Z){return(0,a9.isNativeInput)(Z)||(0,c9.containsOnlyInlineElements)(Z)?r0(r0([],q,!0),[Z],!1):r0(r0([],q,!0),(0,u9.getDeepestBlockElements)(Z),!0)},[])}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.findAllInputs=void 0;var q=l2;Object.defineProperty(J,"findAllInputs",{enumerable:!0,get:function(){return q.findAllInputs}})})(z6);var D6={},a2={};Object.defineProperty(a2,"__esModule",{value:!0});a2.isCollapsedWhitespaces=r9;function r9(J){return!/[^\t\n\r ]/.test(J)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isCollapsedWhitespaces=void 0;var q=a2;Object.defineProperty(J,"isCollapsedWhitespaces",{enumerable:!0,get:function(){return q.isCollapsedWhitespaces}})})(D6);var s2={},r2={};Object.defineProperty(r2,"__esModule",{value:!0});r2.isElement=o9;var i9=g2;function o9(J){return(0,i9.isNumber)(J)?!1:!!J&&!!J.nodeType&&J.nodeType===Node.ELEMENT_NODE}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isElement=void 0;var q=r2;Object.defineProperty(J,"isElement",{enumerable:!0,get:function(){return q.isElement}})})(s2);var F6={},i2={},o2={},n2={};Object.defineProperty(n2,"__esModule",{value:!0});n2.isLeaf=n9;function n9(J){return J===null?!1:J.childNodes.length===0}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isLeaf=void 0;var q=n2;Object.defineProperty(J,"isLeaf",{enumerable:!0,get:function(){return q.isLeaf}})})(o2);var t2={},e2={};Object.defineProperty(e2,"__esModule",{value:!0});e2.isNodeEmpty=ZJ;var t9=L1,e9=s2,JJ=R0,qJ=A1;function ZJ(J,q){var Z="";return(0,qJ.isSingleTag)(J)&&!(0,t9.isLineBreakTag)(J)?!1:((0,e9.isElement)(J)&&(0,JJ.isNativeInput)(J)?Z=J.value:J.textContent!==null&&(Z=J.textContent.replace("","")),q!==void 0&&(Z=Z.replace(new RegExp(q,"g"),"")),Z.trim().length===0)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isNodeEmpty=void 0;var q=e2;Object.defineProperty(J,"isNodeEmpty",{enumerable:!0,get:function(){return q.isNodeEmpty}})})(t2);Object.defineProperty(i2,"__esModule",{value:!0});i2.isEmpty=QJ;var XJ=o2,KJ=t2;function QJ(J,q){J.normalize();for(var Z=[J];Z.length>0;){var X=Z.shift();if(X){if(J=X,(0,XJ.isLeaf)(J)&&!(0,KJ.isNodeEmpty)(J,q))return!1;Z.push.apply(Z,Array.from(J.childNodes))}}return!0}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isEmpty=void 0;var q=i2;Object.defineProperty(J,"isEmpty",{enumerable:!0,get:function(){return q.isEmpty}})})(F6);var W6={},J3={};Object.defineProperty(J3,"__esModule",{value:!0});J3.isFragment=GJ;var VJ=g2;function GJ(J){return(0,VJ.isNumber)(J)?!1:!!J&&!!J.nodeType&&J.nodeType===Node.DOCUMENT_FRAGMENT_NODE}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isFragment=void 0;var q=J3;Object.defineProperty(J,"isFragment",{enumerable:!0,get:function(){return q.isFragment}})})(W6);var L6={},q3={};Object.defineProperty(q3,"__esModule",{value:!0});q3.isHTMLString=UJ;var YJ=W1;function UJ(J){var q=(0,YJ.make)("div");return q.innerHTML=J,q.childElementCount>0}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isHTMLString=void 0;var q=q3;Object.defineProperty(J,"isHTMLString",{enumerable:!0,get:function(){return q.isHTMLString}})})(L6);var A6={},Z3={};Object.defineProperty(Z3,"__esModule",{value:!0});Z3.offset=HJ;function HJ(J){var q=J.getBoundingClientRect(),Z=window.pageXOffset||document.documentElement.scrollLeft,X=window.pageYOffset||document.documentElement.scrollTop,K=q.top+X,Q=q.left+Z;return{top:K,left:Q,bottom:K+q.height,right:Q+q.width}}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.offset=void 0;var q=Z3;Object.defineProperty(J,"offset",{enumerable:!0,get:function(){return q.offset}})})(A6);var N6={},X3={};Object.defineProperty(X3,"__esModule",{value:!0});X3.prepend=$J;function $J(J,q){Array.isArray(q)?(q=q.reverse(),q.forEach(function(Z){return J.prepend(Z)})):J.prepend(q)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.prepend=void 0;var q=X3;Object.defineProperty(J,"prepend",{enumerable:!0,get:function(){return q.prepend}})})(N6);(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.prepend=J.offset=J.make=J.isLineBreakTag=J.isSingleTag=J.isNodeEmpty=J.isLeaf=J.isHTMLString=J.isFragment=J.isEmpty=J.isElement=J.isContentEditable=J.isCollapsedWhitespaces=J.findAllInputs=J.isNativeInput=J.allInputsSelector=J.getDeepestNode=J.getDeepestBlockElements=J.getContentLength=J.fragmentToString=J.containsOnlyInlineElements=J.canSetCaret=J.calculateBaseline=J.blockElements=J.append=void 0;var q=I2;Object.defineProperty(J,"allInputsSelector",{enumerable:!0,get:function(){return q.allInputsSelector}});var Z=R0;Object.defineProperty(J,"isNativeInput",{enumerable:!0,get:function(){return Z.isNativeInput}});var X=Z6;Object.defineProperty(J,"append",{enumerable:!0,get:function(){return X.append}});var K=T2;Object.defineProperty(J,"blockElements",{enumerable:!0,get:function(){return K.blockElements}});var Q=X6;Object.defineProperty(J,"calculateBaseline",{enumerable:!0,get:function(){return Q.calculateBaseline}});var V=K6;Object.defineProperty(J,"canSetCaret",{enumerable:!0,get:function(){return V.canSetCaret}});var G=F1;Object.defineProperty(J,"containsOnlyInlineElements",{enumerable:!0,get:function(){return G.containsOnlyInlineElements}});var Y=G6;Object.defineProperty(J,"fragmentToString",{enumerable:!0,get:function(){return Y.fragmentToString}});var U=Y6;Object.defineProperty(J,"getContentLength",{enumerable:!0,get:function(){return U.getContentLength}});var H=p2;Object.defineProperty(J,"getDeepestBlockElements",{enumerable:!0,get:function(){return H.getDeepestBlockElements}});var $=H6;Object.defineProperty(J,"getDeepestNode",{enumerable:!0,get:function(){return $.getDeepestNode}});var D=z6;Object.defineProperty(J,"findAllInputs",{enumerable:!0,get:function(){return D.findAllInputs}});var A=D6;Object.defineProperty(J,"isCollapsedWhitespaces",{enumerable:!0,get:function(){return A.isCollapsedWhitespaces}});var W=w2;Object.defineProperty(J,"isContentEditable",{enumerable:!0,get:function(){return W.isContentEditable}});var z=s2;Object.defineProperty(J,"isElement",{enumerable:!0,get:function(){return z.isElement}});var L=F6;Object.defineProperty(J,"isEmpty",{enumerable:!0,get:function(){return L.isEmpty}});var N=W6;Object.defineProperty(J,"isFragment",{enumerable:!0,get:function(){return N.isFragment}});var P=L6;Object.defineProperty(J,"isHTMLString",{enumerable:!0,get:function(){return P.isHTMLString}});var _=o2;Object.defineProperty(J,"isLeaf",{enumerable:!0,get:function(){return _.isLeaf}});var j=t2;Object.defineProperty(J,"isNodeEmpty",{enumerable:!0,get:function(){return j.isNodeEmpty}});var O=L1;Object.defineProperty(J,"isLineBreakTag",{enumerable:!0,get:function(){return O.isLineBreakTag}});var T=A1;Object.defineProperty(J,"isSingleTag",{enumerable:!0,get:function(){return T.isSingleTag}});var b=W1;Object.defineProperty(J,"make",{enumerable:!0,get:function(){return b.make}});var R=A6;Object.defineProperty(J,"offset",{enumerable:!0,get:function(){return R.offset}});var M=N6;Object.defineProperty(J,"prepend",{enumerable:!0,get:function(){return M.prepend}})})(M0);var N1={};Object.defineProperty(N1,"__esModule",{value:!0});N1.getContenteditableSlice=DJ;var zJ=M0;function DJ(J,q,Z,X,K){var Q;K===void 0&&(K=!1);var V=document.createRange();if(X==="left"?(V.setStart(J,0),V.setEnd(q,Z)):(V.setStart(q,Z),V.setEnd(J,J.childNodes.length)),K===!0){var G=V.extractContents();return(0,zJ.fragmentToString)(G)}var Y=V.cloneContents(),U=document.createElement("div");U.appendChild(Y);var H=(Q=U.textContent)!==null&&Q!==void 0?Q:"";return H}Object.defineProperty(D1,"__esModule",{value:!0});D1.checkContenteditableSliceForEmptiness=LJ;var FJ=M0,WJ=N1;function LJ(J,q,Z,X){var K=(0,WJ.getContenteditableSlice)(J,q,Z,X);return(0,FJ.isCollapsedWhitespaces)(K)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.checkContenteditableSliceForEmptiness=void 0;var q=D1;Object.defineProperty(J,"checkContenteditableSliceForEmptiness",{enumerable:!0,get:function(){return q.checkContenteditableSliceForEmptiness}})})(j2);var P6={};(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.getContenteditableSlice=void 0;var q=N1;Object.defineProperty(J,"getContenteditableSlice",{enumerable:!0,get:function(){return q.getContenteditableSlice}})})(P6);var M6={},K3={};Object.defineProperty(K3,"__esModule",{value:!0});K3.focus=NJ;var AJ=M0;function NJ(J,q){var Z,X;if(q===void 0&&(q=!0),(0,AJ.isNativeInput)(J)){J.focus();var K=q?0:J.value.length;J.setSelectionRange(K,K)}else{var Q=document.createRange(),V=window.getSelection();if(!V)return;var G=function(D){var A=document.createTextNode("");D.appendChild(A),Q.setStart(A,0),Q.setEnd(A,0)},Y=function(D){return D!=null},U=J.childNodes,H=q?U[0]:U[U.length-1];if(Y(H)){for(;Y(H)&&H.nodeType!==Node.TEXT_NODE;)H=q?H.firstChild:H.lastChild;if(Y(H)&&H.nodeType===Node.TEXT_NODE){var $=(X=(Z=H.textContent)===null||Z===void 0?void 0:Z.length)!==null&&X!==void 0?X:0,K=q?0:$;Q.setStart(H,K),Q.setEnd(H,K)}else G(J)}else G(J);V.removeAllRanges(),V.addRange(Q)}}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.focus=void 0;var q=K3;Object.defineProperty(J,"focus",{enumerable:!0,get:function(){return q.focus}})})(M6);var Q3={},P1={};Object.defineProperty(P1,"__esModule",{value:!0});P1.getCaretNodeAndOffset=PJ;function PJ(){var J=window.getSelection();if(J===null)return[null,0];var{focusNode:q,focusOffset:Z}=J;return q===null?[null,0]:(q.nodeType!==Node.TEXT_NODE&&q.childNodes.length>0&&(q.childNodes[Z]!==void 0?(q=q.childNodes[Z],Z=0):(q=q.childNodes[Z-1],q.textContent!==null&&(Z=q.textContent.length))),[q,Z])}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.getCaretNodeAndOffset=void 0;var q=P1;Object.defineProperty(J,"getCaretNodeAndOffset",{enumerable:!0,get:function(){return q.getCaretNodeAndOffset}})})(Q3);var R6={},M1={};Object.defineProperty(M1,"__esModule",{value:!0});M1.getRange=MJ;function MJ(){var J=window.getSelection();return J&&J.rangeCount?J.getRangeAt(0):null}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.getRange=void 0;var q=M1;Object.defineProperty(J,"getRange",{enumerable:!0,get:function(){return q.getRange}})})(R6);var O6={},V3={};Object.defineProperty(V3,"__esModule",{value:!0});V3.isCaretAtEndOfInput=jJ;var e4=M0,RJ=Q3,OJ=j2;function jJ(J){var q=(0,e4.getDeepestNode)(J,!0);if(q===null)return!0;if((0,e4.isNativeInput)(q))return q.selectionEnd===q.value.length;var Z=(0,RJ.getCaretNodeAndOffset)(),X=Z[0],K=Z[1];return X===null?!1:(0,OJ.checkContenteditableSliceForEmptiness)(J,X,K,"right")}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isCaretAtEndOfInput=void 0;var q=V3;Object.defineProperty(J,"isCaretAtEndOfInput",{enumerable:!0,get:function(){return q.isCaretAtEndOfInput}})})(O6);var j6={},G3={};Object.defineProperty(G3,"__esModule",{value:!0});G3.isCaretAtStartOfInput=SJ;var i0=M0,IJ=P1,_J=D1;function SJ(J){var q=(0,i0.getDeepestNode)(J);if(q===null||(0,i0.isEmpty)(J))return!0;if((0,i0.isNativeInput)(q))return q.selectionEnd===0;if((0,i0.isEmpty)(J))return!0;var Z=(0,IJ.getCaretNodeAndOffset)(),X=Z[0],K=Z[1];return X===null?!1:(0,_J.checkContenteditableSliceForEmptiness)(J,X,K,"left")}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isCaretAtStartOfInput=void 0;var q=G3;Object.defineProperty(J,"isCaretAtStartOfInput",{enumerable:!0,get:function(){return q.isCaretAtStartOfInput}})})(j6);var I6={},Y3={};Object.defineProperty(Y3,"__esModule",{value:!0});Y3.save=BJ;var xJ=M0,TJ=M1;function BJ(){var J=(0,TJ.getRange)(),q=(0,xJ.make)("span");if(q.id="cursor",q.hidden=!0,!!J)return J.insertNode(q),function(){var Z=window.getSelection();Z&&(J.setStartAfter(q),J.setEndAfter(q),Z.removeAllRanges(),Z.addRange(J),setTimeout(function(){q.remove()},150))}}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.save=void 0;var q=Y3;Object.defineProperty(J,"save",{enumerable:!0,get:function(){return q.save}})})(I6);(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.save=J.isCaretAtStartOfInput=J.isCaretAtEndOfInput=J.getRange=J.getCaretNodeAndOffset=J.focus=J.getContenteditableSlice=J.checkContenteditableSliceForEmptiness=void 0;var q=j2;Object.defineProperty(J,"checkContenteditableSliceForEmptiness",{enumerable:!0,get:function(){return q.checkContenteditableSliceForEmptiness}});var Z=P6;Object.defineProperty(J,"getContenteditableSlice",{enumerable:!0,get:function(){return Z.getContenteditableSlice}});var X=M6;Object.defineProperty(J,"focus",{enumerable:!0,get:function(){return X.focus}});var K=Q3;Object.defineProperty(J,"getCaretNodeAndOffset",{enumerable:!0,get:function(){return K.getCaretNodeAndOffset}});var Q=R6;Object.defineProperty(J,"getRange",{enumerable:!0,get:function(){return Q.getRange}});var V=O6;Object.defineProperty(J,"isCaretAtEndOfInput",{enumerable:!0,get:function(){return V.isCaretAtEndOfInput}});var G=j6;Object.defineProperty(J,"isCaretAtStartOfInput",{enumerable:!0,get:function(){return G.isCaretAtStartOfInput}});var Y=I6;Object.defineProperty(J,"save",{enumerable:!0,get:function(){return Y.save}})})(q6);class _6 extends B{keydown(J){switch(this.beforeKeydownProcessing(J),J.keyCode){case x.BACKSPACE:this.backspace(J);break;case x.DELETE:this.delete(J);break;case x.ENTER:this.enter(J);break;case x.DOWN:case x.RIGHT:this.arrowRightAndDown(J);break;case x.UP:case x.LEFT:this.arrowLeftAndUp(J);break;case x.TAB:this.tabPressed(J);break}J.key==="/"&&!J.ctrlKey&&!J.metaKey&&this.slashPressed(J),J.code==="Slash"&&(J.ctrlKey||J.metaKey)&&(J.preventDefault(),this.commandSlashPressed())}beforeKeydownProcessing(J){this.needToolbarClosing(J)&&K5(J.keyCode)&&(this.Editor.Toolbar.close(),J.ctrlKey||J.metaKey||J.altKey||J.shiftKey||this.Editor.BlockSelection.clearSelection(J))}keyup(J){J.shiftKey||this.Editor.UI.checkEmptiness()}dragOver(J){let q=this.Editor.BlockManager.getBlockByChildNode(J.target);q.dropTarget=!0}dragLeave(J){let q=this.Editor.BlockManager.getBlockByChildNode(J.target);q.dropTarget=!1}handleCommandC(J){let{BlockSelection:q}=this.Editor;q.anyBlockSelected&&q.copySelectedBlocks(J)}handleCommandX(J){let{BlockSelection:q,BlockManager:Z,Caret:X}=this.Editor;q.anyBlockSelected&&q.copySelectedBlocks(J).then(()=>{let K=Z.removeSelectedBlocks(),Q=Z.insertDefaultBlockAtIndex(K,!0);X.setToBlock(Q,X.positions.START),q.clearSelection(J)})}tabPressed(J){let{InlineToolbar:q,Caret:Z}=this.Editor;if(q.opened)return;(J.shiftKey?Z.navigatePrevious(!0):Z.navigateNext(!0))&&J.preventDefault()}commandSlashPressed(){this.Editor.BlockSelection.selectedBlocks.length>1||this.activateBlockSettings()}slashPressed(J){!this.Editor.UI.nodes.wrapper.contains(J.target)||!this.Editor.BlockManager.currentBlock.isEmpty||(J.preventDefault(),this.Editor.Caret.insertContentAtCaretPosition("/"),this.activateToolbox())}enter(J){let{BlockManager:q,UI:Z}=this.Editor,X=q.currentBlock;if(X===void 0||X.tool.isLineBreaksEnabled||Z.someToolbarOpened&&Z.someFlipperButtonFocused||J.shiftKey&&!G2)return;let K=X;X.currentInput!==void 0&&t0(X.currentInput)&&!X.hasMedia?this.Editor.BlockManager.insertDefaultBlockAtIndex(this.Editor.BlockManager.currentBlockIndex):X.currentInput&&e0(X.currentInput)?K=this.Editor.BlockManager.insertDefaultBlockAtIndex(this.Editor.BlockManager.currentBlockIndex+1):K=this.Editor.BlockManager.split(),this.Editor.Caret.setToBlock(K),this.Editor.Toolbar.moveAndOpen(K),J.preventDefault()}backspace(J){let{BlockManager:q,Caret:Z}=this.Editor,{currentBlock:X,previousBlock:K}=q;if(X===void 0||!I.isCollapsed||!X.currentInput||!t0(X.currentInput))return;if(J.preventDefault(),this.Editor.Toolbar.close(),X.currentInput!==X.firstInput){Z.navigatePrevious();return}if(K===null)return;if(K.isEmpty){q.removeBlock(K);return}if(X.isEmpty){q.removeBlock(X);let Q=q.currentBlock;Z.setToBlock(Q,Z.positions.END);return}s4(K,X)?this.mergeBlocks(K,X):Z.setToBlock(K,Z.positions.END)}delete(J){let{BlockManager:q,Caret:Z}=this.Editor,{currentBlock:X,nextBlock:K}=q;if(!I.isCollapsed||!e0(X.currentInput))return;if(J.preventDefault(),this.Editor.Toolbar.close(),X.currentInput!==X.lastInput){Z.navigateNext();return}if(K===null)return;if(K.isEmpty){q.removeBlock(K);return}if(X.isEmpty){q.removeBlock(X),Z.setToBlock(K,Z.positions.START);return}s4(X,K)?this.mergeBlocks(X,K):Z.setToBlock(K,Z.positions.START)}mergeBlocks(J,q){let{BlockManager:Z,Toolbar:X}=this.Editor;J.lastInput!==void 0&&(q6.focus(J.lastInput,!1),Z.mergeBlocks(J,q).then(()=>{X.close()}))}arrowRightAndDown(J){let q=P0.usedKeys.includes(J.keyCode)&&(!J.shiftKey||J.keyCode===x.TAB);if(this.Editor.UI.someToolbarOpened&&q)return;this.Editor.Toolbar.close();let{currentBlock:Z}=this.Editor.BlockManager,X=((Z==null?void 0:Z.currentInput)!==void 0?e0(Z.currentInput):void 0)||this.Editor.BlockSelection.anyBlockSelected;if(J.shiftKey&&J.keyCode===x.DOWN&&X){this.Editor.CrossBlockSelection.toggleBlockSelectedState();return}if(J.keyCode===x.DOWN||J.keyCode===x.RIGHT&&!this.isRtl?this.Editor.Caret.navigateNext():this.Editor.Caret.navigatePrevious()){J.preventDefault();return}Z1(()=>{this.Editor.BlockManager.currentBlock&&this.Editor.BlockManager.currentBlock.updateCurrentInput()},20)(),this.Editor.BlockSelection.clearSelection(J)}arrowLeftAndUp(J){if(this.Editor.UI.someToolbarOpened){if(P0.usedKeys.includes(J.keyCode)&&(!J.shiftKey||J.keyCode===x.TAB))return;this.Editor.UI.closeAllToolbars()}this.Editor.Toolbar.close();let{currentBlock:q}=this.Editor.BlockManager,Z=((q==null?void 0:q.currentInput)!==void 0?t0(q.currentInput):void 0)||this.Editor.BlockSelection.anyBlockSelected;if(J.shiftKey&&J.keyCode===x.UP&&Z){this.Editor.CrossBlockSelection.toggleBlockSelectedState(!1);return}if(J.keyCode===x.UP||J.keyCode===x.LEFT&&!this.isRtl?this.Editor.Caret.navigatePrevious():this.Editor.Caret.navigateNext()){J.preventDefault();return}Z1(()=>{this.Editor.BlockManager.currentBlock&&this.Editor.BlockManager.currentBlock.updateCurrentInput()},20)(),this.Editor.BlockSelection.clearSelection(J)}needToolbarClosing(J){let q=J.keyCode===x.ENTER&&this.Editor.Toolbar.toolbox.opened,Z=J.keyCode===x.ENTER&&this.Editor.BlockSettings.opened,X=J.keyCode===x.ENTER&&this.Editor.InlineToolbar.opened,K=J.keyCode===x.TAB;return!(J.shiftKey||K||q||Z||X)}activateToolbox(){this.Editor.Toolbar.opened||this.Editor.Toolbar.moveAndOpen(),this.Editor.Toolbar.toolbox.open()}activateBlockSettings(){this.Editor.Toolbar.opened||this.Editor.Toolbar.moveAndOpen(),this.Editor.BlockSettings.opened||this.Editor.BlockSettings.open()}}class q1{constructor(J){this.blocks=[],this.workingArea=J}get length(){return this.blocks.length}get array(){return this.blocks}get nodes(){return Q5(this.workingArea.children)}static set(J,q,Z){return isNaN(Number(q))?(Reflect.set(J,q,Z),!0):(J.insert(+q,Z),!0)}static get(J,q){return isNaN(Number(q))?Reflect.get(J,q):J.get(+q)}push(J){this.blocks.push(J),this.insertToDOM(J)}swap(J,q){let Z=this.blocks[q];F.swap(this.blocks[J].holder,Z.holder),this.blocks[q]=this.blocks[J],this.blocks[J]=Z}move(J,q){let Z=this.blocks.splice(q,1)[0],X=J-1,K=Math.max(0,X),Q=this.blocks[K];J>0?this.insertToDOM(Z,"afterend",Q):this.insertToDOM(Z,"beforebegin",Q),this.blocks.splice(J,0,Z);let V=this.composeBlockEvent("move",{fromIndex:q,toIndex:J});Z.call(G0.MOVED,V)}insert(J,q,Z=!1){if(!this.length){this.push(q);return}J>this.length&&(J=this.length),Z&&(this.blocks[J].holder.remove(),this.blocks[J].call(G0.REMOVED));let X=Z?1:0;if(this.blocks.splice(J,X,q),J>0){let K=this.blocks[J-1];this.insertToDOM(q,"afterend",K)}else{let K=this.blocks[J+1];K?this.insertToDOM(q,"beforebegin",K):this.insertToDOM(q)}}replace(J,q){if(this.blocks[J]===void 0)throw Error("Incorrect index");this.blocks[J].holder.replaceWith(q.holder),this.blocks[J]=q}insertMany(J,q){let Z=new DocumentFragment;for(let X of J)Z.appendChild(X.holder);if(this.length>0){if(q>0){let X=Math.min(q-1,this.length-1);this.blocks[X].holder.after(Z)}else q===0&&this.workingArea.prepend(Z);this.blocks.splice(q,0,...J)}else this.blocks.push(...J),this.workingArea.appendChild(Z);J.forEach((X)=>X.call(G0.RENDERED))}remove(J){isNaN(J)&&(J=this.length-1),this.blocks[J].holder.remove(),this.blocks[J].call(G0.REMOVED),this.blocks.splice(J,1)}removeAll(){this.workingArea.innerHTML="",this.blocks.forEach((J)=>J.call(G0.REMOVED)),this.blocks.length=0}insertAfter(J,q){let Z=this.blocks.indexOf(J);this.insert(Z+1,q)}get(J){return this.blocks[J]}indexOf(J){return this.blocks.indexOf(J)}insertToDOM(J,q,Z){q?Z.holder.insertAdjacentElement(q,J.holder):this.workingArea.appendChild(J.holder),J.call(G0.RENDERED)}composeBlockEvent(J,q){return new CustomEvent(J,{detail:q})}}var J5="block-removed",q5="block-added",CJ="block-moved",Z5="block-changed";class S6{constructor(){this.completed=Promise.resolve()}add(J){return new Promise((q,Z)=>{this.completed=this.completed.then(J).then(q).catch(Z)})}}class x6 extends B{constructor(){super(...arguments),this._currentBlockIndex=-1,this._blocks=null}get currentBlockIndex(){return this._currentBlockIndex}set currentBlockIndex(J){this._currentBlockIndex=J}get firstBlock(){return this._blocks[0]}get lastBlock(){return this._blocks[this._blocks.length-1]}get currentBlock(){return this._blocks[this.currentBlockIndex]}set currentBlock(J){this.currentBlockIndex=this.getBlockIndex(J)}get nextBlock(){return this.currentBlockIndex===this._blocks.length-1?null:this._blocks[this.currentBlockIndex+1]}get nextContentfulBlock(){return this.blocks.slice(this.currentBlockIndex+1).find((J)=>!!J.inputs.length)}get previousContentfulBlock(){return this.blocks.slice(0,this.currentBlockIndex).reverse().find((J)=>!!J.inputs.length)}get previousBlock(){return this.currentBlockIndex===0?null:this._blocks[this.currentBlockIndex-1]}get blocks(){return this._blocks.array}get isEditorEmpty(){return this.blocks.every((J)=>J.isEmpty)}prepare(){let J=new q1(this.Editor.UI.nodes.redactor);this._blocks=new Proxy(J,{set:q1.set,get:q1.get}),this.listeners.on(document,"copy",(q)=>this.Editor.BlockEvents.handleCommandC(q))}toggleReadOnly(J){J?this.disableModuleBindings():this.enableModuleBindings()}composeBlock({tool:J,data:q={},id:Z=void 0,tunes:X={}}){let K=this.Editor.ReadOnly.isEnabled,Q=this.Editor.Tools.blockTools.get(J),V=new c({id:Z,data:q,tool:Q,api:this.Editor.API,readOnly:K,tunesData:X},this.eventsDispatcher);return K||window.requestIdleCallback(()=>{this.bindBlockEvents(V)},{timeout:2000}),V}insert({id:J=void 0,tool:q=this.config.defaultBlock,data:Z={},index:X,needToFocus:K=!0,replace:Q=!1,tunes:V={}}={}){let G=X;G===void 0&&(G=this.currentBlockIndex+(Q?0:1));let Y=this.composeBlock({id:J,tool:q,data:Z,tunes:V});return Q&&this.blockDidMutated(J5,this.getBlockByIndex(G),{index:G}),this._blocks.insert(G,Y,Q),this.blockDidMutated(q5,Y,{index:G}),K?this.currentBlockIndex=G:G<=this.currentBlockIndex&&this.currentBlockIndex++,Y}insertMany(J,q=0){this._blocks.insertMany(J,q)}async update(J,q,Z){if(!q&&!Z)return J;let X=await J.data,K=this.composeBlock({id:J.id,tool:J.name,data:Object.assign({},X,q??{}),tunes:Z??J.tunes}),Q=this.getBlockIndex(J);return this._blocks.replace(Q,K),this.blockDidMutated(Z5,K,{index:Q}),K}replace(J,q,Z){let X=this.getBlockIndex(J);return this.insert({tool:q,data:Z,index:X,replace:!0})}paste(J,q,Z=!1){let X=this.insert({tool:J,replace:Z});try{window.requestIdleCallback(()=>{X.call(G0.ON_PASTE,q)})}catch(K){y(`${J}: onPaste callback call is failed`,"error",K)}return X}insertDefaultBlockAtIndex(J,q=!1){let Z=this.composeBlock({tool:this.config.defaultBlock});return this._blocks[J]=Z,this.blockDidMutated(q5,Z,{index:J}),q?this.currentBlockIndex=J:J<=this.currentBlockIndex&&this.currentBlockIndex++,Z}insertAtEnd(){return this.currentBlockIndex=this.blocks.length-1,this.insert()}async mergeBlocks(J,q){let Z;if(J.name===q.name&&J.mergeable){let X=await q.data;if(J0(X)){console.error("Could not merge Block. Failed to extract original Block data.");return}let[K]=W2([X],J.tool.sanitizeConfig);Z=K}else if(J.mergeable&&K1(q,"export")&&K1(J,"import")){let X=await q.exportDataAsString(),K=Z0(X,J.tool.sanitizeConfig);Z=r4(K,J.tool.conversionConfig)}Z!==void 0&&(await J.mergeWith(Z),this.removeBlock(q),this.currentBlockIndex=this._blocks.indexOf(J))}removeBlock(J,q=!0){return new Promise((Z)=>{let X=this._blocks.indexOf(J);if(!this.validateIndex(X))throw Error("Can't find a Block to remove");this._blocks.remove(X),J.destroy(),this.blockDidMutated(J5,J,{index:X}),this.currentBlockIndex>=X&&this.currentBlockIndex--,this.blocks.length?X===0&&(this.currentBlockIndex=0):(this.unsetCurrentBlock(),q&&this.insert()),Z()})}removeSelectedBlocks(){let J;for(let q=this.blocks.length-1;q>=0;q--)this.blocks[q].selected&&(this.removeBlock(this.blocks[q]),J=q);return J}removeAllBlocks(){for(let J=this.blocks.length-1;J>=0;J--)this._blocks.remove(J);this.unsetCurrentBlock(),this.insert(),this.currentBlock.firstInput.focus()}split(){let J=this.Editor.Caret.extractFragmentFromCaretPosition(),q=F.make("div");q.appendChild(J);let Z={text:F.isEmpty(q)?"":q.innerHTML};return this.insert({data:Z})}getBlockByIndex(J){return J===-1&&(J=this._blocks.length-1),this._blocks[J]}getBlockIndex(J){return this._blocks.indexOf(J)}getBlockById(J){return this._blocks.array.find((q)=>q.id===J)}getBlock(J){F.isElement(J)||(J=J.parentNode);let q=this._blocks.nodes,Z=J.closest(`.${c.CSS.wrapper}`),X=q.indexOf(Z);if(X>=0)return this._blocks[X]}setCurrentBlockByChildNode(J){F.isElement(J)||(J=J.parentNode);let q=J.closest(`.${c.CSS.wrapper}`);if(!q)return;let Z=q.closest(`.${this.Editor.UI.CSS.editorWrapper}`);if(Z!=null&&Z.isEqualNode(this.Editor.UI.nodes.wrapper))return this.currentBlockIndex=this._blocks.nodes.indexOf(q),this.currentBlock.updateCurrentInput(),this.currentBlock}getBlockByChildNode(J){if(!J||!(J instanceof Node))return;F.isElement(J)||(J=J.parentNode);let q=J.closest(`.${c.CSS.wrapper}`);return this.blocks.find((Z)=>Z.holder===q)}swap(J,q){this._blocks.swap(J,q),this.currentBlockIndex=q}move(J,q=this.currentBlockIndex){if(isNaN(J)||isNaN(q)){y("Warning during 'move' call: incorrect indices provided.","warn");return}if(!this.validateIndex(J)||!this.validateIndex(q)){y("Warning during 'move' call: indices cannot be lower than 0 or greater than the amount of blocks.","warn");return}this._blocks.move(J,q),this.currentBlockIndex=J,this.blockDidMutated(CJ,this.currentBlock,{fromIndex:q,toIndex:J})}async convert(J,q,Z){if(!await J.save())throw Error("Could not convert Block. Failed to extract original Block data.");let X=this.Editor.Tools.blockTools.get(q);if(!X)throw Error(`Could not convert Block. Tool «${q}» not found.`);let K=await J.exportDataAsString(),Q=Z0(K,X.sanitizeConfig),V=r4(Q,X.conversionConfig,X.settings);return Z&&(V=Object.assign(V,Z)),this.replace(J,X.name,V)}unsetCurrentBlock(){this.currentBlockIndex=-1}async clear(J=!1){let q=new S6;[...this.blocks].forEach((Z)=>{q.add(async()=>{await this.removeBlock(Z,!1)})}),await q.completed,this.unsetCurrentBlock(),J&&this.insert(),this.Editor.UI.checkEmptiness()}async destroy(){await Promise.all(this.blocks.map((J)=>J.destroy()))}bindBlockEvents(J){let{BlockEvents:q}=this.Editor;this.readOnlyMutableListeners.on(J.holder,"keydown",(Z)=>{q.keydown(Z)}),this.readOnlyMutableListeners.on(J.holder,"keyup",(Z)=>{q.keyup(Z)}),this.readOnlyMutableListeners.on(J.holder,"dragover",(Z)=>{q.dragOver(Z)}),this.readOnlyMutableListeners.on(J.holder,"dragleave",(Z)=>{q.dragLeave(Z)}),J.on("didMutated",(Z)=>this.blockDidMutated(Z5,Z,{index:this.getBlockIndex(Z)}))}disableModuleBindings(){this.readOnlyMutableListeners.clearAll()}enableModuleBindings(){this.readOnlyMutableListeners.on(document,"cut",(J)=>this.Editor.BlockEvents.handleCommandX(J)),this.blocks.forEach((J)=>{this.bindBlockEvents(J)})}validateIndex(J){return!(J<0||J>=this._blocks.length)}blockDidMutated(J,q,Z){let X=new CustomEvent(J,{detail:{target:new V0(q),...Z}});return this.eventsDispatcher.emit(H5,{event:X}),q}}class T6 extends B{constructor(){super(...arguments),this.anyBlockSelectedCache=null,this.needToSelectAll=!1,this.nativeInputSelected=!1,this.readyToBlockSelection=!1}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:{}}}get allBlocksSelected(){let{BlockManager:J}=this.Editor;return J.blocks.every((q)=>q.selected===!0)}set allBlocksSelected(J){let{BlockManager:q}=this.Editor;q.blocks.forEach((Z)=>{Z.selected=J}),this.clearCache()}get anyBlockSelected(){let{BlockManager:J}=this.Editor;return this.anyBlockSelectedCache===null&&(this.anyBlockSelectedCache=J.blocks.some((q)=>q.selected===!0)),this.anyBlockSelectedCache}get selectedBlocks(){return this.Editor.BlockManager.blocks.filter((J)=>J.selected)}prepare(){this.selection=new I,T0.add({name:"CMD+A",handler:(J)=>{let{BlockManager:q,ReadOnly:Z}=this.Editor;if(Z.isEnabled){J.preventDefault(),this.selectAllBlocks();return}q.currentBlock&&this.handleCommandA(J)},on:this.Editor.UI.nodes.redactor})}toggleReadOnly(){I.get().removeAllRanges(),this.allBlocksSelected=!1}unSelectBlockByIndex(J){let{BlockManager:q}=this.Editor,Z;isNaN(J)?Z=q.currentBlock:Z=q.getBlockByIndex(J),Z.selected=!1,this.clearCache()}clearSelection(J,q=!1){let{BlockManager:Z,Caret:X,RectangleSelection:K}=this.Editor;this.needToSelectAll=!1,this.nativeInputSelected=!1,this.readyToBlockSelection=!1;let Q=J&&J instanceof KeyboardEvent,V=Q&&K5(J.keyCode);if(this.anyBlockSelected&&Q&&V&&!I.isSelectionExists){let G=Z.removeSelectedBlocks();Z.insertDefaultBlockAtIndex(G,!0),X.setToBlock(Z.currentBlock),Z1(()=>{let Y=J.key;X.insertContentAtCaretPosition(Y.length>1?"":Y)},20)()}if(this.Editor.CrossBlockSelection.clear(J),!this.anyBlockSelected||K.isRectActivated()){this.Editor.RectangleSelection.clearSelection();return}q&&this.selection.restore(),this.allBlocksSelected=!1}copySelectedBlocks(J){J.preventDefault();let q=F.make("div");this.selectedBlocks.forEach((K)=>{let Q=Z0(K.holder.innerHTML,this.sanitizerConfig),V=F.make("p");V.innerHTML=Q,q.appendChild(V)});let Z=Array.from(q.childNodes).map((K)=>K.textContent).join(` + +`),X=q.innerHTML;return J.clipboardData.setData("text/plain",Z),J.clipboardData.setData("text/html",X),Promise.all(this.selectedBlocks.map((K)=>K.save())).then((K)=>{try{J.clipboardData.setData(this.Editor.Paste.MIME_TYPE,JSON.stringify(K))}catch{}})}selectBlockByIndex(J){let{BlockManager:q}=this.Editor,Z=q.getBlockByIndex(J);Z!==void 0&&this.selectBlock(Z)}selectBlock(J){this.selection.save(),I.get().removeAllRanges(),J.selected=!0,this.clearCache(),this.Editor.InlineToolbar.close()}unselectBlock(J){J.selected=!1,this.clearCache()}clearCache(){this.anyBlockSelectedCache=null}destroy(){T0.remove(this.Editor.UI.nodes.redactor,"CMD+A")}handleCommandA(J){if(this.Editor.RectangleSelection.clearSelection(),F.isNativeInput(J.target)&&!this.readyToBlockSelection){this.readyToBlockSelection=!0;return}let q=this.Editor.BlockManager.getBlock(J.target),Z=q.inputs;if(Z.length>1&&!this.readyToBlockSelection){this.readyToBlockSelection=!0;return}if(Z.length===1&&!this.needToSelectAll){this.needToSelectAll=!0;return}this.needToSelectAll?(J.preventDefault(),this.selectAllBlocks(),this.needToSelectAll=!1,this.readyToBlockSelection=!1):this.readyToBlockSelection&&(J.preventDefault(),this.selectBlock(q),this.needToSelectAll=!0)}selectAllBlocks(){this.selection.save(),I.get().removeAllRanges(),this.allBlocksSelected=!0,this.Editor.InlineToolbar.close()}}class U1 extends B{get positions(){return{START:"start",END:"end",DEFAULT:"default"}}static get CSS(){return{shadowCaret:"cdx-shadow-caret"}}setToBlock(J,q=this.positions.DEFAULT,Z=0){var X;let{BlockManager:K,BlockSelection:Q}=this.Editor;if(Q.clearSelection(),!J.focusable){(X=window.getSelection())==null||X.removeAllRanges(),Q.selectBlock(J),K.currentBlock=J;return}let V;switch(q){case this.positions.START:V=J.firstInput;break;case this.positions.END:V=J.lastInput;break;default:V=J.currentInput}if(!V)return;let G,Y=Z;if(q===this.positions.START)G=F.getDeepestNode(V,!1),Y=0;else if(q===this.positions.END)G=F.getDeepestNode(V,!0),Y=F.getContentLength(G);else{let{node:U,offset:H}=F.getNodeByOffset(V,Z);U?(G=U,Y=H):(G=F.getDeepestNode(V,!1),Y=0)}this.set(G,Y),K.setCurrentBlockByChildNode(J.holder),K.currentBlock.currentInput=V}setToInput(J,q=this.positions.DEFAULT,Z=0){let{currentBlock:X}=this.Editor.BlockManager,K=F.getDeepestNode(J);switch(q){case this.positions.START:this.set(K,0);break;case this.positions.END:this.set(K,F.getContentLength(K));break;default:Z&&this.set(K,Z)}X.currentInput=J}set(J,q=0){let{top:Z,bottom:X}=I.setCursor(J,q),{innerHeight:K}=window;Z<0?window.scrollBy(0,Z-30):X>K&&window.scrollBy(0,X-K+30)}setToTheLastBlock(){let J=this.Editor.BlockManager.lastBlock;if(J)if(J.tool.isDefault&&J.isEmpty)this.setToBlock(J);else{let q=this.Editor.BlockManager.insertAtEnd();this.setToBlock(q)}}extractFragmentFromCaretPosition(){let J=I.get();if(J.rangeCount){let q=J.getRangeAt(0),Z=this.Editor.BlockManager.currentBlock.currentInput;if(q.deleteContents(),Z)if(F.isNativeInput(Z)){let X=Z,K=document.createDocumentFragment(),Q=X.value.substring(0,X.selectionStart),V=X.value.substring(X.selectionStart);return K.textContent=V,X.value=Q,K}else{let X=q.cloneRange();return X.selectNodeContents(Z),X.setStart(q.endContainer,q.endOffset),X.extractContents()}}}navigateNext(J=!1){let{BlockManager:q}=this.Editor,{currentBlock:Z,nextBlock:X}=q;if(Z===void 0)return!1;let{nextInput:K,currentInput:Q}=Z,V=Q!==void 0?e0(Q):void 0,G=X,Y=J||V||!Z.focusable;if(K&&Y)return this.setToInput(K,this.positions.START),!0;if(G===null){if(Z.tool.isDefault||!Y)return!1;G=q.insertAtEnd()}return Y?(this.setToBlock(G,this.positions.START),!0):!1}navigatePrevious(J=!1){let{currentBlock:q,previousBlock:Z}=this.Editor.BlockManager;if(!q)return!1;let{previousInput:X,currentInput:K}=q,Q=K!==void 0?t0(K):void 0,V=J||Q||!q.focusable;return X&&V?(this.setToInput(X,this.positions.END),!0):Z!==null&&V?(this.setToBlock(Z,this.positions.END),!0):!1}createShadow(J){let q=document.createElement("span");q.classList.add(U1.CSS.shadowCaret),J.insertAdjacentElement("beforeend",q)}restoreCaret(J){let q=J.querySelector(`.${U1.CSS.shadowCaret}`);if(!q)return;new I().expandToTag(q);let Z=document.createRange();Z.selectNode(q),Z.extractContents()}insertContentAtCaretPosition(J){let q=document.createDocumentFragment(),Z=document.createElement("div"),X=I.get(),K=I.range;Z.innerHTML=J,Array.from(Z.childNodes).forEach((Y)=>q.appendChild(Y)),q.childNodes.length===0&&q.appendChild(new Text);let Q=q.lastChild;K.deleteContents(),K.insertNode(q);let V=document.createRange(),G=Q.nodeType===Node.TEXT_NODE?Q:Q.firstChild;G!==null&&G.textContent!==null&&V.setStart(G,G.textContent.length),X.removeAllRanges(),X.addRange(V)}}class B6 extends B{constructor(){super(...arguments),this.onMouseUp=()=>{this.listeners.off(document,"mouseover",this.onMouseOver),this.listeners.off(document,"mouseup",this.onMouseUp)},this.onMouseOver=(J)=>{let{BlockManager:q,BlockSelection:Z}=this.Editor;if(J.relatedTarget===null&&J.target===null)return;let X=q.getBlockByChildNode(J.relatedTarget)||this.lastSelectedBlock,K=q.getBlockByChildNode(J.target);if(!(!X||!K)&&K!==X){if(X===this.firstSelectedBlock){I.get().removeAllRanges(),X.selected=!0,K.selected=!0,Z.clearCache();return}if(K===this.firstSelectedBlock){X.selected=!1,K.selected=!1,Z.clearCache();return}this.Editor.InlineToolbar.close(),this.toggleBlocksSelectedState(X,K),this.lastSelectedBlock=K}}}async prepare(){this.listeners.on(document,"mousedown",(J)=>{this.enableCrossBlockSelection(J)})}watchSelection(J){if(J.button!==r7.LEFT)return;let{BlockManager:q}=this.Editor;this.firstSelectedBlock=q.getBlock(J.target),this.lastSelectedBlock=this.firstSelectedBlock,this.listeners.on(document,"mouseover",this.onMouseOver),this.listeners.on(document,"mouseup",this.onMouseUp)}get isCrossBlockSelectionStarted(){return!!this.firstSelectedBlock&&!!this.lastSelectedBlock&&this.firstSelectedBlock!==this.lastSelectedBlock}toggleBlockSelectedState(J=!0){let{BlockManager:q,BlockSelection:Z}=this.Editor;this.lastSelectedBlock||(this.lastSelectedBlock=this.firstSelectedBlock=q.currentBlock),this.firstSelectedBlock===this.lastSelectedBlock&&(this.firstSelectedBlock.selected=!0,Z.clearCache(),I.get().removeAllRanges());let X=q.blocks.indexOf(this.lastSelectedBlock)+(J?1:-1),K=q.blocks[X];K&&(this.lastSelectedBlock.selected!==K.selected?(K.selected=!0,Z.clearCache()):(this.lastSelectedBlock.selected=!1,Z.clearCache()),this.lastSelectedBlock=K,this.Editor.InlineToolbar.close(),K.holder.scrollIntoView({block:"nearest"}))}clear(J){let{BlockManager:q,BlockSelection:Z,Caret:X}=this.Editor,K=q.blocks.indexOf(this.firstSelectedBlock),Q=q.blocks.indexOf(this.lastSelectedBlock);if(Z.anyBlockSelected&&K>-1&&Q>-1&&J&&J instanceof KeyboardEvent)switch(J.keyCode){case x.DOWN:case x.RIGHT:X.setToBlock(q.blocks[Math.max(K,Q)],X.positions.END);break;case x.UP:case x.LEFT:X.setToBlock(q.blocks[Math.min(K,Q)],X.positions.START);break;default:X.setToBlock(q.blocks[Math.max(K,Q)],X.positions.END)}this.firstSelectedBlock=this.lastSelectedBlock=null}enableCrossBlockSelection(J){let{UI:q}=this.Editor;I.isCollapsed||this.Editor.BlockSelection.clearSelection(J),q.nodes.redactor.contains(J.target)?this.watchSelection(J):this.Editor.BlockSelection.clearSelection(J)}toggleBlocksSelectedState(J,q){let{BlockManager:Z,BlockSelection:X}=this.Editor,K=Z.blocks.indexOf(J),Q=Z.blocks.indexOf(q),V=J.selected!==q.selected;for(let G=Math.min(K,Q);G<=Math.max(K,Q);G++){let Y=Z.blocks[G];Y!==this.firstSelectedBlock&&Y!==(V?J:q)&&(Z.blocks[G].selected=!Z.blocks[G].selected,X.clearCache())}}}class C6 extends B{constructor(){super(...arguments),this.isStartedAtEditor=!1}toggleReadOnly(J){J?this.disableModuleBindings():this.enableModuleBindings()}enableModuleBindings(){let{UI:J}=this.Editor;this.readOnlyMutableListeners.on(J.nodes.holder,"drop",async(q)=>{await this.processDrop(q)},!0),this.readOnlyMutableListeners.on(J.nodes.holder,"dragstart",()=>{this.processDragStart()}),this.readOnlyMutableListeners.on(J.nodes.holder,"dragover",(q)=>{this.processDragOver(q)},!0)}disableModuleBindings(){this.readOnlyMutableListeners.clearAll()}async processDrop(J){let{BlockManager:q,Paste:Z,Caret:X}=this.Editor;J.preventDefault(),q.blocks.forEach((Q)=>{Q.dropTarget=!1}),I.isAtEditor&&!I.isCollapsed&&this.isStartedAtEditor&&document.execCommand("delete"),this.isStartedAtEditor=!1;let K=q.setCurrentBlockByChildNode(J.target);if(K)this.Editor.Caret.setToBlock(K,X.positions.END);else{let Q=q.setCurrentBlockByChildNode(q.lastBlock.holder);this.Editor.Caret.setToBlock(Q,X.positions.END)}await Z.processDataTransfer(J.dataTransfer,!0)}processDragStart(){I.isAtEditor&&!I.isCollapsed&&(this.isStartedAtEditor=!0),this.Editor.InlineToolbar.close()}processDragOver(J){J.preventDefault()}}var EJ=180,wJ=400;class E6 extends B{constructor({config:J,eventsDispatcher:q}){super({config:J,eventsDispatcher:q}),this.disabled=!1,this.batchingTimeout=null,this.batchingOnChangeQueue=new Map,this.batchTime=wJ,this.mutationObserver=new MutationObserver((Z)=>{this.redactorChanged(Z)}),this.eventsDispatcher.on(H5,(Z)=>{this.particularBlockChanged(Z.event)}),this.eventsDispatcher.on($5,()=>{this.disable()}),this.eventsDispatcher.on(z5,()=>{this.enable()})}enable(){this.mutationObserver.observe(this.Editor.UI.nodes.redactor,{childList:!0,subtree:!0,characterData:!0,attributes:!0}),this.disabled=!1}disable(){this.mutationObserver.disconnect(),this.disabled=!0}particularBlockChanged(J){this.disabled||!m(this.config.onChange)||(this.batchingOnChangeQueue.set(`block:${J.detail.target.id}:event:${J.type}`,J),this.batchingTimeout&&clearTimeout(this.batchingTimeout),this.batchingTimeout=setTimeout(()=>{let q;this.batchingOnChangeQueue.size===1?q=this.batchingOnChangeQueue.values().next().value:q=Array.from(this.batchingOnChangeQueue.values()),this.config.onChange&&this.config.onChange(this.Editor.API.methods,q),this.batchingOnChangeQueue.clear()},this.batchTime))}redactorChanged(J){this.eventsDispatcher.emit(Y2,{mutations:J})}}var w6=class J extends B{constructor(){super(...arguments),this.MIME_TYPE="application/x-editor-js",this.toolsTags={},this.tagsByTool={},this.toolsPatterns=[],this.toolsFiles={},this.exceptionList=[],this.processTool=(q)=>{try{let Z=q.create({},{},!1);if(q.pasteConfig===!1){this.exceptionList.push(q.name);return}if(!m(Z.onPaste))return;this.getTagsConfig(q),this.getFilesConfig(q),this.getPatternsConfig(q)}catch(Z){y(`Paste handling for «${q.name}» Tool hasn't been set up because of the error`,"warn",Z)}},this.handlePasteEvent=async(q)=>{let{BlockManager:Z,Toolbar:X}=this.Editor,K=Z.setCurrentBlockByChildNode(q.target);!K||this.isNativeBehaviour(q.target)&&!q.clipboardData.types.includes("Files")||K&&this.exceptionList.includes(K.name)||(q.preventDefault(),this.processDataTransfer(q.clipboardData),X.close())}}async prepare(){this.processTools()}toggleReadOnly(q){q?this.unsetCallback():this.setCallback()}async processDataTransfer(q,Z=!1){let{Tools:X}=this.Editor,K=q.types;if((K.includes?K.includes("Files"):K.contains("Files"))&&!J0(this.toolsFiles)){await this.processFiles(q.files);return}let Q=q.getData(this.MIME_TYPE),V=q.getData("text/plain"),G=q.getData("text/html");if(Q)try{this.insertEditorJSData(JSON.parse(Q));return}catch{}Z&&V.trim()&&G.trim()&&(G="<p>"+(G.trim()?G:V)+"</p>");let Y=Object.keys(this.toolsTags).reduce(($,D)=>($[D.toLowerCase()]=this.toolsTags[D].sanitizationConfig??{},$),{}),U=Object.assign({},Y,X.getAllInlineToolsSanitizeConfig(),{br:{}}),H=Z0(G,U);!H.trim()||H.trim()===V||!F.isHTMLString(H)?await this.processText(V):await this.processText(H,!0)}async processText(q,Z=!1){let{Caret:X,BlockManager:K}=this.Editor,Q=Z?this.processHTML(q):this.processPlain(q);if(!Q.length)return;if(Q.length===1){Q[0].isBlock?this.processSingleBlock(Q.pop()):this.processInlinePaste(Q.pop());return}let V=K.currentBlock&&K.currentBlock.tool.isDefault&&K.currentBlock.isEmpty;Q.map(async(G,Y)=>this.insertBlock(G,Y===0&&V)),K.currentBlock&&X.setToBlock(K.currentBlock,X.positions.END)}setCallback(){this.listeners.on(this.Editor.UI.nodes.holder,"paste",this.handlePasteEvent)}unsetCallback(){this.listeners.off(this.Editor.UI.nodes.holder,"paste",this.handlePasteEvent)}processTools(){let q=this.Editor.Tools.blockTools;Array.from(q.values()).forEach(this.processTool)}collectTagNames(q){return Y0(q)?[q]:u(q)?Object.keys(q):[]}getTagsConfig(q){if(q.pasteConfig===!1)return;let Z=q.pasteConfig.tags||[],X=[];Z.forEach((K)=>{let Q=this.collectTagNames(K);X.push(...Q),Q.forEach((V)=>{if(Object.prototype.hasOwnProperty.call(this.toolsTags,V)){y(`Paste handler for «${q.name}» Tool on «${V}» tag is skipped because it is already used by «${this.toolsTags[V].tool.name}» Tool.`,"warn");return}let G=u(K)?K[V]:null;this.toolsTags[V.toUpperCase()]={tool:q,sanitizationConfig:G}})}),this.tagsByTool[q.name]=X.map((K)=>K.toUpperCase())}getFilesConfig(q){if(q.pasteConfig===!1)return;let{files:Z={}}=q.pasteConfig,{extensions:X,mimeTypes:K}=Z;!X&&!K||(X&&!Array.isArray(X)&&(y(`«extensions» property of the onDrop config for «${q.name}» Tool should be an array`),X=[]),K&&!Array.isArray(K)&&(y(`«mimeTypes» property of the onDrop config for «${q.name}» Tool should be an array`),K=[]),K&&(K=K.filter((Q)=>e7(Q)?!0:(y(`MIME type value «${Q}» for the «${q.name}» Tool is not a valid MIME type`,"warn"),!1))),this.toolsFiles[q.name]={extensions:X||[],mimeTypes:K||[]})}getPatternsConfig(q){q.pasteConfig===!1||!q.pasteConfig.patterns||J0(q.pasteConfig.patterns)||Object.entries(q.pasteConfig.patterns).forEach(([Z,X])=>{X instanceof RegExp||y(`Pattern ${X} for «${q.name}» Tool is skipped because it should be a Regexp instance.`,"warn"),this.toolsPatterns.push({key:Z,pattern:X,tool:q})})}isNativeBehaviour(q){return F.isNativeInput(q)}async processFiles(q){let{BlockManager:Z}=this.Editor,X;X=await Promise.all(Array.from(q).map((Q)=>this.processFile(Q))),X=X.filter((Q)=>!!Q);let K=Z.currentBlock.tool.isDefault&&Z.currentBlock.isEmpty;X.forEach((Q,V)=>{Z.paste(Q.type,Q.event,V===0&&K)})}async processFile(q){let Z=t7(q),X=Object.entries(this.toolsFiles).find(([Q,{mimeTypes:V,extensions:G}])=>{let[Y,U]=q.type.split("/"),H=G.find((D)=>D.toLowerCase()===Z.toLowerCase()),$=V.find((D)=>{let[A,W]=D.split("/");return A===Y&&(W===U||W==="*")});return!!H||!!$});if(!X)return;let[K]=X;return{event:this.composePasteEvent("file",{file:q}),type:K}}processHTML(q){let{Tools:Z}=this.Editor,X=F.make("DIV");return X.innerHTML=q,this.getNodes(X).map((K)=>{let Q,V=Z.defaultTool,G=!1;switch(K.nodeType){case Node.DOCUMENT_FRAGMENT_NODE:Q=F.make("div"),Q.appendChild(K);break;case Node.ELEMENT_NODE:Q=K,G=!0,this.toolsTags[Q.tagName]&&(V=this.toolsTags[Q.tagName].tool);break}let{tags:Y}=V.pasteConfig||{tags:[]},U=Y.reduce((D,A)=>(this.collectTagNames(A).forEach((W)=>{let z=u(A)?A[W]:null;D[W.toLowerCase()]=z||{}}),D),{}),H=Object.assign({},U,V.baseSanitizeConfig);if(Q.tagName.toLowerCase()==="table"){let D=Z0(Q.outerHTML,H);Q=F.make("div",void 0,{innerHTML:D}).firstChild}else Q.innerHTML=Z0(Q.innerHTML,H);let $=this.composePasteEvent("tag",{data:Q});return{content:Q,isBlock:G,tool:V.name,event:$}}).filter((K)=>{let Q=F.isEmpty(K.content),V=F.isSingleTag(K.content);return!Q||V})}processPlain(q){let{defaultBlock:Z}=this.config;if(!q)return[];let X=Z;return q.split(/\r?\n/).filter((K)=>K.trim()).map((K)=>{let Q=F.make("div");Q.textContent=K;let V=this.composePasteEvent("tag",{data:Q});return{content:Q,tool:X,isBlock:!1,event:V}})}async processSingleBlock(q){let{Caret:Z,BlockManager:X}=this.Editor,{currentBlock:K}=X;if(!K||q.tool!==K.name||!F.containsOnlyInlineElements(q.content.innerHTML)){this.insertBlock(q,(K==null?void 0:K.tool.isDefault)&&K.isEmpty);return}Z.insertContentAtCaretPosition(q.content.innerHTML)}async processInlinePaste(q){let{BlockManager:Z,Caret:X}=this.Editor,{content:K}=q;if(Z.currentBlock&&Z.currentBlock.tool.isDefault&&K.textContent.length<J.PATTERN_PROCESSING_MAX_LENGTH){let Q=await this.processPattern(K.textContent);if(Q){let V=Z.currentBlock&&Z.currentBlock.tool.isDefault&&Z.currentBlock.isEmpty,G=Z.paste(Q.tool,Q.event,V);X.setToBlock(G,X.positions.END);return}}if(Z.currentBlock&&Z.currentBlock.currentInput){let Q=Z.currentBlock.tool.baseSanitizeConfig;document.execCommand("insertHTML",!1,Z0(K.innerHTML,Q))}else this.insertBlock(q)}async processPattern(q){let Z=this.toolsPatterns.find((X)=>{let K=X.pattern.exec(q);return K?q===K.shift():!1});return Z?{event:this.composePasteEvent("pattern",{key:Z.key,data:q}),tool:Z.tool.name}:void 0}insertBlock(q,Z=!1){let{BlockManager:X,Caret:K}=this.Editor,{currentBlock:Q}=X,V;if(Z&&Q&&Q.isEmpty){V=X.paste(q.tool,q.event,!0),K.setToBlock(V,K.positions.END);return}V=X.paste(q.tool,q.event),K.setToBlock(V,K.positions.END)}insertEditorJSData(q){let{BlockManager:Z,Caret:X,Tools:K}=this.Editor;W2(q,(Q)=>K.blockTools.get(Q).sanitizeConfig).forEach(({tool:Q,data:V},G)=>{let Y=!1;G===0&&(Y=Z.currentBlock&&Z.currentBlock.tool.isDefault&&Z.currentBlock.isEmpty);let U=Z.insert({tool:Q,data:V,replace:Y});X.setToBlock(U,X.positions.END)})}processElementNode(q,Z,X){let K=Object.keys(this.toolsTags),Q=q,{tool:V}=this.toolsTags[Q.tagName]||{},G=this.tagsByTool[V==null?void 0:V.name]||[],Y=K.includes(Q.tagName),U=F.blockElements.includes(Q.tagName.toLowerCase()),H=Array.from(Q.children).some(({tagName:D})=>K.includes(D)&&!G.includes(D)),$=Array.from(Q.children).some(({tagName:D})=>F.blockElements.includes(D.toLowerCase()));if(!U&&!Y&&!H)return X.appendChild(Q),[...Z,X];if(Y&&!H||U&&!$&&!H)return[...Z,X,Q]}getNodes(q){let Z=Array.from(q.childNodes),X,K=(Q,V)=>{if(F.isEmpty(V)&&!F.isSingleTag(V))return Q;let G=Q[Q.length-1],Y=new DocumentFragment;switch(G&&F.isFragment(G)&&(Y=Q.pop()),V.nodeType){case Node.ELEMENT_NODE:if(X=this.processElementNode(V,Q,Y),X)return X;break;case Node.TEXT_NODE:return Y.appendChild(V),[...Q,Y];default:return[...Q,Y]}return[...Q,...Array.from(V.childNodes).reduce(K,[])]};return Z.reduce(K,[])}composePasteEvent(q,Z){return new CustomEvent(q,{detail:Z})}};w6.PATTERN_PROCESSING_MAX_LENGTH=450;var yJ=w6;class y6 extends B{constructor(){super(...arguments),this.toolsDontSupportReadOnly=[],this.readOnlyEnabled=!1}get isEnabled(){return this.readOnlyEnabled}async prepare(){let{Tools:J}=this.Editor,{blockTools:q}=J,Z=[];Array.from(q.entries()).forEach(([X,K])=>{K.isReadOnlySupported||Z.push(X)}),this.toolsDontSupportReadOnly=Z,this.config.readOnly&&Z.length>0&&this.throwCriticalError(),this.toggle(this.config.readOnly,!0)}async toggle(J=!this.readOnlyEnabled,q=!1){J&&this.toolsDontSupportReadOnly.length>0&&this.throwCriticalError();let Z=this.readOnlyEnabled;this.readOnlyEnabled=J;for(let K in this.Editor)this.Editor[K].toggleReadOnly&&this.Editor[K].toggleReadOnly(J);if(Z===J)return this.readOnlyEnabled;if(q)return this.readOnlyEnabled;this.Editor.ModificationsObserver.disable();let X=await this.Editor.Saver.save();return await this.Editor.BlockManager.clear(),await this.Editor.Renderer.render(X.blocks),this.Editor.ModificationsObserver.enable(),this.readOnlyEnabled}throwCriticalError(){throw new D2(`To enable read-only mode all connected tools should support it. Tools ${this.toolsDontSupportReadOnly.join(", ")} don't support read-only mode.`)}}class h0 extends B{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=[]}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"}}prepare(){this.enableModuleBindings()}startSelection(J,q){let Z=document.elementFromPoint(J-window.pageXOffset,q-window.pageYOffset);Z.closest(`.${this.Editor.Toolbar.CSS.toolbar}`)||(this.Editor.BlockSelection.allBlocksSelected=!1,this.clearSelection(),this.stackOfSelected=[]);let X=[`.${c.CSS.content}`,`.${this.Editor.Toolbar.CSS.toolbar}`,`.${this.Editor.InlineToolbar.CSS.inlineToolbar}`],K=Z.closest("."+this.Editor.UI.CSS.editorWrapper),Q=X.some((V)=>!!Z.closest(V));!K||Q||(this.mousedown=!0,this.startX=J,this.startY=q)}endSelection(){this.mousedown=!1,this.startX=0,this.startY=0,this.overlayRectangle.style.display="none"}isRectActivated(){return this.isRectSelectionActivated}clearSelection(){this.isRectSelectionActivated=!1}enableModuleBindings(){let{container:J}=this.genHTML();this.listeners.on(J,"mousedown",(q)=>{this.processMouseDown(q)},!1),this.listeners.on(document.body,"mousemove",K2((q)=>{this.processMouseMove(q)},10),{passive:!0}),this.listeners.on(document.body,"mouseleave",()=>{this.processMouseLeave()}),this.listeners.on(window,"scroll",K2((q)=>{this.processScroll(q)},10),{passive:!0}),this.listeners.on(document.body,"mouseup",()=>{this.processMouseUp()},!1)}processMouseDown(J){if(J.button!==this.MAIN_MOUSE_BUTTON)return;J.target.closest(F.allInputsSelector)!==null||this.startSelection(J.pageX,J.pageY)}processMouseMove(J){this.changingRectangle(J),this.scrollByZones(J.clientY)}processMouseLeave(){this.clearSelection(),this.endSelection()}processScroll(J){this.changingRectangle(J)}processMouseUp(){this.clearSelection(),this.endSelection()}scrollByZones(J){if(this.inScrollZone=null,J<=this.HEIGHT_OF_SCROLL_ZONE&&(this.inScrollZone=this.TOP_SCROLL_ZONE),document.documentElement.clientHeight-J<=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)}genHTML(){let{UI:J}=this.Editor,q=J.nodes.holder.querySelector("."+J.CSS.editorWrapper),Z=F.make("div",h0.CSS.overlay,{}),X=F.make("div",h0.CSS.overlayContainer,{}),K=F.make("div",h0.CSS.rect,{});return X.appendChild(K),Z.appendChild(X),q.appendChild(Z),this.overlayRectangle=K,{container:q,overlay:Z}}scrollVertical(J){if(!(this.inScrollZone&&this.mousedown))return;let q=window.pageYOffset;window.scrollBy(0,J),this.mouseY+=window.pageYOffset-q,setTimeout(()=>{this.scrollVertical(J)},0)}changingRectangle(J){if(!this.mousedown)return;J.pageY!==void 0&&(this.mouseX=J.pageX,this.mouseY=J.pageY);let{rightPos:q,leftPos:Z,index:X}=this.genInfoForMouseSelection(),K=this.startX>q&&this.mouseX>q,Q=this.startX<Z&&this.mouseX<Z;this.rectCrossesBlocks=!(K||Q),this.isRectSelectionActivated||(this.rectCrossesBlocks=!1,this.isRectSelectionActivated=!0,this.shrinkRectangleToPoint(),this.overlayRectangle.style.display="block"),this.updateRectangleSize(),this.Editor.Toolbar.close(),X!==void 0&&(this.trySelectNextBlock(X),this.inverseSelection(),I.get().removeAllRanges())}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`}inverseSelection(){let J=this.Editor.BlockManager.getBlockByIndex(this.stackOfSelected[0]).selected;if(this.rectCrossesBlocks&&!J)for(let q of this.stackOfSelected)this.Editor.BlockSelection.selectBlockByIndex(q);if(!this.rectCrossesBlocks&&J)for(let q of this.stackOfSelected)this.Editor.BlockSelection.unSelectBlockByIndex(q)}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`)}genInfoForMouseSelection(){let J=document.body.offsetWidth/2,q=this.mouseY-window.pageYOffset,Z=document.elementFromPoint(J,q),X=this.Editor.BlockManager.getBlockByChildNode(Z),K;X!==void 0&&(K=this.Editor.BlockManager.blocks.findIndex((U)=>U.holder===X.holder));let Q=this.Editor.BlockManager.lastBlock.holder.querySelector("."+c.CSS.content),V=Number.parseInt(window.getComputedStyle(Q).width,10)/2,G=J-V,Y=J+V;return{index:K,leftPos:G,rightPos:Y}}addBlockInSelection(J){this.rectCrossesBlocks&&this.Editor.BlockSelection.selectBlockByIndex(J),this.stackOfSelected.push(J)}trySelectNextBlock(J){let q=this.stackOfSelected[this.stackOfSelected.length-1]===J,Z=this.stackOfSelected.length;if(q)return;let V=this.stackOfSelected[Z-1]-this.stackOfSelected[Z-2]>0,G=0;Z>1&&(G=V?1:-1);let Y=J>this.stackOfSelected[Z-1]&&G===1,U=J<this.stackOfSelected[Z-1]&&G===-1,H=!(Y||U||G===0);if(!H&&(J>this.stackOfSelected[Z-1]||this.stackOfSelected[Z-1]===void 0)){let A=this.stackOfSelected[Z-1]+1||J;for(A;A<=J;A++)this.addBlockInSelection(A);return}if(!H&&J<this.stackOfSelected[Z-1]){for(let A=this.stackOfSelected[Z-1]-1;A>=J;A--)this.addBlockInSelection(A);return}if(!H)return;let $=Z-1,D;for(J>this.stackOfSelected[Z-1]?D=()=>J>this.stackOfSelected[$]:D=()=>J<this.stackOfSelected[$];D();)this.rectCrossesBlocks&&this.Editor.BlockSelection.unSelectBlockByIndex(this.stackOfSelected[$]),this.stackOfSelected.pop(),$--}}class v6 extends B{async render(J){return new Promise((q)=>{let{Tools:Z,BlockManager:X}=this.Editor;if(J.length===0)X.insert();else{let K=J.map(({type:Q,data:V,tunes:G,id:Y})=>{Z.available.has(Q)===!1&&(e(`Tool «${Q}» is not found. Check 'tools' property at the Editor.js config.`,"warn"),V=this.composeStubDataForTool(Q,V,Y),Q=Z.stubTool);let U;try{U=X.composeBlock({id:Y,tool:Q,data:V,tunes:G})}catch(H){y(`Block «${Q}» skipped because of plugins error`,"error",{data:V,error:H}),V=this.composeStubDataForTool(Q,V,Y),Q=Z.stubTool,U=X.composeBlock({id:Y,tool:Q,data:V,tunes:G})}return U});X.insertMany(K)}window.requestIdleCallback(()=>{q()},{timeout:2000})})}composeStubDataForTool(J,q,Z){let{Tools:X}=this.Editor,K=J;if(X.unavailable.has(J)){let Q=X.unavailable.get(J).toolbox;Q!==void 0&&Q[0].title!==void 0&&(K=Q[0].title)}return{savedData:{id:Z,type:J,data:q},title:K}}}class k6 extends B{async save(){let{BlockManager:J,Tools:q}=this.Editor,Z=J.blocks,X=[];try{Z.forEach((V)=>{X.push(this.getSavedData(V))});let K=await Promise.all(X),Q=await W2(K,(V)=>q.blockTools.get(V).sanitizeConfig);return this.makeOutput(Q)}catch(K){e("Saving failed due to the Error %o","error",K)}}async getSavedData(J){let q=await J.save(),Z=q&&await J.validate(q.data);return{...q,isValid:Z}}makeOutput(J){let q=[];return J.forEach(({id:Z,tool:X,data:K,tunes:Q,isValid:V})=>{if(!V){y(`Block «${X}» skipped because saved data is invalid`);return}if(X===this.Editor.Tools.stubTool){q.push(K);return}let G={id:Z,type:X,data:K,...!J0(Q)&&{tunes:Q}};q.push(G)}),{time:+new Date,blocks:q,version:"2.31.5"}}}(function(){try{if(typeof document<"u"){var J=document.createElement("style");J.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(J)}}catch(q){console.error("vite-plugin-css-injected-by-js",q)}})();var vJ='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M8 9V7.2C8 7.08954 8.08954 7 8.2 7L12 7M16 9V7.2C16 7.08954 15.9105 7 15.8 7L12 7M12 7L12 17M12 17H10M12 17H14"/></svg>';function kJ(J){let q=document.createElement("div");q.innerHTML=J.trim();let Z=document.createDocumentFragment();return Z.append(...Array.from(q.childNodes)),Z}class U3{static get DEFAULT_PLACEHOLDER(){return""}constructor({data:J,config:q,api:Z,readOnly:X}){this.api=Z,this.readOnly=X,this._CSS={block:this.api.styles.block,wrapper:"ce-paragraph"},this.readOnly||(this.onKeyUp=this.onKeyUp.bind(this)),this._placeholder=q.placeholder?q.placeholder:U3.DEFAULT_PLACEHOLDER,this._data=J??{},this._element=null,this._preserveBlank=q.preserveBlank??!1}onKeyUp(J){if(J.code!=="Backspace"&&J.code!=="Delete"||!this._element)return;let{textContent:q}=this._element;q===""&&(this._element.innerHTML="")}drawView(){let J=document.createElement("DIV");return J.classList.add(this._CSS.wrapper,this._CSS.block),J.contentEditable="false",J.dataset.placeholderActive=this.api.i18n.t(this._placeholder),this._data.text&&(J.innerHTML=this._data.text),this.readOnly||(J.contentEditable="true",J.addEventListener("keyup",this.onKeyUp)),J}render(){return this._element=this.drawView(),this._element}merge(J){if(!this._element)return;this._data.text+=J.text;let q=kJ(J.text);this._element.appendChild(q),this._element.normalize()}validate(J){return!(J.text.trim()===""&&!this._preserveBlank)}save(J){return{text:J.innerHTML}}onPaste(J){let q={text:J.detail.data.innerHTML};this._data=q,window.requestAnimationFrame(()=>{this._element&&(this._element.innerHTML=this._data.text||"")})}static get conversionConfig(){return{export:"text",import:"text"}}static get sanitize(){return{text:{br:!0}}}static get isReadOnlySupported(){return!0}static get pasteConfig(){return{tags:["P"]}}static get toolbox(){return{icon:vJ,title:"Text"}}}class R1{constructor(){this.commandName="bold"}static get sanitize(){return{b:{}}}render(){return{icon:T8,name:"bold",onActivate:()=>{document.execCommand(this.commandName)},isActive:()=>document.queryCommandState(this.commandName)}}get shortcut(){return"CMD+B"}}R1.isInline=!0;R1.title="Bold";class O1{constructor(){this.commandName="italic",this.CSS={button:"ce-inline-tool",buttonActive:"ce-inline-tool--active",buttonModifier:"ce-inline-tool--italic"},this.nodes={button:null}}static get sanitize(){return{i:{}}}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=k8,this.nodes.button}surround(){document.execCommand(this.commandName)}checkState(){let J=document.queryCommandState(this.commandName);return this.nodes.button.classList.toggle(this.CSS.buttonActive,J),J}get shortcut(){return"CMD+I"}}O1.isInline=!0;O1.title="Italic";class j1{constructor({api:J}){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=J.toolbar,this.inlineToolbar=J.inlineToolbar,this.notifier=J.notifier,this.i18n=J.i18n,this.selection=new I}static get sanitize(){return{a:{href:!0,target:"_blank",rel:"nofollow"}}}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=i4,this.nodes.button}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",(J)=>{J.keyCode===this.ENTER_KEY&&this.enterPressed(J)}),this.nodes.input}surround(J){if(J){this.inputOpened?(this.selection.restore(),this.selection.removeFakeBackground()):(this.selection.setFakeBackground(),this.selection.save());let q=this.selection.findParentTag("A");if(q){this.inputOpened?(this.closeActions(!1),this.checkState()):(this.selection.expandToTag(q),this.unlink(),this.closeActions(),this.checkState(),this.toolbar.close());return}}this.toggleActions()}checkState(){let J=this.selection.findParentTag("A");if(J){this.nodes.button.innerHTML=f8,this.nodes.button.classList.add(this.CSS.buttonUnlink),this.nodes.button.classList.add(this.CSS.buttonActive),this.openActions();let q=J.getAttribute("href");this.nodes.input.defaultValue=q!=="null"?q:"",this.selection.save()}else this.nodes.button.innerHTML=i4,this.nodes.button.classList.remove(this.CSS.buttonUnlink),this.nodes.button.classList.remove(this.CSS.buttonActive);return!!J}clear(){this.closeActions()}get shortcut(){return"CMD+K"}toggleActions(){this.inputOpened?this.closeActions(!1):this.openActions(!0)}openActions(J=!1){this.nodes.input.classList.add(this.CSS.inputShowed),J&&this.nodes.input.focus(),this.inputOpened=!0}closeActions(J=!0){if(this.selection.isFakeBackgroundEnabled){let q=new I;q.save(),this.selection.restore(),this.selection.removeFakeBackground(),q.restore()}this.nodes.input.classList.remove(this.CSS.inputShowed),this.nodes.input.value="",J&&this.selection.clearSaved(),this.inputOpened=!1}enterPressed(J){let q=this.nodes.input.value||"";if(!q.trim()){this.selection.restore(),this.unlink(),J.preventDefault(),this.closeActions();return}if(!this.validateURL(q)){this.notifier.show({message:"Pasted link is not valid.",style:"error"}),y("Incorrect Link pasted","warn",q);return}q=this.prepareLink(q),this.selection.restore(),this.selection.removeFakeBackground(),this.insertLink(q),J.preventDefault(),J.stopPropagation(),J.stopImmediatePropagation(),this.selection.collapseToEnd(),this.inlineToolbar.close()}validateURL(J){return!/\s/.test(J)}prepareLink(J){return J=J.trim(),J=this.addProtocol(J),J}addProtocol(J){if(/^(\w+):(\/\/)?/.test(J))return J;let q=/^\/[^/\s]/.test(J),Z=J.substring(0,1)==="#",X=/^\/\/[^/\s]/.test(J);return!q&&!Z&&!X&&(J="http://"+J),J}insertLink(J){let q=this.selection.findParentTag("A");q&&this.selection.expandToTag(q),document.execCommand(this.commandLink,!1,J)}unlink(){document.execCommand(this.commandUnlink)}}j1.isInline=!0;j1.title="Link";class H3{constructor({api:J}){this.i18nAPI=J.i18n,this.blocksAPI=J.blocks,this.selectionAPI=J.selection,this.toolsAPI=J.tools,this.caretAPI=J.caret}async render(){let J=I.get(),q=this.blocksAPI.getBlockByElement(J.anchorNode);if(q===void 0)return[];let Z=this.toolsAPI.getBlockTools(),X=await F5(q,Z);if(X.length===0)return[];let K=X.reduce((Y,U)=>{var H;return(H=U.toolbox)==null||H.forEach(($)=>{Y.push({icon:$.icon,title:r.t(t.toolNames,$.title),name:U.name,closeOnActivate:!0,onActivate:async()=>{let D=await this.blocksAPI.convert(q.id,U.name,$.data);this.caretAPI.setToBlock(D,"end")}})}),Y},[]),Q=await q.getActiveToolboxEntry(),V=Q!==void 0?Q.icon:m5,G=!C0();return{icon:V,name:"convert-to",hint:{title:this.i18nAPI.t("Convert to")},children:{searchable:G,items:K,onOpen:()=>{G&&(this.selectionAPI.setFakeBackground(),this.selectionAPI.save())},onClose:()=>{G&&(this.selectionAPI.restore(),this.selectionAPI.removeFakeBackground())}}}}}H3.isInline=!0;class $3{constructor({data:J,api:q}){this.CSS={wrapper:"ce-stub",info:"ce-stub__info",title:"ce-stub__title",subtitle:"ce-stub__subtitle"},this.api=q,this.title=J.title||this.api.i18n.t("Error"),this.subtitle=this.api.i18n.t("The block can not be displayed correctly."),this.savedData=J.savedData,this.wrapper=this.make()}render(){return this.wrapper}save(){return this.savedData}make(){let J=F.make("div",this.CSS.wrapper),q=p8,Z=F.make("div",this.CSS.info),X=F.make("div",this.CSS.title,{textContent:this.title}),K=F.make("div",this.CSS.subtitle,{textContent:this.subtitle});return J.innerHTML=q,Z.appendChild(X),Z.appendChild(K),J.appendChild(Z),J}}$3.isReadOnlySupported=!0;class g6 extends z1{constructor(){super(...arguments),this.type=A0.Inline}get title(){return this.constructable[Y1.Title]}create(){return new this.constructable({api:this.api,config:this.settings})}get isReadOnlySupported(){return this.constructable[Y1.IsReadOnlySupported]??!1}}class m6 extends z1{constructor(){super(...arguments),this.type=A0.Tune}create(J,q){return new this.constructable({api:this.api,config:this.settings,block:q,data:J})}}class l extends Map{get blockTools(){let J=Array.from(this.entries()).filter(([,q])=>q.isBlock());return new l(J)}get inlineTools(){let J=Array.from(this.entries()).filter(([,q])=>q.isInline());return new l(J)}get blockTunes(){let J=Array.from(this.entries()).filter(([,q])=>q.isTune());return new l(J)}get internalTools(){let J=Array.from(this.entries()).filter(([,q])=>q.isInternal);return new l(J)}get externalTools(){let J=Array.from(this.entries()).filter(([,q])=>!q.isInternal);return new l(J)}}var{defineProperty:gJ,getOwnPropertyDescriptor:mJ}=Object,b6=(J,q,Z,X)=>{for(var K=X>1?void 0:X?mJ(q,Z):q,Q=J.length-1,V;Q>=0;Q--)(V=J[Q])&&(K=(X?V(q,Z,K):V(K))||K);return X&&K&&gJ(q,Z,K),K};class I1 extends z1{constructor(){super(...arguments),this.type=A0.Block,this.inlineTools=new l,this.tunes=new l}create(J,q,Z){return new this.constructable({data:J,block:q,readOnly:Z,api:this.api,config:this.settings})}get isReadOnlySupported(){return this.constructable[_0.IsReadOnlySupported]===!0}get isLineBreaksEnabled(){return this.constructable[_0.IsEnabledLineBreaks]}get toolbox(){let J=this.constructable[_0.Toolbox],q=this.config[n0.Toolbox];if(!J0(J)&&q!==!1)return q?Array.isArray(J)?Array.isArray(q)?q.map((Z,X)=>{let K=J[X];return K?{...K,...Z}:Z}):[q]:Array.isArray(q)?q:[{...J,...q}]:Array.isArray(J)?J:[J]}get conversionConfig(){return this.constructable[_0.ConversionConfig]}get enabledInlineTools(){return this.config[n0.EnabledInlineTools]||!1}get enabledBlockTunes(){return this.config[n0.EnabledBlockTunes]}get pasteConfig(){return this.constructable[_0.PasteConfig]??{}}get sanitizeConfig(){let J=super.sanitizeConfig,q=this.baseSanitizeConfig;if(J0(J))return q;let Z={};for(let X in J)if(Object.prototype.hasOwnProperty.call(J,X)){let K=J[X];u(K)?Z[X]=Object.assign({},q,K):Z[X]=K}return Z}get baseSanitizeConfig(){let J={};return Array.from(this.inlineTools.values()).forEach((q)=>Object.assign(J,q.sanitizeConfig)),Array.from(this.tunes.values()).forEach((q)=>Object.assign(J,q.sanitizeConfig)),J}}b6([B0],I1.prototype,"sanitizeConfig",1);b6([B0],I1.prototype,"baseSanitizeConfig",1);class f6{constructor(J,q,Z){this.api=Z,this.config=J,this.editorConfig=q}get(J){let{class:q,isInternal:Z=!1,...X}=this.config[J],K=this.getConstructor(q),Q=q[H2.IsTune];return new K({name:J,constructable:q,config:X,api:this.api.getMethodsForTool(J,Q),isDefault:J===this.editorConfig.defaultBlock,defaultPlaceholder:this.editorConfig.placeholder,isInternal:Z})}getConstructor(J){switch(!0){case J[Y1.IsInline]:return g6;case J[H2.IsTune]:return m6;default:return I1}}}class z3{constructor({api:J}){this.CSS={animation:"wobble"},this.api=J}render(){return{icon:B8,title:this.api.i18n.t("Move down"),onActivate:()=>this.handleClick(),name:"move-down"}}handleClick(){let J=this.api.blocks.getCurrentBlockIndex(),q=this.api.blocks.getBlockByIndex(J+1);if(!q)throw Error("Unable to move Block down since it is already the last");let Z=q.holder,X=Z.getBoundingClientRect(),K=Math.abs(window.innerHeight-Z.offsetHeight);X.top<window.innerHeight&&(K=window.scrollY+Z.offsetHeight),window.scrollTo(0,K),this.api.blocks.move(J+1),this.api.toolbar.toggleBlockSettings(!0)}}z3.isTune=!0;class D3{constructor({api:J}){this.api=J}render(){return{icon:y8,title:this.api.i18n.t("Delete"),name:"delete",confirmation:{title:this.api.i18n.t("Click to delete"),onActivate:()=>this.handleClick()}}}handleClick(){this.api.blocks.delete()}}D3.isTune=!0;class F3{constructor({api:J}){this.CSS={animation:"wobble"},this.api=J}render(){return{icon:w8,title:this.api.i18n.t("Move up"),onActivate:()=>this.handleClick(),name:"move-up"}}handleClick(){let J=this.api.blocks.getCurrentBlockIndex(),q=this.api.blocks.getBlockByIndex(J),Z=this.api.blocks.getBlockByIndex(J-1);if(J===0||!q||!Z)throw Error("Unable to move Block up since it is already the first");let X=q.holder,K=Z.holder,Q=X.getBoundingClientRect(),V=K.getBoundingClientRect(),G;V.top>0?G=Math.abs(Q.top)-Math.abs(V.top):G=Math.abs(Q.top)+V.height,window.scrollBy(0,-1*G),this.api.blocks.move(J-1),this.api.toolbar.toggleBlockSettings(!0)}}F3.isTune=!0;var{defineProperty:bJ,getOwnPropertyDescriptor:fJ}=Object,pJ=(J,q,Z,X)=>{for(var K=X>1?void 0:X?fJ(q,Z):q,Q=J.length-1,V;Q>=0;Q--)(V=J[Q])&&(K=(X?V(q,Z,K):V(K))||K);return X&&K&&bJ(q,Z,K),K};class W3 extends B{constructor(){super(...arguments),this.stubTool="stub",this.toolsAvailable=new l,this.toolsUnavailable=new l}get available(){return this.toolsAvailable}get unavailable(){return this.toolsUnavailable}get inlineTools(){return this.available.inlineTools}get blockTools(){return this.available.blockTools}get blockTunes(){return this.available.blockTunes}get defaultTool(){return this.blockTools.get(this.config.defaultBlock)}get internal(){return this.available.internalTools}async prepare(){if(this.validateTools(),this.config.tools=Q2({},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");let J=this.prepareConfig();this.factory=new f6(J,this.config,this.Editor.API);let q=this.getListOfPrepareFunctions(J);if(q.length===0)return Promise.resolve();await n7(q,(Z)=>{this.toolPrepareMethodSuccess(Z)},(Z)=>{this.toolPrepareMethodFallback(Z)}),this.prepareBlockTools()}getAllInlineToolsSanitizeConfig(){let J={};return Array.from(this.inlineTools.values()).forEach((q)=>{Object.assign(J,q.sanitizeConfig)}),J}destroy(){Object.values(this.available).forEach(async(J)=>{m(J.reset)&&await J.reset()})}get internalTools(){return{convertTo:{class:H3,isInternal:!0},link:{class:j1,isInternal:!0},bold:{class:R1,isInternal:!0},italic:{class:O1,isInternal:!0},paragraph:{class:U3,inlineToolbar:!0,isInternal:!0},stub:{class:$3,isInternal:!0},moveUp:{class:F3,isInternal:!0},delete:{class:D3,isInternal:!0},moveDown:{class:z3,isInternal:!0}}}toolPrepareMethodSuccess(J){let q=this.factory.get(J.toolName);if(q.isInline()){let Z=["render"].filter((X)=>!q.create()[X]);if(Z.length){y(`Incorrect Inline Tool: ${q.name}. Some of required methods is not implemented %o`,"warn",Z),this.toolsUnavailable.set(q.name,q);return}}this.toolsAvailable.set(q.name,q)}toolPrepareMethodFallback(J){this.toolsUnavailable.set(J.toolName,this.factory.get(J.toolName))}getListOfPrepareFunctions(J){let q=[];return Object.entries(J).forEach(([Z,X])=>{q.push({function:m(X.class.prepare)?X.class.prepare:()=>{},data:{toolName:Z,config:X.config}})}),q}prepareBlockTools(){Array.from(this.blockTools.values()).forEach((J)=>{this.assignInlineToolsToBlockTool(J),this.assignBlockTunesToBlockTool(J)})}assignInlineToolsToBlockTool(J){if(this.config.inlineToolbar!==!1){if(J.enabledInlineTools===!0){J.inlineTools=new l(Array.isArray(this.config.inlineToolbar)?this.config.inlineToolbar.map((q)=>[q,this.inlineTools.get(q)]):Array.from(this.inlineTools.entries()));return}Array.isArray(J.enabledInlineTools)&&(J.inlineTools=new l(["convertTo",...J.enabledInlineTools].map((q)=>[q,this.inlineTools.get(q)])))}}assignBlockTunesToBlockTool(J){if(J.enabledBlockTunes!==!1){if(Array.isArray(J.enabledBlockTunes)){let q=new l(J.enabledBlockTunes.map((Z)=>[Z,this.blockTunes.get(Z)]));J.tunes=new l([...q,...this.blockTunes.internalTools]);return}if(Array.isArray(this.config.tunes)){let q=new l(this.config.tunes.map((Z)=>[Z,this.blockTunes.get(Z)]));J.tunes=new l([...q,...this.blockTunes.internalTools]);return}J.tunes=this.blockTunes.internalTools}}validateTools(){for(let J in this.config.tools)if(Object.prototype.hasOwnProperty.call(this.config.tools,J)){if(J in this.internalTools)return;let q=this.config.tools[J];if(!m(q)&&!m(q.class))throw Error(`Tool «${J}» must be a constructor function or an object with function in the «class» property`)}}prepareConfig(){let J={};for(let q in this.config.tools)u(this.config.tools[q])?J[q]=this.config.tools[q]:J[q]={class:this.config.tools[q]};return J}}pJ([B0],W3.prototype,"getAllInlineToolsSanitizeConfig",1);var hJ=':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)}\n';class p6 extends B{constructor(){super(...arguments),this.isMobile=!1,this.contentRectCache=null,this.resizeDebouncer=a4(()=>{this.windowResize()},200),this.selectionChangeDebounced=a4(()=>{this.selectionChanged()},EJ),this.documentTouchedListener=(J)=>{this.documentTouched(J)}}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"}}get contentRect(){if(this.contentRectCache!==null)return this.contentRectCache;let J=this.nodes.wrapper.querySelector(`.${c.CSS.content}`);return J?(this.contentRectCache=J.getBoundingClientRect(),this.contentRectCache):{width:650,left:0,right:0}}async prepare(){this.setIsMobile(),this.make(),this.loadStyles()}toggleReadOnly(J){J?this.unbindReadOnlySensitiveListeners():window.requestIdleCallback(()=>{this.bindReadOnlySensitiveListeners()},{timeout:2000})}checkEmptiness(){let{BlockManager:J}=this.Editor;this.nodes.wrapper.classList.toggle(this.CSS.editorEmpty,J.isEditorEmpty)}get someToolbarOpened(){let{Toolbar:J,BlockSettings:q,InlineToolbar:Z}=this.Editor;return!!(q.opened||Z.opened||J.toolbox.opened)}get someFlipperButtonFocused(){return this.Editor.Toolbar.toolbox.hasFocus()?!0:Object.entries(this.Editor).filter(([J,q])=>q.flipper instanceof P0).some(([J,q])=>q.flipper.hasFocus())}destroy(){this.nodes.holder.innerHTML="",this.unbindReadOnlyInsensitiveListeners()}closeAllToolbars(){let{Toolbar:J,BlockSettings:q,InlineToolbar:Z}=this.Editor;q.close(),Z.close(),J.toolbox.close()}setIsMobile(){let J=window.innerWidth<V5;J!==this.isMobile&&this.eventsDispatcher.emit(c0,{isEnabled:this.isMobile}),this.isMobile=J}make(){this.nodes.holder=F.getHolder(this.config.holder),this.nodes.wrapper=F.make("div",[this.CSS.editorWrapper,...this.isRtl?[this.CSS.editorRtlFix]:[]]),this.nodes.redactor=F.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()}loadStyles(){if(F.get("editor-js-styles"))return;let q=F.make("style",null,{id:"editor-js-styles",textContent:hJ.toString()});this.config.style&&!J0(this.config.style)&&this.config.style.nonce&&q.setAttribute("nonce",this.config.style.nonce),F.prepend(document.head,q)}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})}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)}bindReadOnlySensitiveListeners(){this.readOnlyMutableListeners.on(this.nodes.redactor,"click",(J)=>{this.redactorClicked(J)},!1),this.readOnlyMutableListeners.on(document,"keydown",(J)=>{this.documentKeydown(J)},!0),this.readOnlyMutableListeners.on(document,"mousedown",(J)=>{this.documentClicked(J)},!0),this.watchBlockHoveredEvents(),this.enableInputsEmptyMark()}watchBlockHoveredEvents(){let J;this.readOnlyMutableListeners.on(this.nodes.redactor,"mousemove",K2((q)=>{let Z=q.target.closest(".ce-block");this.Editor.BlockSelection.anyBlockSelected||Z&&J!==Z&&(J=Z,this.eventsDispatcher.emit(i5,{block:this.Editor.BlockManager.getBlockByChildNode(Z)}))},20),{passive:!0})}unbindReadOnlySensitiveListeners(){this.readOnlyMutableListeners.clearAll()}windowResize(){this.contentRectCache=null,this.setIsMobile()}documentKeydown(J){switch(J.keyCode){case x.ENTER:this.enterPressed(J);break;case x.BACKSPACE:case x.DELETE:this.backspacePressed(J);break;case x.ESC:this.escapePressed(J);break;default:this.defaultBehaviour(J);break}}defaultBehaviour(J){let{currentBlock:q}=this.Editor.BlockManager,Z=J.target.closest(`.${this.CSS.editorWrapper}`),X=J.altKey||J.ctrlKey||J.metaKey||J.shiftKey;if(q!==void 0&&Z===null){this.Editor.BlockEvents.keydown(J);return}Z||q&&X||(this.Editor.BlockManager.unsetCurrentBlock(),this.Editor.Toolbar.close())}backspacePressed(J){let{BlockManager:q,BlockSelection:Z,Caret:X}=this.Editor;if(Z.anyBlockSelected&&!I.isSelectionExists){let K=q.removeSelectedBlocks(),Q=q.insertDefaultBlockAtIndex(K,!0);X.setToBlock(Q,X.positions.START),Z.clearSelection(J),J.preventDefault(),J.stopPropagation(),J.stopImmediatePropagation()}}escapePressed(J){this.Editor.BlockSelection.clearSelection(J),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()}enterPressed(J){let{BlockManager:q,BlockSelection:Z}=this.Editor;if(this.someToolbarOpened)return;let X=q.currentBlockIndex>=0;if(Z.anyBlockSelected&&!I.isSelectionExists){Z.clearSelection(J),J.preventDefault(),J.stopImmediatePropagation(),J.stopPropagation();return}if(!this.someToolbarOpened&&X&&J.target.tagName==="BODY"){let K=this.Editor.BlockManager.insert();J.preventDefault(),this.Editor.Caret.setToBlock(K),this.Editor.Toolbar.moveAndOpen(K)}this.Editor.BlockSelection.clearSelection(J)}documentClicked(J){var q,Z;if(!J.isTrusted)return;let X=J.target;this.nodes.holder.contains(X)||I.isAtEditor||(this.Editor.BlockManager.unsetCurrentBlock(),this.Editor.Toolbar.close());let K=(q=this.Editor.BlockSettings.nodes.wrapper)==null?void 0:q.contains(X),Q=(Z=this.Editor.Toolbar.nodes.settingsToggler)==null?void 0:Z.contains(X),V=K||Q;if(this.Editor.BlockSettings.opened&&!V){this.Editor.BlockSettings.close();let G=this.Editor.BlockManager.getBlockByChildNode(X);this.Editor.Toolbar.moveAndOpen(G)}this.Editor.BlockSelection.clearSelection(J)}documentTouched(J){let q=J.target;if(q===this.nodes.redactor){let Z=J instanceof MouseEvent?J.clientX:J.touches[0].clientX,X=J instanceof MouseEvent?J.clientY:J.touches[0].clientY;q=document.elementFromPoint(Z,X)}try{this.Editor.BlockManager.setCurrentBlockByChildNode(q)}catch{this.Editor.RectangleSelection.isRectActivated()||this.Editor.Caret.setToTheLastBlock()}this.Editor.ReadOnly.isEnabled||this.Editor.Toolbar.moveAndOpen()}redactorClicked(J){if(!I.isCollapsed)return;let q=J.target,Z=J.metaKey||J.ctrlKey,X=F.getClosestAnchor(q);if(X&&Z){J.stopImmediatePropagation(),J.stopPropagation();let K=X.getAttribute("href"),Q=q8(K);X8(Q);return}this.processBottomZoneClick(J)}processBottomZoneClick(J){let q=this.Editor.BlockManager.getBlockByIndex(-1),Z=F.offset(q.holder).bottom,X=J.pageY,{BlockSelection:K}=this.Editor;if(J.target instanceof Element&&J.target.isEqualNode(this.nodes.redactor)&&!K.anyBlockSelected&&Z<X){J.stopImmediatePropagation(),J.stopPropagation();let{BlockManager:Q,Caret:V,Toolbar:G}=this.Editor;(!Q.lastBlock.tool.isDefault||!Q.lastBlock.isEmpty)&&Q.insertAtEnd(),V.setToTheLastBlock(),G.moveAndOpen(Q.lastBlock)}}selectionChanged(){let{CrossBlockSelection:J,BlockSelection:q}=this.Editor,Z=I.anchorElement;if(J.isCrossBlockSelectionStarted&&q.anyBlockSelected&&I.get().removeAllRanges(),!Z){I.range||this.Editor.InlineToolbar.close();return}let X=Z.closest(`.${c.CSS.content}`);(X===null||X.closest(`.${I.CSS.editorWrapper}`)!==this.nodes.wrapper)&&(this.Editor.InlineToolbar.containsNode(Z)||this.Editor.InlineToolbar.close(),Z.dataset.inlineToolbar!=="true")||(this.Editor.BlockManager.currentBlock||this.Editor.BlockManager.setCurrentBlockByChildNode(Z),this.Editor.InlineToolbar.tryToShow(!0))}enableInputsEmptyMark(){function J(q){let Z=q.target;G5(Z)}this.readOnlyMutableListeners.on(this.nodes.wrapper,"input",J),this.readOnlyMutableListeners.on(this.nodes.wrapper,"focusin",J),this.readOnlyMutableListeners.on(this.nodes.wrapper,"focusout",J)}}var dJ={BlocksAPI:W5,CaretAPI:L5,EventsAPI:A5,I18nAPI:F2,API:N5,InlineToolbarAPI:P5,ListenersAPI:M5,NotifierAPI:j5,ReadOnlyAPI:I5,SanitizerAPI:S5,SaverAPI:x5,SelectionAPI:T5,ToolsAPI:B5,StylesAPI:C5,ToolbarAPI:E5,TooltipAPI:y5,UiAPI:v5,BlockSettings:l5,Toolbar:o5,InlineToolbar:t5,BlockEvents:_6,BlockManager:x6,BlockSelection:T6,Caret:U1,CrossBlockSelection:B6,DragNDrop:C6,ModificationsObserver:E6,Paste:yJ,ReadOnly:y6,RectangleSelection:h0,Renderer:v6,Saver:k6,Tools:W3,UI:p6};class h6{constructor(J){this.moduleInstances={},this.eventsDispatcher=new E0;let q,Z;this.isReady=new Promise((X,K)=>{q=X,Z=K}),Promise.resolve().then(async()=>{this.configuration=J,this.validate(),this.init(),await this.start(),await this.render();let{BlockManager:X,Caret:K,UI:Q,ModificationsObserver:V}=this.moduleInstances;Q.checkEmptiness(),V.enable(),this.configuration.autofocus===!0&&this.configuration.readOnly!==!0&&K.setToBlock(X.blocks[0],K.positions.START),q()}).catch((X)=>{y(`Editor.js is not ready because of ${X}`,"error"),Z(X)})}set configuration(J){var q,Z;u(J)?this.config={...J}:this.config={holder:J},V2(!!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=X5.VERBOSE),i7(this.config.logLevel),V2(!!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;let X={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,(J0(this.config.data)||!this.config.data.blocks||this.config.data.blocks.length===0)&&(this.config.data={blocks:[X]}),this.config.readOnly=this.config.readOnly||!1,(q=this.config.i18n)!=null&&q.messages&&r.setDictionary(this.config.i18n.messages),this.config.i18n.direction=((Z=this.config.i18n)==null?void 0:Z.direction)||"ltr"}get configuration(){return this.config}validate(){let{holderId:J,holder:q}=this.config;if(J&&q)throw Error("«holderId» and «holder» param can't assign at the same time.");if(Y0(q)&&!F.get(q))throw Error(`element with ID «${q}» is missing. Pass correct holder's ID.`);if(q&&u(q)&&!F.isElement(q))throw Error("«holder» value must be an Element node")}init(){this.constructModules(),this.configureModules()}async start(){await["Tools","UI","BlockManager","Paste","BlockSelection","RectangleSelection","CrossBlockSelection","ReadOnly"].reduce((J,q)=>J.then(async()=>{try{await this.moduleInstances[q].prepare()}catch(Z){if(Z instanceof D2)throw Error(Z.message);y(`Module ${q} was skipped because of %o`,"warn",Z)}}),Promise.resolve())}render(){return this.moduleInstances.Renderer.render(this.config.data.blocks)}constructModules(){Object.entries(dJ).forEach(([J,q])=>{try{this.moduleInstances[J]=new q({config:this.configuration,eventsDispatcher:this.eventsDispatcher})}catch(Z){y("[constructModules]",`Module ${J} skipped because`,"error",Z)}})}configureModules(){for(let J in this.moduleInstances)Object.prototype.hasOwnProperty.call(this.moduleInstances,J)&&(this.moduleInstances[J].state=this.getModulesDiff(J))}getModulesDiff(J){let q={};for(let Z in this.moduleInstances)Z!==J&&(q[Z]=this.moduleInstances[Z]);return q}}class d6{static get version(){return"2.31.5"}constructor(J){let q=()=>{};u(J)&&m(J.onReady)&&(q=J.onReady);let Z=new h6(J);this.isReady=Z.isReady.then(()=>{this.exportAPI(Z),q()})}exportAPI(J){let q=["configuration"],Z=()=>{Object.values(J.moduleInstances).forEach((X)=>{m(X.destroy)&&X.destroy(),X.listeners.removeAll()}),S8(),J=null;for(let X in this)Object.prototype.hasOwnProperty.call(this,X)&&delete this[X];Object.setPrototypeOf(this,null)};q.forEach((X)=>{this[X]=J[X]}),this.destroy=Z,Object.setPrototypeOf(this,J.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(([X,K])=>{Object.entries(K).forEach(([Q,V])=>{this[V]=J.moduleInstances.API.methods[X][Q]})})}}(function(){try{if(typeof document<"u"){var J=document.createElement("style");J.appendChild(document.createTextNode(".ce-header{padding:.6em 0 3px;margin:0;line-height:1.25em;outline:none}.ce-header p,.ce-header div{padding:0!important;margin:0!important}")),document.head.appendChild(J)}}catch(q){console.error("vite-plugin-css-injected-by-js",q)}})();var cJ='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M6 7L6 12M6 17L6 12M6 12L12 12M12 7V12M12 17L12 12"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M19 17V10.2135C19 10.1287 18.9011 10.0824 18.836 10.1367L16 12.5"/></svg>',uJ='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M6 7L6 12M6 17L6 12M6 12L12 12M12 7V12M12 17L12 12"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M16 11C16 10 19 9.5 19 12C19 13.9771 16.0684 13.9997 16.0012 16.8981C15.9999 16.9533 16.0448 17 16.1 17L19.3 17"/></svg>',lJ='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M6 7L6 12M6 17L6 12M6 12L12 12M12 7V12M12 17L12 12"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M16 11C16 10.5 16.8323 10 17.6 10C18.3677 10 19.5 10.311 19.5 11.5C19.5 12.5315 18.7474 12.9022 18.548 12.9823C18.5378 12.9864 18.5395 13.0047 18.5503 13.0063C18.8115 13.0456 20 13.3065 20 14.8C20 16 19.5 17 17.8 17C17.8 17 16 17 16 16.3"/></svg>',aJ='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M6 7L6 12M6 17L6 12M6 12L12 12M12 7V12M12 17L12 12"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M18 10L15.2834 14.8511C15.246 14.9178 15.294 15 15.3704 15C16.8489 15 18.7561 15 20.2 15M19 17C19 15.7187 19 14.8813 19 13.6"/></svg>',sJ='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M6 7L6 12M6 17L6 12M6 12L12 12M12 7V12M12 17L12 12"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M16 15.9C16 15.9 16.3768 17 17.8 17C19.5 17 20 15.6199 20 14.7C20 12.7323 17.6745 12.0486 16.1635 12.9894C16.094 13.0327 16 12.9846 16 12.9027V10.1C16 10.0448 16.0448 10 16.1 10H19.8"/></svg>',rJ='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M6 7L6 12M6 17L6 12M6 12L12 12M12 7V12M12 17L12 12"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M19.5 10C16.5 10.5 16 13.3285 16 15M16 15V15C16 16.1046 16.8954 17 18 17H18.3246C19.3251 17 20.3191 16.3492 20.2522 15.3509C20.0612 12.4958 16 12.6611 16 15Z"/></svg>',iJ='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M9 7L9 12M9 17V12M9 12L15 12M15 7V12M15 17L15 12"/></svg>';class c6{constructor({data:J,config:q,api:Z,readOnly:X}){this.api=Z,this.readOnly=X,this._settings=q,this._data=this.normalizeData(J),this._element=this.getTag()}get _CSS(){return{block:this.api.styles.block,wrapper:"ce-header"}}isHeaderData(J){return J.text!==void 0}normalizeData(J){let q={text:"",level:this.defaultLevel.number};return this.isHeaderData(J)&&(q.text=J.text||"",J.level!==void 0&&!isNaN(parseInt(J.level.toString()))&&(q.level=parseInt(J.level.toString()))),q}render(){return this._element}renderSettings(){return this.levels.map((J)=>({icon:J.svg,label:this.api.i18n.t(`Heading ${J.number}`),onActivate:()=>this.setLevel(J.number),closeOnActivate:!0,isActive:this.currentLevel.number===J.number,render:()=>document.createElement("div")}))}setLevel(J){this.data={level:J,text:this.data.text}}merge(J){this._element.insertAdjacentHTML("beforeend",J.text)}validate(J){return J.text.trim()!==""}save(J){return{text:J.innerHTML,level:this.currentLevel.number}}static get conversionConfig(){return{export:"text",import:"text"}}static get sanitize(){return{level:!1,text:{}}}static get isReadOnlySupported(){return!0}get data(){return this._data.text=this._element.innerHTML,this._data.level=this.currentLevel.number,this._data}set data(J){if(this._data=this.normalizeData(J),J.level!==void 0&&this._element.parentNode){let q=this.getTag();q.innerHTML=this._element.innerHTML,this._element.parentNode.replaceChild(q,this._element),this._element=q}J.text!==void 0&&(this._element.innerHTML=this._data.text||"")}getTag(){let J=document.createElement(this.currentLevel.tag);return J.innerHTML=this._data.text||"",J.classList.add(this._CSS.wrapper),J.contentEditable=this.readOnly?"false":"true",J.dataset.placeholder=this.api.i18n.t(this._settings.placeholder||""),J}get currentLevel(){let J=this.levels.find((q)=>q.number===this._data.level);return J||(J=this.defaultLevel),J}get defaultLevel(){if(this._settings.defaultLevel){let J=this.levels.find((q)=>q.number===this._settings.defaultLevel);if(J)return J;console.warn("(ง'̀-'́)ง Heading Tool: the default level specified was not found in available levels")}return this.levels[1]}get levels(){let J=[{number:1,tag:"H1",svg:cJ},{number:2,tag:"H2",svg:uJ},{number:3,tag:"H3",svg:lJ},{number:4,tag:"H4",svg:aJ},{number:5,tag:"H5",svg:sJ},{number:6,tag:"H6",svg:rJ}];return this._settings.levels?J.filter((q)=>this._settings.levels.includes(q.number)):J}onPaste(J){let q=J.detail;if("data"in q){let Z=q.data,X=this.defaultLevel.number;switch(Z.tagName){case"H1":X=1;break;case"H2":X=2;break;case"H3":X=3;break;case"H4":X=4;break;case"H5":X=5;break;case"H6":X=6;break}this._settings.levels&&(X=this._settings.levels.reduce((K,Q)=>Math.abs(Q-X)<Math.abs(K-X)?Q:K)),this.data={level:X,text:Z.innerHTML}}}static get pasteConfig(){return{tags:["H1","H2","H3","H4","H5","H6"]}}static get toolbox(){return{icon:iJ,title:"Heading"}}}(function(){try{if(typeof document<"u"){var J=document.createElement("style");J.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(J)}}catch(q){console.error("vite-plugin-css-injected-by-js",q)}})();var oJ='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M8 9V7.2C8 7.08954 8.08954 7 8.2 7L12 7M16 9V7.2C16 7.08954 15.9105 7 15.8 7L12 7M12 7L12 17M12 17H10M12 17H14"/></svg>';function nJ(J){let q=document.createElement("div");q.innerHTML=J.trim();let Z=document.createDocumentFragment();return Z.append(...Array.from(q.childNodes)),Z}class L3{static get DEFAULT_PLACEHOLDER(){return""}constructor({data:J,config:q,api:Z,readOnly:X}){this.api=Z,this.readOnly=X,this._CSS={block:this.api.styles.block,wrapper:"ce-paragraph"},this.readOnly||(this.onKeyUp=this.onKeyUp.bind(this)),this._placeholder=q.placeholder?q.placeholder:L3.DEFAULT_PLACEHOLDER,this._data=J??{},this._element=null,this._preserveBlank=q.preserveBlank??!1}onKeyUp(J){if(J.code!=="Backspace"&&J.code!=="Delete"||!this._element)return;let{textContent:q}=this._element;q===""&&(this._element.innerHTML="")}drawView(){let J=document.createElement("DIV");return J.classList.add(this._CSS.wrapper,this._CSS.block),J.contentEditable="false",J.dataset.placeholderActive=this.api.i18n.t(this._placeholder),this._data.text&&(J.innerHTML=this._data.text),this.readOnly||(J.contentEditable="true",J.addEventListener("keyup",this.onKeyUp)),J}render(){return this._element=this.drawView(),this._element}merge(J){if(!this._element)return;this._data.text+=J.text;let q=nJ(J.text);this._element.appendChild(q),this._element.normalize()}validate(J){return!(J.text.trim()===""&&!this._preserveBlank)}save(J){return{text:J.innerHTML}}onPaste(J){let q={text:J.detail.data.innerHTML};this._data=q,window.requestAnimationFrame(()=>{this._element&&(this._element.innerHTML=this._data.text||"")})}static get conversionConfig(){return{export:"text",import:"text"}}static get sanitize(){return{text:{br:!0}}}static get isReadOnlySupported(){return!0}static get pasteConfig(){return{tags:["P"]}}static get toolbox(){return{icon:oJ,title:"Text"}}}(function(){try{if(typeof document<"u"){var J=document.createElement("style");J.appendChild(document.createTextNode('.cdx-list{margin:0;padding:0;outline:none;display:grid;counter-reset:item;gap:var(--spacing-s);padding:var(--spacing-xs);--spacing-s: 8px;--spacing-xs: 6px;--list-counter-type: numeric;--radius-border: 5px;--checkbox-background: #fff;--color-border: #C9C9C9;--color-bg-checked: #369FFF;--line-height: 1.45em;--color-bg-checked-hover: #0059AB;--color-tick: #fff;--size-checkbox: 1.2em}.cdx-list__item{line-height:var(--line-height);display:grid;grid-template-columns:auto 1fr;grid-template-rows:auto auto;grid-template-areas:"checkbox content" ". child"}.cdx-list__item-children{display:grid;grid-area:child;gap:var(--spacing-s);padding-top:var(--spacing-s)}.cdx-list__item [contenteditable]{outline:none}.cdx-list__item-content{word-break:break-word;white-space:pre-wrap;grid-area:content;padding-left:var(--spacing-s)}.cdx-list__item:before{counter-increment:item;white-space:nowrap}.cdx-list-ordered .cdx-list__item:before{content:counters(item,".",var(--list-counter-type)) "."}.cdx-list-ordered{counter-reset:item}.cdx-list-unordered .cdx-list__item:before{content:"•"}.cdx-list-checklist .cdx-list__item:before{content:""}.cdx-list__settings .cdx-settings-button{width:50%}.cdx-list__checkbox{padding-top:calc((var(--line-height) - var(--size-checkbox)) / 2);grid-area:checkbox;width:var(--size-checkbox);height:var(--size-checkbox);display:flex;cursor:pointer}.cdx-list__checkbox svg{opacity:0;height:var(--size-checkbox);width:var(--size-checkbox);left:-1px;top:-1px;position:absolute}@media (hover: hover){.cdx-list__checkbox:not(.cdx-list__checkbox--no-hover):hover .cdx-list__checkbox-check svg{opacity:1}}.cdx-list__checkbox--checked{line-height:var(--line-height)}@media (hover: hover){.cdx-list__checkbox--checked:not(.cdx-list__checkbox--checked--no-hover):hover .cdx-checklist__checkbox-check{background:var(--color-bg-checked-hover);border-color:var(--color-bg-checked-hover)}}.cdx-list__checkbox--checked .cdx-list__checkbox-check{background:var(--color-bg-checked);border-color:var(--color-bg-checked)}.cdx-list__checkbox--checked .cdx-list__checkbox-check svg{opacity:1}.cdx-list__checkbox--checked .cdx-list__checkbox-check svg path{stroke:var(--color-tick)}.cdx-list__checkbox--checked .cdx-list__checkbox-check:before{opacity:0;visibility:visible;transform:scale(2.5)}.cdx-list__checkbox-check{cursor:pointer;display:inline-block;position:relative;margin:0 auto;width:var(--size-checkbox);height:var(--size-checkbox);box-sizing:border-box;border-radius:var(--radius-border);border:1px solid var(--color-border);background:var(--checkbox-background)}.cdx-list__checkbox-check:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;border-radius:100%;background-color:var(--color-bg-checked);visibility:hidden;pointer-events:none;transform:scale(1);transition:transform .4s ease-out,opacity .4s}.cdx-list__checkbox-check--disabled{pointer-events:none}.cdx-list-start-with-field{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-list-start-with-field--invalid{background:#FFECED;border:1px solid #E13F3F}.cdx-list-start-with-field--invalid .cdx-list-start-with-field__input{color:#e13f3f}.cdx-list-start-with-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% - var(--toolbox-buttons-size) - var(--icon-margin-right))}.cdx-list-start-with-field__input::placeholder{color:var(--grayText);font-weight:500}')),document.head.appendChild(J)}}catch(q){console.error("vite-plugin-css-injected-by-js",q)}})();var tJ='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M7 12L10.4884 15.8372C10.5677 15.9245 10.705 15.9245 10.7844 15.8372L17 9"/></svg>',u6='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M9.2 12L11.0586 13.8586C11.1367 13.9367 11.2633 13.9367 11.3414 13.8586L14.7 10.5"/><rect width="14" height="14" x="5" y="5" stroke="currentColor" stroke-width="2" rx="4"/></svg>',l6='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><line x1="9" x2="19" y1="7" y2="7" stroke="currentColor" stroke-linecap="round" stroke-width="2"/><line x1="9" x2="19" y1="12" y2="12" stroke="currentColor" stroke-linecap="round" stroke-width="2"/><line x1="9" x2="19" y1="17" y2="17" stroke="currentColor" stroke-linecap="round" stroke-width="2"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M5.00001 17H4.99002"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M5.00001 12H4.99002"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M5.00001 7H4.99002"/></svg>',a6='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><line x1="12" x2="19" y1="7" y2="7" stroke="currentColor" stroke-linecap="round" stroke-width="2"/><line x1="12" x2="19" y1="12" y2="12" stroke="currentColor" stroke-linecap="round" stroke-width="2"/><line x1="12" x2="19" y1="17" y2="17" stroke="currentColor" stroke-linecap="round" stroke-width="2"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M7.79999 14L7.79999 7.2135C7.79999 7.12872 7.7011 7.0824 7.63597 7.13668L4.79999 9.5"/></svg>',eJ='<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10 14.2L10 7.4135C10 7.32872 9.90111 7.28241 9.83598 7.33668L7 9.7" stroke="black" stroke-width="1.6" stroke-linecap="round"/><path d="M13.2087 14.2H13.2" stroke="black" stroke-width="1.6" stroke-linecap="round"/></svg>',Jq='<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.2087 14.2H13.2" stroke="black" stroke-width="1.6" stroke-linecap="round"/><path d="M10 14.2L10 9.5" stroke="black" stroke-width="1.6" stroke-linecap="round"/><path d="M10 7.01L10 7" stroke="black" stroke-width="1.8" stroke-linecap="round"/></svg>',qq='<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.2087 14.2H13.2" stroke="black" stroke-width="1.6" stroke-linecap="round"/><path d="M10 14.2L10 7.2" stroke="black" stroke-width="1.6" stroke-linecap="round"/></svg>',Zq='<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M16.0087 14.2H16" stroke="black" stroke-width="1.6" stroke-linecap="round"/><path d="M7 14.2L7.78865 12M13 14.2L12.1377 12M7.78865 12C7.78865 12 9.68362 7 10 7C10.3065 7 12.1377 12 12.1377 12M7.78865 12L12.1377 12" stroke="black" stroke-width="1.6" stroke-linecap="round"/></svg>',Xq='<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M14.2087 14.2H14.2" stroke="black" stroke-width="1.6" stroke-linecap="round"/><path d="M11.5 14.5C11.5 14.5 11 13.281 11 12.5M7 9.5C7 9.5 7.5 8.5 9 8.5C10.5 8.5 11 9.5 11 10.5L11 11.5M11 11.5L11 12.5M11 11.5C11 11.5 7 11 7 13C7 15.3031 11 15 11 12.5" stroke="black" stroke-width="1.6" stroke-linecap="round"/></svg>',Kq='<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M8 14.2L8 7.4135C8 7.32872 7.90111 7.28241 7.83598 7.33668L5 9.7" stroke="black" stroke-width="1.6" stroke-linecap="round"/><path d="M14 13L16.4167 10.7778M16.4167 10.7778L14 8.5M16.4167 10.7778H11.6562" stroke="black" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>',C1=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function Qq(J){if(J.__esModule)return J;var q=J.default;if(typeof q=="function"){var Z=function X(){return this instanceof X?Reflect.construct(q,arguments,this.constructor):q.apply(this,arguments)};Z.prototype=q.prototype}else Z={};return Object.defineProperty(Z,"__esModule",{value:!0}),Object.keys(J).forEach(function(X){var K=Object.getOwnPropertyDescriptor(J,X);Object.defineProperty(Z,X,K.get?K:{enumerable:!0,get:function(){return J[X]}})}),Z}var v={},M3={},R3={};Object.defineProperty(R3,"__esModule",{value:!0});R3.allInputsSelector=Vq;function Vq(){var J=["text","password","email","number","search","tel","url"];return"[contenteditable=true], textarea, input:not([type]), "+J.map(function(q){return'input[type="'.concat(q,'"]')}).join(", ")}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.allInputsSelector=void 0;var q=R3;Object.defineProperty(J,"allInputsSelector",{enumerable:!0,get:function(){return q.allInputsSelector}})})(M3);var j0={},O3={};Object.defineProperty(O3,"__esModule",{value:!0});O3.isNativeInput=Gq;function Gq(J){var q=["INPUT","TEXTAREA"];return J&&J.tagName?q.includes(J.tagName):!1}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isNativeInput=void 0;var q=O3;Object.defineProperty(J,"isNativeInput",{enumerable:!0,get:function(){return q.isNativeInput}})})(j0);var o6={},j3={};Object.defineProperty(j3,"__esModule",{value:!0});j3.append=Yq;function Yq(J,q){Array.isArray(q)?q.forEach(function(Z){J.appendChild(Z)}):J.appendChild(q)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.append=void 0;var q=j3;Object.defineProperty(J,"append",{enumerable:!0,get:function(){return q.append}})})(o6);var I3={},_3={};Object.defineProperty(_3,"__esModule",{value:!0});_3.blockElements=Uq;function Uq(){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(J){Object.defineProperty(J,"__esModule",{value:!0}),J.blockElements=void 0;var q=_3;Object.defineProperty(J,"blockElements",{enumerable:!0,get:function(){return q.blockElements}})})(I3);var n6={},S3={};Object.defineProperty(S3,"__esModule",{value:!0});S3.calculateBaseline=Hq;function Hq(J){var q=window.getComputedStyle(J),Z=parseFloat(q.fontSize),X=parseFloat(q.lineHeight)||Z*1.2,K=parseFloat(q.paddingTop),Q=parseFloat(q.borderTopWidth),V=parseFloat(q.marginTop),G=Z*0.8,Y=(X-Z)/2,U=V+Q+K+Y+G;return U}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.calculateBaseline=void 0;var q=S3;Object.defineProperty(J,"calculateBaseline",{enumerable:!0,get:function(){return q.calculateBaseline}})})(n6);var t6={},x3={},T3={},B3={};Object.defineProperty(B3,"__esModule",{value:!0});B3.isContentEditable=$q;function $q(J){return J.contentEditable==="true"}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isContentEditable=void 0;var q=B3;Object.defineProperty(J,"isContentEditable",{enumerable:!0,get:function(){return q.isContentEditable}})})(T3);Object.defineProperty(x3,"__esModule",{value:!0});x3.canSetCaret=Fq;var zq=j0,Dq=T3;function Fq(J){var q=!0;if((0,zq.isNativeInput)(J))switch(J.type){case"file":case"checkbox":case"radio":case"hidden":case"submit":case"button":case"image":case"reset":q=!1;break}else q=(0,Dq.isContentEditable)(J);return q}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.canSetCaret=void 0;var q=x3;Object.defineProperty(J,"canSetCaret",{enumerable:!0,get:function(){return q.canSetCaret}})})(t6);var w1={},C3={};function Wq(J,q,Z){let X=Z.value!==void 0?"value":"get",K=Z[X],Q=`#${q}Cache`;if(Z[X]=function(...V){return this[Q]===void 0&&(this[Q]=K.apply(this,V)),this[Q]},X==="get"&&Z.set){let V=Z.set;Z.set=function(G){delete J[Q],V.apply(this,G)}}return Z}function e6(){let J={win:!1,mac:!1,x11:!1,linux:!1},q=Object.keys(J).find((Z)=>window.navigator.appVersion.toLowerCase().indexOf(Z)!==-1);return q!==void 0&&(J[q]=!0),J}function E3(J){return J!=null&&J!==""&&(typeof J!="object"||Object.keys(J).length>0)}function Lq(J){return!E3(J)}var Aq=()=>typeof window<"u"&&window.navigator!==null&&E3(window.navigator.platform)&&(/iP(ad|hone|od)/.test(window.navigator.platform)||window.navigator.platform==="MacIntel"&&window.navigator.maxTouchPoints>1);function Nq(J){let q=e6();return J=J.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,"+"),q.mac?J=J.replace(/ctrl|cmd/gi,"⌘").replace(/alt/gi,"⌥"):J=J.replace(/cmd/gi,"Ctrl").replace(/windows/gi,"WIN"),J}function Pq(J){return J[0].toUpperCase()+J.slice(1)}function Mq(J){let q=document.createElement("div");q.style.position="absolute",q.style.left="-999px",q.style.bottom="-999px",q.innerHTML=J,document.body.appendChild(q);let Z=window.getSelection(),X=document.createRange();if(X.selectNode(q),Z===null)throw Error("Cannot copy text to clipboard");Z.removeAllRanges(),Z.addRange(X),document.execCommand("copy"),document.body.removeChild(q)}function Rq(J,q,Z){let X;return(...K)=>{let Q=this,V=()=>{X=void 0,Z!==!0&&J.apply(Q,K)},G=Z===!0&&X!==void 0;window.clearTimeout(X),X=window.setTimeout(V,q),G&&J.apply(Q,K)}}function F0(J){return Object.prototype.toString.call(J).match(/\s([a-zA-Z]+)/)[1].toLowerCase()}function Oq(J){return F0(J)==="boolean"}function J7(J){return F0(J)==="function"||F0(J)==="asyncfunction"}function jq(J){return J7(J)&&/^\s*class\s+/.test(J.toString())}function Iq(J){return F0(J)==="number"}function x1(J){return F0(J)==="object"}function _q(J){return Promise.resolve(J)===J}function Sq(J){return F0(J)==="string"}function xq(J){return F0(J)==="undefined"}function P3(J,...q){if(!q.length)return J;let Z=q.shift();if(x1(J)&&x1(Z))for(let X in Z)x1(Z[X])?(J[X]===void 0&&Object.assign(J,{[X]:{}}),P3(J[X],Z[X])):Object.assign(J,{[X]:Z[X]});return P3(J,...q)}function Tq(J,q,Z){let X=`«${q}» is deprecated and will be removed in the next major release. Please use the «${Z}» instead.`;J&&console.warn(X)}function Bq(J){try{return new URL(J).href}catch{}return J.substring(0,2)==="//"?window.location.protocol+J:window.location.origin+J}function Cq(J){return J>47&&J<58||J===32||J===13||J===229||J>64&&J<91||J>95&&J<112||J>185&&J<193||J>218&&J<223}var Eq={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},wq={LEFT:0,WHEEL:1,RIGHT:2,BACKWARD:3,FORWARD:4};class q7{constructor(){this.completed=Promise.resolve()}add(J){return new Promise((q,Z)=>{this.completed=this.completed.then(J).then(q).catch(Z)})}}function yq(J,q,Z=void 0){let X,K,Q,V=null,G=0;Z||(Z={});let Y=function(){G=Z.leading===!1?0:Date.now(),V=null,Q=J.apply(X,K),V===null&&(X=K=null)};return function(){let U=Date.now();!G&&Z.leading===!1&&(G=U);let H=q-(U-G);return X=this,K=arguments,H<=0||H>q?(V&&(clearTimeout(V),V=null),G=U,Q=J.apply(X,K),V===null&&(X=K=null)):!V&&Z.trailing!==!1&&(V=setTimeout(Y,H)),Q}}var vq=Object.freeze(Object.defineProperty({__proto__:null,PromiseQueue:q7,beautifyShortcut:Nq,cacheable:Wq,capitalize:Pq,copyTextToClipboard:Mq,debounce:Rq,deepMerge:P3,deprecationAssert:Tq,getUserOS:e6,getValidUrl:Bq,isBoolean:Oq,isClass:jq,isEmpty:Lq,isFunction:J7,isIosDevice:Aq,isNumber:Iq,isObject:x1,isPrintableKey:Cq,isPromise:_q,isString:Sq,isUndefined:xq,keyCodes:Eq,mouseButtons:wq,notEmpty:E3,throttle:yq,typeOf:F0},Symbol.toStringTag,{value:"Module"})),w3=Qq(vq);Object.defineProperty(C3,"__esModule",{value:!0});C3.containsOnlyInlineElements=mq;var kq=w3,gq=I3;function mq(J){var q;(0,kq.isString)(J)?(q=document.createElement("div"),q.innerHTML=J):q=J;var Z=function(X){return!(0,gq.blockElements)().includes(X.tagName.toLowerCase())&&Array.from(X.children).every(Z)};return Array.from(q.children).every(Z)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.containsOnlyInlineElements=void 0;var q=C3;Object.defineProperty(J,"containsOnlyInlineElements",{enumerable:!0,get:function(){return q.containsOnlyInlineElements}})})(w1);var Z7={},y3={},y1={},v3={};Object.defineProperty(v3,"__esModule",{value:!0});v3.make=bq;function bq(J,q,Z){var X;q===void 0&&(q=null),Z===void 0&&(Z={});var K=document.createElement(J);if(Array.isArray(q)){var Q=q.filter(function(G){return G!==void 0});(X=K.classList).add.apply(X,Q)}else q!==null&&K.classList.add(q);for(var V in Z)Object.prototype.hasOwnProperty.call(Z,V)&&(K[V]=Z[V]);return K}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.make=void 0;var q=v3;Object.defineProperty(J,"make",{enumerable:!0,get:function(){return q.make}})})(y1);Object.defineProperty(y3,"__esModule",{value:!0});y3.fragmentToString=pq;var fq=y1;function pq(J){var q=(0,fq.make)("div");return q.appendChild(J),q.innerHTML}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.fragmentToString=void 0;var q=y3;Object.defineProperty(J,"fragmentToString",{enumerable:!0,get:function(){return q.fragmentToString}})})(Z7);var X7={},k3={};Object.defineProperty(k3,"__esModule",{value:!0});k3.getContentLength=dq;var hq=j0;function dq(J){var q,Z;return(0,hq.isNativeInput)(J)?J.value.length:J.nodeType===Node.TEXT_NODE?J.length:(Z=(q=J.textContent)===null||q===void 0?void 0:q.length)!==null&&Z!==void 0?Z:0}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.getContentLength=void 0;var q=k3;Object.defineProperty(J,"getContentLength",{enumerable:!0,get:function(){return q.getContentLength}})})(X7);var g3={},m3={},s6=C1&&C1.__spreadArray||function(J,q,Z){if(Z||arguments.length===2)for(var X=0,K=q.length,Q;X<K;X++)(Q||!(X in q))&&(Q||(Q=Array.prototype.slice.call(q,0,X)),Q[X]=q[X]);return J.concat(Q||Array.prototype.slice.call(q))};Object.defineProperty(m3,"__esModule",{value:!0});m3.getDeepestBlockElements=K7;var cq=w1;function K7(J){return(0,cq.containsOnlyInlineElements)(J)?[J]:Array.from(J.children).reduce(function(q,Z){return s6(s6([],q,!0),K7(Z),!0)},[])}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.getDeepestBlockElements=void 0;var q=m3;Object.defineProperty(J,"getDeepestBlockElements",{enumerable:!0,get:function(){return q.getDeepestBlockElements}})})(g3);var Q7={},b3={},v1={},f3={};Object.defineProperty(f3,"__esModule",{value:!0});f3.isLineBreakTag=uq;function uq(J){return["BR","WBR"].includes(J.tagName)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isLineBreakTag=void 0;var q=f3;Object.defineProperty(J,"isLineBreakTag",{enumerable:!0,get:function(){return q.isLineBreakTag}})})(v1);var k1={},p3={};Object.defineProperty(p3,"__esModule",{value:!0});p3.isSingleTag=lq;function lq(J){return["AREA","BASE","BR","COL","COMMAND","EMBED","HR","IMG","INPUT","KEYGEN","LINK","META","PARAM","SOURCE","TRACK","WBR"].includes(J.tagName)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isSingleTag=void 0;var q=p3;Object.defineProperty(J,"isSingleTag",{enumerable:!0,get:function(){return q.isSingleTag}})})(k1);Object.defineProperty(b3,"__esModule",{value:!0});b3.getDeepestNode=V7;var aq=j0,sq=v1,rq=k1;function V7(J,q){q===void 0&&(q=!1);var Z=q?"lastChild":"firstChild",X=q?"previousSibling":"nextSibling";if(J.nodeType===Node.ELEMENT_NODE&&J[Z]){var K=J[Z];if((0,rq.isSingleTag)(K)&&!(0,aq.isNativeInput)(K)&&!(0,sq.isLineBreakTag)(K))if(K[X])K=K[X];else if(K.parentNode!==null&&K.parentNode[X])K=K.parentNode[X];else return K.parentNode;return V7(K,q)}return J}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.getDeepestNode=void 0;var q=b3;Object.defineProperty(J,"getDeepestNode",{enumerable:!0,get:function(){return q.getDeepestNode}})})(Q7);var G7={},h3={},_1=C1&&C1.__spreadArray||function(J,q,Z){if(Z||arguments.length===2)for(var X=0,K=q.length,Q;X<K;X++)(Q||!(X in q))&&(Q||(Q=Array.prototype.slice.call(q,0,X)),Q[X]=q[X]);return J.concat(Q||Array.prototype.slice.call(q))};Object.defineProperty(h3,"__esModule",{value:!0});h3.findAllInputs=eq;var iq=w1,oq=g3,nq=M3,tq=j0;function eq(J){return Array.from(J.querySelectorAll((0,nq.allInputsSelector)())).reduce(function(q,Z){return(0,tq.isNativeInput)(Z)||(0,iq.containsOnlyInlineElements)(Z)?_1(_1([],q,!0),[Z],!1):_1(_1([],q,!0),(0,oq.getDeepestBlockElements)(Z),!0)},[])}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.findAllInputs=void 0;var q=h3;Object.defineProperty(J,"findAllInputs",{enumerable:!0,get:function(){return q.findAllInputs}})})(G7);var Y7={},d3={};Object.defineProperty(d3,"__esModule",{value:!0});d3.isCollapsedWhitespaces=JZ;function JZ(J){return!/[^\t\n\r ]/.test(J)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isCollapsedWhitespaces=void 0;var q=d3;Object.defineProperty(J,"isCollapsedWhitespaces",{enumerable:!0,get:function(){return q.isCollapsedWhitespaces}})})(Y7);var c3={},u3={};Object.defineProperty(u3,"__esModule",{value:!0});u3.isElement=ZZ;var qZ=w3;function ZZ(J){return(0,qZ.isNumber)(J)?!1:!!J&&!!J.nodeType&&J.nodeType===Node.ELEMENT_NODE}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isElement=void 0;var q=u3;Object.defineProperty(J,"isElement",{enumerable:!0,get:function(){return q.isElement}})})(c3);var U7={},l3={},a3={},s3={};Object.defineProperty(s3,"__esModule",{value:!0});s3.isLeaf=XZ;function XZ(J){return J===null?!1:J.childNodes.length===0}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isLeaf=void 0;var q=s3;Object.defineProperty(J,"isLeaf",{enumerable:!0,get:function(){return q.isLeaf}})})(a3);var r3={},i3={};Object.defineProperty(i3,"__esModule",{value:!0});i3.isNodeEmpty=YZ;var KZ=v1,QZ=c3,VZ=j0,GZ=k1;function YZ(J,q){var Z="";return(0,GZ.isSingleTag)(J)&&!(0,KZ.isLineBreakTag)(J)?!1:((0,QZ.isElement)(J)&&(0,VZ.isNativeInput)(J)?Z=J.value:J.textContent!==null&&(Z=J.textContent.replace("","")),q!==void 0&&(Z=Z.replace(new RegExp(q,"g"),"")),Z.trim().length===0)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isNodeEmpty=void 0;var q=i3;Object.defineProperty(J,"isNodeEmpty",{enumerable:!0,get:function(){return q.isNodeEmpty}})})(r3);Object.defineProperty(l3,"__esModule",{value:!0});l3.isEmpty=$Z;var UZ=a3,HZ=r3;function $Z(J,q){J.normalize();for(var Z=[J];Z.length>0;){var X=Z.shift();if(X){if(J=X,(0,UZ.isLeaf)(J)&&!(0,HZ.isNodeEmpty)(J,q))return!1;Z.push.apply(Z,Array.from(J.childNodes))}}return!0}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isEmpty=void 0;var q=l3;Object.defineProperty(J,"isEmpty",{enumerable:!0,get:function(){return q.isEmpty}})})(U7);var H7={},o3={};Object.defineProperty(o3,"__esModule",{value:!0});o3.isFragment=DZ;var zZ=w3;function DZ(J){return(0,zZ.isNumber)(J)?!1:!!J&&!!J.nodeType&&J.nodeType===Node.DOCUMENT_FRAGMENT_NODE}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isFragment=void 0;var q=o3;Object.defineProperty(J,"isFragment",{enumerable:!0,get:function(){return q.isFragment}})})(H7);var $7={},n3={};Object.defineProperty(n3,"__esModule",{value:!0});n3.isHTMLString=WZ;var FZ=y1;function WZ(J){var q=(0,FZ.make)("div");return q.innerHTML=J,q.childElementCount>0}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isHTMLString=void 0;var q=n3;Object.defineProperty(J,"isHTMLString",{enumerable:!0,get:function(){return q.isHTMLString}})})($7);var z7={},t3={};Object.defineProperty(t3,"__esModule",{value:!0});t3.offset=LZ;function LZ(J){var q=J.getBoundingClientRect(),Z=window.pageXOffset||document.documentElement.scrollLeft,X=window.pageYOffset||document.documentElement.scrollTop,K=q.top+X,Q=q.left+Z;return{top:K,left:Q,bottom:K+q.height,right:Q+q.width}}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.offset=void 0;var q=t3;Object.defineProperty(J,"offset",{enumerable:!0,get:function(){return q.offset}})})(z7);var D7={},e3={};Object.defineProperty(e3,"__esModule",{value:!0});e3.prepend=AZ;function AZ(J,q){Array.isArray(q)?(q=q.reverse(),q.forEach(function(Z){return J.prepend(Z)})):J.prepend(q)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.prepend=void 0;var q=e3;Object.defineProperty(J,"prepend",{enumerable:!0,get:function(){return q.prepend}})})(D7);(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.prepend=J.offset=J.make=J.isLineBreakTag=J.isSingleTag=J.isNodeEmpty=J.isLeaf=J.isHTMLString=J.isFragment=J.isEmpty=J.isElement=J.isContentEditable=J.isCollapsedWhitespaces=J.findAllInputs=J.isNativeInput=J.allInputsSelector=J.getDeepestNode=J.getDeepestBlockElements=J.getContentLength=J.fragmentToString=J.containsOnlyInlineElements=J.canSetCaret=J.calculateBaseline=J.blockElements=J.append=void 0;var q=M3;Object.defineProperty(J,"allInputsSelector",{enumerable:!0,get:function(){return q.allInputsSelector}});var Z=j0;Object.defineProperty(J,"isNativeInput",{enumerable:!0,get:function(){return Z.isNativeInput}});var X=o6;Object.defineProperty(J,"append",{enumerable:!0,get:function(){return X.append}});var K=I3;Object.defineProperty(J,"blockElements",{enumerable:!0,get:function(){return K.blockElements}});var Q=n6;Object.defineProperty(J,"calculateBaseline",{enumerable:!0,get:function(){return Q.calculateBaseline}});var V=t6;Object.defineProperty(J,"canSetCaret",{enumerable:!0,get:function(){return V.canSetCaret}});var G=w1;Object.defineProperty(J,"containsOnlyInlineElements",{enumerable:!0,get:function(){return G.containsOnlyInlineElements}});var Y=Z7;Object.defineProperty(J,"fragmentToString",{enumerable:!0,get:function(){return Y.fragmentToString}});var U=X7;Object.defineProperty(J,"getContentLength",{enumerable:!0,get:function(){return U.getContentLength}});var H=g3;Object.defineProperty(J,"getDeepestBlockElements",{enumerable:!0,get:function(){return H.getDeepestBlockElements}});var $=Q7;Object.defineProperty(J,"getDeepestNode",{enumerable:!0,get:function(){return $.getDeepestNode}});var D=G7;Object.defineProperty(J,"findAllInputs",{enumerable:!0,get:function(){return D.findAllInputs}});var A=Y7;Object.defineProperty(J,"isCollapsedWhitespaces",{enumerable:!0,get:function(){return A.isCollapsedWhitespaces}});var W=T3;Object.defineProperty(J,"isContentEditable",{enumerable:!0,get:function(){return W.isContentEditable}});var z=c3;Object.defineProperty(J,"isElement",{enumerable:!0,get:function(){return z.isElement}});var L=U7;Object.defineProperty(J,"isEmpty",{enumerable:!0,get:function(){return L.isEmpty}});var N=H7;Object.defineProperty(J,"isFragment",{enumerable:!0,get:function(){return N.isFragment}});var P=$7;Object.defineProperty(J,"isHTMLString",{enumerable:!0,get:function(){return P.isHTMLString}});var _=a3;Object.defineProperty(J,"isLeaf",{enumerable:!0,get:function(){return _.isLeaf}});var j=r3;Object.defineProperty(J,"isNodeEmpty",{enumerable:!0,get:function(){return j.isNodeEmpty}});var O=v1;Object.defineProperty(J,"isLineBreakTag",{enumerable:!0,get:function(){return O.isLineBreakTag}});var T=k1;Object.defineProperty(J,"isSingleTag",{enumerable:!0,get:function(){return T.isSingleTag}});var b=y1;Object.defineProperty(J,"make",{enumerable:!0,get:function(){return b.make}});var R=z7;Object.defineProperty(J,"offset",{enumerable:!0,get:function(){return R.offset}});var M=D7;Object.defineProperty(J,"prepend",{enumerable:!0,get:function(){return M.prepend}})})(v);var i="cdx-list",a={wrapper:i,item:`${i}__item`,itemContent:`${i}__item-content`,itemChildren:`${i}__item-children`};class K0{static get CSS(){return{...a,orderedList:`${i}-ordered`}}constructor(J,q){this.config=q,this.readOnly=J}renderWrapper(J){let q;return J===!0?q=v.make("ol",[K0.CSS.wrapper,K0.CSS.orderedList]):q=v.make("ol",[K0.CSS.orderedList,K0.CSS.itemChildren]),q}renderItem(J,q){let Z=v.make("li",K0.CSS.item),X=v.make("div",K0.CSS.itemContent,{innerHTML:J,contentEditable:(!this.readOnly).toString()});return Z.appendChild(X),Z}getItemContent(J){let q=J.querySelector(`.${K0.CSS.itemContent}`);return!q||v.isEmpty(q)?"":q.innerHTML}getItemMeta(){return{}}composeDefaultMeta(){return{}}}class Q0{static get CSS(){return{...a,unorderedList:`${i}-unordered`}}constructor(J,q){this.config=q,this.readOnly=J}renderWrapper(J){let q;return J===!0?q=v.make("ul",[Q0.CSS.wrapper,Q0.CSS.unorderedList]):q=v.make("ul",[Q0.CSS.unorderedList,Q0.CSS.itemChildren]),q}renderItem(J,q){let Z=v.make("li",Q0.CSS.item),X=v.make("div",Q0.CSS.itemContent,{innerHTML:J,contentEditable:(!this.readOnly).toString()});return Z.appendChild(X),Z}getItemContent(J){let q=J.querySelector(`.${Q0.CSS.itemContent}`);return!q||v.isEmpty(q)?"":q.innerHTML}getItemMeta(){return{}}composeDefaultMeta(){return{}}}function O0(J){return J.nodeType===Node.ELEMENT_NODE}var a0={},J4={},g1={},m1={};Object.defineProperty(m1,"__esModule",{value:!0});m1.getContenteditableSlice=PZ;var NZ=v;function PZ(J,q,Z,X,K){var Q;K===void 0&&(K=!1);var V=document.createRange();if(X==="left"?(V.setStart(J,0),V.setEnd(q,Z)):(V.setStart(q,Z),V.setEnd(J,J.childNodes.length)),K===!0){var G=V.extractContents();return(0,NZ.fragmentToString)(G)}var Y=V.cloneContents(),U=document.createElement("div");U.appendChild(Y);var H=(Q=U.textContent)!==null&&Q!==void 0?Q:"";return H}Object.defineProperty(g1,"__esModule",{value:!0});g1.checkContenteditableSliceForEmptiness=OZ;var MZ=v,RZ=m1;function OZ(J,q,Z,X){var K=(0,RZ.getContenteditableSlice)(J,q,Z,X);return(0,MZ.isCollapsedWhitespaces)(K)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.checkContenteditableSliceForEmptiness=void 0;var q=g1;Object.defineProperty(J,"checkContenteditableSliceForEmptiness",{enumerable:!0,get:function(){return q.checkContenteditableSliceForEmptiness}})})(J4);var F7={};(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.getContenteditableSlice=void 0;var q=m1;Object.defineProperty(J,"getContenteditableSlice",{enumerable:!0,get:function(){return q.getContenteditableSlice}})})(F7);var W7={},q4={};Object.defineProperty(q4,"__esModule",{value:!0});q4.focus=IZ;var jZ=v;function IZ(J,q){var Z,X;if(q===void 0&&(q=!0),(0,jZ.isNativeInput)(J)){J.focus();var K=q?0:J.value.length;J.setSelectionRange(K,K)}else{var Q=document.createRange(),V=window.getSelection();if(!V)return;var G=function(D,A){A===void 0&&(A=!1);var W=document.createTextNode("");A?D.insertBefore(W,D.firstChild):D.appendChild(W),Q.setStart(W,0),Q.setEnd(W,0)},Y=function(D){return D!=null},U=J.childNodes,H=q?U[0]:U[U.length-1];if(Y(H)){for(;Y(H)&&H.nodeType!==Node.TEXT_NODE;)H=q?H.firstChild:H.lastChild;if(Y(H)&&H.nodeType===Node.TEXT_NODE){var $=(X=(Z=H.textContent)===null||Z===void 0?void 0:Z.length)!==null&&X!==void 0?X:0,K=q?0:$;Q.setStart(H,K),Q.setEnd(H,K)}else G(J,q)}else G(J);V.removeAllRanges(),V.addRange(Q)}}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.focus=void 0;var q=q4;Object.defineProperty(J,"focus",{enumerable:!0,get:function(){return q.focus}})})(W7);var Z4={},b1={};Object.defineProperty(b1,"__esModule",{value:!0});b1.getCaretNodeAndOffset=_Z;function _Z(){var J=window.getSelection();if(J===null)return[null,0];var{focusNode:q,focusOffset:Z}=J;return q===null?[null,0]:(q.nodeType!==Node.TEXT_NODE&&q.childNodes.length>0&&(q.childNodes[Z]!==void 0?(q=q.childNodes[Z],Z=0):(q=q.childNodes[Z-1],q.textContent!==null&&(Z=q.textContent.length))),[q,Z])}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.getCaretNodeAndOffset=void 0;var q=b1;Object.defineProperty(J,"getCaretNodeAndOffset",{enumerable:!0,get:function(){return q.getCaretNodeAndOffset}})})(Z4);var L7={},f1={};Object.defineProperty(f1,"__esModule",{value:!0});f1.getRange=SZ;function SZ(){var J=window.getSelection();return J&&J.rangeCount?J.getRangeAt(0):null}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.getRange=void 0;var q=f1;Object.defineProperty(J,"getRange",{enumerable:!0,get:function(){return q.getRange}})})(L7);var A7={},X4={};Object.defineProperty(X4,"__esModule",{value:!0});X4.isCaretAtEndOfInput=BZ;var r6=v,xZ=Z4,TZ=J4;function BZ(J){var q=(0,r6.getDeepestNode)(J,!0);if(q===null)return!0;if((0,r6.isNativeInput)(q))return q.selectionEnd===q.value.length;var Z=(0,xZ.getCaretNodeAndOffset)(),X=Z[0],K=Z[1];return X===null?!1:(0,TZ.checkContenteditableSliceForEmptiness)(J,X,K,"right")}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isCaretAtEndOfInput=void 0;var q=X4;Object.defineProperty(J,"isCaretAtEndOfInput",{enumerable:!0,get:function(){return q.isCaretAtEndOfInput}})})(A7);var N7={},K4={};Object.defineProperty(K4,"__esModule",{value:!0});K4.isCaretAtStartOfInput=wZ;var S1=v,CZ=b1,EZ=g1;function wZ(J){var q=(0,S1.getDeepestNode)(J);if(q===null||(0,S1.isEmpty)(J))return!0;if((0,S1.isNativeInput)(q))return q.selectionEnd===0;if((0,S1.isEmpty)(J))return!0;var Z=(0,CZ.getCaretNodeAndOffset)(),X=Z[0],K=Z[1];return X===null?!1:(0,EZ.checkContenteditableSliceForEmptiness)(J,X,K,"left")}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isCaretAtStartOfInput=void 0;var q=K4;Object.defineProperty(J,"isCaretAtStartOfInput",{enumerable:!0,get:function(){return q.isCaretAtStartOfInput}})})(N7);var P7={},Q4={};Object.defineProperty(Q4,"__esModule",{value:!0});Q4.save=kZ;var yZ=v,vZ=f1;function kZ(){var J=(0,vZ.getRange)(),q=(0,yZ.make)("span");if(q.id="cursor",q.hidden=!0,!!J)return J.insertNode(q),function(){var Z=window.getSelection();Z&&(J.setStartAfter(q),J.setEndAfter(q),Z.removeAllRanges(),Z.addRange(J),setTimeout(function(){q.remove()},150))}}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.save=void 0;var q=Q4;Object.defineProperty(J,"save",{enumerable:!0,get:function(){return q.save}})})(P7);(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.save=J.isCaretAtStartOfInput=J.isCaretAtEndOfInput=J.getRange=J.getCaretNodeAndOffset=J.focus=J.getContenteditableSlice=J.checkContenteditableSliceForEmptiness=void 0;var q=J4;Object.defineProperty(J,"checkContenteditableSliceForEmptiness",{enumerable:!0,get:function(){return q.checkContenteditableSliceForEmptiness}});var Z=F7;Object.defineProperty(J,"getContenteditableSlice",{enumerable:!0,get:function(){return Z.getContenteditableSlice}});var X=W7;Object.defineProperty(J,"focus",{enumerable:!0,get:function(){return X.focus}});var K=Z4;Object.defineProperty(J,"getCaretNodeAndOffset",{enumerable:!0,get:function(){return K.getCaretNodeAndOffset}});var Q=L7;Object.defineProperty(J,"getRange",{enumerable:!0,get:function(){return Q.getRange}});var V=A7;Object.defineProperty(J,"isCaretAtEndOfInput",{enumerable:!0,get:function(){return V.isCaretAtEndOfInput}});var G=N7;Object.defineProperty(J,"isCaretAtStartOfInput",{enumerable:!0,get:function(){return G.isCaretAtStartOfInput}});var Y=P7;Object.defineProperty(J,"save",{enumerable:!0,get:function(){return Y.save}})})(a0);class h{static get CSS(){return{...a,checklist:`${i}-checklist`,itemChecked:`${i}__checkbox--checked`,noHover:`${i}__checkbox--no-hover`,checkbox:`${i}__checkbox-check`,checkboxContainer:`${i}__checkbox`,checkboxCheckDisabled:`${i}__checkbox-check--disabled`}}constructor(J,q){this.config=q,this.readOnly=J}renderWrapper(J){let q;return J===!0?(q=v.make("ul",[h.CSS.wrapper,h.CSS.checklist]),q.addEventListener("click",(Z)=>{let X=Z.target;if(X){let K=X.closest(`.${h.CSS.checkboxContainer}`);K&&K.contains(X)&&this.toggleCheckbox(K)}})):q=v.make("ul",[h.CSS.checklist,h.CSS.itemChildren]),q}renderItem(J,q){let Z=v.make("li",[h.CSS.item,h.CSS.item]),X=v.make("div",h.CSS.itemContent,{innerHTML:J,contentEditable:(!this.readOnly).toString()}),K=v.make("span",h.CSS.checkbox),Q=v.make("div",h.CSS.checkboxContainer);return q.checked===!0&&Q.classList.add(h.CSS.itemChecked),this.readOnly&&Q.classList.add(h.CSS.checkboxCheckDisabled),K.innerHTML=tJ,Q.appendChild(K),Z.appendChild(Q),Z.appendChild(X),Z}getItemContent(J){let q=J.querySelector(`.${h.CSS.itemContent}`);return!q||v.isEmpty(q)?"":q.innerHTML}getItemMeta(J){let q=J.querySelector(`.${h.CSS.checkboxContainer}`);return{checked:q?q.classList.contains(h.CSS.itemChecked):!1}}composeDefaultMeta(){return{checked:!1}}toggleCheckbox(J){J.classList.toggle(h.CSS.itemChecked),J.classList.add(h.CSS.noHover),J.addEventListener("mouseleave",()=>this.removeSpecialHoverBehavior(J),{once:!0})}removeSpecialHoverBehavior(J){J.classList.remove(h.CSS.noHover)}}function A3(J,q="after"){let Z=[],X;function K(Q){switch(q){case"after":return Q.nextElementSibling;case"before":return Q.previousElementSibling}}for(X=K(J);X!==null;)Z.push(X),X=K(X);return Z.length!==0?Z:null}function U0(J,q=!0){let Z=J;return J.classList.contains(a.item)&&(Z=J.querySelector(`.${a.itemChildren}`)),Z===null?[]:q?Array.from(Z.querySelectorAll(`:scope > .${a.item}`)):Array.from(Z.querySelectorAll(`.${a.item}`))}function gZ(J){return J.nextElementSibling===null}function mZ(J){return J.querySelector(`.${a.itemChildren}`)!==null}function D0(J){return J.querySelector(`.${a.itemChildren}`)}function N3(J){let q=J;J.classList.contains(a.item)&&(q=D0(J)),q!==null&&U0(q).length===0&&q.remove()}function T1(J){return J.querySelector(`.${a.itemContent}`)}function y0(J,q=!0){let Z=T1(J);Z&&a0.focus(Z,q)}class B1{get currentItem(){let J=window.getSelection();if(!J)return null;let q=J.anchorNode;return!q||(O0(q)||(q=q.parentNode),!q)||!O0(q)?null:q.closest(`.${a.item}`)}get currentItemLevel(){let J=this.currentItem;if(J===null)return null;let q=J.parentNode,Z=0;for(;q!==null&&q!==this.listWrapper;)O0(q)&&q.classList.contains(a.item)&&(Z+=1),q=q.parentNode;return Z+1}constructor({data:J,config:q,api:Z,readOnly:X,block:K},Q){this.config=q,this.data=J,this.readOnly=X,this.api=Z,this.block=K,this.renderer=Q}render(){return this.listWrapper=this.renderer.renderWrapper(!0),this.data.items.length?this.appendItems(this.data.items,this.listWrapper):this.appendItems([{content:"",meta:{},items:[]}],this.listWrapper),this.readOnly||this.listWrapper.addEventListener("keydown",(J)=>{switch(J.key){case"Enter":J.shiftKey||this.enterPressed(J);break;case"Backspace":this.backspace(J);break;case"Tab":J.shiftKey?this.shiftTab(J):this.addTab(J);break}},!1),"start"in this.data.meta&&this.data.meta.start!==void 0&&this.changeStartWith(this.data.meta.start),"counterType"in this.data.meta&&this.data.meta.counterType!==void 0&&this.changeCounters(this.data.meta.counterType),this.listWrapper}save(J){let q=J??this.listWrapper,Z=(Q)=>U0(Q).map((V)=>{let G=D0(V),Y=this.renderer.getItemContent(V),U=this.renderer.getItemMeta(V),H=G?Z(G):[];return{content:Y,meta:U,items:H}}),X=q?Z(q):[],K={style:this.data.style,meta:{},items:X};return this.data.style==="ordered"&&(K.meta={start:this.data.meta.start,counterType:this.data.meta.counterType}),K}static get pasteConfig(){return{tags:["OL","UL","LI"]}}merge(J){let q=this.block.holder.querySelectorAll(`.${a.item}`),Z=q[q.length-1],X=T1(Z);if(Z===null||X===null||(X.insertAdjacentHTML("beforeend",J.items[0].content),this.listWrapper===void 0))return;let K=U0(this.listWrapper);if(K.length===0)return;let Q=K[K.length-1],V=D0(Q),G=J.items.shift();G!==void 0&&(G.items.length!==0&&(V===null&&(V=this.renderer.renderWrapper(!1)),this.appendItems(G.items,V)),J.items.length>0&&this.appendItems(J.items,this.listWrapper))}onPaste(J){let q=J.detail.data;this.data=this.pasteHandler(q);let Z=this.listWrapper;Z&&Z.parentNode&&Z.parentNode.replaceChild(this.render(),Z)}pasteHandler(J){let{tagName:q}=J,Z="unordered",X;switch(q){case"OL":Z="ordered",X="ol";break;case"UL":case"LI":Z="unordered",X="ul"}let K={style:Z,meta:{},items:[]};Z==="ordered"&&(this.data.meta.counterType="numeric",this.data.meta.start=1);let Q=(V)=>Array.from(V.querySelectorAll(":scope > li")).map((G)=>{let Y=G.querySelector(`:scope > ${X}`),U=Y?Q(Y):[];return{content:G.innerHTML??"",meta:{},items:U}});return K.items=Q(J),K}changeStartWith(J){this.listWrapper.style.setProperty("counter-reset",`item ${J-1}`),this.data.meta.start=J}changeCounters(J){this.listWrapper.style.setProperty("--list-counter-type",J),this.data.meta.counterType=J}enterPressed(J){var q;let Z=this.currentItem;if(J.stopPropagation(),J.preventDefault(),J.isComposing||Z===null)return;let X=((q=this.renderer)==null?void 0:q.getItemContent(Z).trim().length)===0,K=Z.parentNode===this.listWrapper,Q=Z.previousElementSibling===null,V=this.api.blocks.getCurrentBlockIndex();if(K&&X)if(gZ(Z)&&!mZ(Z)){Q?this.convertItemToDefaultBlock(V,!0):this.convertItemToDefaultBlock();return}else{this.splitList(Z);return}else if(X){this.unshiftItem(Z);return}else this.splitItem(Z)}backspace(J){var q;let Z=this.currentItem;if(Z!==null&&a0.isCaretAtStartOfInput(Z)&&((q=window.getSelection())==null?void 0:q.isCollapsed)!==!1){if(J.stopPropagation(),Z.parentNode===this.listWrapper&&Z.previousElementSibling===null){this.convertFirstItemToDefaultBlock();return}J.preventDefault(),this.mergeItemWithPrevious(Z)}}shiftTab(J){J.stopPropagation(),J.preventDefault(),this.currentItem!==null&&this.unshiftItem(this.currentItem)}unshiftItem(J){if(!J.parentNode||!O0(J.parentNode))return;let q=J.parentNode.closest(`.${a.item}`);if(!q)return;let Z=D0(J);if(J.parentElement===null)return;let X=A3(J);X!==null&&(Z===null&&(Z=this.renderer.renderWrapper(!1)),X.forEach((K)=>{Z.appendChild(K)}),J.appendChild(Z)),q.after(J),y0(J,!1),N3(q)}splitList(J){let q=U0(J),Z=this.block,X=this.api.blocks.getCurrentBlockIndex();if(q.length!==0){let G=q[0];this.unshiftItem(G),y0(J,!1)}if(J.previousElementSibling===null&&J.parentNode===this.listWrapper){this.convertItemToDefaultBlock(X);return}let K=A3(J);if(K===null)return;let Q=this.renderer.renderWrapper(!0);K.forEach((G)=>{Q.appendChild(G)});let V=this.save(Q);V.meta.start=this.data.style=="ordered"?1:void 0,this.api.blocks.insert(Z==null?void 0:Z.name,V,this.config,X+1),this.convertItemToDefaultBlock(X+1),Q.remove()}splitItem(J){let[q,Z]=a0.getCaretNodeAndOffset();if(q===null)return;let X=T1(J),K;X===null?K="":K=a0.getContenteditableSlice(X,q,Z,"right",!0);let Q=D0(J),V=this.renderItem(K);J==null||J.after(V),Q&&V.appendChild(Q),y0(V)}mergeItemWithPrevious(J){let{previousElementSibling:q,parentNode:Z}=J;if(Z===null||!O0(Z))return;let X=Z.closest(`.${a.item}`);if(!q&&!X||q&&!O0(q))return;let K;if(q){let H=U0(q,!1);H.length!==0&&H.length!==0?K=H[H.length-1]:K=q}else K=X;let Q=this.renderer.getItemContent(J);if(!K)return;y0(K,!1);let V=T1(K);if(V===null)return;V.insertAdjacentHTML("beforeend",Q);let G=U0(J);if(G.length===0){J.remove(),N3(K);return}let Y=q||X,U=D0(Y)??this.renderer.renderWrapper(!1);q?G.forEach((H)=>{U.appendChild(H)}):G.forEach((H)=>{U.prepend(H)}),D0(Y)===null&&K.appendChild(U),J.remove()}addTab(J){var q;J.stopPropagation(),J.preventDefault();let Z=this.currentItem;if(!Z)return;if(((q=this.config)==null?void 0:q.maxLevel)!==void 0){let Q=this.currentItemLevel;if(Q!==null&&Q===this.config.maxLevel)return}let X=Z.previousSibling;if(X===null||!O0(X))return;let K=D0(X);if(K)K.appendChild(Z),U0(Z).forEach((Q)=>{K.appendChild(Q)});else{let Q=this.renderer.renderWrapper(!1);Q.appendChild(Z),U0(Z).forEach((V)=>{Q.appendChild(V)}),X.appendChild(Q)}N3(Z),y0(Z,!1)}convertItemToDefaultBlock(J,q){let Z,X=this.currentItem,K=X!==null?this.renderer.getItemContent(X):"";q===!0&&this.api.blocks.delete(),J!==void 0?Z=this.api.blocks.insert(void 0,{text:K},void 0,J):Z=this.api.blocks.insert(),X==null||X.remove(),this.api.caret.setToBlock(Z,"start")}convertFirstItemToDefaultBlock(){let J=this.currentItem;if(J===null)return;let q=U0(J);if(q.length!==0){let Q=q[0];this.unshiftItem(Q),y0(J)}let Z=A3(J),X=this.api.blocks.getCurrentBlockIndex(),K=Z===null;this.convertItemToDefaultBlock(X,K)}renderItem(J,q){let Z=q??this.renderer.composeDefaultMeta();switch(!0){case this.renderer instanceof K0:return this.renderer.renderItem(J,Z);case this.renderer instanceof Q0:return this.renderer.renderItem(J,Z);default:return this.renderer.renderItem(J,Z)}}appendItems(J,q){J.forEach((Z)=>{var X;let K=this.renderItem(Z.content,Z.meta);if(q.appendChild(K),Z.items.length){let Q=(X=this.renderer)==null?void 0:X.renderWrapper(!1);this.appendItems(Z.items,Q),K.appendChild(Q)}})}}var v0={wrapper:`${i}-start-with-field`,input:`${i}-start-with-field__input`,startWithElementWrapperInvalid:`${i}-start-with-field--invalid`};function bZ(J,{value:q,placeholder:Z,attributes:X,sanitize:K}){let Q=v.make("div",v0.wrapper),V=v.make("input",v0.input,{placeholder:Z,tabIndex:-1,value:q});for(let G in X)V.setAttribute(G,X[G]);return Q.appendChild(V),V.addEventListener("input",()=>{K!==void 0&&(V.value=K(V.value));let G=V.checkValidity();!G&&!Q.classList.contains(v0.startWithElementWrapperInvalid)&&Q.classList.add(v0.startWithElementWrapperInvalid),G&&Q.classList.contains(v0.startWithElementWrapperInvalid)&&Q.classList.remove(v0.startWithElementWrapperInvalid),G&&J(V.value)}),Q}var l0=new Map([["Numeric","numeric"],["Lower Roman","lower-roman"],["Upper Roman","upper-roman"],["Lower Alpha","lower-alpha"],["Upper Alpha","upper-alpha"]]),i6=new Map([["numeric",eJ],["lower-roman",Jq],["upper-roman",qq],["lower-alpha",Xq],["upper-alpha",Zq]]);function fZ(J){return J.replace(/\D+/g,"")}function pZ(J){return typeof J.items[0]=="string"}function hZ(J){return!("meta"in J)}function dZ(J){return typeof J.items[0]!="string"&&"text"in J.items[0]&&"checked"in J.items[0]&&typeof J.items[0].text=="string"&&typeof J.items[0].checked=="boolean"}function cZ(J){let q=[];return pZ(J)?(J.items.forEach((Z)=>{q.push({content:Z,meta:{},items:[]})}),{style:J.style,meta:{},items:q}):dZ(J)?(J.items.forEach((Z)=>{q.push({content:Z.text,meta:{checked:Z.checked},items:[]})}),{style:"checklist",meta:{},items:q}):hZ(J)?{style:J.style,meta:{},items:J.items}:structuredClone(J)}class E1{static get isReadOnlySupported(){return!0}static get enableLineBreaks(){return!0}static get toolbox(){return[{icon:l6,title:"Unordered List",data:{style:"unordered"}},{icon:a6,title:"Ordered List",data:{style:"ordered"}},{icon:u6,title:"Checklist",data:{style:"checklist"}}]}static get pasteConfig(){return{tags:["OL","UL","LI"]}}static get conversionConfig(){return{export:(J)=>E1.joinRecursive(J),import:(J,q)=>({meta:{},items:[{content:J,meta:{},items:[]}],style:(q==null?void 0:q.defaultStyle)!==void 0?q.defaultStyle:"unordered"})}}get listStyle(){return this.data.style||this.defaultListStyle}set listStyle(J){var q;this.data.style=J,this.changeTabulatorByStyle();let Z=this.list.render();(q=this.listElement)==null||q.replaceWith(Z),this.listElement=Z}constructor({data:J,config:q,api:Z,readOnly:X,block:K}){var Q;this.api=Z,this.readOnly=X,this.config=q,this.block=K,this.defaultListStyle=((Q=this.config)==null?void 0:Q.defaultStyle)||"unordered",this.defaultCounterTypes=this.config.counterTypes||Array.from(l0.values());let V={style:this.defaultListStyle,meta:{},items:[]};this.data=Object.keys(J).length?cZ(J):V,this.listStyle==="ordered"&&this.data.meta.counterType===void 0&&(this.data.meta.counterType="numeric"),this.changeTabulatorByStyle()}static joinRecursive(J){return J.items.map((q)=>`${q.content} ${E1.joinRecursive(q)}`).join("")}render(){return this.listElement=this.list.render(),this.listElement}save(){return this.data=this.list.save(),this.data}merge(J){this.list.merge(J)}renderSettings(){let J=[{label:this.api.i18n.t("Unordered"),icon:l6,closeOnActivate:!0,isActive:this.listStyle=="unordered",onActivate:()=>{this.listStyle="unordered"}},{label:this.api.i18n.t("Ordered"),icon:a6,closeOnActivate:!0,isActive:this.listStyle=="ordered",onActivate:()=>{this.listStyle="ordered"}},{label:this.api.i18n.t("Checklist"),icon:u6,closeOnActivate:!0,isActive:this.listStyle=="checklist",onActivate:()=>{this.listStyle="checklist"}}];if(this.listStyle==="ordered"){let q=bZ((K)=>this.changeStartWith(Number(K)),{value:String(this.data.meta.start??1),placeholder:"",attributes:{required:"true"},sanitize:(K)=>fZ(K)}),Z=[{label:this.api.i18n.t("Start with"),icon:Kq,children:{items:[{element:q,type:"html"}]}}],X={label:this.api.i18n.t("Counter type"),icon:i6.get(this.data.meta.counterType),children:{items:[]}};l0.forEach((K,Q)=>{let V=l0.get(Q);this.defaultCounterTypes.includes(V)&&X.children.items.push({title:this.api.i18n.t(Q),icon:i6.get(V),isActive:this.data.meta.counterType===l0.get(Q),closeOnActivate:!0,onActivate:()=>{this.changeCounters(l0.get(Q))}})}),X.children.items.length>1&&Z.push(X),J.push({type:"separator"},...Z)}return J}onPaste(J){let{tagName:q}=J.detail.data;switch(q){case"OL":this.listStyle="ordered";break;case"UL":case"LI":this.listStyle="unordered"}this.list.onPaste(J)}pasteHandler(J){return this.list.pasteHandler(J)}changeCounters(J){var q;(q=this.list)==null||q.changeCounters(J),this.data.meta.counterType=J}changeStartWith(J){var q;(q=this.list)==null||q.changeStartWith(J),this.data.meta.start=J}changeTabulatorByStyle(){switch(this.listStyle){case"ordered":this.list=new B1({data:this.data,readOnly:this.readOnly,api:this.api,config:this.config,block:this.block},new K0(this.readOnly,this.config));break;case"unordered":this.list=new B1({data:this.data,readOnly:this.readOnly,api:this.api,config:this.config,block:this.block},new Q0(this.readOnly,this.config));break;case"checklist":this.list=new B1({data:this.data,readOnly:this.readOnly,api:this.api,config:this.config,block:this.block},new h(this.readOnly,this.config));break}}}(function(){try{if(typeof document<"u"){var J=document.createElement("style");J.appendChild(document.createTextNode(".ce-code__textarea{min-height:200px;font-family:Menlo,Monaco,Consolas,Courier New,monospace;color:#41314e;line-height:1.6em;font-size:12px;background:#f8f7fa;border:1px solid #f1f1f4;box-shadow:none;white-space:pre;word-wrap:normal;overflow-x:auto;resize:vertical}")),document.head.appendChild(J)}}catch(q){console.error("vite-plugin-css-injected-by-js",q)}})();function uZ(J,q){let Z="";for(;Z!==` +`&&q>0;)q=q-1,Z=J.substr(q,1);return Z===` +`&&(q+=1),q}var lZ='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 8L5 12L9 16"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 8L19 12L15 16"/></svg>';class V4{static get isReadOnlySupported(){return!0}static get enableLineBreaks(){return!0}constructor({data:J,config:q,api:Z,readOnly:X}){this.api=Z,this.readOnly=X,this.placeholder=this.api.i18n.t(q.placeholder||V4.DEFAULT_PLACEHOLDER),this.CSS={baseClass:this.api.styles.block,input:this.api.styles.input,wrapper:"ce-code",textarea:"ce-code__textarea"},this.nodes={holder:null,textarea:null},this.data={code:J.code??""},this.nodes.holder=this.drawView()}render(){return this.nodes.holder}save(J){return{code:J.querySelector("textarea").value}}onPaste(J){switch(J.type){case"tag":{let q=J.detail.data;this.handleHTMLPaste(q);break}}}get data(){return this._data}set data(J){this._data=J,this.nodes.textarea&&(this.nodes.textarea.value=J.code)}static get toolbox(){return{icon:lZ,title:"Code"}}static get DEFAULT_PLACEHOLDER(){return"Enter a code"}static get pasteConfig(){return{tags:["pre"]}}static get sanitize(){return{code:!0}}tabHandler(J){J.stopPropagation(),J.preventDefault();let{target:q,shiftKey:Z}=J,X=q.selectionStart,K=q.value,Q=" ",V;if(!Z)V=X+Q.length,q.value=K.substring(0,X)+Q+K.substring(X);else{let G=uZ(K,X);if(K.substr(G,Q.length)!==Q)return;q.value=K.substring(0,G)+K.substring(G+Q.length),V=X-Q.length}q.setSelectionRange(V,V)}drawView(){let J=document.createElement("div"),q=document.createElement("textarea");return J.classList.add(this.CSS.baseClass,this.CSS.wrapper),q.classList.add(this.CSS.textarea,this.CSS.input),q.value=this.data.code,q.placeholder=this.placeholder,this.readOnly&&(q.disabled=!0),J.appendChild(q),q.addEventListener("keydown",(Z)=>{switch(Z.code){case"Tab":this.tabHandler(Z);break}}),this.nodes.textarea=q,J}handleHTMLPaste(J){this.data={code:J.innerHTML}}}(function(){try{if(typeof document<"u"){var J=document.createElement("style");J.appendChild(document.createTextNode(".cdx-quote-icon svg{transform:rotate(180deg)}.cdx-quote{margin:0}.cdx-quote__text{min-height:158px;margin-bottom:10px}.cdx-quote [contentEditable=true][data-placeholder]:before{position:absolute;content:attr(data-placeholder);color:#707684;font-weight:400;opacity:0}.cdx-quote [contentEditable=true][data-placeholder]:empty:before{opacity:1}.cdx-quote [contentEditable=true][data-placeholder]:empty:focus:before{opacity:0}.cdx-quote-settings{display:flex}.cdx-quote-settings .cdx-settings-button{width:50%}")),document.head.appendChild(J)}}catch(q){console.error("vite-plugin-css-injected-by-js",q)}})();var aZ='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M18 7L6 7"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M18 17H6"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M16 12L8 12"/></svg>',sZ='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M17 7L5 7"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M17 17H5"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M13 12L5 12"/></svg>',rZ='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 10.8182L9 10.8182C8.80222 10.8182 8.60888 10.7649 8.44443 10.665C8.27998 10.5651 8.15181 10.4231 8.07612 10.257C8.00043 10.0909 7.98063 9.90808 8.01922 9.73174C8.0578 9.55539 8.15304 9.39341 8.29289 9.26627C8.43275 9.13913 8.61093 9.05255 8.80491 9.01747C8.99889 8.98239 9.19996 9.00039 9.38268 9.0692C9.56541 9.13801 9.72159 9.25453 9.83147 9.40403C9.94135 9.55353 10 9.72929 10 9.90909L10 12.1818C10 12.664 9.78929 13.1265 9.41421 13.4675C9.03914 13.8084 8.53043 14 8 14"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 10.8182L15 10.8182C14.8022 10.8182 14.6089 10.7649 14.4444 10.665C14.28 10.5651 14.1518 10.4231 14.0761 10.257C14.0004 10.0909 13.9806 9.90808 14.0192 9.73174C14.0578 9.55539 14.153 9.39341 14.2929 9.26627C14.4327 9.13913 14.6109 9.05255 14.8049 9.01747C14.9989 8.98239 15.2 9.00039 15.3827 9.0692C15.5654 9.13801 15.7216 9.25453 15.8315 9.40403C15.9414 9.55353 16 9.72929 16 9.90909L16 12.1818C16 12.664 15.7893 13.1265 15.4142 13.4675C15.0391 13.8084 14.5304 14 14 14"/></svg>',c1=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function iZ(J){if(J.__esModule)return J;var q=J.default;if(typeof q=="function"){var Z=function X(){return this instanceof X?Reflect.construct(q,arguments,this.constructor):q.apply(this,arguments)};Z.prototype=q.prototype}else Z={};return Object.defineProperty(Z,"__esModule",{value:!0}),Object.keys(J).forEach(function(X){var K=Object.getOwnPropertyDescriptor(J,X);Object.defineProperty(Z,X,K.get?K:{enumerable:!0,get:function(){return J[X]}})}),Z}var h1={},Y4={},U4={};Object.defineProperty(U4,"__esModule",{value:!0});U4.allInputsSelector=oZ;function oZ(){var J=["text","password","email","number","search","tel","url"];return"[contenteditable=true], textarea, input:not([type]), "+J.map(function(q){return'input[type="'.concat(q,'"]')}).join(", ")}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.allInputsSelector=void 0;var q=U4;Object.defineProperty(J,"allInputsSelector",{enumerable:!0,get:function(){return q.allInputsSelector}})})(Y4);var I0={},H4={};Object.defineProperty(H4,"__esModule",{value:!0});H4.isNativeInput=nZ;function nZ(J){var q=["INPUT","TEXTAREA"];return J&&J.tagName?q.includes(J.tagName):!1}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isNativeInput=void 0;var q=H4;Object.defineProperty(J,"isNativeInput",{enumerable:!0,get:function(){return q.isNativeInput}})})(I0);var R7={},$4={};Object.defineProperty($4,"__esModule",{value:!0});$4.append=tZ;function tZ(J,q){Array.isArray(q)?q.forEach(function(Z){J.appendChild(Z)}):J.appendChild(q)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.append=void 0;var q=$4;Object.defineProperty(J,"append",{enumerable:!0,get:function(){return q.append}})})(R7);var z4={},D4={};Object.defineProperty(D4,"__esModule",{value:!0});D4.blockElements=eZ;function eZ(){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(J){Object.defineProperty(J,"__esModule",{value:!0}),J.blockElements=void 0;var q=D4;Object.defineProperty(J,"blockElements",{enumerable:!0,get:function(){return q.blockElements}})})(z4);var O7={},F4={};Object.defineProperty(F4,"__esModule",{value:!0});F4.calculateBaseline=JX;function JX(J){var q=window.getComputedStyle(J),Z=parseFloat(q.fontSize),X=parseFloat(q.lineHeight)||Z*1.2,K=parseFloat(q.paddingTop),Q=parseFloat(q.borderTopWidth),V=parseFloat(q.marginTop),G=Z*0.8,Y=(X-Z)/2,U=V+Q+K+Y+G;return U}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.calculateBaseline=void 0;var q=F4;Object.defineProperty(J,"calculateBaseline",{enumerable:!0,get:function(){return q.calculateBaseline}})})(O7);var j7={},W4={},L4={},A4={};Object.defineProperty(A4,"__esModule",{value:!0});A4.isContentEditable=qX;function qX(J){return J.contentEditable==="true"}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isContentEditable=void 0;var q=A4;Object.defineProperty(J,"isContentEditable",{enumerable:!0,get:function(){return q.isContentEditable}})})(L4);Object.defineProperty(W4,"__esModule",{value:!0});W4.canSetCaret=KX;var ZX=I0,XX=L4;function KX(J){var q=!0;if((0,ZX.isNativeInput)(J))switch(J.type){case"file":case"checkbox":case"radio":case"hidden":case"submit":case"button":case"image":case"reset":q=!1;break}else q=(0,XX.isContentEditable)(J);return q}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.canSetCaret=void 0;var q=W4;Object.defineProperty(J,"canSetCaret",{enumerable:!0,get:function(){return q.canSetCaret}})})(j7);var u1={},N4={};function QX(J,q,Z){let X=Z.value!==void 0?"value":"get",K=Z[X],Q=`#${q}Cache`;if(Z[X]=function(...V){return this[Q]===void 0&&(this[Q]=K.apply(this,V)),this[Q]},X==="get"&&Z.set){let V=Z.set;Z.set=function(G){delete J[Q],V.apply(this,G)}}return Z}function I7(){let J={win:!1,mac:!1,x11:!1,linux:!1},q=Object.keys(J).find((Z)=>window.navigator.appVersion.toLowerCase().indexOf(Z)!==-1);return q!==void 0&&(J[q]=!0),J}function P4(J){return J!=null&&J!==""&&(typeof J!="object"||Object.keys(J).length>0)}function VX(J){return!P4(J)}var GX=()=>typeof window<"u"&&window.navigator!==null&&P4(window.navigator.platform)&&(/iP(ad|hone|od)/.test(window.navigator.platform)||window.navigator.platform==="MacIntel"&&window.navigator.maxTouchPoints>1);function YX(J){let q=I7();return J=J.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,"+"),q.mac?J=J.replace(/ctrl|cmd/gi,"⌘").replace(/alt/gi,"⌥"):J=J.replace(/cmd/gi,"Ctrl").replace(/windows/gi,"WIN"),J}function UX(J){return J[0].toUpperCase()+J.slice(1)}function HX(J){let q=document.createElement("div");q.style.position="absolute",q.style.left="-999px",q.style.bottom="-999px",q.innerHTML=J,document.body.appendChild(q);let Z=window.getSelection(),X=document.createRange();if(X.selectNode(q),Z===null)throw Error("Cannot copy text to clipboard");Z.removeAllRanges(),Z.addRange(X),document.execCommand("copy"),document.body.removeChild(q)}function $X(J,q,Z){let X;return(...K)=>{let Q=this,V=()=>{X=void 0,Z!==!0&&J.apply(Q,K)},G=Z===!0&&X!==void 0;window.clearTimeout(X),X=window.setTimeout(V,q),G&&J.apply(Q,K)}}function W0(J){return Object.prototype.toString.call(J).match(/\s([a-zA-Z]+)/)[1].toLowerCase()}function zX(J){return W0(J)==="boolean"}function _7(J){return W0(J)==="function"||W0(J)==="asyncfunction"}function DX(J){return _7(J)&&/^\s*class\s+/.test(J.toString())}function FX(J){return W0(J)==="number"}function d1(J){return W0(J)==="object"}function WX(J){return Promise.resolve(J)===J}function LX(J){return W0(J)==="string"}function AX(J){return W0(J)==="undefined"}function G4(J,...q){if(!q.length)return J;let Z=q.shift();if(d1(J)&&d1(Z))for(let X in Z)d1(Z[X])?(J[X]===void 0&&Object.assign(J,{[X]:{}}),G4(J[X],Z[X])):Object.assign(J,{[X]:Z[X]});return G4(J,...q)}function NX(J,q,Z){let X=`«${q}» is deprecated and will be removed in the next major release. Please use the «${Z}» instead.`;J&&console.warn(X)}function PX(J){try{return new URL(J).href}catch{}return J.substring(0,2)==="//"?window.location.protocol+J:window.location.origin+J}function MX(J){return J>47&&J<58||J===32||J===13||J===229||J>64&&J<91||J>95&&J<112||J>185&&J<193||J>218&&J<223}var RX={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},OX={LEFT:0,WHEEL:1,RIGHT:2,BACKWARD:3,FORWARD:4};class S7{constructor(){this.completed=Promise.resolve()}add(J){return new Promise((q,Z)=>{this.completed=this.completed.then(J).then(q).catch(Z)})}}function jX(J,q,Z=void 0){let X,K,Q,V=null,G=0;Z||(Z={});let Y=function(){G=Z.leading===!1?0:Date.now(),V=null,Q=J.apply(X,K),V===null&&(X=K=null)};return function(){let U=Date.now();!G&&Z.leading===!1&&(G=U);let H=q-(U-G);return X=this,K=arguments,H<=0||H>q?(V&&(clearTimeout(V),V=null),G=U,Q=J.apply(X,K),V===null&&(X=K=null)):!V&&Z.trailing!==!1&&(V=setTimeout(Y,H)),Q}}var IX=Object.freeze(Object.defineProperty({__proto__:null,PromiseQueue:S7,beautifyShortcut:YX,cacheable:QX,capitalize:UX,copyTextToClipboard:HX,debounce:$X,deepMerge:G4,deprecationAssert:NX,getUserOS:I7,getValidUrl:PX,isBoolean:zX,isClass:DX,isEmpty:VX,isFunction:_7,isIosDevice:GX,isNumber:FX,isObject:d1,isPrintableKey:MX,isPromise:WX,isString:LX,isUndefined:AX,keyCodes:RX,mouseButtons:OX,notEmpty:P4,throttle:jX,typeOf:W0},Symbol.toStringTag,{value:"Module"})),M4=iZ(IX);Object.defineProperty(N4,"__esModule",{value:!0});N4.containsOnlyInlineElements=xX;var _X=M4,SX=z4;function xX(J){var q;(0,_X.isString)(J)?(q=document.createElement("div"),q.innerHTML=J):q=J;var Z=function(X){return!(0,SX.blockElements)().includes(X.tagName.toLowerCase())&&Array.from(X.children).every(Z)};return Array.from(q.children).every(Z)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.containsOnlyInlineElements=void 0;var q=N4;Object.defineProperty(J,"containsOnlyInlineElements",{enumerable:!0,get:function(){return q.containsOnlyInlineElements}})})(u1);var x7={},R4={},l1={},O4={};Object.defineProperty(O4,"__esModule",{value:!0});O4.make=TX;function TX(J,q,Z){var X;q===void 0&&(q=null),Z===void 0&&(Z={});var K=document.createElement(J);if(Array.isArray(q)){var Q=q.filter(function(G){return G!==void 0});(X=K.classList).add.apply(X,Q)}else q!==null&&K.classList.add(q);for(var V in Z)Object.prototype.hasOwnProperty.call(Z,V)&&(K[V]=Z[V]);return K}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.make=void 0;var q=O4;Object.defineProperty(J,"make",{enumerable:!0,get:function(){return q.make}})})(l1);Object.defineProperty(R4,"__esModule",{value:!0});R4.fragmentToString=CX;var BX=l1;function CX(J){var q=(0,BX.make)("div");return q.appendChild(J),q.innerHTML}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.fragmentToString=void 0;var q=R4;Object.defineProperty(J,"fragmentToString",{enumerable:!0,get:function(){return q.fragmentToString}})})(x7);var T7={},j4={};Object.defineProperty(j4,"__esModule",{value:!0});j4.getContentLength=wX;var EX=I0;function wX(J){var q,Z;return(0,EX.isNativeInput)(J)?J.value.length:J.nodeType===Node.TEXT_NODE?J.length:(Z=(q=J.textContent)===null||q===void 0?void 0:q.length)!==null&&Z!==void 0?Z:0}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.getContentLength=void 0;var q=j4;Object.defineProperty(J,"getContentLength",{enumerable:!0,get:function(){return q.getContentLength}})})(T7);var I4={},_4={},M7=c1&&c1.__spreadArray||function(J,q,Z){if(Z||arguments.length===2)for(var X=0,K=q.length,Q;X<K;X++)(Q||!(X in q))&&(Q||(Q=Array.prototype.slice.call(q,0,X)),Q[X]=q[X]);return J.concat(Q||Array.prototype.slice.call(q))};Object.defineProperty(_4,"__esModule",{value:!0});_4.getDeepestBlockElements=B7;var yX=u1;function B7(J){return(0,yX.containsOnlyInlineElements)(J)?[J]:Array.from(J.children).reduce(function(q,Z){return M7(M7([],q,!0),B7(Z),!0)},[])}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.getDeepestBlockElements=void 0;var q=_4;Object.defineProperty(J,"getDeepestBlockElements",{enumerable:!0,get:function(){return q.getDeepestBlockElements}})})(I4);var C7={},S4={},a1={},x4={};Object.defineProperty(x4,"__esModule",{value:!0});x4.isLineBreakTag=vX;function vX(J){return["BR","WBR"].includes(J.tagName)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isLineBreakTag=void 0;var q=x4;Object.defineProperty(J,"isLineBreakTag",{enumerable:!0,get:function(){return q.isLineBreakTag}})})(a1);var s1={},T4={};Object.defineProperty(T4,"__esModule",{value:!0});T4.isSingleTag=kX;function kX(J){return["AREA","BASE","BR","COL","COMMAND","EMBED","HR","IMG","INPUT","KEYGEN","LINK","META","PARAM","SOURCE","TRACK","WBR"].includes(J.tagName)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isSingleTag=void 0;var q=T4;Object.defineProperty(J,"isSingleTag",{enumerable:!0,get:function(){return q.isSingleTag}})})(s1);Object.defineProperty(S4,"__esModule",{value:!0});S4.getDeepestNode=E7;var gX=I0,mX=a1,bX=s1;function E7(J,q){q===void 0&&(q=!1);var Z=q?"lastChild":"firstChild",X=q?"previousSibling":"nextSibling";if(J.nodeType===Node.ELEMENT_NODE&&J[Z]){var K=J[Z];if((0,bX.isSingleTag)(K)&&!(0,gX.isNativeInput)(K)&&!(0,mX.isLineBreakTag)(K))if(K[X])K=K[X];else if(K.parentNode!==null&&K.parentNode[X])K=K.parentNode[X];else return K.parentNode;return E7(K,q)}return J}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.getDeepestNode=void 0;var q=S4;Object.defineProperty(J,"getDeepestNode",{enumerable:!0,get:function(){return q.getDeepestNode}})})(C7);var w7={},B4={},p1=c1&&c1.__spreadArray||function(J,q,Z){if(Z||arguments.length===2)for(var X=0,K=q.length,Q;X<K;X++)(Q||!(X in q))&&(Q||(Q=Array.prototype.slice.call(q,0,X)),Q[X]=q[X]);return J.concat(Q||Array.prototype.slice.call(q))};Object.defineProperty(B4,"__esModule",{value:!0});B4.findAllInputs=cX;var fX=u1,pX=I4,hX=Y4,dX=I0;function cX(J){return Array.from(J.querySelectorAll((0,hX.allInputsSelector)())).reduce(function(q,Z){return(0,dX.isNativeInput)(Z)||(0,fX.containsOnlyInlineElements)(Z)?p1(p1([],q,!0),[Z],!1):p1(p1([],q,!0),(0,pX.getDeepestBlockElements)(Z),!0)},[])}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.findAllInputs=void 0;var q=B4;Object.defineProperty(J,"findAllInputs",{enumerable:!0,get:function(){return q.findAllInputs}})})(w7);var y7={},C4={};Object.defineProperty(C4,"__esModule",{value:!0});C4.isCollapsedWhitespaces=uX;function uX(J){return!/[^\t\n\r ]/.test(J)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isCollapsedWhitespaces=void 0;var q=C4;Object.defineProperty(J,"isCollapsedWhitespaces",{enumerable:!0,get:function(){return q.isCollapsedWhitespaces}})})(y7);var E4={},w4={};Object.defineProperty(w4,"__esModule",{value:!0});w4.isElement=aX;var lX=M4;function aX(J){return(0,lX.isNumber)(J)?!1:!!J&&!!J.nodeType&&J.nodeType===Node.ELEMENT_NODE}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isElement=void 0;var q=w4;Object.defineProperty(J,"isElement",{enumerable:!0,get:function(){return q.isElement}})})(E4);var v7={},y4={},v4={},k4={};Object.defineProperty(k4,"__esModule",{value:!0});k4.isLeaf=sX;function sX(J){return J===null?!1:J.childNodes.length===0}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isLeaf=void 0;var q=k4;Object.defineProperty(J,"isLeaf",{enumerable:!0,get:function(){return q.isLeaf}})})(v4);var g4={},m4={};Object.defineProperty(m4,"__esModule",{value:!0});m4.isNodeEmpty=tX;var rX=a1,iX=E4,oX=I0,nX=s1;function tX(J,q){var Z="";return(0,nX.isSingleTag)(J)&&!(0,rX.isLineBreakTag)(J)?!1:((0,iX.isElement)(J)&&(0,oX.isNativeInput)(J)?Z=J.value:J.textContent!==null&&(Z=J.textContent.replace("","")),q!==void 0&&(Z=Z.replace(new RegExp(q,"g"),"")),Z.trim().length===0)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isNodeEmpty=void 0;var q=m4;Object.defineProperty(J,"isNodeEmpty",{enumerable:!0,get:function(){return q.isNodeEmpty}})})(g4);Object.defineProperty(y4,"__esModule",{value:!0});y4.isEmpty=qK;var eX=v4,JK=g4;function qK(J,q){J.normalize();for(var Z=[J];Z.length>0;){var X=Z.shift();if(X){if(J=X,(0,eX.isLeaf)(J)&&!(0,JK.isNodeEmpty)(J,q))return!1;Z.push.apply(Z,Array.from(J.childNodes))}}return!0}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isEmpty=void 0;var q=y4;Object.defineProperty(J,"isEmpty",{enumerable:!0,get:function(){return q.isEmpty}})})(v7);var k7={},b4={};Object.defineProperty(b4,"__esModule",{value:!0});b4.isFragment=XK;var ZK=M4;function XK(J){return(0,ZK.isNumber)(J)?!1:!!J&&!!J.nodeType&&J.nodeType===Node.DOCUMENT_FRAGMENT_NODE}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isFragment=void 0;var q=b4;Object.defineProperty(J,"isFragment",{enumerable:!0,get:function(){return q.isFragment}})})(k7);var g7={},f4={};Object.defineProperty(f4,"__esModule",{value:!0});f4.isHTMLString=QK;var KK=l1;function QK(J){var q=(0,KK.make)("div");return q.innerHTML=J,q.childElementCount>0}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.isHTMLString=void 0;var q=f4;Object.defineProperty(J,"isHTMLString",{enumerable:!0,get:function(){return q.isHTMLString}})})(g7);var m7={},p4={};Object.defineProperty(p4,"__esModule",{value:!0});p4.offset=VK;function VK(J){var q=J.getBoundingClientRect(),Z=window.pageXOffset||document.documentElement.scrollLeft,X=window.pageYOffset||document.documentElement.scrollTop,K=q.top+X,Q=q.left+Z;return{top:K,left:Q,bottom:K+q.height,right:Q+q.width}}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.offset=void 0;var q=p4;Object.defineProperty(J,"offset",{enumerable:!0,get:function(){return q.offset}})})(m7);var b7={},h4={};Object.defineProperty(h4,"__esModule",{value:!0});h4.prepend=GK;function GK(J,q){Array.isArray(q)?(q=q.reverse(),q.forEach(function(Z){return J.prepend(Z)})):J.prepend(q)}(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.prepend=void 0;var q=h4;Object.defineProperty(J,"prepend",{enumerable:!0,get:function(){return q.prepend}})})(b7);(function(J){Object.defineProperty(J,"__esModule",{value:!0}),J.prepend=J.offset=J.make=J.isLineBreakTag=J.isSingleTag=J.isNodeEmpty=J.isLeaf=J.isHTMLString=J.isFragment=J.isEmpty=J.isElement=J.isContentEditable=J.isCollapsedWhitespaces=J.findAllInputs=J.isNativeInput=J.allInputsSelector=J.getDeepestNode=J.getDeepestBlockElements=J.getContentLength=J.fragmentToString=J.containsOnlyInlineElements=J.canSetCaret=J.calculateBaseline=J.blockElements=J.append=void 0;var q=Y4;Object.defineProperty(J,"allInputsSelector",{enumerable:!0,get:function(){return q.allInputsSelector}});var Z=I0;Object.defineProperty(J,"isNativeInput",{enumerable:!0,get:function(){return Z.isNativeInput}});var X=R7;Object.defineProperty(J,"append",{enumerable:!0,get:function(){return X.append}});var K=z4;Object.defineProperty(J,"blockElements",{enumerable:!0,get:function(){return K.blockElements}});var Q=O7;Object.defineProperty(J,"calculateBaseline",{enumerable:!0,get:function(){return Q.calculateBaseline}});var V=j7;Object.defineProperty(J,"canSetCaret",{enumerable:!0,get:function(){return V.canSetCaret}});var G=u1;Object.defineProperty(J,"containsOnlyInlineElements",{enumerable:!0,get:function(){return G.containsOnlyInlineElements}});var Y=x7;Object.defineProperty(J,"fragmentToString",{enumerable:!0,get:function(){return Y.fragmentToString}});var U=T7;Object.defineProperty(J,"getContentLength",{enumerable:!0,get:function(){return U.getContentLength}});var H=I4;Object.defineProperty(J,"getDeepestBlockElements",{enumerable:!0,get:function(){return H.getDeepestBlockElements}});var $=C7;Object.defineProperty(J,"getDeepestNode",{enumerable:!0,get:function(){return $.getDeepestNode}});var D=w7;Object.defineProperty(J,"findAllInputs",{enumerable:!0,get:function(){return D.findAllInputs}});var A=y7;Object.defineProperty(J,"isCollapsedWhitespaces",{enumerable:!0,get:function(){return A.isCollapsedWhitespaces}});var W=L4;Object.defineProperty(J,"isContentEditable",{enumerable:!0,get:function(){return W.isContentEditable}});var z=E4;Object.defineProperty(J,"isElement",{enumerable:!0,get:function(){return z.isElement}});var L=v7;Object.defineProperty(J,"isEmpty",{enumerable:!0,get:function(){return L.isEmpty}});var N=k7;Object.defineProperty(J,"isFragment",{enumerable:!0,get:function(){return N.isFragment}});var P=g7;Object.defineProperty(J,"isHTMLString",{enumerable:!0,get:function(){return P.isHTMLString}});var _=v4;Object.defineProperty(J,"isLeaf",{enumerable:!0,get:function(){return _.isLeaf}});var j=g4;Object.defineProperty(J,"isNodeEmpty",{enumerable:!0,get:function(){return j.isNodeEmpty}});var O=a1;Object.defineProperty(J,"isLineBreakTag",{enumerable:!0,get:function(){return O.isLineBreakTag}});var T=s1;Object.defineProperty(J,"isSingleTag",{enumerable:!0,get:function(){return T.isSingleTag}});var b=l1;Object.defineProperty(J,"make",{enumerable:!0,get:function(){return b.make}});var R=m7;Object.defineProperty(J,"offset",{enumerable:!0,get:function(){return R.offset}});var M=b7;Object.defineProperty(J,"prepend",{enumerable:!0,get:function(){return M.prepend}})})(h1);var f7=((J)=>(J.Left="left",J.Center="center",J))(f7||{});class s0{constructor({data:J,config:q,api:Z,readOnly:X,block:K}){let{DEFAULT_ALIGNMENT:Q}=s0;this.api=Z,this.readOnly=X,this.quotePlaceholder=Z.i18n.t((q==null?void 0:q.quotePlaceholder)??s0.DEFAULT_QUOTE_PLACEHOLDER),this.captionPlaceholder=Z.i18n.t((q==null?void 0:q.captionPlaceholder)??s0.DEFAULT_CAPTION_PLACEHOLDER),this.data={text:J.text||"",caption:J.caption||"",alignment:Object.values(f7).includes(J.alignment)?J.alignment:(q==null?void 0:q.defaultAlignment)??Q},this.css={baseClass:this.api.styles.block,wrapper:"cdx-quote",text:"cdx-quote__text",input:this.api.styles.input,caption:"cdx-quote__caption"},this.block=K}static get isReadOnlySupported(){return!0}static get toolbox(){return{icon:rZ,title:"Quote"}}static get contentless(){return!0}static get enableLineBreaks(){return!0}static get DEFAULT_QUOTE_PLACEHOLDER(){return"Enter a quote"}static get DEFAULT_CAPTION_PLACEHOLDER(){return"Enter a caption"}static get DEFAULT_ALIGNMENT(){return"left"}static get conversionConfig(){return{import:"text",export:function(J){return J.caption?`${J.text} — ${J.caption}`:J.text}}}get CSS(){return{baseClass:this.api.styles.block,wrapper:"cdx-quote",text:"cdx-quote__text",input:this.api.styles.input,caption:"cdx-quote__caption"}}get settings(){return[{name:"left",icon:sZ},{name:"center",icon:aZ}]}render(){let J=h1.make("blockquote",[this.css.baseClass,this.css.wrapper]),q=h1.make("div",[this.css.input,this.css.text],{contentEditable:!this.readOnly,innerHTML:this.data.text}),Z=h1.make("div",[this.css.input,this.css.caption],{contentEditable:!this.readOnly,innerHTML:this.data.caption});return q.dataset.placeholder=this.quotePlaceholder,Z.dataset.placeholder=this.captionPlaceholder,J.appendChild(q),J.appendChild(Z),J}save(J){let q=J.querySelector(`.${this.css.text}`),Z=J.querySelector(`.${this.css.caption}`);return Object.assign(this.data,{text:(q==null?void 0:q.innerHTML)??"",caption:(Z==null?void 0:Z.innerHTML)??""})}static get sanitize(){return{text:{br:!0},caption:{br:!0},alignment:{}}}renderSettings(){let J=(q)=>q&&q[0].toUpperCase()+q.slice(1);return this.settings.map((q)=>({icon:q.icon,label:this.api.i18n.t(`Align ${J(q.name)}`),onActivate:()=>this._toggleTune(q.name),isActive:this.data.alignment===q.name,closeOnActivate:!0}))}_toggleTune(J){this.data.alignment=J,this.block.dispatchChange()}}(function(){try{if(typeof document<"u"){var J=document.createElement("style");J.appendChild(document.createTextNode('.image-tool{--bg-color: #cdd1e0;--front-color: #388ae5;--border-color: #e8e8eb}.image-tool__image{border-radius:3px;overflow:hidden;margin-bottom:10px;padding-bottom:0}.image-tool__image-picture{max-width:100%;vertical-align:bottom;display:block}.image-tool__image-preloader{width:50px;height:50px;border-radius:50%;background-size:cover;margin:auto;position:relative;background-color:var(--bg-color);background-position:center center}.image-tool__image-preloader:after{content:"";position:absolute;z-index:3;width:60px;height:60px;border-radius:50%;border:2px solid var(--bg-color);border-top-color:var(--front-color);left:50%;top:50%;margin-top:-30px;margin-left:-30px;animation:image-preloader-spin 2s infinite linear;box-sizing:border-box}.image-tool__caption{visibility:hidden;position:absolute;bottom:0;left:0;margin-bottom:10px}.image-tool__caption[contentEditable=true][data-placeholder]:before{position:absolute!important;content:attr(data-placeholder);color:#707684;font-weight:400;display:none}.image-tool__caption[contentEditable=true][data-placeholder]:empty:before{display:block}.image-tool__caption[contentEditable=true][data-placeholder]:empty:focus:before{display:none}.image-tool--empty .image-tool__image,.image-tool--empty .image-tool__image-preloader{display:none}.image-tool--empty .image-tool__caption,.image-tool--uploading .image-tool__caption{visibility:hidden!important}.image-tool .cdx-button{display:flex;align-items:center;justify-content:center}.image-tool .cdx-button svg{height:auto;margin:0 6px 0 0}.image-tool--filled .cdx-button,.image-tool--filled .image-tool__image-preloader{display:none}.image-tool--uploading .image-tool__image{min-height:200px;display:flex;border:1px solid var(--border-color);background-color:#fff}.image-tool--uploading .image-tool__image-picture,.image-tool--uploading .cdx-button{display:none}.image-tool--withBorder .image-tool__image{border:1px solid var(--border-color)}.image-tool--withBackground .image-tool__image{padding:15px;background:var(--bg-color)}.image-tool--withBackground .image-tool__image-picture{max-width:60%;margin:0 auto}.image-tool--stretched .image-tool__image-picture{width:100%}.image-tool--caption .image-tool__caption{visibility:visible}.image-tool--caption{padding-bottom:50px}@keyframes image-preloader-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}')),document.head.appendChild(J)}}catch(q){console.error("vite-plugin-css-injected-by-js",q)}})();var YK='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 19V19C9.13623 19 8.20435 19 7.46927 18.6955C6.48915 18.2895 5.71046 17.5108 5.30448 16.5307C5 15.7956 5 14.8638 5 13V12C5 9.19108 5 7.78661 5.67412 6.77772C5.96596 6.34096 6.34096 5.96596 6.77772 5.67412C7.78661 5 9.19108 5 12 5H13.5C14.8956 5 15.5933 5 16.1611 5.17224C17.4395 5.56004 18.44 6.56046 18.8278 7.83886C19 8.40666 19 9.10444 19 10.5V10.5"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M16 13V16M16 19V16M19 16H16M16 16H13"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6.5 17.5L17.5 6.5"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.9919 10.5H19.0015"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.9919 19H11.0015"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13L13 5"/></svg>',UK='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.9919 9.5H19.0015"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.5 5H14.5096"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M14.625 5H15C17.2091 5 19 6.79086 19 9V9.375"/><path stroke="currentColor" stroke-width="2" d="M9.375 5L9 5C6.79086 5 5 6.79086 5 9V9.375"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.3725 5H9.38207"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 9.5H5.00957"/><path stroke="currentColor" stroke-width="2" d="M9.375 19H9C6.79086 19 5 17.2091 5 15V14.625"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.3725 19H9.38207"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 14.55H5.00957"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M16 13V16M16 19V16M19 16H16M16 16H13"/></svg>',p7='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><rect width="14" height="14" x="5" y="5" stroke="currentColor" stroke-width="2" rx="4"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5.13968 15.32L8.69058 11.5661C9.02934 11.2036 9.48873 11 9.96774 11C10.4467 11 10.9061 11.2036 11.2449 11.5661L15.3871 16M13.5806 14.0664L15.0132 12.533C15.3519 12.1705 15.8113 11.9668 16.2903 11.9668C16.7693 11.9668 17.2287 12.1705 17.5675 12.533L18.841 13.9634"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.7778 9.33331H13.7867"/></svg>',HK='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 9L20 12L17 15"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 12H20"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 9L4 12L7 15"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 12H10"/></svg>',$K='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M8 9V7.2C8 7.08954 8.08954 7 8.2 7L12 7M16 9V7.2C16 7.08954 15.9105 7 15.8 7L12 7M12 7L12 17M12 17H10M12 17H14"/></svg>';function k0(J,q=null,Z={}){let X=document.createElement(J);Array.isArray(q)?X.classList.add(...q):q!==null&&X.classList.add(q);for(let K in Z)Z.hasOwnProperty(K)&&(X[K]=Z[K]);return X}var r1=((J)=>(J.Empty="empty",J.Uploading="uploading",J.Filled="filled",J))(r1||{});class h7{constructor({api:J,config:q,onSelectFile:Z,readOnly:X}){this.api=J,this.config=q,this.onSelectFile=Z,this.readOnly=X,this.nodes={wrapper:k0("div",[this.CSS.baseClass,this.CSS.wrapper]),imageContainer:k0("div",[this.CSS.imageContainer]),fileButton:this.createFileButton(),imageEl:void 0,imagePreloader:k0("div",this.CSS.imagePreloader),caption:k0("div",[this.CSS.input,this.CSS.caption],{contentEditable:!this.readOnly})},this.nodes.caption.dataset.placeholder=this.config.captionPlaceholder,this.nodes.imageContainer.appendChild(this.nodes.imagePreloader),this.nodes.wrapper.appendChild(this.nodes.imageContainer),this.nodes.wrapper.appendChild(this.nodes.caption),this.nodes.wrapper.appendChild(this.nodes.fileButton)}applyTune(J,q){this.nodes.wrapper.classList.toggle(`${this.CSS.wrapper}--${J}`,q)}render(){return this.toggleStatus("empty"),this.nodes.wrapper}showPreloader(J){this.nodes.imagePreloader.style.backgroundImage=`url(${J})`,this.toggleStatus("uploading")}hidePreloader(){this.nodes.imagePreloader.style.backgroundImage="",this.toggleStatus("empty")}fillImage(J){let q=/\.mp4$/.test(J)?"VIDEO":"IMG",Z={src:J},X="load";q==="VIDEO"&&(Z.autoplay=!0,Z.loop=!0,Z.muted=!0,Z.playsinline=!0,X="loadeddata"),this.nodes.imageEl=k0(q,this.CSS.imageEl,Z),this.nodes.imageEl.addEventListener(X,()=>{this.toggleStatus("filled"),this.nodes.imagePreloader!==void 0&&(this.nodes.imagePreloader.style.backgroundImage="")}),this.nodes.imageContainer.appendChild(this.nodes.imageEl)}fillCaption(J){this.nodes.caption!==void 0&&(this.nodes.caption.innerHTML=J)}toggleStatus(J){for(let q in r1)if(Object.prototype.hasOwnProperty.call(r1,q)){let Z=r1[q];this.nodes.wrapper.classList.toggle(`${this.CSS.wrapper}--${Z}`,Z===J)}}get CSS(){return{baseClass:this.api.styles.block,loading:this.api.styles.loader,input:this.api.styles.input,button:this.api.styles.button,wrapper:"image-tool",imageContainer:"image-tool__image",imagePreloader:"image-tool__image-preloader",imageEl:"image-tool__image-picture",caption:"image-tool__caption"}}createFileButton(){let J=k0("div",[this.CSS.button]);return J.innerHTML=this.config.buttonContent??`${p7} ${this.api.i18n.t("Select an Image")}`,J.addEventListener("click",()=>{this.onSelectFile()}),J}}function zK(J){return J&&J.__esModule&&Object.prototype.hasOwnProperty.call(J,"default")?J.default:J}var d7={exports:{}};(function(J,q){(function(Z,X){J.exports=X()})(window,function(){return function(Z){var X={};function K(Q){if(X[Q])return X[Q].exports;var V=X[Q]={i:Q,l:!1,exports:{}};return Z[Q].call(V.exports,V,V.exports,K),V.l=!0,V.exports}return K.m=Z,K.c=X,K.d=function(Q,V,G){K.o(Q,V)||Object.defineProperty(Q,V,{enumerable:!0,get:G})},K.r=function(Q){typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(Q,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(Q,"__esModule",{value:!0})},K.t=function(Q,V){if(1&V&&(Q=K(Q)),8&V||4&V&&typeof Q=="object"&&Q&&Q.__esModule)return Q;var G=Object.create(null);if(K.r(G),Object.defineProperty(G,"default",{enumerable:!0,value:Q}),2&V&&typeof Q!="string")for(var Y in Q)K.d(G,Y,function(U){return Q[U]}.bind(null,Y));return G},K.n=function(Q){var V=Q&&Q.__esModule?function(){return Q.default}:function(){return Q};return K.d(V,"a",V),V},K.o=function(Q,V){return Object.prototype.hasOwnProperty.call(Q,V)},K.p="",K(K.s=3)}([function(Z,X){var K=function(){return this}();try{K=K||Function("return this")()}catch{typeof window=="object"&&(K=window)}Z.exports=K},function(Z,X,K){(function(Q){var V=K(2),G=setTimeout;function Y(){}function U(L){if(!(this instanceof U))throw TypeError("Promises must be constructed via new");if(typeof L!="function")throw TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],z(L,this)}function H(L,N){for(;L._state===3;)L=L._value;L._state!==0?(L._handled=!0,U._immediateFn(function(){var P=L._state===1?N.onFulfilled:N.onRejected;if(P!==null){var _;try{_=P(L._value)}catch(j){return void D(N.promise,j)}$(N.promise,_)}else(L._state===1?$:D)(N.promise,L._value)})):L._deferreds.push(N)}function $(L,N){try{if(N===L)throw TypeError("A promise cannot be resolved with itself.");if(N&&(typeof N=="object"||typeof N=="function")){var P=N.then;if(N instanceof U)return L._state=3,L._value=N,void A(L);if(typeof P=="function")return void z((_=P,j=N,function(){_.apply(j,arguments)}),L)}L._state=1,L._value=N,A(L)}catch(O){D(L,O)}var _,j}function D(L,N){L._state=2,L._value=N,A(L)}function A(L){L._state===2&&L._deferreds.length===0&&U._immediateFn(function(){L._handled||U._unhandledRejectionFn(L._value)});for(var N=0,P=L._deferreds.length;N<P;N++)H(L,L._deferreds[N]);L._deferreds=null}function W(L,N,P){this.onFulfilled=typeof L=="function"?L:null,this.onRejected=typeof N=="function"?N:null,this.promise=P}function z(L,N){var P=!1;try{L(function(_){P||(P=!0,$(N,_))},function(_){P||(P=!0,D(N,_))})}catch(_){if(P)return;P=!0,D(N,_)}}U.prototype.catch=function(L){return this.then(null,L)},U.prototype.then=function(L,N){var P=new this.constructor(Y);return H(this,new W(L,N,P)),P},U.prototype.finally=V.a,U.all=function(L){return new U(function(N,P){if(!L||L.length===void 0)throw TypeError("Promise.all accepts an array");var _=Array.prototype.slice.call(L);if(_.length===0)return N([]);var j=_.length;function O(b,R){try{if(R&&(typeof R=="object"||typeof R=="function")){var M=R.then;if(typeof M=="function")return void M.call(R,function(S){O(b,S)},P)}_[b]=R,--j==0&&N(_)}catch(S){P(S)}}for(var T=0;T<_.length;T++)O(T,_[T])})},U.resolve=function(L){return L&&typeof L=="object"&&L.constructor===U?L:new U(function(N){N(L)})},U.reject=function(L){return new U(function(N,P){P(L)})},U.race=function(L){return new U(function(N,P){for(var _=0,j=L.length;_<j;_++)L[_].then(N,P)})},U._immediateFn=typeof Q=="function"&&function(L){Q(L)}||function(L){G(L,0)},U._unhandledRejectionFn=function(L){typeof console<"u"&&console&&console.warn("Possible Unhandled Promise Rejection:",L)},X.a=U}).call(this,K(5).setImmediate)},function(Z,X,K){X.a=function(Q){var V=this.constructor;return this.then(function(G){return V.resolve(Q()).then(function(){return G})},function(G){return V.resolve(Q()).then(function(){return V.reject(G)})})}},function(Z,X,K){function Q(z){return(Q=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(L){return typeof L}:function(L){return L&&typeof Symbol=="function"&&L.constructor===Symbol&&L!==Symbol.prototype?"symbol":typeof L})(z)}K(4);var V,G,Y,U,H,$,D,A=K(8),W=(G=function(z){return new Promise(function(L,N){z=U(z),(z=H(z)).beforeSend&&z.beforeSend();var P=window.XMLHttpRequest?new window.XMLHttpRequest:new window.ActiveXObject("Microsoft.XMLHTTP");P.open(z.method,z.url),P.setRequestHeader("X-Requested-With","XMLHttpRequest"),Object.keys(z.headers).forEach(function(j){var O=z.headers[j];P.setRequestHeader(j,O)});var _=z.ratio;P.upload.addEventListener("progress",function(j){var O=Math.round(j.loaded/j.total*100),T=Math.ceil(O*_/100);z.progress(Math.min(T,100))},!1),P.addEventListener("progress",function(j){var O=Math.round(j.loaded/j.total*100),T=Math.ceil(O*(100-_)/100)+_;z.progress(Math.min(T,100))},!1),P.onreadystatechange=function(){if(P.readyState===4){var j=P.response;try{j=JSON.parse(j)}catch{}var O=A.parseHeaders(P.getAllResponseHeaders()),T={body:j,code:P.status,headers:O};D(P.status)?L(T):N(T)}},P.send(z.data)})},Y=function(z){return z.method="POST",G(z)},U=function(){var z=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};if(z.url&&typeof z.url!="string")throw Error("Url must be a string");if(z.url=z.url||"",z.method&&typeof z.method!="string")throw Error("`method` must be a string or null");if(z.method=z.method?z.method.toUpperCase():"GET",z.headers&&Q(z.headers)!=="object")throw Error("`headers` must be an object or null");if(z.headers=z.headers||{},z.type&&(typeof z.type!="string"||!Object.values(V).includes(z.type)))throw Error("`type` must be taken from module's «contentType» library");if(z.progress&&typeof z.progress!="function")throw Error("`progress` must be a function or null");if(z.progress=z.progress||function(L){},z.beforeSend=z.beforeSend||function(L){},z.ratio&&typeof z.ratio!="number")throw Error("`ratio` must be a number");if(z.ratio<0||z.ratio>100)throw Error("`ratio` must be in a 0-100 interval");if(z.ratio=z.ratio||90,z.accept&&typeof z.accept!="string")throw Error("`accept` must be a string with a list of allowed mime-types");if(z.accept=z.accept||"*/*",z.multiple&&typeof z.multiple!="boolean")throw Error("`multiple` must be a true or false");if(z.multiple=z.multiple||!1,z.fieldName&&typeof z.fieldName!="string")throw Error("`fieldName` must be a string");return z.fieldName=z.fieldName||"files",z},H=function(z){switch(z.method){case"GET":var L=$(z.data,V.URLENCODED);delete z.data,z.url=/\?/.test(z.url)?z.url+"&"+L:z.url+"?"+L;break;case"POST":case"PUT":case"DELETE":case"UPDATE":var N=function(){return(arguments.length>0&&arguments[0]!==void 0?arguments[0]:{}).type||V.JSON}(z);(A.isFormData(z.data)||A.isFormElement(z.data))&&(N=V.FORM),z.data=$(z.data,N),N!==W.contentType.FORM&&(z.headers["content-type"]=N)}return z},$=function(){var z=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};switch(arguments.length>1?arguments[1]:void 0){case V.URLENCODED:return A.urlEncode(z);case V.JSON:return A.jsonEncode(z);case V.FORM:return A.formEncode(z);default:return z}},D=function(z){return z>=200&&z<300},{contentType:V={URLENCODED:"application/x-www-form-urlencoded; charset=utf-8",FORM:"multipart/form-data",JSON:"application/json; charset=utf-8"},request:G,get:function(z){return z.method="GET",G(z)},post:Y,transport:function(z){return z=U(z),A.selectFiles(z).then(function(L){for(var N=new FormData,P=0;P<L.length;P++)N.append(z.fieldName,L[P],L[P].name);A.isObject(z.data)&&Object.keys(z.data).forEach(function(j){var O=z.data[j];N.append(j,O)});var _=z.beforeSend;return z.beforeSend=function(){return _(L)},z.data=N,Y(z)})},selectFiles:function(z){return delete(z=U(z)).beforeSend,A.selectFiles(z)}});Z.exports=W},function(Z,X,K){K.r(X);var Q=K(1);window.Promise=window.Promise||Q.a},function(Z,X,K){(function(Q){var V=Q!==void 0&&Q||typeof self<"u"&&self||window,G=Function.prototype.apply;function Y(U,H){this._id=U,this._clearFn=H}X.setTimeout=function(){return new Y(G.call(setTimeout,V,arguments),clearTimeout)},X.setInterval=function(){return new Y(G.call(setInterval,V,arguments),clearInterval)},X.clearTimeout=X.clearInterval=function(U){U&&U.close()},Y.prototype.unref=Y.prototype.ref=function(){},Y.prototype.close=function(){this._clearFn.call(V,this._id)},X.enroll=function(U,H){clearTimeout(U._idleTimeoutId),U._idleTimeout=H},X.unenroll=function(U){clearTimeout(U._idleTimeoutId),U._idleTimeout=-1},X._unrefActive=X.active=function(U){clearTimeout(U._idleTimeoutId);var H=U._idleTimeout;H>=0&&(U._idleTimeoutId=setTimeout(function(){U._onTimeout&&U._onTimeout()},H))},K(6),X.setImmediate=typeof self<"u"&&self.setImmediate||Q!==void 0&&Q.setImmediate||this&&this.setImmediate,X.clearImmediate=typeof self<"u"&&self.clearImmediate||Q!==void 0&&Q.clearImmediate||this&&this.clearImmediate}).call(this,K(0))},function(Z,X,K){(function(Q,V){(function(G,Y){if(!G.setImmediate){var U,H,$,D,A,W=1,z={},L=!1,N=G.document,P=Object.getPrototypeOf&&Object.getPrototypeOf(G);P=P&&P.setTimeout?P:G,{}.toString.call(G.process)==="[object process]"?U=function(O){V.nextTick(function(){j(O)})}:function(){if(G.postMessage&&!G.importScripts){var O=!0,T=G.onmessage;return G.onmessage=function(){O=!1},G.postMessage("","*"),G.onmessage=T,O}}()?(D="setImmediate$"+Math.random()+"$",A=function(O){O.source===G&&typeof O.data=="string"&&O.data.indexOf(D)===0&&j(+O.data.slice(D.length))},G.addEventListener?G.addEventListener("message",A,!1):G.attachEvent("onmessage",A),U=function(O){G.postMessage(D+O,"*")}):G.MessageChannel?(($=new MessageChannel).port1.onmessage=function(O){j(O.data)},U=function(O){$.port2.postMessage(O)}):N&&("onreadystatechange"in N.createElement("script"))?(H=N.documentElement,U=function(O){var T=N.createElement("script");T.onreadystatechange=function(){j(O),T.onreadystatechange=null,H.removeChild(T),T=null},H.appendChild(T)}):U=function(O){setTimeout(j,0,O)},P.setImmediate=function(O){typeof O!="function"&&(O=Function(""+O));for(var T=Array(arguments.length-1),b=0;b<T.length;b++)T[b]=arguments[b+1];var R={callback:O,args:T};return z[W]=R,U(W),W++},P.clearImmediate=_}function _(O){delete z[O]}function j(O){if(L)setTimeout(j,0,O);else{var T=z[O];if(T){L=!0;try{(function(b){var{callback:R,args:M}=b;switch(M.length){case 0:R();break;case 1:R(M[0]);break;case 2:R(M[0],M[1]);break;case 3:R(M[0],M[1],M[2]);break;default:R.apply(Y,M)}})(T)}finally{_(O),L=!1}}}}})(typeof self>"u"?Q===void 0?this:Q:self)}).call(this,K(0),K(7))},function(Z,X){var K,Q,V=Z.exports={};function G(){throw Error("setTimeout has not been defined")}function Y(){throw Error("clearTimeout has not been defined")}function U(P){if(K===setTimeout)return setTimeout(P,0);if((K===G||!K)&&setTimeout)return K=setTimeout,setTimeout(P,0);try{return K(P,0)}catch{try{return K.call(null,P,0)}catch{return K.call(this,P,0)}}}(function(){try{K=typeof setTimeout=="function"?setTimeout:G}catch{K=G}try{Q=typeof clearTimeout=="function"?clearTimeout:Y}catch{Q=Y}})();var H,$=[],D=!1,A=-1;function W(){D&&H&&(D=!1,H.length?$=H.concat($):A=-1,$.length&&z())}function z(){if(!D){var P=U(W);D=!0;for(var _=$.length;_;){for(H=$,$=[];++A<_;)H&&H[A].run();A=-1,_=$.length}H=null,D=!1,function(j){if(Q===clearTimeout)return clearTimeout(j);if((Q===Y||!Q)&&clearTimeout)return Q=clearTimeout,clearTimeout(j);try{Q(j)}catch{try{return Q.call(null,j)}catch{return Q.call(this,j)}}}(P)}}function L(P,_){this.fun=P,this.array=_}function N(){}V.nextTick=function(P){var _=Array(arguments.length-1);if(arguments.length>1)for(var j=1;j<arguments.length;j++)_[j-1]=arguments[j];$.push(new L(P,_)),$.length!==1||D||U(z)},L.prototype.run=function(){this.fun.apply(null,this.array)},V.title="browser",V.browser=!0,V.env={},V.argv=[],V.version="",V.versions={},V.on=N,V.addListener=N,V.once=N,V.off=N,V.removeListener=N,V.removeAllListeners=N,V.emit=N,V.prependListener=N,V.prependOnceListener=N,V.listeners=function(P){return[]},V.binding=function(P){throw Error("process.binding is not supported")},V.cwd=function(){return"/"},V.chdir=function(P){throw Error("process.chdir is not supported")},V.umask=function(){return 0}},function(Z,X,K){function Q(G,Y){for(var U=0;U<Y.length;U++){var H=Y[U];H.enumerable=H.enumerable||!1,H.configurable=!0,"value"in H&&(H.writable=!0),Object.defineProperty(G,H.key,H)}}var V=K(9);Z.exports=function(){function G(){(function($,D){if(!($ instanceof D))throw TypeError("Cannot call a class as a function")})(this,G)}var Y,U,H;return Y=G,H=[{key:"urlEncode",value:function($){return V($)}},{key:"jsonEncode",value:function($){return JSON.stringify($)}},{key:"formEncode",value:function($){if(this.isFormData($))return $;if(this.isFormElement($))return new FormData($);if(this.isObject($)){var D=new FormData;return Object.keys($).forEach(function(A){var W=$[A];D.append(A,W)}),D}throw Error("`data` must be an instance of Object, FormData or <FORM> HTMLElement")}},{key:"isObject",value:function($){return Object.prototype.toString.call($)==="[object Object]"}},{key:"isFormData",value:function($){return $ instanceof FormData}},{key:"isFormElement",value:function($){return $ instanceof HTMLFormElement}},{key:"selectFiles",value:function(){var $=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};return new Promise(function(D,A){var W=document.createElement("INPUT");W.type="file",$.multiple&&W.setAttribute("multiple","multiple"),$.accept&&W.setAttribute("accept",$.accept),W.style.display="none",document.body.appendChild(W),W.addEventListener("change",function(z){var L=z.target.files;D(L),document.body.removeChild(W)},!1),W.click()})}},{key:"parseHeaders",value:function($){var D=$.trim().split(/[\r\n]+/),A={};return D.forEach(function(W){var z=W.split(": "),L=z.shift(),N=z.join(": ");L&&(A[L]=N)}),A}}],(U=null)&&Q(Y.prototype,U),H&&Q(Y,H),G}()},function(Z,X){var K=function(V){return encodeURIComponent(V).replace(/[!'()*]/g,escape).replace(/%20/g,"+")},Q=function(V,G,Y,U){return G=G||null,Y=Y||"&",U=U||null,V?function(H){for(var $=[],D=0;D<H.length;D++)H[D]&&$.push(H[D]);return $}(Object.keys(V).map(function(H){var $,D,A=H;if(U&&(A=U+"["+A+"]"),typeof V[H]=="object"&&V[H]!==null)$=Q(V[H],null,Y,A);else{G&&(D=A,A=!isNaN(parseFloat(D))&&isFinite(D)?G+Number(A):A);var W=V[H];W=(W=(W=(W=W===!0?"1":W)===!1?"0":W)===0?"0":W)||"",$=K(A)+"="+K(W)}return $})).join(Y).replace(/[!'()*]/g,""):""};Z.exports=Q}])})})(d7);var DK=d7.exports,g0=zK(DK);function d4(J){return J!==void 0&&typeof J.then=="function"}class c7{constructor({config:J,onUpload:q,onError:Z}){this.config=J,this.onUpload=q,this.onError=Z}uploadSelectedFile({onPreview:J}){let q=function(X){let K=new FileReader;K.readAsDataURL(X),K.onload=(Q)=>{J(Q.target.result)}},Z;if(this.config.uploader&&typeof this.config.uploader.uploadByFile=="function"){let X=this.config.uploader.uploadByFile;Z=g0.selectFiles({accept:this.config.types??"image/*"}).then((K)=>{q(K[0]);let Q=X(K[0]);return d4(Q)||console.warn("Custom uploader method uploadByFile should return a Promise"),Q})}else Z=g0.transport({url:this.config.endpoints.byFile,data:this.config.additionalRequestData,accept:this.config.types??"image/*",headers:this.config.additionalRequestHeaders,beforeSend:(X)=>{q(X[0])},fieldName:this.config.field??"image"}).then((X)=>X.body);Z.then((X)=>{this.onUpload(X)}).catch((X)=>{this.onError(X)})}uploadByUrl(J){let q;this.config.uploader&&typeof this.config.uploader.uploadByUrl=="function"?(q=this.config.uploader.uploadByUrl(J),d4(q)||console.warn("Custom uploader method uploadByUrl should return a Promise")):q=g0.post({url:this.config.endpoints.byUrl,data:Object.assign({url:J},this.config.additionalRequestData),type:g0.contentType.JSON,headers:this.config.additionalRequestHeaders}).then((Z)=>Z.body),q.then((Z)=>{this.onUpload(Z)}).catch((Z)=>{this.onError(Z)})}uploadByFile(J,{onPreview:q}){let Z=new FileReader;Z.readAsDataURL(J),Z.onload=(K)=>{q(K.target.result)};let X;if(this.config.uploader&&typeof this.config.uploader.uploadByFile=="function")X=this.config.uploader.uploadByFile(J),d4(X)||console.warn("Custom uploader method uploadByFile should return a Promise");else{let K=new FormData;K.append(this.config.field??"image",J),this.config.additionalRequestData&&Object.keys(this.config.additionalRequestData).length&&Object.entries(this.config.additionalRequestData).forEach(([Q,V])=>{K.append(Q,V)}),X=g0.post({url:this.config.endpoints.byFile,data:K,type:g0.contentType.JSON,headers:this.config.additionalRequestHeaders}).then((Q)=>Q.body)}X.then((K)=>{this.onUpload(K)}).catch((K)=>{this.onError(K)})}}class i1{constructor({data:J,config:q,api:Z,readOnly:X,block:K}){this.isCaptionEnabled=null,this.api=Z,this.block=K,this.config={endpoints:q.endpoints,additionalRequestData:q.additionalRequestData,additionalRequestHeaders:q.additionalRequestHeaders,field:q.field,types:q.types,captionPlaceholder:this.api.i18n.t(q.captionPlaceholder??"Caption"),buttonContent:q.buttonContent,uploader:q.uploader,actions:q.actions,features:q.features||{}},this.uploader=new c7({config:this.config,onUpload:(Q)=>this.onUpload(Q),onError:(Q)=>this.uploadingFailed(Q)}),this.ui=new h7({api:Z,config:this.config,onSelectFile:()=>{this.uploader.uploadSelectedFile({onPreview:(Q)=>{this.ui.showPreloader(Q)}})},readOnly:X}),this._data={caption:"",withBorder:!1,withBackground:!1,stretched:!1,file:{url:""}},this.data=J}static get isReadOnlySupported(){return!0}static get toolbox(){return{icon:p7,title:"Image"}}static get tunes(){return[{name:"withBorder",icon:UK,title:"With border",toggle:!0},{name:"stretched",icon:HK,title:"Stretch image",toggle:!0},{name:"withBackground",icon:YK,title:"With background",toggle:!0}]}render(){var J,q,Z;return(((J=this.config.features)==null?void 0:J.caption)===!0||((q=this.config.features)==null?void 0:q.caption)===void 0||((Z=this.config.features)==null?void 0:Z.caption)==="optional"&&this.data.caption)&&(this.isCaptionEnabled=!0,this.ui.applyTune("caption",!0)),this.ui.render()}validate(J){return!!J.file.url}save(){let J=this.ui.nodes.caption;return this._data.caption=J.innerHTML,this.data}renderSettings(){var J;let q=i1.tunes.concat(this.config.actions||[]),Z={border:"withBorder",background:"withBackground",stretch:"stretched",caption:"caption"};((J=this.config.features)==null?void 0:J.caption)==="optional"&&q.push({name:"caption",icon:$K,title:"With caption",toggle:!0});let X=q.filter((Q)=>{var V,G;let Y=Object.keys(Z).find((U)=>Z[U]===Q.name);return Y==="caption"?((V=this.config.features)==null?void 0:V.caption)!==!1:Y==null||((G=this.config.features)==null?void 0:G[Y])!==!1}),K=(Q)=>{let V=this.data[Q.name];return Q.name==="caption"&&(V=this.isCaptionEnabled??V),V};return X.map((Q)=>({icon:Q.icon,label:this.api.i18n.t(Q.title),name:Q.name,toggle:Q.toggle,isActive:K(Q),onActivate:()=>{if(typeof Q.action=="function"){Q.action(Q.name);return}let V=!K(Q);Q.name==="caption"&&(this.isCaptionEnabled=!(this.isCaptionEnabled??!1),V=this.isCaptionEnabled),this.tuneToggled(Q.name,V)}}))}appendCallback(){this.ui.nodes.fileButton.click()}static get pasteConfig(){return{tags:[{img:{src:!0}}],patterns:{image:/https?:\/\/\S+\.(gif|jpe?g|tiff|png|svg|webp)(\?[a-z0-9=]*)?$/i},files:{mimeTypes:["image/*"]}}}async onPaste(J){switch(J.type){case"tag":{let q=J.detail.data;if(/^blob:/.test(q.src)){let Z=await(await fetch(q.src)).blob();this.uploadFile(Z);break}this.uploadUrl(q.src);break}case"pattern":{let q=J.detail.data;this.uploadUrl(q);break}case"file":{let q=J.detail.file;this.uploadFile(q);break}}}set data(J){var q;this.image=J.file,this._data.caption=J.caption||"",this.ui.fillCaption(this._data.caption),i1.tunes.forEach(({name:Z})=>{let X=typeof J[Z]<"u"?J[Z]===!0||J[Z]==="true":!1;this.setTune(Z,X)}),J.caption?this.setTune("caption",!0):((q=this.config.features)==null?void 0:q.caption)===!0&&this.setTune("caption",!0)}get data(){return this._data}set image(J){this._data.file=J||{url:""},J&&J.url&&this.ui.fillImage(J.url)}onUpload(J){J.success&&J.file?this.image=J.file:this.uploadingFailed("incorrect response: "+JSON.stringify(J))}uploadingFailed(J){console.log("Image Tool: uploading failed because of",J),this.api.notifier.show({message:this.api.i18n.t("Couldn’t upload image. Please try another."),style:"error"}),this.ui.hidePreloader()}tuneToggled(J,q){J==="caption"?(this.ui.applyTune(J,q),q==!1&&(this._data.caption="",this.ui.fillCaption(""))):this.setTune(J,q)}setTune(J,q){this._data[J]=q,this.ui.applyTune(J,q),J==="stretched"&&Promise.resolve().then(()=>{this.block.stretched=q}).catch((Z)=>{console.error(Z)})}uploadFile(J){this.uploader.uploadByFile(J,{onPreview:(q)=>{this.ui.showPreloader(q)}})}uploadUrl(J){this.ui.showPreloader(J),this.uploader.uploadByUrl(J)}}export{s0 as Quote,L3 as Paragraph,E1 as List,i1 as ImageTool,c6 as Header,d6 as EditorJS,V4 as Code}; diff --git a/assets/admin/lib/package.json b/assets/admin/lib/package.json new file mode 100644 index 0000000..8375cfa --- /dev/null +++ b/assets/admin/lib/package.json @@ -0,0 +1,14 @@ +{ + "scripts": { + "build": "bun build build.js --outdir=dist --format=esm --splitting --minify" + }, + "dependencies": { + "@editorjs/editorjs": "^2.30.0", + "@editorjs/header": "^2.8.0", + "@editorjs/paragraph": "^2.11.0", + "@editorjs/list": "^2.0.0", + "@editorjs/code": "^2.9.0", + "@editorjs/quote": "^2.7.0", + "@editorjs/image": "^2.10.0" + } +} diff --git a/assets/components/code-run.js b/assets/components/code-run.js new file mode 100644 index 0000000..5ee35de --- /dev/null +++ b/assets/components/code-run.js @@ -0,0 +1,13 @@ +import { define, BaseElement, reactive } from "shared" + +class CodeRun extends BaseElement { + connectedCallback() { + this.state = reactive({ open: false }, () => this.render()) + super.connectedCallback() + } + render() { + this.toggleAttribute('open', this.state.open) + } +} + +define("code-run", CodeRun)
\ No newline at end of file diff --git a/assets/components/site-greeting.js b/assets/components/site-greeting.js new file mode 100644 index 0000000..c224132 --- /dev/null +++ b/assets/components/site-greeting.js @@ -0,0 +1,10 @@ +import { BaseElement } from "../lib/shared.js" + +class SiteGreeting extends BaseElement { + connectedCallback() { + const name = this.getAttribute('name') || 'world' + this.textContent = `Hello, ${name}!` + } +} + +define('site-greeting', SiteGreeting) diff --git a/assets/lib/.gitignore b/assets/lib/.gitignore new file mode 100644 index 0000000..a14702c --- /dev/null +++ b/assets/lib/.gitignore @@ -0,0 +1,34 @@ +# dependencies (bun install) +node_modules + +# output +out +dist +*.tgz + +# code coverage +coverage +*.lcov + +# logs +logs +_.log +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# caches +.eslintcache +.cache +*.tsbuildinfo + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/assets/lib/build.js b/assets/lib/build.js new file mode 100644 index 0000000..173fbe3 --- /dev/null +++ b/assets/lib/build.js @@ -0,0 +1 @@ +export { thumbHashToDataURL } from 'thumbhash' diff --git a/assets/lib/bun.lock b/assets/lib/bun.lock new file mode 100644 index 0000000..0ba7ea2 --- /dev/null +++ b/assets/lib/bun.lock @@ -0,0 +1,15 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "lib", + "dependencies": { + "thumbhash": "^0.1.1", + }, + }, + }, + "packages": { + "thumbhash": ["thumbhash@0.1.1", "", {}, "sha512-kH5pKeIIBPQXAOni2AiY/Cu/NKdkFREdpH+TLdM0g6WA7RriCv0kPLgP731ady67MhTAqrVG/4mnEeibVuCJcg=="], + } +} diff --git a/assets/lib/package.json b/assets/lib/package.json new file mode 100644 index 0000000..806efa7 --- /dev/null +++ b/assets/lib/package.json @@ -0,0 +1,8 @@ +{ + "scripts": { + "build": "bun build build.js --outdir=dist --format=esm --minify" + }, + "dependencies": { + "thumbhash": "^0.1.1" + } +} diff --git a/assets/lib/shared.js b/assets/lib/shared.js new file mode 100644 index 0000000..940b608 --- /dev/null +++ b/assets/lib/shared.js @@ -0,0 +1,130 @@ +/** + * Base class for web components. + * Calls render() on connect and on any observed attribute change. + */ +export class BaseElement extends HTMLElement { + connectedCallback() { + this.render() + } + + attributeChangedCallback() { + this.render() + } + + render() { } + + /** + * Read an attribute with type coercion based on the fallback type. + * - number fallback → parseFloat + * - boolean fallback → true if attribute present and not "false" + * - otherwise → string or fallback + */ + attr(name, fallback = null) { + const val = this.getAttribute(name) + if (val === null) return fallback + if (typeof fallback === 'number') return parseFloat(val) + if (typeof fallback === 'boolean') return val !== 'false' + return val + } + + /** + * Dispatch a CustomEvent from this element. + */ + dispatch(type, detail = {}, options = {}) { + this.dispatchEvent(new CustomEvent(type, { bubbles: true, composed: true, detail, ...options })) + } +} + +/** + * Register a custom element and return the class. + * Lets you export default define('my-el', class extends BaseElement { ... }) + */ +export function define(tag, Cls) { + customElements.define(tag, Cls) + return Cls +} + +/** + * Tagged template for inline HTML strings. + */ +export function html(strings, ...values) { + return strings.reduce((acc, str, i) => acc + str + (values[i] ?? ''), '').trim() +} + +/** + * Tagged template for inline CSS strings. + */ +export function css(strings, ...values) { + return strings.reduce((acc, str, i) => acc + str + (values[i] ?? ''), '').trim() +} + +/** + * Create a DOM element with optional attributes and children. + * + * @param {string} tag + * @param {Record<string, string|boolean>} [attrs] + * @param {...(Node|string)} children + * @returns {HTMLElement} + * + * @example + * el('button', { type: 'button', class: 'btn' }, 'Click me') + */ +export function el(tag, attrs = {}, ...children) { + const node = document.createElement(tag) + for (const [k, v] of Object.entries(attrs)) { + if (v === false || v == null) continue + if (v === true) node.setAttribute(k, '') + else node.setAttribute(k, v) + } + node.append(...children) + return node +} + +/** + * Shorthand for querySelector. Scoped to `root` (default: document). + * @template {Element} T + * @param {string} selector + * @param {ParentNode} [root] + * @returns {T|null} + */ +export function qs(selector, root = document) { + return root.querySelector(selector) +} + +/** + * Shorthand for querySelectorAll. Returns a plain Array. + * @template {Element} T + * @param {string} selector + * @param {ParentNode} [root] + * @returns {T[]} + */ +export function qsa(selector, root = document) { + return Array.from(root.querySelectorAll(selector)) +} + +/** + * Add an event listener and return a cleanup function. + * @param {EventTarget} target + * @param {string} event + * @param {EventListener} handler + * @param {AddEventListenerOptions} [options] + * @returns {() => void} unlisten + */ +export function on(target, event, handler, options) { + target.addEventListener(event, handler, options) + return () => target.removeEventListener(event, handler, options) +} + +/** + * Wrap a plain object in a Proxy that calls onChange on any top-level property set. + * Shallow only — nested mutations do not trigger onChange. + */ +export function reactive(init, onChange) { + return new Proxy(init, { + set(target, key, value) { + target[key] = value + onChange() + return true + }, + }) +} diff --git a/assets/static/image.png b/assets/static/image.png Binary files differnew file mode 100644 index 0000000..dad4fdd --- /dev/null +++ b/assets/static/image.png diff --git a/assets/styles/admin.css b/assets/styles/admin.css new file mode 100644 index 0000000..6b8d841 --- /dev/null +++ b/assets/styles/admin.css @@ -0,0 +1,30 @@ +form { + display: block; +} + +table { + border-collapse: collapse; + width: 100%; +} + +th, +td { + padding: 0.5rem 0.75rem; + text-align: left; + border: 1px solid #ddd; +} + +th { + background: #f5f5f5; + font-weight: 600; +} + +tr:nth-child(even) td { + background: #fafafa; +} + +#editor { + padding: 0.5rem; + margin-block: 15px; + min-height: 340px; +}
\ No newline at end of file diff --git a/assets/styles/main.css b/assets/styles/main.css new file mode 100644 index 0000000..0397c97 --- /dev/null +++ b/assets/styles/main.css @@ -0,0 +1,60 @@ +*, +*::before, +*::after { + box-sizing: border-box; +} + +:root { + --color-bg: #ffffff; + --color-text: #1a1a1a; + --color-link: #0057b7; + --color-accent: #deb3a1; + --font-body: Georgia, serif; + --max-width: 72ch; +} + +body { + margin: 0; + padding: 2rem 1rem; + background: var(--color-bg); + color: var(--color-text); + font-family: var(--font-body); + line-height: 1.6; + + *::selection { + background-color: var(--color-accent); + } +} + +main { + max-width: var(--max-width); + margin: 0 auto; +} + +a { + color: var(--color-link); +} + +pre, +code { + font-family: var(--font-mono); + font-size: 0.9em; +} + +pre { + overflow-x: auto; + padding: 1rem; + background: #f5f5f5; + border-radius: 4px; +} + +img { + max-width: 100%; +} + +#logo { + max-height: 240px; + min-height: 240px; + height: 100%; + padding-block: 18px; +}
\ No newline at end of file diff --git a/cmd/iblog/main.go b/cmd/iblog/main.go new file mode 100644 index 0000000..1c94f4d --- /dev/null +++ b/cmd/iblog/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + "os" +) + +func main() { + if len(os.Args) < 2 { + usage() + os.Exit(1) + } + switch os.Args[1] { + case "serve": + cmdServe(os.Args[2:]) + case "user": + cmdUser(os.Args[2:]) + default: + fmt.Fprintf(os.Stderr, "unknown command %q\n", os.Args[1]) + usage() + os.Exit(1) + } +} + +func usage() { + fmt.Fprintln(os.Stderr, `iblog + +Commands: + serve [--port N] [--root PATH] serve site + admin UI (default port 8080) + user [--root PATH] add <name> add user to password file + user [--root PATH] list list users + user [--root PATH] delete <name> remove user + user [--root PATH] passwd <name> change user password`) +} diff --git a/cmd/iblog/serve.go b/cmd/iblog/serve.go new file mode 100644 index 0000000..7636142 --- /dev/null +++ b/cmd/iblog/serve.go @@ -0,0 +1,110 @@ +package main + +import ( + "flag" + "fmt" + "html/template" + "os" + "path/filepath" + + iblog "iblog" + "iblog/internal/db" + "iblog/internal/media" + "iblog/internal/server" + + "github.com/davidbyttow/govips/v2/vips" +) + +func cmdServe(args []string) { + fs := flag.NewFlagSet("serve", flag.ExitOnError) + port := fs.String("port", "8080", "port to listen on") + root := fs.String("root", ".", "site root directory") + _ = fs.Parse(args) + + dataDir := filepath.Join(*root, "data") + dbPath := filepath.Join(dataDir, "iblog.db") + passwordFile := filepath.Join(dataDir, ".passwords") + publicMedia := filepath.Join(dataDir, "media") + outputDir := filepath.Join(*root, "public") + userComponents := filepath.Join(*root, "components") + userStyles := filepath.Join(*root, "styles") + + if err := vips.Startup(nil); err != nil { + fmt.Fprintln(os.Stderr, "govips startup:", err) + os.Exit(1) + } + + defer vips.Shutdown() + + database := mustOpenDB(dataDir, dbPath) + defer database.Close() + + if err := os.MkdirAll(publicMedia, 0755); err != nil { + fmt.Fprintln(os.Stderr, "media dir:", err) + os.Exit(1) + } + + tmpl, err := template.New("").ParseFS(iblog.SiteTemplates, "templates/*.html", "templates/**/*.html") + if err != nil { + fmt.Fprintln(os.Stderr, "site template load error:", err) + os.Exit(1) + } + + adminSrv := server.NewAdminServer(passwordFile, database, outputDir, iblog.AdminAssets, iblog.SiteAssets, tmpl) + engine := adminSrv.Engine() + + publicMIMEs := server.AllowedMIMEs{ + ".html": "text/html; charset=utf-8", + ".css": "text/css; charset=utf-8", + ".js": "application/javascript; charset=utf-8", + ".json": "application/json", + ".xml": "application/xml", + ".txt": "text/plain; charset=utf-8", + ".ico": "image/x-icon", + ".svg": "image/svg+xml", + ".png": "image/png", + ".jpg": "image/jpeg", + ".webp": "image/webp", + } + + mediaSrv := media.NewMediaHandler(publicMedia) + adminSrv.RegisterUploadRoute(mediaSrv.HandleUpload) + engine.GET("/media/*filepath", mediaSrv.HandleServe) + + if info, err := os.Stat(userComponents); err == nil && info.IsDir() { + engine.GET("/components/*filepath", server.FileHandler(userComponents, nil)) + } + + if info, err := os.Stat(userStyles); err == nil && info.IsDir() { + engine.GET("/styles/*filepath", server.FileHandler(userStyles, nil)) + } + + frontpage := server.NewFrontpageHandler(database) + engine.GET("/", frontpage.Serve) + + postHandler := server.NewPostHandler(database, tmpl, outputDir) + engine.GET("/:slug", postHandler.Serve) + + engine.NoRoute(server.PublicFileHandler(outputDir, publicMIMEs)) + + addr := "localhost:" + *port + fmt.Printf("listening on http://localhost%s\n", addr) + fmt.Printf(" admin UI: http://localhost%s/admin/\n", addr) + if err := engine.Run(addr); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func mustOpenDB(dataDir, dbPath string) *db.DB { + if err := os.MkdirAll(dataDir, 0755); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + database, err := db.Open(dbPath) + if err != nil { + fmt.Fprintln(os.Stderr, "db:", err) + os.Exit(1) + } + return database +} diff --git a/cmd/iblog/user.go b/cmd/iblog/user.go new file mode 100644 index 0000000..f9d422a --- /dev/null +++ b/cmd/iblog/user.go @@ -0,0 +1,68 @@ +package main + +import ( + "flag" + "fmt" + auth "iblog/internal" + "os" + "path/filepath" +) + +func cmdUser(args []string) { + f := flag.NewFlagSet("user", flag.ExitOnError) + root := f.String("root", ".", "site root directory") + _ = f.Parse(args) + remaining := f.Args() + if len(remaining) == 0 { + fmt.Fprintln(os.Stderr, "usage: iblog user [--root PATH] <add|list|delete|passwd> [username]") + os.Exit(1) + } + passwordFile := filepath.Join(*root, "data", ".passwords") + a := auth.NewAuth(passwordFile) + switch remaining[0] { + case "add": + requireArg(remaining, 1, "iblog user add <username>") + if err := a.AddUser(remaining[1]); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + fmt.Println("user added:", remaining[1]) + case "list": + users, err := a.ListUsers() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + if len(users) == 0 { + fmt.Println("(no users)") + return + } + for _, u := range users { + fmt.Println(u) + } + case "delete": + requireArg(remaining, 1, "iblog user delete <username>") + if err := a.DeleteUser(remaining[1]); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + fmt.Println("user deleted:", remaining[1]) + case "passwd": + requireArg(remaining, 1, "iblog user passwd <username>") + if err := a.ChangePassword(remaining[1]); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + fmt.Println("password updated:", remaining[1]) + default: + fmt.Fprintf(os.Stderr, "unknown user subcommand %q\n", remaining[0]) + os.Exit(1) + } +} + +func requireArg(args []string, n int, usage string) { + if len(args) <= n { + fmt.Fprintln(os.Stderr, "usage:", usage) + os.Exit(1) + } +} diff --git a/embed.go b/embed.go new file mode 100644 index 0000000..13e26b7 --- /dev/null +++ b/embed.go @@ -0,0 +1,18 @@ +package iblog + +import "embed" + +// SiteTemplates holds the bundled site layout templates. +// +//go:embed templates +var SiteTemplates embed.FS + +// SiteAssets holds built-in site-facing assets (lib, components, styles, static). +// +//go:embed assets/lib/shared.js assets/lib/dist assets/components assets/styles assets/static +var SiteAssets embed.FS + +// AdminAssets holds the admin UI JavaScript bundle. +// +//go:embed assets/admin/lib/dist assets/admin/lib/build.js assets/admin/lib/component-tool.js +var AdminAssets embed.FS @@ -0,0 +1,54 @@ +module iblog + +go 1.26 + +require ( + github.com/davidbyttow/govips/v2 v2.18.0 + github.com/gin-gonic/gin v1.12.0 + github.com/google/uuid v1.6.0 + golang.org/x/crypto v0.49.0 + golang.org/x/term v0.41.0 + modernc.org/sqlite v1.48.0 +) + +require golang.org/x/sys v0.42.0 // indirect + +require ( + github.com/bytedance/gopkg v0.1.3 // indirect + github.com/bytedance/sonic v1.15.0 // indirect + github.com/bytedance/sonic/loader v0.5.0 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.12 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.30.1 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/goccy/go-yaml v1.19.2 // indirect + github.com/gosimple/slug v1.15.0 // indirect + github.com/gosimple/unidecode v1.0.1 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/ncruces/go-strftime v1.0.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/quic-go/qpack v0.6.0 // indirect + github.com/quic-go/quic-go v0.59.0 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.3.1 // indirect + go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect + go.n16f.net/thumbhash v1.1.0 // indirect + golang.org/x/arch v0.22.0 // indirect + golang.org/x/image v0.38.0 // indirect + golang.org/x/net v0.52.0 // indirect + golang.org/x/text v0.35.0 // indirect + google.golang.org/protobuf v1.36.10 // indirect + modernc.org/libc v1.70.0 // indirect + modernc.org/mathutil v1.7.1 // indirect + modernc.org/memory v1.11.0 // indirect +) @@ -0,0 +1,147 @@ +github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= +github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= +github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE= +github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= +github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= +github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= +github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davidbyttow/govips/v2 v2.18.0 h1:pZRshWVYvewP/TZx3yZ7YeC42WyLXg53tHy5Qt8nT9E= +github.com/davidbyttow/govips/v2 v2.18.0/go.mod h1:8+nst5zfMoats12PgmmAPh6p5OfjDaXK0BXMFl/vOcM= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= +github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= +github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8= +github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w= +github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= +github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gosimple/slug v1.15.0 h1:wRZHsRrRcs6b0XnxMUBM6WK1U1Vg5B0R7VkIf1Xzobo= +github.com/gosimple/slug v1.15.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= +github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= +github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= +github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= +github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= +github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE= +go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= +go.n16f.net/thumbhash v1.1.0 h1:aBEvuAd4yiwzeQ7Sm4BZoHJYbrQ1ewjrmrRlCE79snk= +go.n16f.net/thumbhash v1.1.0/go.mod h1:mo9pP7WtfdV9ojIamGFR/Vc0PaPA2l0CUtmYQf/SweU= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI= +golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/image v0.38.0 h1:5l+q+Y9JDC7mBOMjo4/aPhMDcxEptsX+Tt3GgRQRPuE= +golang.org/x/image v0.38.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY= +golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= +golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis= +modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/ccgo/v4 v4.32.0 h1:hjG66bI/kqIPX1b2yT6fr/jt+QedtP2fqojG2VrFuVw= +modernc.org/ccgo/v4 v4.32.0/go.mod h1:6F08EBCx5uQc38kMGl+0Nm0oWczoo1c7cgpzEry7Uc0= +modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM= +modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU= +modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= +modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= +modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo= +modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= +modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= +modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= +modernc.org/libc v1.70.0 h1:U58NawXqXbgpZ/dcdS9kMshu08aiA6b7gusEusqzNkw= +modernc.org/libc v1.70.0/go.mod h1:OVmxFGP1CI/Z4L3E0Q3Mf1PDE0BucwMkcXjjLntvHJo= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= +modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= +modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= +modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= +modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= +modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= +modernc.org/sqlite v1.48.0 h1:ElZyLop3Q2mHYk5IFPPXADejZrlHu7APbpB0sF78bq4= +modernc.org/sqlite v1.48.0/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/internal/auth.go b/internal/auth.go new file mode 100644 index 0000000..5c5991e --- /dev/null +++ b/internal/auth.go @@ -0,0 +1,198 @@ +// Package auth manages a htpasswd-compatible password file (bcrypt entries). +// The file format is one "username:$2a$..." entry per line. +// nginx auth_basic accepts this file directly via auth_basic_user_file. +package auth + +import ( + "bufio" + "fmt" + "os" + "strings" + "syscall" + + "golang.org/x/crypto/bcrypt" + "golang.org/x/term" +) + +type Auth struct { + path string +} + +func NewAuth(path string) *Auth { return &Auth{path: path} } + +// IsConfigured reports whether the password file exists and contains at least one user. +// It does not create the file as a side effect. +func (a *Auth) IsConfigured() bool { + info, err := os.Stat(a.path) + return err == nil && info.Size() > 0 +} + +// AddUserWithPassword adds a user with an already-known password (for HTTP handlers). +func (a *Auth) AddUserWithPassword(username, password string) error { + users, err := a.read() + if err != nil && !os.IsNotExist(err) { + return err + } + if users == nil { + users = make(map[string]string) + } + if _, exists := users[username]; exists { + return fmt.Errorf("user %q already exists", username) + } + hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return err + } + users[username] = string(hash) + return a.write(users) +} + +func (a *Auth) AddUser(username string) error { + users, err := a.read() + if err != nil && !os.IsNotExist(err) { + return err + } + if users == nil { + users = make(map[string]string) + } + if _, exists := users[username]; exists { + return fmt.Errorf("user %q already exists", username) + } + pw, err := readPassword("Password: ") + if err != nil { + return err + } + confirm, err := readPassword("Confirm: ") + if err != nil { + return err + } + if pw != confirm { + return fmt.Errorf("passwords do not match") + } + hash, err := bcrypt.GenerateFromPassword([]byte(pw), bcrypt.DefaultCost) + if err != nil { + return err + } + users[username] = string(hash) + return a.write(users) +} + +func (a *Auth) ChangePassword(username string) error { + users, err := a.read() + if err != nil { + return err + } + if _, exists := users[username]; !exists { + return fmt.Errorf("user %q not found", username) + } + pw, err := readPassword("New password: ") + if err != nil { + return err + } + confirm, err := readPassword("Confirm: ") + if err != nil { + return err + } + if pw != confirm { + return fmt.Errorf("passwords do not match") + } + hash, err := bcrypt.GenerateFromPassword([]byte(pw), bcrypt.DefaultCost) + if err != nil { + return err + } + users[username] = string(hash) + return a.write(users) +} + +func (a *Auth) DeleteUser(username string) error { + users, err := a.read() + if err != nil { + return err + } + if _, exists := users[username]; !exists { + return fmt.Errorf("user %q not found", username) + } + delete(users, username) + return a.write(users) +} + +func (a *Auth) ListUsers() ([]string, error) { + users, err := a.read() + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, err + } + names := make([]string, 0, len(users)) + for k := range users { + names = append(names, k) + } + return names, nil +} + +func (a *Auth) Verify(username, password string) (bool, error) { + users, err := a.read() + if err != nil { + return false, err + } + hash, ok := users[username] + if !ok { + return false, nil + } + err = bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) + if err == bcrypt.ErrMismatchedHashAndPassword { + return false, nil + } + return err == nil, err +} + +func (a *Auth) read() (map[string]string, error) { + var _, staterr = os.Stat(a.path) + if os.IsNotExist(staterr) { + var cf, cferr = os.Create(a.path) + if cferr != nil { + return nil, cferr + } + defer cf.Close() + } + + f, err := os.Open(a.path) + if err != nil { + return nil, err + } + defer f.Close() + users := make(map[string]string) + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + user, hash, ok := strings.Cut(line, ":") + if ok { + users[user] = hash + } + } + return users, scanner.Err() +} + +func (a *Auth) write(users map[string]string) error { + f, err := os.OpenFile(a.path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + return err + } + defer f.Close() + w := bufio.NewWriter(f) + for user, hash := range users { + fmt.Fprintf(w, "%s:%s\n", user, hash) + } + return w.Flush() +} + +func readPassword(prompt string) (string, error) { + fmt.Print(prompt) + b, err := term.ReadPassword(int(syscall.Stdin)) + fmt.Println() + return string(b), err +} diff --git a/internal/builder/editorjs.go b/internal/builder/editorjs.go new file mode 100644 index 0000000..68237d0 --- /dev/null +++ b/internal/builder/editorjs.go @@ -0,0 +1,255 @@ +package builder + +import ( + "encoding/json" + "fmt" + "html/template" + "strings" +) + +// PageData is passed to HTML templates when rendering posts. +type PageData struct { + Title string + Content template.HTML + ComponentScripts []string + Date string + Tags []string + Path string +} + +type EditorDocument struct { + Version string `json:"version"` + Time int64 `json:"time"` + Blocks []EditorBlock `json:"blocks"` +} + +type EditorBlock struct { + Type string `json:"type"` + Data json.RawMessage `json:"data"` +} + +// RenderEditorJS converts EditorJS block JSON to HTML and extracts script URLs. +// Returns the rendered HTML body, script URLs, and any error. +func RenderEditorJS(blocksJSON string) (html string, scripts []string, err error) { + if blocksJSON == "" || blocksJSON == "[]" { + return "", nil, nil + } + + var doc EditorDocument + if err := json.Unmarshal([]byte(blocksJSON), &doc); err != nil { + return "", nil, err + } + + var buf strings.Builder + var scriptURLs []string + + for _, block := range doc.Blocks { + blockHTML, blockScripts, err := renderBlock(block) + if err != nil { + return "", nil, err + } + if blockHTML != "" { + buf.WriteString(blockHTML) + } + scriptURLs = append(scriptURLs, blockScripts...) + } + + return buf.String(), scriptURLs, nil +} + +func renderBlock(block EditorBlock) (html string, scripts []string, err error) { + switch block.Type { + case "paragraph": + return renderParagraph(block.Data) + case "header": + return renderHeader(block.Data) + case "image": + return renderImage(block.Data) + case "list": + return renderList(block.Data) + case "code": + return renderCode(block.Data) + case "quote": + return renderQuote(block.Data) + case "script": + return renderScript(block.Data) + case "component": + return renderComponent(block.Data) + default: + return "", nil, nil + } +} + +func renderParagraph(data json.RawMessage) (string, []string, error) { + var d struct { + Text string `json:"text"` + } + if err := json.Unmarshal(data, &d); err != nil { + return "", nil, err + } + if strings.TrimSpace(d.Text) == "" { + return "", nil, nil + } + return fmt.Sprintf("<p>%s</p>\n", template.HTML(d.Text)), nil, nil +} + +func renderHeader(data json.RawMessage) (string, []string, error) { + var d struct { + Text string `json:"text"` + Level int `json:"level"` + } + if err := json.Unmarshal(data, &d); err != nil { + return "", nil, err + } + if d.Level < 1 || d.Level > 6 { + d.Level = 2 + } + if strings.TrimSpace(d.Text) == "" { + return "", nil, nil + } + return fmt.Sprintf("<h%d>%s</h%d>\n", d.Level, template.HTML(d.Text), d.Level), nil, nil +} + +// renderImage renders an EditorJS image block. +// The thumbhash (if present) is placed in data-thumbhash on the <figure> so +// the client can decode it and use it as a blurry placeholder via JS. +func renderImage(data json.RawMessage) (string, []string, error) { + var d struct { + File struct { + URL string `json:"url"` + Thumbhash string `json:"thumbhash"` + } `json:"file"` + Caption string `json:"caption"` + } + if err := json.Unmarshal(data, &d); err != nil { + return "", nil, err + } + if d.File.URL == "" { + return "", nil, nil + } + + alt := d.Caption + if alt == "" { + alt = "image" + } + + var figAttrs string + if d.File.Thumbhash != "" { + figAttrs = fmt.Sprintf(` data-thumbhash="%s"`, template.HTMLEscapeString(d.File.Thumbhash)) + } + + var buf strings.Builder + fmt.Fprintf(&buf, "<figure%s>", figAttrs) + fmt.Fprintf(&buf, `<img src="%s" alt="%s" loading="lazy" decoding="async">`, + template.HTMLEscapeString(d.File.URL), + template.HTMLEscapeString(alt), + ) + if d.Caption != "" { + fmt.Fprintf(&buf, "<figcaption>%s</figcaption>", template.HTML(d.Caption)) + } + buf.WriteString("</figure>\n") + return buf.String(), nil, nil +} + +type listItem struct { + Content string `json:"content"` + Items []listItem `json:"items"` +} + +func renderList(data json.RawMessage) (string, []string, error) { + var d struct { + Style string `json:"style"` + Items []listItem `json:"items"` + } + if err := json.Unmarshal(data, &d); err != nil { + return "", nil, err + } + if len(d.Items) == 0 { + return "", nil, nil + } + tag := "ul" + if d.Style == "ordered" { + tag = "ol" + } + var buf strings.Builder + renderListItems(&buf, d.Items, tag) + return buf.String(), nil, nil +} + +func renderListItems(buf *strings.Builder, items []listItem, tag string) { + fmt.Fprintf(buf, "<%s>\n", tag) + for _, item := range items { + fmt.Fprintf(buf, "<li>%s", template.HTML(item.Content)) + if len(item.Items) > 0 { + renderListItems(buf, item.Items, tag) + } + buf.WriteString("</li>\n") + } + fmt.Fprintf(buf, "</%s>\n", tag) +} + +func renderCode(data json.RawMessage) (string, []string, error) { + var d struct { + Code string `json:"code"` + } + if err := json.Unmarshal(data, &d); err != nil { + return "", nil, err + } + if strings.TrimSpace(d.Code) == "" { + return "", nil, nil + } + return fmt.Sprintf("<pre><code>%s</code></pre>\n", template.HTMLEscapeString(d.Code)), nil, nil +} + +func renderQuote(data json.RawMessage) (string, []string, error) { + var d struct { + Text string `json:"text"` + Caption string `json:"caption"` + } + if err := json.Unmarshal(data, &d); err != nil { + return "", nil, err + } + if strings.TrimSpace(d.Text) == "" { + return "", nil, nil + } + var buf strings.Builder + fmt.Fprintf(&buf, "<blockquote>\n<p>%s</p>\n", template.HTML(d.Text)) + if d.Caption != "" { + fmt.Fprintf(&buf, "<cite>%s</cite>\n", template.HTML(d.Caption)) + } + buf.WriteString("</blockquote>\n") + return buf.String(), nil, nil +} + +func renderScript(data json.RawMessage) (string, []string, error) { + var d struct { + Src string `json:"src"` + } + if err := json.Unmarshal(data, &d); err != nil { + return "", nil, err + } + if d.Src == "" { + return "", nil, nil + } + return "", []string{d.Src}, nil +} + +func renderComponent(data json.RawMessage) (string, []string, error) { + var d struct { + Name string `json:"name"` + Props map[string]string `json:"props"` + } + if err := json.Unmarshal(data, &d); err != nil { + return "", nil, err + } + if d.Name == "" { + return "", nil, nil + } + var buf strings.Builder + fmt.Fprintf(&buf, "<%s", template.HTMLEscapeString(d.Name)) + for k, v := range d.Props { + fmt.Fprintf(&buf, " %s=\"%s\"", template.HTMLEscapeString(k), template.HTMLEscapeString(v)) + } + fmt.Fprintf(&buf, "></%s>\n", template.HTMLEscapeString(d.Name)) + return buf.String(), []string{"/assets/components/" + d.Name + ".js"}, nil +} diff --git a/internal/db/db.go b/internal/db/db.go new file mode 100644 index 0000000..d348b96 --- /dev/null +++ b/internal/db/db.go @@ -0,0 +1,66 @@ +package db + +import ( + "database/sql" + + _ "modernc.org/sqlite" +) + +type DB struct { + db *sql.DB +} + +func Open(path string) (*DB, error) { + sqldb, err := sql.Open("sqlite", path) + if err != nil { + return nil, err + } + _, err = sqldb.Exec(` + CREATE TABLE IF NOT EXISTS pages ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + path TEXT NOT NULL UNIQUE, + html_path TEXT NOT NULL, + title TEXT NOT NULL DEFAULT '', + date TEXT DEFAULT '', + tags TEXT DEFAULT '[]', + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + CREATE INDEX IF NOT EXISTS idx_pages_path ON pages(path); + CREATE INDEX IF NOT EXISTS idx_pages_date ON pages(date); + CREATE TABLE IF NOT EXISTS posts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + slug TEXT NOT NULL UNIQUE, + title TEXT NOT NULL DEFAULT '', + date TEXT DEFAULT '', + tags TEXT DEFAULT '[]', + draft INTEGER NOT NULL DEFAULT 0, + blocks TEXT NOT NULL DEFAULT '[]', + updated_at INTEGER NOT NULL DEFAULT (cast(strftime('%s','now') * 1000000 as integer)) + ); + CREATE INDEX IF NOT EXISTS idx_posts_slug ON posts(slug); + CREATE INDEX IF NOT EXISTS idx_posts_date ON posts(date); + CREATE TABLE IF NOT EXISTS redirects ( + from_slug TEXT PRIMARY KEY, + to_slug TEXT NOT NULL + ); + CREATE TABLE IF NOT EXISTS settings ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL DEFAULT '' + ); + CREATE VIRTUAL TABLE IF NOT EXISTS pages_fts USING fts5( + path UNINDEXED, + title, + content, + tokenize = 'porter unicode61' + ); + `) + if err != nil { + return nil, err + } + return &DB{db: sqldb}, nil +} + +func (d *DB) Close() error { return d.db.Close() } + +// RawDB returns the underlying *sql.DB for advanced queries. +func (d *DB) RawDB() *sql.DB { return d.db } diff --git a/internal/db/pages.go b/internal/db/pages.go new file mode 100644 index 0000000..fc048c0 --- /dev/null +++ b/internal/db/pages.go @@ -0,0 +1,84 @@ +package db + +import ( + "database/sql" + "encoding/json" + "strings" + "time" +) + +type PageMeta struct { + Path string + HTMLPath string + Title string + Date string + Tags []string + UpdatedAt time.Time +} + +func (d *DB) UpsertPage(p PageMeta) error { + tags, _ := json.Marshal(p.Tags) + _, err := d.db.Exec(` + INSERT INTO pages (path, html_path, title, date, tags, updated_at) + VALUES (?, ?, ?, ?, ?, ?) + ON CONFLICT(path) DO UPDATE SET + html_path = excluded.html_path, + title = excluded.title, + date = excluded.date, + tags = excluded.tags, + updated_at = excluded.updated_at + `, p.Path, p.HTMLPath, p.Title, p.Date, string(tags), p.UpdatedAt.UTC()) + return err +} + +func (d *DB) DeletePage(path string) error { + _, err := d.db.Exec(`DELETE FROM pages WHERE path = ?`, path) + return err +} + +func (d *DB) GetPage(path string) (*PageMeta, error) { + row := d.db.QueryRow( + `SELECT path, html_path, title, date, tags FROM pages WHERE path = ?`, path) + var p PageMeta + var tagsJSON string + if err := row.Scan(&p.Path, &p.HTMLPath, &p.Title, &p.Date, &tagsJSON); err != nil { + return nil, err + } + _ = json.Unmarshal([]byte(tagsJSON), &p.Tags) + return &p, nil +} + +func (d *DB) ListPages() ([]PageMeta, error) { + rows, err := d.db.Query( + `SELECT path, html_path, title, date, tags FROM pages ORDER BY date DESC, path`) + if err != nil { + return nil, err + } + defer rows.Close() + return scanPages(rows) +} + +func (d *DB) ListByTag(tag string) ([]PageMeta, error) { + needle := `%"` + strings.ReplaceAll(tag, `"`, `\"`) + `"%` + rows, err := d.db.Query( + `SELECT path, html_path, title, date, tags FROM pages WHERE tags LIKE ? ORDER BY date DESC`, needle) + if err != nil { + return nil, err + } + defer rows.Close() + return scanPages(rows) +} + +func scanPages(rows *sql.Rows) ([]PageMeta, error) { + var pages []PageMeta + for rows.Next() { + var p PageMeta + var tagsJSON string + if err := rows.Scan(&p.Path, &p.HTMLPath, &p.Title, &p.Date, &tagsJSON); err != nil { + return nil, err + } + _ = json.Unmarshal([]byte(tagsJSON), &p.Tags) + pages = append(pages, p) + } + return pages, rows.Err() +} diff --git a/internal/db/posts.go b/internal/db/posts.go new file mode 100644 index 0000000..3c90b5f --- /dev/null +++ b/internal/db/posts.go @@ -0,0 +1,248 @@ +package db + +import ( + "database/sql" + "encoding/json" + "time" +) + +type PostRecord struct { + Id string + Slug string + Title string + Date string + Tags []string + Draft bool + Blocks string // raw EditorJS JSON + UpdatedAt int64 // Unix microseconds +} + +func (d *DB) UpsertPost(p PostRecord) error { + tags, _ := json.Marshal(p.Tags) + draft := 0 + if p.Draft { + draft = 1 + } + _, err := d.db.Exec(` + INSERT INTO posts (slug, title, date, tags, draft, blocks, updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?) + ON CONFLICT(slug) DO UPDATE SET + title = excluded.title, + date = excluded.date, + tags = excluded.tags, + draft = excluded.draft, + blocks = excluded.blocks, + updated_at = excluded.updated_at + `, p.Slug, p.Title, p.Date, string(tags), draft, p.Blocks, p.UpdatedAt) + return err +} + +func (d *DB) GetPostBySlug(slug string) (*PostRecord, error) { + row := d.db.QueryRow( + `SELECT id, slug, title, date, tags, draft, blocks, updated_at FROM posts WHERE slug = ?`, slug) + var p PostRecord + var tagsJSON string + var draft int + if err := row.Scan(&p.Id, &p.Slug, &p.Title, &p.Date, &tagsJSON, &draft, &p.Blocks, &p.UpdatedAt); err != nil { + return nil, err + } + p.Draft = draft != 0 + _ = json.Unmarshal([]byte(tagsJSON), &p.Tags) + if p.Tags == nil { + p.Tags = []string{} + } + return &p, nil +} + +func (d *DB) GetPostById(id string) (*PostRecord, error) { + row := d.db.QueryRow( + `SELECT id, slug, title, date, tags, draft, blocks, updated_at FROM posts WHERE id = ?`, id) + var p PostRecord + var tagsJSON string + var draft int + if err := row.Scan(&p.Id, &p.Slug, &p.Title, &p.Date, &tagsJSON, &draft, &p.Blocks, &p.UpdatedAt); err != nil { + return nil, err + } + p.Draft = draft != 0 + _ = json.Unmarshal([]byte(tagsJSON), &p.Tags) + if p.Tags == nil { + p.Tags = []string{} + } + return &p, nil +} + +func (d *DB) ListPosts(includeDrafts bool) ([]PostRecord, error) { + query := `SELECT id, slug, title, date, tags, draft, blocks, updated_at FROM posts` + if !includeDrafts { + query += ` WHERE draft = 0` + } + query += ` ORDER BY date DESC, slug` + rows, err := d.db.Query(query) + if err != nil { + return nil, err + } + defer rows.Close() + return scanPosts(rows) +} + +func (d *DB) DeletePostBySlug(slug string) error { + _, err := d.db.Exec(`DELETE FROM posts WHERE slug = ?`, slug) + return err +} + +func (d *DB) DeletePostById(id string) error { + _, err := d.db.Exec(`DELETE FROM posts WHERE id = ?`, id) + return err +} + +func scanPosts(rows *sql.Rows) ([]PostRecord, error) { + var posts []PostRecord + for rows.Next() { + var p PostRecord + var tagsJSON string + var draft int + if err := rows.Scan(&p.Id, &p.Slug, &p.Title, &p.Date, &tagsJSON, &draft, &p.Blocks, &p.UpdatedAt); err != nil { + return nil, err + } + p.Draft = draft != 0 + _ = json.Unmarshal([]byte(tagsJSON), &p.Tags) + if p.Tags == nil { + p.Tags = []string{} + } + posts = append(posts, p) + } + return posts, rows.Err() +} + +func (p *PostRecord) GetPostRawPath() string { + return "/" + p.Slug +} + +func (p *PostRecord) GetUpdatedTime() time.Time { + return time.UnixMicro(p.UpdatedAt).UTC() +} + +func (d *DB) AddRedirect(fromSlug, toSlug string) error { + _, err := d.db.Exec( + `INSERT INTO redirects (from_slug, to_slug) VALUES (?, ?) + ON CONFLICT(from_slug) DO UPDATE SET to_slug = excluded.to_slug`, + fromSlug, toSlug, + ) + return err +} + +func (d *DB) GetRedirect(fromSlug string) (string, error) { + var toSlug string + err := d.db.QueryRow( + `SELECT to_slug FROM redirects WHERE from_slug = ?`, fromSlug, + ).Scan(&toSlug) + return toSlug, err +} + +func (d *DB) CollapseRedirects(oldSlug, newSlug string) error { + _, err := d.db.Exec( + `UPDATE redirects SET to_slug = ? WHERE to_slug = ?`, newSlug, oldSlug, + ) + return err +} + +func (d *DB) RenamePost(oldSlug, newSlug string) error { + if oldSlug == newSlug { + return nil + } + tx, err := d.db.Begin() + if err != nil { + return err + } + defer tx.Rollback() + + var p PostRecord + var tagsJSON string + var draft int + row := tx.QueryRow( + `SELECT slug, title, date, tags, draft, blocks, updated_at FROM posts WHERE slug = ?`, oldSlug) + if err := row.Scan(&p.Slug, &p.Title, &p.Date, &tagsJSON, &draft, &p.Blocks, &p.UpdatedAt); err != nil { + return err + } + p.Draft = draft != 0 + _ = json.Unmarshal([]byte(tagsJSON), &p.Tags) + + tags, _ := json.Marshal(p.Tags) + draftInt := 0 + if p.Draft { + draftInt = 1 + } + _, err = tx.Exec(` + INSERT INTO posts (slug, title, date, tags, draft, blocks, updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?)`, + newSlug, p.Title, p.Date, string(tags), draftInt, p.Blocks, p.UpdatedAt, + ) + if err != nil { + return err + } + + if _, err = tx.Exec(`DELETE FROM posts WHERE slug = ?`, oldSlug); err != nil { + return err + } + + if _, err = tx.Exec( + `UPDATE redirects SET to_slug = ? WHERE to_slug = ?`, newSlug, oldSlug, + ); err != nil { + return err + } + + if _, err = tx.Exec(` + INSERT INTO redirects (from_slug, to_slug) VALUES (?, ?) + ON CONFLICT(from_slug) DO UPDATE SET to_slug = excluded.to_slug`, + oldSlug, newSlug, + ); err != nil { + return err + } + + return tx.Commit() +} + +func (d *DB) RenameAndUpsertPost(oldSlug string, p PostRecord) error { + if oldSlug == p.Slug { + return d.UpsertPost(p) + } + tx, err := d.db.Begin() + if err != nil { + return err + } + defer tx.Rollback() + + tags, _ := json.Marshal(p.Tags) + draftInt := 0 + if p.Draft { + draftInt = 1 + } + + if _, err = tx.Exec(` + INSERT INTO posts (slug, title, date, tags, draft, blocks, updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?)`, + p.Slug, p.Title, p.Date, string(tags), draftInt, p.Blocks, p.UpdatedAt, + ); err != nil { + return err + } + + if _, err = tx.Exec(`DELETE FROM posts WHERE slug = ?`, oldSlug); err != nil { + return err + } + + if _, err = tx.Exec( + `UPDATE redirects SET to_slug = ? WHERE to_slug = ?`, p.Slug, oldSlug, + ); err != nil { + return err + } + + if _, err = tx.Exec(` + INSERT INTO redirects (from_slug, to_slug) VALUES (?, ?) + ON CONFLICT(from_slug) DO UPDATE SET to_slug = excluded.to_slug`, + oldSlug, p.Slug, + ); err != nil { + return err + } + + return tx.Commit() +} diff --git a/internal/db/posts_test.go b/internal/db/posts_test.go new file mode 100644 index 0000000..804e47d --- /dev/null +++ b/internal/db/posts_test.go @@ -0,0 +1,186 @@ +package db + +import ( + "database/sql" + "testing" +) + +func openTestDB(t *testing.T) *DB { + t.Helper() + d, err := Open(":memory:") + if err != nil { + t.Fatalf("open test db: %v", err) + } + t.Cleanup(func() { d.Close() }) + return d +} + +func TestAddAndGetRedirect(t *testing.T) { + d := openTestDB(t) + + if err := d.AddRedirect("old-slug", "new-slug"); err != nil { + t.Fatalf("AddRedirect: %v", err) + } + + got, err := d.GetRedirect("old-slug") + if err != nil { + t.Fatalf("GetRedirect: %v", err) + } + if got != "new-slug" { + t.Errorf("got %q, want %q", got, "new-slug") + } +} + +func TestGetRedirect_NotFound(t *testing.T) { + d := openTestDB(t) + + _, err := d.GetRedirect("missing") + if err != sql.ErrNoRows { + t.Errorf("expected sql.ErrNoRows, got %v", err) + } +} + +func TestCollapseRedirects(t *testing.T) { + d := openTestDB(t) + + if err := d.AddRedirect("a", "b"); err != nil { + t.Fatalf("AddRedirect: %v", err) + } + if err := d.CollapseRedirects("b", "c"); err != nil { + t.Fatalf("CollapseRedirects: %v", err) + } + + got, err := d.GetRedirect("a") + if err != nil { + t.Fatalf("GetRedirect: %v", err) + } + if got != "c" { + t.Errorf("got %q, want %q", got, "c") + } +} + +func TestAddRedirect_Upsert(t *testing.T) { + d := openTestDB(t) + + if err := d.AddRedirect("old", "first"); err != nil { + t.Fatalf("AddRedirect first: %v", err) + } + if err := d.AddRedirect("old", "second"); err != nil { + t.Fatalf("AddRedirect second: %v", err) + } + + got, err := d.GetRedirect("old") + if err != nil { + t.Fatalf("GetRedirect: %v", err) + } + if got != "second" { + t.Errorf("got %q, want %q", got, "second") + } +} + +func TestRenamePost(t *testing.T) { + d := openTestDB(t) + + original := PostRecord{ + Slug: "original-slug", + Title: "My Post", + Date: "2026-04-04", + Tags: []string{"go"}, + Draft: false, + Blocks: `[{"type":"paragraph","data":{"text":"hello"}}]`, + UpdatedAt: 1000000, + } + if err := d.UpsertPost(original); err != nil { + t.Fatalf("UpsertPost: %v", err) + } + + if err := d.RenamePost("original-slug", "new-slug"); err != nil { + t.Fatalf("RenamePost: %v", err) + } + + got, err := d.GetPostBySlug("new-slug") + if err != nil { + t.Fatalf("GetPost new-slug: %v", err) + } + if got.Title != "My Post" { + t.Errorf("title: got %q, want %q", got.Title, "My Post") + } + + _, err = d.GetPostBySlug("original-slug") + if err == nil { + t.Error("expected old slug to be deleted, but GetPost returned no error") + } + + toSlug, err := d.GetRedirect("original-slug") + if err != nil { + t.Fatalf("GetRedirect: %v", err) + } + if toSlug != "new-slug" { + t.Errorf("redirect: got %q, want %q", toSlug, "new-slug") + } +} + +func TestRenameAndUpsertPost(t *testing.T) { + d := openTestDB(t) + + if err := d.UpsertPost(PostRecord{Slug: "old", Title: "Old Title", Date: "2026-01-01", Blocks: "[]", UpdatedAt: 1}); err != nil { + t.Fatalf("UpsertPost: %v", err) + } + + updated := PostRecord{ + Slug: "new", + Title: "New Title", + Date: "2026-04-04", + Tags: []string{"go"}, + Blocks: "[]", + UpdatedAt: 2, + } + if err := d.RenameAndUpsertPost("old", updated); err != nil { + t.Fatalf("RenameAndUpsertPost: %v", err) + } + + got, err := d.GetPostBySlug("new") + if err != nil { + t.Fatalf("GetPost new: %v", err) + } + if got.Title != "New Title" { + t.Errorf("title: got %q, want %q", got.Title, "New Title") + } + if got.Date != "2026-04-04" { + t.Errorf("date: got %q, want %q", got.Date, "2026-04-04") + } + + if _, err := d.GetPostBySlug("old"); err == nil { + t.Error("expected old slug to be deleted") + } + + toSlug, err := d.GetRedirect("old") + if err != nil { + t.Fatalf("GetRedirect: %v", err) + } + if toSlug != "new" { + t.Errorf("redirect: got %q, want %q", toSlug, "new") + } +} + +func TestRenamePost_CollapsesChain(t *testing.T) { + d := openTestDB(t) + + if err := d.UpsertPost(PostRecord{Slug: "b", Title: "B", Blocks: "[]", UpdatedAt: 1}); err != nil { + t.Fatalf("UpsertPost: %v", err) + } + if err := d.AddRedirect("a", "b"); err != nil { + t.Fatalf("AddRedirect: %v", err) + } + if err := d.RenamePost("b", "c"); err != nil { + t.Fatalf("RenamePost: %v", err) + } + + got, err := d.GetRedirect("a") + if err != nil { + t.Fatalf("GetRedirect a: %v", err) + } + if got != "c" { + t.Errorf("chain collapse: got %q, want %q", got, "c") + } +} diff --git a/internal/db/search.go b/internal/db/search.go new file mode 100644 index 0000000..1214e2d --- /dev/null +++ b/internal/db/search.go @@ -0,0 +1,62 @@ +package db + +type SearchPage struct { + Path string + Title string + Content string +} + +type SearchResult struct { + Path string + Title string + Snippet string +} + +func (d *DB) IndexPage(p SearchPage) error { + tx, err := d.db.Begin() + if err != nil { + return err + } + defer tx.Rollback() + + if _, err = tx.Exec(`DELETE FROM pages_fts WHERE path = ?`, p.Path); err != nil { + return err + } + if _, err = tx.Exec( + `INSERT INTO pages_fts (path, title, content) VALUES (?, ?, ?)`, + p.Path, p.Title, p.Content, + ); err != nil { + return err + } + return tx.Commit() +} + +func (d *DB) UnindexPage(path string) error { + _, err := d.db.Exec(`DELETE FROM pages_fts WHERE path = ?`, path) + return err +} + +// Search runs a full-text query and returns up to 20 results with snippets. +func (d *DB) Search(query string) ([]SearchResult, error) { + rows, err := d.db.Query(` + SELECT path, title, + snippet(pages_fts, 2, '<mark>', '</mark>', '...', 20) + FROM pages_fts + WHERE pages_fts MATCH ? + ORDER BY rank + LIMIT 20 + `, query) + if err != nil { + return nil, err + } + defer rows.Close() + var results []SearchResult + for rows.Next() { + var r SearchResult + if err := rows.Scan(&r.Path, &r.Title, &r.Snippet); err != nil { + return nil, err + } + results = append(results, r) + } + return results, rows.Err() +} diff --git a/internal/db/settings.go b/internal/db/settings.go new file mode 100644 index 0000000..6b2a007 --- /dev/null +++ b/internal/db/settings.go @@ -0,0 +1,21 @@ +package db + +import "database/sql" + +func (d *DB) GetSetting(key string) (string, error) { + var val string + err := d.db.QueryRow(`SELECT value FROM settings WHERE key = ?`, key).Scan(&val) + if err == sql.ErrNoRows { + return "", nil + } + return val, err +} + +func (d *DB) SetSetting(key, value string) error { + _, err := d.db.Exec( + `INSERT INTO settings (key, value) VALUES (?, ?) + ON CONFLICT(key) DO UPDATE SET value = excluded.value`, + key, value, + ) + return err +} diff --git a/internal/media/handler.go b/internal/media/handler.go new file mode 100644 index 0000000..6b4d114 --- /dev/null +++ b/internal/media/handler.go @@ -0,0 +1,221 @@ +package media + +import ( + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" +) + +const ( + maxUploadSize = 20 << 20 // 20 MB + maxWidth = 3000 +) + +// allowedMIMEs maps detected content types to canonical file extensions. +var allowedMIMEs = map[string]string{ + "image/jpeg": ".jpg", + "image/png": ".png", + "image/webp": ".webp", +} + +// MediaHandler handles image uploads and on-the-fly image serving. +type MediaHandler struct { + storageDir string +} + +// NewMediaHandler returns a MediaHandler that stores files in storageDir. +// The directory is created if it does not exist. +func NewMediaHandler(storageDir string) *MediaHandler { + _ = os.MkdirAll(storageDir, 0755) + return &MediaHandler{storageDir: storageDir} +} + +// HandleUpload handles POST /admin/upload/image. +// Expects multipart/form-data with an "image" field. +// Returns EditorJS-compatible JSON: {"success":1,"file":{"url":"/media/<uuid>.webp"}} +func (h *MediaHandler) HandleUpload(c *gin.Context) { + c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, maxUploadSize) + + file, _, err := c.Request.FormFile("image") + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"success": 0, "error": err.Error()}) + return + } + defer file.Close() + + // Sniff MIME type from first 512 bytes + sniff := make([]byte, 512) + n, _ := file.Read(sniff) + mimeType := http.DetectContentType(sniff[:n]) + ext, ok := allowedMIMEs[mimeType] + if !ok { + c.JSON(http.StatusBadRequest, gin.H{"success": 0, "error": "unsupported image type: " + mimeType}) + return + } + + // Rewind so the full file is copied to disk + seeker, ok := file.(io.Seeker) + if !ok { + c.JSON(http.StatusInternalServerError, gin.H{"success": 0, "error": "file not seekable"}) + return + } + if _, err := seeker.Seek(0, io.SeekStart); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"success": 0, "error": "seek error"}) + return + } + + if err := os.MkdirAll(h.storageDir, 0755); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"success": 0, "error": "storage error"}) + return + } + + id := uuid.New().String() + destPath := filepath.Join(h.storageDir, id+ext) + + out, err := os.Create(destPath) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"success": 0, "error": "storage error"}) + return + } + + if _, err := io.Copy(out, file); err != nil { + out.Close() + c.JSON(http.StatusInternalServerError, gin.H{"success": 0, "error": "write error"}) + return + } + if err := out.Close(); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"success": 0, "error": "finalize error"}) + return + } + + fileInfo := gin.H{"url": "/media/" + id + ".webp"} + if w, h, th, err := ImageInfo(destPath); err == nil { + fileInfo["width"] = w + fileInfo["height"] = h + fileInfo["thumbhash"] = th + } + + c.JSON(http.StatusOK, gin.H{"success": 1, "file": fileInfo}) +} + +// HandleServe handles GET /media/*filepath. +// Format is determined by the file extension (.webp or .jpg/.jpeg). +// The optional ?w=<pixels> query param resizes the image (clamped to 3000; error if <= 0). +func (h *MediaHandler) HandleServe(c *gin.Context) { + filename := filepath.Base(strings.TrimPrefix(c.Param("filepath"), "/")) + ext := strings.ToLower(filepath.Ext(filename)) + base := strings.TrimSuffix(filename, filepath.Ext(filename)) + + var format, contentType string + switch ext { + case ".webp": + format, contentType = "webp", "image/webp" + case ".jpg", ".jpeg": + format, contentType = "jpeg", "image/jpeg" + default: + c.AbortWithStatus(http.StatusNotFound) + return + } + + width := 0 + if wStr := c.Query("w"); wStr != "" { + w, err := strconv.Atoi(wStr) + if err != nil || w <= 0 { + c.AbortWithStatus(http.StatusBadRequest) + return + } + if w > maxWidth { + w = maxWidth + } + width = w + } + + cacheName := cacheKey(base, width, format) + cachePath := filepath.Join(h.storageDir, cacheName) + + // Cache hit: serve the existing variant directly + if f, err := os.Open(cachePath); err == nil { + defer f.Close() + info, _ := f.Stat() + c.Header("Content-Type", contentType) + http.ServeContent(c.Writer, c.Request, cacheName, info.ModTime(), f) + return + } + + origPath, err := findOriginal(h.storageDir, base) + if err != nil { + c.AbortWithStatus(http.StatusNotFound) + return + } + + // Skip processing if format matches original and no resize is requested + origExt := strings.ToLower(filepath.Ext(origPath)) + sameFormat := origExt == ext || + (origExt == ".jpg" && ext == ".jpeg") || + (origExt == ".jpeg" && ext == ".jpg") + if width == 0 && sameFormat { + f, err := os.Open(origPath) + if err != nil { + c.AbortWithStatus(http.StatusInternalServerError) + return + } + defer f.Close() + info, _ := f.Stat() + c.Header("Content-Type", contentType) + http.ServeContent(c.Writer, c.Request, filename, info.ModTime(), f) + return + } + + result, err := ConvertAndResize(origPath, width, format) + if err != nil { + c.AbortWithStatus(http.StatusInternalServerError) + return + } + + // Atomic cache write — concurrent writers are harmless (last rename wins) + tmp := cachePath + ".tmp" + if err := os.WriteFile(tmp, result, 0644); err == nil { + _ = os.Rename(tmp, cachePath) + } + + c.Header("Content-Type", contentType) + c.Data(http.StatusOK, contentType, result) +} + +// cacheKey returns the filename for a processed image variant. +// - No width: "<base>.webp" / "<base>.jpg" +// - With width: "<base>_<width>w.webp" / "<base>_<width>w.jpg" +func cacheKey(base string, width int, format string) string { + ext := ".webp" + if format == "jpeg" { + ext = ".jpg" + } + if width > 0 { + return fmt.Sprintf("%s_%dw%s", base, width, ext) + } + return base + ext +} + +// findOriginal finds the original upload file for a given UUID base name. +// Cache variants (which contain '_' before the extension) are excluded. +func findOriginal(dir, base string) (string, error) { + matches, err := filepath.Glob(filepath.Join(dir, base+".*")) + if err != nil { + return "", err + } + for _, m := range matches { + name := filepath.Base(m) + nameBase := strings.TrimSuffix(name, filepath.Ext(name)) + if !strings.Contains(nameBase, "_") { + return m, nil + } + } + return "", fmt.Errorf("original not found for %q in %s", base, dir) +} diff --git a/internal/media/handler_test.go b/internal/media/handler_test.go new file mode 100644 index 0000000..d65544c --- /dev/null +++ b/internal/media/handler_test.go @@ -0,0 +1,198 @@ +package media + +import ( + "bytes" + "encoding/json" + "image" + "image/jpeg" + "mime/multipart" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "testing" + + "github.com/gin-gonic/gin" +) + +func init() { + gin.SetMode(gin.TestMode) +} + +func newTestHandler(t *testing.T) (*MediaHandler, string) { + t.Helper() + dir := t.TempDir() + return NewMediaHandler(dir), dir +} + +func testJPEGBytes(t *testing.T) []byte { + t.Helper() + img := image.NewRGBA(image.Rect(0, 0, 100, 60)) + var buf bytes.Buffer + if err := jpeg.Encode(&buf, img, nil); err != nil { + t.Fatalf("encode jpeg: %v", err) + } + return buf.Bytes() +} + +func multipartUpload(t *testing.T, field, filename string, data []byte) *http.Request { + t.Helper() + var body bytes.Buffer + w := multipart.NewWriter(&body) + fw, err := w.CreateFormFile(field, filename) + if err != nil { + t.Fatalf("create form file: %v", err) + } + fw.Write(data) + w.Close() + req, _ := http.NewRequest(http.MethodPost, "/admin/upload/image", &body) + req.Header.Set("Content-Type", w.FormDataContentType()) + return req +} + +// TestUploadValid verifies a valid JPEG upload returns success with a /media/ URL. +func TestUploadValid(t *testing.T) { + h, dir := newTestHandler(t) + rr := httptest.NewRecorder() + c, _ := gin.CreateTestContext(rr) + c.Request = multipartUpload(t, "image", "photo.jpg", testJPEGBytes(t)) + + h.HandleUpload(c) + + if rr.Code != http.StatusOK { + t.Fatalf("expected 200, got %d: %s", rr.Code, rr.Body.String()) + } + var resp map[string]interface{} + if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil { + t.Fatalf("parse response: %v", err) + } + if resp["success"] != float64(1) { + t.Fatalf("expected success=1, got %v", resp) + } + file, ok := resp["file"].(map[string]interface{}) + if !ok { + t.Fatal("expected file object in response") + } + url, _ := file["url"].(string) + if len(url) < 7 || url[:7] != "/media/" { + t.Fatalf("expected /media/ URL, got %q", url) + } + entries, _ := os.ReadDir(dir) + if len(entries) == 0 { + t.Fatal("expected original to be written to storage dir") + } +} + +// TestUploadInvalidMIME rejects non-image content. +func TestUploadInvalidMIME(t *testing.T) { + h, _ := newTestHandler(t) + rr := httptest.NewRecorder() + c, _ := gin.CreateTestContext(rr) + c.Request = multipartUpload(t, "image", "evil.txt", []byte("not an image at all")) + + h.HandleUpload(c) + + if rr.Code != http.StatusBadRequest { + t.Fatalf("expected 400, got %d", rr.Code) + } +} + +// TestUploadWrongField rejects request missing the "image" field. +func TestUploadWrongField(t *testing.T) { + h, _ := newTestHandler(t) + rr := httptest.NewRecorder() + c, _ := gin.CreateTestContext(rr) + c.Request = multipartUpload(t, "file", "photo.jpg", testJPEGBytes(t)) + + h.HandleUpload(c) + + if rr.Code != http.StatusBadRequest { + t.Fatalf("expected 400, got %d", rr.Code) + } +} + +// TestServeCacheHit serves a pre-existing cache file directly. +func TestServeCacheHit(t *testing.T) { + h, dir := newTestHandler(t) + if err := os.WriteFile(filepath.Join(dir, "abc123.webp"), []byte("FAKE_WEBP"), 0644); err != nil { + t.Fatal(err) + } + rr := httptest.NewRecorder() + c, _ := gin.CreateTestContext(rr) + c.Request = httptest.NewRequest(http.MethodGet, "/media/abc123.webp", nil) + c.Params = gin.Params{{Key: "filepath", Value: "/abc123.webp"}} + + h.HandleServe(c) + + if rr.Code != http.StatusOK { + t.Fatalf("expected 200, got %d", rr.Code) + } + if ct := rr.Header().Get("Content-Type"); ct != "image/webp" { + t.Fatalf("expected image/webp, got %q", ct) + } +} + +// TestServeNotFound returns 404 when no original exists. +func TestServeNotFound(t *testing.T) { + h, _ := newTestHandler(t) + rr := httptest.NewRecorder() + c, _ := gin.CreateTestContext(rr) + c.Request = httptest.NewRequest(http.MethodGet, "/media/missing.webp", nil) + c.Params = gin.Params{{Key: "filepath", Value: "/missing.webp"}} + + h.HandleServe(c) + + if rr.Code != http.StatusNotFound { + t.Fatalf("expected 404, got %d", rr.Code) + } +} + +// TestServeInvalidWidth returns 400 for non-positive width. +func TestServeInvalidWidth(t *testing.T) { + h, _ := newTestHandler(t) + rr := httptest.NewRecorder() + c, _ := gin.CreateTestContext(rr) + c.Request = httptest.NewRequest(http.MethodGet, "/media/abc.webp?w=0", nil) + c.Params = gin.Params{{Key: "filepath", Value: "/abc.webp"}} + + h.HandleServe(c) + + if rr.Code != http.StatusBadRequest { + t.Fatalf("expected 400, got %d", rr.Code) + } +} + +// TestServeUnknownFormat returns 404 for unrecognised extensions. +func TestServeUnknownFormat(t *testing.T) { + h, _ := newTestHandler(t) + rr := httptest.NewRecorder() + c, _ := gin.CreateTestContext(rr) + c.Request = httptest.NewRequest(http.MethodGet, "/media/abc.png", nil) + c.Params = gin.Params{{Key: "filepath", Value: "/abc.png"}} + + h.HandleServe(c) + + if rr.Code != http.StatusNotFound { + t.Fatalf("expected 404, got %d", rr.Code) + } +} + +// TestCacheKey verifies the cache filename scheme. +func TestCacheKey(t *testing.T) { + cases := []struct { + base, format string + width int + want string + }{ + {"uuid1", "webp", 0, "uuid1.webp"}, + {"uuid1", "webp", 800, "uuid1_800w.webp"}, + {"uuid1", "jpeg", 0, "uuid1.jpg"}, + {"uuid1", "jpeg", 400, "uuid1_400w.jpg"}, + } + for _, tc := range cases { + got := cacheKey(tc.base, tc.width, tc.format) + if got != tc.want { + t.Errorf("cacheKey(%q, %d, %q) = %q, want %q", tc.base, tc.width, tc.format, got, tc.want) + } + } +} diff --git a/internal/media/process.go b/internal/media/process.go new file mode 100644 index 0000000..b295380 --- /dev/null +++ b/internal/media/process.go @@ -0,0 +1,82 @@ +package media + +import ( + "bytes" + "encoding/base64" + "fmt" + "image/png" + + "github.com/davidbyttow/govips/v2/vips" + thumbhash "go.n16f.net/thumbhash" +) + +// ImageInfo returns the pixel dimensions and a base64-encoded thumbhash for +// the image at path. The thumbhash is generated from a ≤100px thumbnail so +// it is fast regardless of the original size. +func ImageInfo(path string) (width, height int, thumbhashB64 string, err error) { + img, err := vips.NewImageFromFile(path) + if err != nil { + return 0, 0, "", fmt.Errorf("load %s: %w", path, err) + } + defer img.Close() + + width = img.Width() + height = img.Height() + + const maxThumbDim = 100 + if width > maxThumbDim || height > maxThumbDim { + scale := float64(maxThumbDim) / float64(max(width, height)) + if err := img.Resize(scale, vips.KernelLanczos3); err != nil { + return width, height, "", fmt.Errorf("resize for thumbhash: %w", err) + } + } + + pngBytes, _, err := img.ExportPng(vips.NewPngExportParams()) + if err != nil { + return width, height, "", fmt.Errorf("export png for thumbhash: %w", err) + } + + goImg, err := png.Decode(bytes.NewReader(pngBytes)) + if err != nil { + return width, height, "", fmt.Errorf("decode png for thumbhash: %w", err) + } + + hash := thumbhash.EncodeImage(goImg) + return width, height, base64.StdEncoding.EncodeToString(hash), nil +} + +// ConvertAndResize loads the image at src, optionally resizes it to width pixels +// wide (maintaining aspect ratio), and exports it in the requested format. +// +// width <= 0 means no resize. format must be "webp" or "jpeg". +func ConvertAndResize(src string, width int, format string) ([]byte, error) { + img, err := vips.NewImageFromFile(src) + if err != nil { + return nil, fmt.Errorf("load image %s: %w", src, err) + } + defer img.Close() + + if width > 0 && width < img.Width() { + scale := float64(width) / float64(img.Width()) + if err := img.Resize(scale, vips.KernelLanczos3); err != nil { + return nil, fmt.Errorf("resize: %w", err) + } + } + + switch format { + case "webp": + buf, _, err := img.ExportWebp(vips.NewWebpExportParams()) + if err != nil { + return nil, fmt.Errorf("export webp: %w", err) + } + return buf, nil + case "jpeg": + buf, _, err := img.ExportJpeg(vips.NewJpegExportParams()) + if err != nil { + return nil, fmt.Errorf("export jpeg: %w", err) + } + return buf, nil + default: + return nil, fmt.Errorf("unsupported format %q: must be webp or jpeg", format) + } +} diff --git a/internal/media/process_test.go b/internal/media/process_test.go new file mode 100644 index 0000000..da9d4a8 --- /dev/null +++ b/internal/media/process_test.go @@ -0,0 +1,78 @@ +package media + +import ( + "image" + "image/png" + "os" + "path/filepath" + "testing" + + "github.com/davidbyttow/govips/v2/vips" +) + +func TestMain(m *testing.M) { + vips.Startup(nil) + code := m.Run() + vips.Shutdown() + os.Exit(code) +} + +// testPNG writes a 100×60 PNG to a temp file and returns its path. +func testPNG(t *testing.T) string { + t.Helper() + img := image.NewRGBA(image.Rect(0, 0, 100, 60)) + path := filepath.Join(t.TempDir(), "src.png") + f, err := os.Create(path) + if err != nil { + t.Fatalf("create test png: %v", err) + } + if err := png.Encode(f, img); err != nil { + t.Fatalf("encode test png: %v", err) + } + f.Close() + return path +} + +func TestConvertToWebP(t *testing.T) { + out, err := ConvertAndResize(testPNG(t), 0, "webp") + if err != nil { + t.Fatalf("ConvertAndResize: %v", err) + } + if len(out) == 0 { + t.Fatal("expected non-empty output") + } +} + +func TestConvertToJPEG(t *testing.T) { + out, err := ConvertAndResize(testPNG(t), 0, "jpeg") + if err != nil { + t.Fatalf("ConvertAndResize: %v", err) + } + if len(out) == 0 { + t.Fatal("expected non-empty output") + } +} + +func TestResizeAndConvert(t *testing.T) { + out, err := ConvertAndResize(testPNG(t), 50, "webp") + if err != nil { + t.Fatalf("ConvertAndResize resize: %v", err) + } + if len(out) == 0 { + t.Fatal("expected non-empty output") + } +} + +func TestConvertUnsupportedFormat(t *testing.T) { + _, err := ConvertAndResize(testPNG(t), 0, "avif") + if err == nil { + t.Fatal("expected error for unsupported format") + } +} + +func TestConvertMissingFile(t *testing.T) { + _, err := ConvertAndResize("/nonexistent/file.png", 0, "webp") + if err == nil { + t.Fatal("expected error for missing file") + } +} diff --git a/internal/server/adminserver.go b/internal/server/adminserver.go new file mode 100644 index 0000000..6b46bf3 --- /dev/null +++ b/internal/server/adminserver.go @@ -0,0 +1,664 @@ +// Package admin provides an HTTP server for managing posts via a web UI. +// Admin pages are served dynamically and are never written to the static +// output directory — they are intentionally excluded from the site generator. +package server + +import ( + "bytes" + "encoding/json" + "fmt" + "html/template" + "io" + "io/fs" + "net/http" + "os" + "path/filepath" + "regexp" + "strings" + "sync/atomic" + "time" + + "github.com/gin-gonic/gin" + "github.com/gosimple/slug" + + auth "iblog/internal" + "iblog/internal/db" +) + +// Server is an http.Handler that serves the admin post-management UI. +type Server struct { + // AuthFile is the path to the htpasswd-compatible passwords file. + AuthFile string + // DB provides access to all post records and search indexing + DB *db.DB + // PublicDir is the directory where cache files are stored + PublicDir string + // DataDir is the writable data directory (parent of AuthFile) + DataDir string + + adminAssets fs.FS + siteAssets fs.FS + configured atomic.Bool + + engine *gin.Engine +} + +// Post holds the metadata and content for form display and submission. +type Post struct { + Slug string + Id string + Title string + Date string + Tags []string + Draft bool + Blocks string // raw EditorJS JSON +} + +// NewAdminServer creates a new admin server with Gin routing and auth middleware. +// tmpl must be provided before any routes are registered so SetHTMLTemplate is +// called at the right time (Gin requires this to be thread-safe). +func NewAdminServer(authFile string, database *db.DB, publicDir string, adminAssets, siteAssets fs.FS, tmpl *template.Template) *Server { + s := &Server{ + AuthFile: authFile, + DB: database, + PublicDir: publicDir, + DataDir: filepath.Dir(authFile), + adminAssets: adminAssets, + siteAssets: siteAssets, + } + s.configured.Store(auth.NewAuth(authFile).IsConfigured()) + + s.engine = gin.Default() + s.engine.SetTrustedProxies(nil) + s.engine.SetHTMLTemplate(tmpl) + + // Redirect to /setup when the site has not been configured yet. + // Skip the setup route itself and all asset paths so the form loads properly. + s.engine.Use(func(c *gin.Context) { + if !s.configured.Load() { + p := c.Request.URL.Path + if p != "/setup" && !strings.HasPrefix(p, "/assets/") { + c.Redirect(http.StatusFound, "/setup") + c.Abort() + return + } + } + c.Next() + }) + + // Setup routes — no auth, only accessible before the site is configured. + s.engine.GET("/setup", s.handleSetupGet) + s.engine.POST("/setup", s.handleSetupPost) + + // Silent auth probe: returns 200 if authenticated, 403 if not. + // Must not send WWW-Authenticate so the browser never shows a login dialog. + s.engine.GET("/admin/ping", func(c *gin.Context) { + if s.AuthFile == "" { + c.AbortWithStatus(http.StatusForbidden) + return + } + username, password, ok := c.Request.BasicAuth() + if !ok { + c.AbortWithStatus(http.StatusForbidden) + return + } + a := auth.NewAuth(s.AuthFile) + valid, err := a.Verify(username, password) + if err != nil || !valid { + c.AbortWithStatus(http.StatusForbidden) + return + } + c.Status(http.StatusOK) + }) + + // Admin post routes (protected) + admin := s.engine.Group("/admin") + admin.Use(s.authMiddleware()) + { + admin.GET("/", s.handleList) + admin.GET("/settings", s.handleSettingsGet) + admin.POST("/settings", s.handleSettingsPost) + admin.GET("/new", s.handleNew) + admin.POST("/", s.handleNewPost) + admin.GET("/:slug", s.handleEdit) + admin.POST("/:slug", s.handleEdit) + admin.DELETE("/:slug", s.handleDelete) + } + + // Asset serving: /assets/admin/* requires auth and is served from embedded adminAssets. + // All other /assets/* are public and served from embedded siteAssets. + { + adminMW := s.authMiddleware() + adminHandler := EmbedHandler(s.adminAssets, "assets") + siteHandler := EmbedHandler(s.siteAssets, "assets") + s.engine.GET("/assets/*filepath", func(c *gin.Context) { + fp := c.Param("filepath") + if strings.HasPrefix(fp, "/admin") { + adminMW(c) + if c.IsAborted() { + return + } + adminHandler(c) + return + } + // Logo override: serve user-uploaded logo before falling back to embedded. + if fp == "/static/image.png" { + s.handleLogo(c) + return + } + siteHandler(c) + }) + } + + return s +} + +func (s *Server) Engine() *gin.Engine { + return s.engine +} + +// RegisterUploadRoute registers handler under POST /admin/upload/image +// behind the admin Basic Auth middleware. +func (s *Server) RegisterUploadRoute(handler gin.HandlerFunc) { + admin := s.engine.Group("/admin") + admin.Use(s.authMiddleware()) + admin.POST("/upload/image", handler) +} + +func (s *Server) authMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + if s.AuthFile == "" { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + if _, err := os.Stat(s.AuthFile); os.IsNotExist(err) { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + username, password, ok := c.Request.BasicAuth() + if !ok { + c.Header("WWW-Authenticate", `Basic realm="Admin"`) + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + a := auth.NewAuth(s.AuthFile) + valid, err := a.Verify(username, password) + + if err != nil || !valid { + c.Header("WWW-Authenticate", `Basic realm="Admin"`) + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + c.Next() + } +} + +func (s *Server) handleList(c *gin.Context) { + posts, err := s.DB.ListPosts(true) // includeDrafts=true for admin view + if err != nil { + c.HTML(http.StatusInternalServerError, "base", gin.H{ + "Title": "Error", + "ContentTemplate": "error-content", + "Message": "Failed to list posts: " + err.Error(), + }) + return + } + + // Convert PostRecord to Post for template + var formPosts []Post + for _, p := range posts { + formPosts = append(formPosts, Post{ + Slug: p.Slug, + Title: p.Title, + Date: p.Date, + Tags: p.Tags, + Draft: p.Draft, + Blocks: p.Blocks, + }) + } + + c.HTML(http.StatusOK, "base", gin.H{ + "Title": "Posts", + "ContentTemplate": "list-content", + "Posts": formPosts, + }) +} + +func (s *Server) handleNew(c *gin.Context) { + c.HTML(http.StatusOK, "base", gin.H{ + "Title": "New Post", + "ContentTemplate": "form-content", + "Action": "/admin/", + "Post": Post{ + Date: time.Now().Format("2006-01-02"), + Blocks: "[]", + }, + "IsNew": true, + }) +} + +func (s *Server) handleNewPost(c *gin.Context) { + p := postFromForm(c) + if p.Title == "" { + s.renderError(c, "Title is required") + return + } + + if p.Slug == "" { + p.Slug = slugify(p.Title) + } + if !validSlug.MatchString(p.Slug) { + s.renderError(c, fmt.Sprintf("Invalid slug %q: use lowercase letters, numbers, and hyphens only", p.Slug)) + return + } + + // Check if post already exists + existing, err := s.DB.GetPostBySlug(p.Slug) + if err == nil && existing != nil { + s.renderError(c, fmt.Sprintf("Post %q already exists", p.Slug)) + return + } + + // Prepare post record with current timestamp + record := db.PostRecord{ + Slug: p.Slug, + Title: p.Title, + Date: p.Date, + Tags: p.Tags, + Draft: p.Draft, + Blocks: p.Blocks, + UpdatedAt: time.Now().UnixMicro(), + } + + // Upsert post + if err := s.DB.UpsertPost(record); err != nil { + c.HTML(http.StatusInternalServerError, "base", gin.H{ + "Title": "Error", + "ContentTemplate": "error-content", + "Message": err.Error(), + }) + return + } + + // Index in search database + plainText := extractPlainTextFromEditorJS(p.Blocks) + _ = s.DB.IndexPage(db.SearchPage{ + Path: "/" + p.Slug, + Title: p.Title, + Content: plainText, + }) + + c.Redirect(http.StatusSeeOther, "/admin/") +} + +func (s *Server) handleEdit(c *gin.Context) { + slug := c.Param("slug") + rec, err := s.DB.GetPostBySlug(slug) + if err != nil { + c.HTML(http.StatusNotFound, "base", gin.H{ + "Title": "Not Found", + "ContentTemplate": "error-content", + "Message": "Post not found", + }) + return + } + + p := Post{ + Slug: rec.Slug, + Title: rec.Title, + Date: rec.Date, + Tags: rec.Tags, + Draft: rec.Draft, + Blocks: rec.Blocks, + } + + if c.Request.Method == http.MethodPost { + updated := postFromForm(c) + if updated.Title == "" { + s.renderError(c, "Title is required") + return + } + + targetSlug := slug // default: slug unchanged + + record := db.PostRecord{ + Slug: targetSlug, + Title: updated.Title, + Date: updated.Date, + Tags: updated.Tags, + Draft: updated.Draft, + Blocks: updated.Blocks, + UpdatedAt: time.Now().UnixMicro(), + } + + if updated.Slug != "" && updated.Slug != slug { + // Slug rename requested + if !validSlug.MatchString(updated.Slug) { + s.renderError(c, fmt.Sprintf("Invalid slug %q: use lowercase letters, numbers, and hyphens only", updated.Slug)) + return + } + // Check target slug is not already taken + if existing, err := s.DB.GetPostBySlug(updated.Slug); err == nil && existing != nil { + s.renderError(c, fmt.Sprintf("Post %q already exists", updated.Slug)) + return + } + // Atomically rename and update content in one transaction + record.Slug = updated.Slug + if err := s.DB.RenameAndUpsertPost(slug, record); err != nil { + s.renderError(c, "Failed to rename post: "+err.Error()) + return + } + // Invalidate old cache and remove old search entry + s.invalidatePostCache(slug) + _ = s.DB.UnindexPage("/" + slug) + targetSlug = updated.Slug + } else { + if err := s.DB.UpsertPost(record); err != nil { + c.HTML(http.StatusInternalServerError, "base", gin.H{ + "Title": "Error", + "ContentTemplate": "error-content", + "Message": err.Error(), + }) + return + } + } + + // Invalidate cache for the target slug + s.invalidatePostCache(targetSlug) + + // Re-index with target slug + plainText := extractPlainTextFromEditorJS(updated.Blocks) + _ = s.DB.IndexPage(db.SearchPage{ + Path: "/" + targetSlug, + Title: updated.Title, + Content: plainText, + }) + + c.Redirect(http.StatusSeeOther, "/admin/") + return + } + + // GET: render form + c.HTML(http.StatusOK, "base", gin.H{ + "Title": "Edit Post", + "ContentTemplate": "form-content", + "Action": "/admin/" + slug, + "Post": p, + "IsNew": false, + }) +} + +func (s *Server) handleDelete(c *gin.Context) { + slug := c.Param("slug") + if err := s.DB.DeletePostBySlug(slug); err != nil { + c.HTML(http.StatusInternalServerError, "base", gin.H{ + "Title": "Error", + "ContentTemplate": "error-content", + "Message": err.Error(), + }) + return + } + + // Invalidate cache files + s.invalidatePostCache(slug) + + // Remove from search index + _ = s.DB.UnindexPage("/" + slug) + + c.Redirect(http.StatusSeeOther, "/admin/") +} + +func (s *Server) handleSetupGet(c *gin.Context) { + if s.configured.Load() { + c.Redirect(http.StatusFound, "/admin/") + return + } + c.HTML(http.StatusOK, "setup.html", gin.H{}) +} + +func (s *Server) handleSetupPost(c *gin.Context) { + if s.configured.Load() { + c.Redirect(http.StatusFound, "/admin/") + return + } + + username := strings.TrimSpace(c.PostForm("username")) + password := c.PostForm("password") + confirm := c.PostForm("confirm") + siteTitle := strings.TrimSpace(c.PostForm("site_title")) + siteDesc := strings.TrimSpace(c.PostForm("site_description")) + + redisplay := func(msg string) { + c.HTML(http.StatusBadRequest, "setup.html", gin.H{ + "Error": msg, + "Username": username, + "SiteTitle": siteTitle, + "SiteDescription": siteDesc, + }) + } + + if username == "" { + redisplay("Username is required") + return + } + if password == "" { + redisplay("Password is required") + return + } + if password != confirm { + redisplay("Passwords do not match") + return + } + + // Create admin user + a := auth.NewAuth(s.AuthFile) + if err := a.AddUserWithPassword(username, password); err != nil { + redisplay("Failed to create user: " + err.Error()) + return + } + + // Save optional site settings + if siteTitle != "" { + _ = s.DB.SetSetting("site_title", siteTitle) + } + if siteDesc != "" { + _ = s.DB.SetSetting("site_description", siteDesc) + } + + // Save optional logo (ignore errors — setup can proceed without it) + _ = s.saveLogoUpload(c) + + s.configured.Store(true) + c.Redirect(http.StatusSeeOther, "/admin/") +} + +func (s *Server) handleSettingsGet(c *gin.Context) { + title, _ := s.DB.GetSetting("site_title") + desc, _ := s.DB.GetSetting("site_description") + c.HTML(http.StatusOK, "base", gin.H{ + "Title": "Settings", + "ContentTemplate": "settings-content", + "SiteTitle": title, + "SiteDescription": desc, + }) +} + +func (s *Server) handleSettingsPost(c *gin.Context) { + siteTitle := strings.TrimSpace(c.PostForm("site_title")) + siteDesc := strings.TrimSpace(c.PostForm("site_description")) + + _ = s.DB.SetSetting("site_title", siteTitle) + _ = s.DB.SetSetting("site_description", siteDesc) + + if err := s.saveLogoUpload(c); err != nil { + title, _ := s.DB.GetSetting("site_title") + desc, _ := s.DB.GetSetting("site_description") + c.HTML(http.StatusBadRequest, "base", gin.H{ + "Title": "Settings", + "ContentTemplate": "settings-content", + "Error": err.Error(), + "SiteTitle": title, + "SiteDescription": desc, + }) + return + } + + title, _ := s.DB.GetSetting("site_title") + desc, _ := s.DB.GetSetting("site_description") + c.HTML(http.StatusOK, "base", gin.H{ + "Title": "Settings", + "ContentTemplate": "settings-content", + "Success": true, + "SiteTitle": title, + "SiteDescription": desc, + }) +} + +// saveLogoUpload saves an uploaded logo file to DataDir if one was provided. +// Returns nil if no file was uploaded (blank is not an error). +func (s *Server) saveLogoUpload(c *gin.Context) error { + file, _, err := c.Request.FormFile("logo") + if err != nil { + return nil // no file — not an error + } + defer file.Close() + + sniff := make([]byte, 512) + n, _ := file.Read(sniff) + mimeType := http.DetectContentType(sniff[:n]) + extMap := map[string]string{ + "image/jpeg": ".jpg", + "image/png": ".png", + "image/webp": ".webp", + } + ext, ok := extMap[mimeType] + if !ok { + return fmt.Errorf("unsupported image type: %s", mimeType) + } + + logoPath := filepath.Join(s.DataDir, "logo"+ext) + out, err := os.Create(logoPath) + if err != nil { + return fmt.Errorf("could not save logo: %w", err) + } + defer out.Close() + _, err = io.Copy(out, io.MultiReader(bytes.NewReader(sniff[:n]), file)) + return err +} + +func (s *Server) handleLogo(c *gin.Context) { + extTypes := map[string]string{ + ".png": "image/png", + ".jpg": "image/jpeg", + ".webp": "image/webp", + } + for ext, ct := range extTypes { + p := filepath.Join(s.DataDir, "logo"+ext) + if f, err := os.Open(p); err == nil { + defer f.Close() + info, _ := f.Stat() + c.Header("Content-Type", ct) + http.ServeContent(c.Writer, c.Request, "image"+ext, info.ModTime(), f) + return + } + } + // Fall back to embedded default + data, err := fs.ReadFile(s.siteAssets, "assets/static/image.png") + if err != nil { + c.AbortWithStatus(http.StatusNotFound) + return + } + c.Data(http.StatusOK, "image/png", data) +} + +// invalidatePostCache removes cache files for a post to force regeneration. +func (s *Server) invalidatePostCache(slug string) { + htmlCache := filepath.Join(s.PublicDir, slug+".html") + jsonCache := filepath.Join(s.PublicDir, slug+".json") + _ = os.Remove(htmlCache) + _ = os.Remove(jsonCache) +} + +func (s *Server) renderError(c *gin.Context, msg string) { + c.HTML(http.StatusBadRequest, "base", gin.H{ + "Title": "Error", + "ContentTemplate": "error-content", + "Message": msg, + }) +} + +func postFromForm(c *gin.Context) Post { + tagsStr := strings.TrimSpace(c.PostForm("tags")) + var tags []string + if tagsStr != "" { + for _, t := range strings.Split(tagsStr, ",") { + t = strings.TrimSpace(t) + if t != "" { + tags = append(tags, t) + } + } + } + + draft := c.PostForm("draft") != "" + + return Post{ + Slug: strings.TrimSpace(c.PostForm("slug")), + Title: strings.TrimSpace(c.PostForm("title")), + Date: strings.TrimSpace(c.PostForm("date")), + Tags: tags, + Blocks: c.PostForm("blocks"), + Draft: draft, + } +} + +// slugify converts a title to a URL-safe slug. +var validSlug = regexp.MustCompile(`^[a-z0-9]+(?:-[a-z0-9]+)*$`) + +func slugify(title string) string { + s := slug.Make(title) + if s == "" { + s = fmt.Sprintf("post-%d", time.Now().Unix()) + } + return s +} + +// extractPlainTextFromEditorJS extracts plain text from EditorJS blocks for indexing. +// It's a simple extraction that pulls text from paragraph and header blocks. +func extractPlainTextFromEditorJS(blocksJSON string) string { + if blocksJSON == "" || blocksJSON == "[]" { + return "" + } + + type block struct { + Type string `json:"type"` + Data json.RawMessage `json:"data"` + } + + type doc struct { + Blocks []block `json:"blocks"` + } + + var d doc + if err := json.Unmarshal([]byte(blocksJSON), &d); err != nil { + return "" + } + + var texts []string + for _, b := range d.Blocks { + switch b.Type { + case "paragraph", "header": + var data struct { + Text string `json:"text"` + } + if err := json.Unmarshal(b.Data, &data); err == nil && data.Text != "" { + texts = append(texts, data.Text) + } + } + } + + return strings.Join(texts, " ") +} diff --git a/internal/server/fileserver.go b/internal/server/fileserver.go new file mode 100644 index 0000000..d231519 --- /dev/null +++ b/internal/server/fileserver.go @@ -0,0 +1,111 @@ +package server + +import ( + "io/fs" + "mime" + "net/http" + "os" + "path" + "path/filepath" + "strings" + + "github.com/gin-gonic/gin" +) + +// AllowedMIMEs maps file extensions (including dot) to Content-Type values. +// A nil map means any extension is allowed; MIME type is detected automatically. +type AllowedMIMEs map[string]string + +// FileHandler returns a Gin HandlerFunc that serves files from the given root directory. +// The wildcard param name is *filepath. +func FileHandler(root string, allowed AllowedMIMEs) gin.HandlerFunc { + return func(c *gin.Context) { + filepath := c.Param("filepath") + serveFile(c, root, filepath, allowed) + } +} + +// PublicFileHandler returns a Gin HandlerFunc that serves files using the request URL path. +// Used for NoRoute fallback to serve public static files. +func PublicFileHandler(root string, allowed AllowedMIMEs) gin.HandlerFunc { + return func(c *gin.Context) { + filepath := c.Request.URL.Path + serveFile(c, root, filepath, allowed) + } +} + +func serveFile(c *gin.Context, root, requestPath string, allowed AllowedMIMEs) { + // Clean the path to prevent directory traversal + clean := path.Clean(requestPath) + if strings.Contains(clean, "..") { + c.AbortWithStatus(http.StatusNotFound) + return + } + + // Remove leading slash for filepath.Join + clean = strings.TrimPrefix(clean, "/") + + // Map empty path or directory to index.html (clean URLs) + if clean == "" || clean == "." { + clean = "index.html" + } + + fullPath := filepath.Join(root, clean) + + // Check file existence; if directory, try index.html inside it + info, err := os.Stat(fullPath) + if err == nil && info.IsDir() { + fullPath = filepath.Join(fullPath, "index.html") + info, err = os.Stat(fullPath) + } + if err != nil { + // Try appending .html for clean URLs (e.g. /about -> /about.html) + htmlPath := fullPath + ".html" + if info2, err2 := os.Stat(htmlPath); err2 == nil && !info2.IsDir() { + fullPath = htmlPath + info = info2 + err = nil + } + } + if err != nil || info.IsDir() { + c.AbortWithStatus(http.StatusNotFound) + return + } + + // Determine MIME type + ext := filepath.Ext(fullPath) + var mimeType string + if allowed != nil { + var ok bool + mimeType, ok = allowed[ext] + if !ok { + c.AbortWithStatus(http.StatusNotFound) + return + } + } else { + mimeType = mime.TypeByExtension(ext) + if mimeType == "" { + mimeType = "application/octet-stream" + } + } + + // Serve the file with proper MIME type + c.Header("Content-Type", mimeType) + c.File(fullPath) +} + +// EmbedHandler serves files from an embedded (or any) fs.FS. +// root is the subdirectory within fsys to serve from. +// The gin wildcard param must be named *filepath. +func EmbedHandler(fsys fs.FS, root string) gin.HandlerFunc { + sub, err := fs.Sub(fsys, root) + if err != nil { + panic("EmbedHandler: " + err.Error()) + } + fileServer := http.FileServer(http.FS(sub)) + return func(c *gin.Context) { + req := c.Request.Clone(c.Request.Context()) + req.URL.Path = c.Param("filepath") + fileServer.ServeHTTP(c.Writer, req) + } +} diff --git a/internal/server/fileserver_test.go b/internal/server/fileserver_test.go new file mode 100644 index 0000000..a6ef926 --- /dev/null +++ b/internal/server/fileserver_test.go @@ -0,0 +1,44 @@ +package server_test + +import ( + "net/http" + "net/http/httptest" + "testing" + "testing/fstest" + + "github.com/gin-gonic/gin" + "iblog/internal/server" +) + +func init() { gin.SetMode(gin.TestMode) } + +func TestEmbedHandler_ServesFile(t *testing.T) { + fsys := fstest.MapFS{ + "root/hello.js": {Data: []byte(`console.log("hi")`)}, + } + r := gin.New() + r.GET("/assets/*filepath", server.EmbedHandler(fsys, "root")) + + w := httptest.NewRecorder() + r.ServeHTTP(w, httptest.NewRequest("GET", "/assets/hello.js", nil)) + + if w.Code != http.StatusOK { + t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String()) + } + if got := w.Body.String(); got != `console.log("hi")` { + t.Fatalf("unexpected body: %q", got) + } +} + +func TestEmbedHandler_NotFound(t *testing.T) { + fsys := fstest.MapFS{} + r := gin.New() + r.GET("/assets/*filepath", server.EmbedHandler(fsys, "root")) + + w := httptest.NewRecorder() + r.ServeHTTP(w, httptest.NewRequest("GET", "/assets/missing.js", nil)) + + if w.Code != http.StatusNotFound { + t.Fatalf("expected 404, got %d", w.Code) + } +} diff --git a/internal/server/frontpage.go b/internal/server/frontpage.go new file mode 100644 index 0000000..faca83c --- /dev/null +++ b/internal/server/frontpage.go @@ -0,0 +1,87 @@ +package server + +import ( + "net/http" + "strings" + + "iblog/internal/db" + + "github.com/gin-gonic/gin" +) + +type FrontpageHandler struct { + DB *db.DB +} + +type frontpageData struct { + Posts []db.PostRecord + Query string + ActiveTag string + SiteTitle string + SiteDescription string +} + +func NewFrontpageHandler(database *db.DB) *FrontpageHandler { + return &FrontpageHandler{DB: database} +} + +func (h *FrontpageHandler) Serve(c *gin.Context) { + query := strings.TrimSpace(c.Query("q")) + tag := strings.TrimSpace(c.Query("tag")) + + title, _ := h.DB.GetSetting("site_title") + desc, _ := h.DB.GetSetting("site_description") + + data := frontpageData{ + Query: query, + ActiveTag: tag, + SiteTitle: title, + SiteDescription: desc, + } + + if query != "" { + results, err := h.DB.Search(query) + if err == nil { + data.Posts = searchResultsToPosts(results, h.DB) + } + } else { + posts, err := h.DB.ListPosts(false) // exclude drafts + if err == nil { + if tag != "" { + posts = filterByTag(posts, tag) + } + data.Posts = posts + } + } + + c.HTML(http.StatusOK, "index.html", data) +} + +func filterByTag(posts []db.PostRecord, tag string) []db.PostRecord { + var filtered []db.PostRecord + for _, p := range posts { + for _, t := range p.Tags { + if strings.EqualFold(t, tag) { + filtered = append(filtered, p) + break + } + } + } + return filtered +} + +func searchResultsToPosts(results []db.SearchResult, database *db.DB) []db.PostRecord { + var posts []db.PostRecord + for _, r := range results { + slug := strings.TrimPrefix(r.Path, "/") + if slug == r.Path { + continue // not a post + } + post, err := database.GetPostBySlug(slug) + if err != nil || post.Draft { + continue + } + posts = append(posts, *post) + } + return posts +} diff --git a/internal/server/posthandler.go b/internal/server/posthandler.go new file mode 100644 index 0000000..f3a885c --- /dev/null +++ b/internal/server/posthandler.go @@ -0,0 +1,190 @@ +package server + +import ( + "encoding/json" + "fmt" + "html/template" + "net/http" + "os" + "path/filepath" + "strings" + + "iblog/internal/builder" + "iblog/internal/db" + + "github.com/gin-gonic/gin" +) + +type PostHandler struct { + DB *db.DB + Templates *template.Template + PublicDir string +} + +func NewPostHandler(database *db.DB, tmpl *template.Template, publicDir string) *PostHandler { + return &PostHandler{ + DB: database, + Templates: tmpl, + PublicDir: publicDir, + } +} + +// Serve dispatches to ServeHTML or ServeJSON based on the slug extension. +func (h *PostHandler) Serve(c *gin.Context) { + slug := c.Param("slug") + if strings.HasSuffix(slug, ".json") { + h.ServeJSON(c) + return + } + h.ServeHTML(c) +} + +// ServeHTML serves the HTML version of a post. +func (h *PostHandler) ServeHTML(c *gin.Context) { + slug := c.Param("slug") + slug = strings.TrimSuffix(slug, ".html") + + post, err := h.DB.GetPostBySlug(slug) + if err != nil { + if toSlug, rerr := h.DB.GetRedirect(slug); rerr == nil { + c.Redirect(http.StatusMovedPermanently, "/"+toSlug) + return + } + c.AbortWithStatus(http.StatusNotFound) + return + } + + cacheFile := filepath.Join(h.PublicDir, slug+".html") + + // Check cache freshness + if isCacheFresh(cacheFile, post.UpdatedAt) { + if f, err := os.Open(cacheFile); err == nil { + defer f.Close() + info, _ := os.Stat(cacheFile) + c.Header("Content-Type", "text/html; charset=utf-8") + http.ServeContent(c.Writer, c.Request, slug+".html", info.ModTime(), f) + return + } + } + + // Render post + html, err := h.renderPostHTML(post) + if err != nil { + fmt.Fprintf(os.Stderr, "render post %s: %v\n", slug, err) + c.AbortWithStatus(http.StatusInternalServerError) + return + } + + // Atomic write: write to temp file, then rename + if err := os.MkdirAll(h.PublicDir, 0755); err != nil { + c.AbortWithStatus(http.StatusInternalServerError) + return + } + + tmpFile := cacheFile + ".tmp" + if err := os.WriteFile(tmpFile, []byte(html), 0644); err != nil { + c.AbortWithStatus(http.StatusInternalServerError) + return + } + if err := os.Rename(tmpFile, cacheFile); err != nil { + os.Remove(tmpFile) + c.AbortWithStatus(http.StatusInternalServerError) + return + } + + // Serve rendered HTML + c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(html)) +} + +// ServeJSON handles GET /posts/:slug.json and serves the JSON version of a post. +func (h *PostHandler) ServeJSON(c *gin.Context) { + slug := c.Param("slug") + slug = strings.TrimSuffix(slug, ".json") + + post, err := h.DB.GetPostBySlug(slug) + if err != nil { + if toSlug, rerr := h.DB.GetRedirect(slug); rerr == nil { + c.Redirect(http.StatusMovedPermanently, "/"+toSlug+".json") + return + } + c.AbortWithStatus(http.StatusNotFound) + return + } + + cacheFile := filepath.Join(h.PublicDir, slug+".json") + + // Check cache freshness + if isCacheFresh(cacheFile, post.UpdatedAt) { + if f, err := os.Open(cacheFile); err == nil { + defer f.Close() + info, _ := os.Stat(cacheFile) + c.Header("Content-Type", "application/json") + http.ServeContent(c.Writer, c.Request, slug+".json", info.ModTime(), f) + return + } + } + + // Generate JSON representation + jsonData := map[string]interface{}{ + "slug": post.Slug, + "title": post.Title, + "date": post.Date, + "tags": post.Tags, + "blocks": json.RawMessage(post.Blocks), + } + jsonBytes, _ := json.MarshalIndent(jsonData, "", " ") + + // Atomic write + if err := os.MkdirAll(h.PublicDir, 0755); err != nil { + c.AbortWithStatus(http.StatusInternalServerError) + return + } + + tmpFile := cacheFile + ".tmp" + if err := os.WriteFile(tmpFile, jsonBytes, 0644); err != nil { + c.AbortWithStatus(http.StatusInternalServerError) + return + } + if err := os.Rename(tmpFile, cacheFile); err != nil { + os.Remove(tmpFile) + c.AbortWithStatus(http.StatusInternalServerError) + return + } + + // Serve JSON + c.Header("Content-Type", "application/json") + c.Data(http.StatusOK, "application/json", jsonBytes) +} + +// renderPostHTML renders a post to HTML using the base template. +func (h *PostHandler) renderPostHTML(post *db.PostRecord) (string, error) { + htmlBody, scripts, err := builder.RenderEditorJS(post.Blocks) + if err != nil { + return "", fmt.Errorf("render editorjs: %w", err) + } + + pageData := builder.PageData{ + Title: post.Title, + Content: template.HTML(htmlBody), + ComponentScripts: scripts, + Date: post.Date, + Tags: post.Tags, + Path: "/" + post.Slug, + } + + var buf strings.Builder + if err := h.Templates.ExecuteTemplate(&buf, "post.html", pageData); err != nil { + return "", fmt.Errorf("template: %w", err) + } + return buf.String(), nil +} + +// isCacheFresh checks if the cache file is newer than the post's updated_at timestamp. +func isCacheFresh(cacheFile string, updatedAtMicros int64) bool { + info, err := os.Stat(cacheFile) + if err != nil { + return false + } + cacheModTime := info.ModTime().UnixMicro() + return cacheModTime >= updatedAtMicros +} diff --git a/templates/admin/error.html b/templates/admin/error.html new file mode 100644 index 0000000..36a4a8a --- /dev/null +++ b/templates/admin/error.html @@ -0,0 +1,7 @@ +{{define "error-content"}} + <div class="alert"> + <h2>Error</h2> + <p>{{.Message}}</p> + <p><a href="javascript:history.back()">Go back</a></p> + </div> +{{end}} diff --git a/templates/admin/form.html b/templates/admin/form.html new file mode 100644 index 0000000..133951f --- /dev/null +++ b/templates/admin/form.html @@ -0,0 +1,82 @@ +{{define "form-content"}} +<form method="POST" action="{{.Action}}" id="postForm"> + <label for="title">Title</label> + <input type="text" id="title" name="title" value="{{.Post.Title}}" required autofocus> + + {{if not .IsNew}} + <label for="slug">Slug</label> + <input type="text" id="slug" name="slug" value="{{.Post.Slug}}"> + {{end}} + + <label for="date">Date</label> + <input type="date" id="date" name="date" value="{{.Post.Date}}"> + + <label for="tags">Tags</label> + <input type="text" id="tags" name="tags" value="{{range $i, $t := .Post.Tags}}{{if $i}}, {{end}}{{$t}}{{end}}" + placeholder="tag1, tag2, tag3"> + <label for="draft"> + <input type="checkbox" id="draft" name="draft" {{if .Post.Draft}} checked{{end}}> + Draft (not published) + </label> + <button type="submit" class="btn btn-primary"> + {{if .IsNew}}Create Post{{else}}Save Changes{{end}} + </button> + <a href="/admin/" class="btn btn-secondary">Cancel</a> + <div id="editor" style="border: 1px solid #ccc; border-radius: 4px; min-height: 340px;"></div> + <input type="hidden" id="blocks" name="blocks" value="{{.Post.Blocks}}"> +</form> +<script type="module"> + import { EditorJS, Header, Paragraph, List, Code, Quote, ImageTool } from 'editorjs-bundle' + import ComponentTool from '/assets/admin/lib/component-tool.js' + import { qs, on } from 'shared' + + const blocksField = qs('#blocks') + const form = qs('#postForm') + + let editorData = { blocks: [] } + try { + const parsed = JSON.parse(blocksField.value || '[]') + if (Array.isArray(parsed)) { + editorData = { blocks: parsed } + } else if (parsed && typeof parsed === 'object') { + editorData = parsed + } + } catch (e) { + console.error('Failed to parse blocks JSON:', e) + } + + const editor = new EditorJS({ + holder: 'editor', + tools: { + header: Header, + paragraph: Paragraph, + list: List, + code: Code, + quote: Quote, + image: { + class: ImageTool, + config: { + endpoints: { byFile: '/admin/upload/image' }, + }, + }, + component: ComponentTool, + }, + data: editorData, + }) + + // Sync editor content back to hidden field before form submission + on(form, 'submit', async (e) => { + e.preventDefault() + + try { + const saved = await editor.save() + blocksField.value = JSON.stringify(saved) + } catch (err) { + console.error('Failed to save editor data:', err) + return + } + + form.submit() + }) +</script> +{{end}}
\ No newline at end of file diff --git a/templates/admin/index.html b/templates/admin/index.html new file mode 100644 index 0000000..decc138 --- /dev/null +++ b/templates/admin/index.html @@ -0,0 +1,36 @@ +{{define "base"}} +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Admin — {{.Title}}</title> + <link rel="stylesheet" href="/assets/styles/admin.css"> + <script type="importmap"> + { + "imports": { + "editorjs-bundle": "/assets/admin/lib/dist/build.js", + "shared": "/assets/lib/shared.js" + } + } + </script> +</head> + +<body> + <nav> + <a href="/">forside</a> + <a href="/admin">admin</a> + <a href="/admin/new">nytt innlegg</a> + <a href="/admin/settings">innstillinger</a> + </nav> + <main> + {{if eq .ContentTemplate "list-content"}}{{template "list-content" .}}{{end}} + {{if eq .ContentTemplate "form-content"}}{{template "form-content" .}}{{end}} + {{if eq .ContentTemplate "error-content"}}{{template "error-content" .}}{{end}} + {{if eq .ContentTemplate "settings-content"}}{{template "settings-content" .}}{{end}} + </main> +</body> + +</html> +{{end}}
\ No newline at end of file diff --git a/templates/admin/list.html b/templates/admin/list.html new file mode 100644 index 0000000..8e488b6 --- /dev/null +++ b/templates/admin/list.html @@ -0,0 +1,50 @@ +{{define "list-content"}} +{{if .Posts}} +<table> + <thead> + <tr> + <th>Tittel</th> + <th>Dato</th> + <th>Emne</th> + <th>Status</th> + <th></th> + </tr> + </thead> + <tbody> + {{range .Posts}} + <tr> + <td>{{.Title}}</td> + <td>{{.Date}}</td> + <td> + {{if .Tags}} + <div> + {{range .Tags}}<span class="tag">{{.}} </span>{{end}} + </div> + {{end}} + </td> + <td> + {{if .Draft}}<span>Ikke publisert</span>{{else}}<span>Publisert</span>{{end}} + </td> + <td> + <div> + <a href="/admin/{{.Slug}}">Rediger</a> + <button onclick=" deletePost('{{.Slug}}', '{{.Title}}' )">Slett</button> + </div> + </td> + </tr> + {{end}} + </tbody> +</table> +{{else}} +<div>Ingen innlegg</div> +{{end}} + +<script> + function deletePost(slug, title) { + if (!confirm('Slett "' + title + '"?')) return; + fetch('/admin/' + slug, { method: 'DELETE' }) + .then(() => window.location.href = '/admin/') + .catch(err => alert('Kunne ikke slette: ' + err)); + } +</script> +{{end}}
\ No newline at end of file diff --git a/templates/admin/settings.html b/templates/admin/settings.html new file mode 100644 index 0000000..fc42ee2 --- /dev/null +++ b/templates/admin/settings.html @@ -0,0 +1,20 @@ +{{define "settings-content"}} +{{if .Success}}<p class="hint">Settings saved.</p>{{end}} +{{if .Error}}<p style="color:#c0392b">{{.Error}}</p>{{end}} +<form method="POST" action="/admin/settings" enctype="multipart/form-data"> + <label for="site_title">Site title</label> + <input type="text" id="site_title" name="site_title" value="{{.SiteTitle}}" placeholder="My blog"> + + <label for="site_description">Site description</label> + <input type="text" id="site_description" name="site_description" value="{{.SiteDescription}}" placeholder="A short description"> + + <label for="logo">Logo image</label> + <input type="file" id="logo" name="logo" accept="image/png,image/jpeg,image/webp"> + <p class="hint">Leave blank to keep the current logo.</p> + + <div class="form-actions"> + <button type="submit" class="btn btn-primary">Save</button> + <a href="/admin/" class="btn btn-secondary">Cancel</a> + </div> +</form> +{{end}} diff --git a/templates/admin/setup.html b/templates/admin/setup.html new file mode 100644 index 0000000..4294238 --- /dev/null +++ b/templates/admin/setup.html @@ -0,0 +1,47 @@ +{{define "setup.html"}} +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Setup</title> + <link rel="stylesheet" href="/assets/styles/admin.css"> + <style> + body { max-width: 480px; margin: 4rem auto; padding: 0 1rem; } + h1 { margin-bottom: 1.5rem; } + .error { color: #c0392b; margin-bottom: 1rem; } + .hint { font-size: 0.8rem; color: #888; margin: 0.2rem 0 0.8rem; } + .form-actions { margin-top: 1.5rem; } + </style> +</head> +<body> + <h1>Setup</h1> + {{if .Error}}<p class="error">{{.Error}}</p>{{end}} + <form method="POST" action="/setup" enctype="multipart/form-data"> + + <label for="username">Admin username</label> + <input type="text" id="username" name="username" value="{{.Username}}" required autofocus autocomplete="username"> + + <label for="password">Password</label> + <input type="password" id="password" name="password" required autocomplete="new-password"> + + <label for="confirm">Confirm password</label> + <input type="password" id="confirm" name="confirm" required autocomplete="new-password"> + + <label for="site_title">Site title</label> + <input type="text" id="site_title" name="site_title" value="{{.SiteTitle}}" placeholder="My blog"> + + <label for="site_description">Site description</label> + <input type="text" id="site_description" name="site_description" value="{{.SiteDescription}}" placeholder="A short description"> + + <label for="logo">Logo image</label> + <input type="file" id="logo" name="logo" accept="image/png,image/jpeg,image/webp"> + <p class="hint">Shown on the front page. Leave blank to keep the default.</p> + + <div class="form-actions"> + <button type="submit" class="btn btn-primary">Create site</button> + </div> + </form> +</body> +</html> +{{end}} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..00cae5c --- /dev/null +++ b/templates/index.html @@ -0,0 +1,47 @@ +{{define "index.html"}} +<!DOCTYPE html> +<html> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>{{.SiteTitle}}</title> + <link rel="icon" href="/assets/static/image.png" type="image/png"> + <link rel="stylesheet" href="/assets/styles/main.css"> +</head> + +<body> + <main> + <img src="/assets/static/image.png" alt="nebb" id="logo"> + <form class="search" method="GET" action="/"> + <input type="search" name="q" required value="{{.Query}}" placeholder="..."> + <button type="submit">Søk</button> + {{if .Query}}<a href="/">Tøm</a>{{end}} + </form> + + {{if .ActiveTag}} + <p class="filter-info">Emne: <strong>{{.ActiveTag}}</strong> <a href="/">tøm</a></p> + {{end}} + + {{if .Posts}} + <ul class="post-list"> + {{range .Posts}} + <li> + <a href="/{{.Slug}}">{{.Title}}</a> + <time>{{.Date}}</time> + {{if .Tags}} + <div class="tags"> + emne: {{range .Tags}}<a href="/?tag={{.}}" class="tag">{{.}}</a> {{end}} + </div> + {{end}} + </li> + {{end}} + </ul> + {{else}} + <p class="empty">Dessverre.</p> + {{end}} + </main> +</body> + +</html> +{{end}}
\ No newline at end of file diff --git a/templates/post.html b/templates/post.html new file mode 100644 index 0000000..10c51ee --- /dev/null +++ b/templates/post.html @@ -0,0 +1,47 @@ +{{define "post.html"}} +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>{{.Title}}</title> + <link rel="stylesheet" href="/assets/styles/main.css"> + <script type="importmap"> + { + "imports": { + "shared": "/assets/lib/shared.js", + "thumbhash": "/assets/lib/dist/build.js" + } + } + </script> + {{- range .ComponentScripts}} + <script type="module" src="{{.}}"></script> + {{- end}} +</head> + +<body> + <a id="edit-link" href="/admin{{.Path}}" hidden>Edit</a> + <main> + <time>{{.Date}}</time> + {{.Content}} + </main> + <script type="module"> + import { thumbHashToDataURL } from 'thumbhash' + for (const fig of document.querySelectorAll('figure[data-thumbhash]')) { + const bytes = Uint8Array.from(atob(fig.dataset.thumbhash), c => c.charCodeAt(0)) + fig.style.backgroundImage = `url(${thumbHashToDataURL(bytes)})` + fig.style.backgroundSize = 'cover' + const img = fig.querySelector('img') + if (img) img.addEventListener('load', () => { fig.style.backgroundImage = '' }, { once: true }) + } + </script> + <script> + fetch('/admin/ping', {credentials: 'include'}) + .then(r => { if (r.ok) document.getElementById('edit-link').hidden = false; }) + .catch(() => {}); + </script> +</body> + +</html> +{{end}}
\ No newline at end of file |
