diff --git a/Build/Sources/TypeScript/rte_ckeditor/ckeditor5.ts b/Build/Sources/TypeScript/rte_ckeditor/ckeditor5.ts index 1525508d6e9a..586a65bf176e 100644 --- a/Build/Sources/TypeScript/rte_ckeditor/ckeditor5.ts +++ b/Build/Sources/TypeScript/rte_ckeditor/ckeditor5.ts @@ -3,9 +3,9 @@ import { customElement, property, query } from 'lit/decorators'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import { prefixAndRebaseCss } from '@typo3/rte-ckeditor/css-prefixer'; import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic'; -import type { WordCount } from '@ckeditor/ckeditor5-word-count'; +import type { Editor, EditorConfig, PluginConstructor } from '@ckeditor/ckeditor5-core'; +import type { WordCount, WordCountConfig } from '@ckeditor/ckeditor5-word-count'; import type { SourceEditing } from '@ckeditor/ckeditor5-source-editing'; -import type { Editor, PluginConstructor } from '@ckeditor/ckeditor5-core'; import type { GeneralHtmlSupportConfig } from '@ckeditor/ckeditor5-html-support'; type PluginModuleDescriptor = { @@ -13,27 +13,15 @@ type PluginModuleDescriptor = { exports: string[], }; -interface CKEditor5Config { +type CKEditor5Config = Omit & { // in TYPO3 always `items` property is used, skipping `string[]` toolbar?: { items: string[], shouldNotGroupWhenFull?: boolean }; - extraPlugins?: string[]; - removePlugins?: string[]; importModules?: Array; removeImportModules?: Array; contentsCss?: string[]; - style?: any; - heading?: any; - alignment?: any; - width?: any; - height?: any; + width?: string|number; + height?: string|number; readOnly?: boolean; - language?: any; - table?: any; - ui?: any; - htmlSupport?: GeneralHtmlSupportConfig; - - wordCount?: any; - typo3link?: any; debug?: boolean; } @@ -44,7 +32,7 @@ interface FormEngineConfig { id?: string; name?: string; value?: string; - validationRules?: any; + validationRules?: string; } const defaultPlugins: PluginModuleDescriptor[] = [ @@ -109,10 +97,101 @@ export class CKEditor5Element extends LitElement { throw new Error('No rich-text content target found.'); } - const removeImportModules: Array = normalizeImportModules(this.options.removeImportModules || []); + const { + // options handled by this wrapper + importModules, + removeImportModules, + width, + height, + readOnly, + debug, + + // options forwarded to CKEditor5 + toolbar, + placeholder, + htmlSupport, + wordCount, + typo3link, + removePlugins, + ...otherOptions + } = this.options; + + if ('extraPlugins' in otherOptions) { + // Drop CKEditor4 style extraPlugins which we do not support for CKEditor5 + // as this string-based list of plugin names works only for bundled plugins. + // `config.importModules` is used for CKEditor5 instead + delete otherOptions.extraPlugins; + } + if ('contentsCss' in otherOptions) { + // Consumed in connectedCallback + delete otherOptions.contentsCss; + } + + const plugins = await this.resolvePlugins(defaultPlugins, importModules, removeImportModules); + + const config: EditorConfig = { + ...otherOptions, + // link.defaultProtocol: 'https://' + toolbar, + plugins, + placeholder, + wordCount, + typo3link: typo3link || null, + removePlugins: removePlugins || [], + }; + + if (htmlSupport !== undefined) { + config.htmlSupport = convertPseudoRegExp(htmlSupport) as GeneralHtmlSupportConfig; + } + + ClassicEditor + .create(this.target, config) + .then((editor: ClassicEditor) => { + this.applyEditableElementStyles(editor, width, height); + this.handleWordCountPlugin(editor, wordCount); + this.applyReadOnly(editor, readOnly); + if (editor.plugins.has('SourceEditing')) { + const sourceEditingPlugin = editor.plugins.get('SourceEditing') as SourceEditing; + editor.model.document.on('change:data', (): void => { + if (!sourceEditingPlugin.isSourceEditingMode) { + editor.updateSourceElement() + } + this.target.dispatchEvent(new Event('change', { bubbles: true, cancelable: true })); + }); + } + + if (debug) { + import('@ckeditor/ckeditor5-inspector').then(({ default: CKEditorInspector }) => CKEditorInspector.attach(editor, { isCollapsed: true })); + } + }); + } + + protected createRenderRoot(): HTMLElement | ShadowRoot { + // const renderRoot = this.attachShadow({mode: 'open'}); + return this; + } + + protected render(): TemplateResult { + return html` + + `; + } + + private async resolvePlugins( + defaultPlugins: Array, + importModulesOption: Array|undefined, + removeImportModulesOption: Array|undefined + ): Promise>> { + const removeImportModules: Array = normalizeImportModules(removeImportModulesOption || []); const importModules: Array = normalizeImportModules([ ...defaultPlugins, - ...(this.options.importModules || []), + ...(importModulesOption || []), ]).map((moduleDescriptor: PluginModuleDescriptor) => { const { module } = moduleDescriptor; let { exports } = moduleDescriptor; @@ -160,81 +239,8 @@ export class CKEditor5Element extends LitElement { .flat(1); // plugins, without those that have been overridden - const plugins: Array> = declaredPlugins + return declaredPlugins .filter(plugin => !overriddenPlugins.includes(plugin as PluginConstructor)); - - const toolbar = this.options.toolbar; - - const config = { - // link.defaultProtocol: 'https://' - // @todo use complete `config` later - currently step-by-step only - toolbar, - plugins, - typo3link: this.options.typo3link || null, - removePlugins: this.options.removePlugins || [], - } as any; - if (this.options.language) { - config.language = this.options.language; - } - if (this.options.style) { - config.style = this.options.style; - } - if (this.options.wordCount) { - config.wordCount = this.options.wordCount; - } - if (this.options.table) { - config.table = this.options.table; - } - if (this.options.heading) { - config.heading = this.options.heading; - } - if (this.options.alignment) { - config.alignment = this.options.alignment; - } - if (this.options.ui) { - config.ui = this.options.ui; - } - if (this.options.htmlSupport) { - config.htmlSupport = convertPseudoRegExp(this.options.htmlSupport) as GeneralHtmlSupportConfig; - } - - ClassicEditor - .create(this.target, config) - .then((editor: ClassicEditor) => { - this.applyEditableElementStyles(editor); - this.handleWordCountPlugin(editor); - this.applyReadOnly(editor); - if (editor.plugins.has('SourceEditing')) { - const sourceEditingPlugin = editor.plugins.get('SourceEditing') as SourceEditing; - editor.model.document.on('change:data', (): void => { - if (!sourceEditingPlugin.isSourceEditingMode) { - editor.updateSourceElement() - } - this.target.dispatchEvent(new Event('change', { bubbles: true, cancelable: true })); - }); - } - - if (this.options.debug) { - import('@ckeditor/ckeditor5-inspector').then(({ default: CKEditorInspector }) => CKEditorInspector.attach(editor, { isCollapsed: true })); - } - }); - } - - protected createRenderRoot(): HTMLElement | ShadowRoot { - // const renderRoot = this.attachShadow({mode: 'open'}); - return this; - } - - protected render(): TemplateResult { - return html` - - `; } private async prefixAndLoadContentsCss(url: string, fieldId: string): Promise { @@ -258,19 +264,22 @@ export class CKEditor5Element extends LitElement { document.adoptedStyleSheets = [...document.adoptedStyleSheets, styleSheet]; } - private applyEditableElementStyles(editor: Editor): void { + private applyEditableElementStyles(editor: Editor, width: string|number|undefined, height: string|number|undefined): void { const view = editor.editing.view; - const styles: Record = { - 'min-height': this.options.height, - 'min-width': this.options.width, + const styles: Record = { + 'min-height': height, + 'min-width': width, }; Object.keys(styles).forEach((key) => { - let assignment: any = styles[key]; - if (!assignment) { + const _assignment: string|number = styles[key]; + if (!_assignment) { return; } - if (isFinite(assignment) && !Number.isNaN(parseFloat(assignment))) { - assignment += 'px'; + let assignment: string; + if (typeof _assignment === 'number' || !Number.isNaN(Number(assignment))) { + assignment = `${_assignment}px`; + } else { + assignment = _assignment } view.change((writer) => { writer.setStyle(key, assignment, view.document.getRoot()); @@ -281,8 +290,8 @@ export class CKEditor5Element extends LitElement { /** * see https://ckeditor.com/docs/ckeditor5/latest/features/word-count.html */ - private handleWordCountPlugin(editor: Editor): void { - if (editor.plugins.has('WordCount') && (this.options?.wordCount?.displayWords || this.options?.wordCount?.displayCharacters)) { + private handleWordCountPlugin(editor: Editor, wordCount: WordCountConfig|undefined): void { + if (editor.plugins.has('WordCount') && (wordCount?.displayWords || wordCount?.displayCharacters)) { const wordCountPlugin = editor.plugins.get('WordCount') as WordCount; this.renderRoot.appendChild(wordCountPlugin.wordCountContainer); } @@ -290,10 +299,9 @@ export class CKEditor5Element extends LitElement { /** * see https://ckeditor.com/docs/ckeditor5/latest/features/read-only.html - * does not work with types yet. so the editor is added with "any". */ - private applyReadOnly(editor: any): void { - if (this.options.readOnly) { + private applyReadOnly(editor: Editor, readOnly: boolean): void { + if (readOnly) { editor.enableReadOnlyMode('typo3-lock'); } } diff --git a/Build/Sources/TypeScript/rte_ckeditor/plugin/typo3-link.ts b/Build/Sources/TypeScript/rte_ckeditor/plugin/typo3-link.ts index 6b2072799bb5..3c609a6d6c8e 100644 --- a/Build/Sources/TypeScript/rte_ckeditor/plugin/typo3-link.ts +++ b/Build/Sources/TypeScript/rte_ckeditor/plugin/typo3-link.ts @@ -27,6 +27,10 @@ export function removeLinkPrefix(attribute: string): string { return attribute; } +export interface Typo3LinkConfig { + routeUrl: string; +} + export interface Typo3LinkDict { attrs?: { linkTitle?: string; @@ -689,7 +693,7 @@ export class Typo3LinkUI extends Core.Plugin { 'Link', this.makeUrlFromModulePath( editor, - (editor.config.get('typo3link') as any)?.routeUrl, + editor.config.get('typo3link')?.routeUrl, additionalParameters )); } @@ -728,5 +732,11 @@ export class Typo3Link extends Core.Plugin { static readonly overrides?: Array = [Link.Link]; } +declare module '@ckeditor/ckeditor5-core' { + interface EditorConfig { + typo3link?: Typo3LinkConfig; + } +} + // Provided for backwards compatibility export default Typo3Link; diff --git a/typo3/sysext/core/Configuration/RTE/SysNews.yaml b/typo3/sysext/core/Configuration/RTE/SysNews.yaml index 0f21c2dea01b..db8082dba3ff 100644 --- a/typo3/sysext/core/Configuration/RTE/SysNews.yaml +++ b/typo3/sysext/core/Configuration/RTE/SysNews.yaml @@ -20,6 +20,3 @@ editor: - Style - Underline - Strike - - extraPlugins: - - autolink diff --git a/typo3/sysext/rte_ckeditor/Classes/Form/Element/RichTextElement.php b/typo3/sysext/rte_ckeditor/Classes/Form/Element/RichTextElement.php index 5d50c1e3b6be..e7df8f5c8e8c 100644 --- a/typo3/sysext/rte_ckeditor/Classes/Form/Element/RichTextElement.php +++ b/typo3/sysext/rte_ckeditor/Classes/Form/Element/RichTextElement.php @@ -117,7 +117,7 @@ public function render(): array $ckeditorAttributes = GeneralUtility::implodeAttributes([ 'id' => $fieldId . 'ckeditor5', - 'options' => GeneralUtility::jsonEncodeForHtmlAttribute($ckeditorConfiguration['options'], false), + 'options' => GeneralUtility::jsonEncodeForHtmlAttribute($ckeditorConfiguration, false), 'form-engine' => GeneralUtility::jsonEncodeForHtmlAttribute([ 'id' => $fieldId, 'name' => $itemFormElementName, @@ -154,12 +154,12 @@ public function render(): array $resultArray['html'] = $this->wrapWithFieldsetAndLegend(implode(LF, $html)); $resultArray['javaScriptModules'][] = JavaScriptModuleInstruction::create('@typo3/rte-ckeditor/ckeditor5.js'); - $uiLanguage = $ckeditorConfiguration['options']['language']['ui']; + $uiLanguage = $ckeditorConfiguration['language']['ui']; if ($this->translationExists($uiLanguage)) { $resultArray['javaScriptModules'][] = JavaScriptModuleInstruction::create('@typo3/ckeditor5/translations/' . $uiLanguage . '.js'); } - $contentLanguage = $ckeditorConfiguration['options']['language']['content']; + $contentLanguage = $ckeditorConfiguration['language']['content']; if ($this->translationExists($contentLanguage)) { $resultArray['javaScriptModules'][] = JavaScriptModuleInstruction::create('@typo3/ckeditor5/translations/' . $contentLanguage . '.js'); } @@ -194,14 +194,10 @@ protected function getLanguageIsoCodeOfContent(): string return $contentLanguage; } - /** - * @return array{options: array, externalPlugins: array} - */ protected function resolveCkEditorConfiguration(): array { $configuration = $this->prepareConfigurationForEditor(); - $externalPlugins = []; foreach ($this->getExtraPlugins() as $extraPluginName => $extraPluginConfig) { $configName = $extraPluginConfig['configName'] ?? $extraPluginName; if (!empty($extraPluginConfig['config']) && is_array($extraPluginConfig['config'])) { @@ -211,20 +207,11 @@ protected function resolveCkEditorConfiguration(): array $configuration[$configName] = array_replace_recursive($extraPluginConfig['config'], $configuration[$configName]); } } - $configuration['extraPlugins'] = ($configuration['extraPlugins'] ?? '') . ',' . $extraPluginName; - if (isset($this->data['parameterArray']['fieldConf']['config']['placeholder'])) { - $configuration['editorplaceholder'] = (string)$this->data['parameterArray']['fieldConf']['config']['placeholder']; - } - - $externalPlugins[] = [ - 'name' => $extraPluginName, - 'resource' => $extraPluginConfig['resource'] ?? null, - ]; } - return [ - 'options' => $configuration, - 'externalPlugins' => $externalPlugins, - ]; + if (isset($this->data['parameterArray']['fieldConf']['config']['placeholder'])) { + $configuration['placeholder'] = (string)$this->data['parameterArray']['fieldConf']['config']['placeholder']; + } + return $configuration; } /** @@ -253,10 +240,8 @@ protected function getExtraPlugins(): array $pluginConfiguration[$pluginName] = [ 'configName' => $configuration['configName'] ?? $pluginName, ]; - if ($configuration['resource'] ?? null) { - $configuration['resource'] = $this->resolveUrlPath($configuration['resource']); - } unset($configuration['configName']); + // CKEditor4 style config, unused in CKEditor5 and not forwarded to the resutling plugin config unset($configuration['resource']); if ($configuration['route'] ?? null) { @@ -354,14 +339,6 @@ protected function prepareConfigurationForEditor(): array // Replace all paths $configuration = $this->replaceAbsolutePathsToRelativeResourcesPath($configuration); - // there are some places where we define an array, but it needs to be a list in order to work - if (is_array($configuration['extraPlugins'] ?? null)) { - $configuration['extraPlugins'] = implode(',', $configuration['extraPlugins']); - } - if (is_array($configuration['removeButtons'] ?? null)) { - $configuration['removeButtons'] = implode(',', $configuration['removeButtons']); - } - // unless explicitly set, the debug mode is enabled in development context if (!isset($configuration['debug'])) { $configuration['debug'] = ($GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] ?? false) && Environment::getContext()->isDevelopment(); @@ -376,7 +353,7 @@ protected function prepareConfigurationForEditor(): array protected function sanitizeFieldId(string $itemFormElementName): string { - $fieldId = (string)preg_replace('/[^a-zA-Z0-9_:.-]/', '_', $itemFormElementName); + $fieldId = (string)preg_replace('/[^a-zA-Z0-9_:-]/', '_', $itemFormElementName); return htmlspecialchars((string)preg_replace('/^[^a-zA-Z]/', 'x', $fieldId)); } diff --git a/typo3/sysext/rte_ckeditor/Configuration/RTE/Default.yaml b/typo3/sysext/rte_ckeditor/Configuration/RTE/Default.yaml index b1334a7ffa53..174bd111d26e 100644 --- a/typo3/sysext/rte_ckeditor/Configuration/RTE/Default.yaml +++ b/typo3/sysext/rte_ckeditor/Configuration/RTE/Default.yaml @@ -66,5 +66,3 @@ editor: - mergeTableCells - tableProperties - tableCellProperties - - extraPlugins: diff --git a/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/ckeditor5.js b/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/ckeditor5.js index 3e95cd7a98ce..b4351086dcf4 100644 --- a/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/ckeditor5.js +++ b/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/ckeditor5.js @@ -10,7 +10,7 @@ * * The TYPO3 project - inspiring people to share! */ -var __decorate=function(t,e,o,r){var i,n=arguments.length,s=n<3?e:null===r?r=Object.getOwnPropertyDescriptor(e,o):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(t,e,o,r);else for(var l=t.length-1;l>=0;l--)(i=t[l])&&(s=(n<3?i(s):n>3?i(e,o,s):i(e,o))||s);return n>3&&s&&Object.defineProperty(e,o,s),s};import{html,LitElement}from"lit";import{customElement,property,query}from"lit/decorators.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import{prefixAndRebaseCss}from"@typo3/rte-ckeditor/css-prefixer.js";import{ClassicEditor}from"@ckeditor/ckeditor5-editor-classic";const defaultPlugins=[{module:"@ckeditor/ckeditor5-block-quote",exports:["BlockQuote"]},{module:"@ckeditor/ckeditor5-essentials",exports:["Essentials"]},{module:"@ckeditor/ckeditor5-find-and-replace",exports:["FindAndReplace"]},{module:"@ckeditor/ckeditor5-heading",exports:["Heading"]},{module:"@ckeditor/ckeditor5-indent",exports:["Indent"]},{module:"@ckeditor/ckeditor5-link",exports:["Link"]},{module:"@ckeditor/ckeditor5-list",exports:["DocumentList"]},{module:"@ckeditor/ckeditor5-paragraph",exports:["Paragraph"]},{module:"@ckeditor/ckeditor5-clipboard",exports:["PastePlainText"]},{module:"@ckeditor/ckeditor5-paste-from-office",exports:["PasteFromOffice"]},{module:"@ckeditor/ckeditor5-remove-format",exports:["RemoveFormat"]},{module:"@ckeditor/ckeditor5-table",exports:["Table","TableToolbar","TableProperties","TableCellProperties"]},{module:"@ckeditor/ckeditor5-typing",exports:["TextTransformation"]},{module:"@ckeditor/ckeditor5-source-editing",exports:["SourceEditing"]},{module:"@ckeditor/ckeditor5-alignment",exports:["Alignment"]},{module:"@ckeditor/ckeditor5-style",exports:["Style"]},{module:"@ckeditor/ckeditor5-html-support",exports:["GeneralHtmlSupport"]},{module:"@ckeditor/ckeditor5-basic-styles",exports:["Bold","Italic","Subscript","Superscript","Strikethrough","Underline"]},{module:"@ckeditor/ckeditor5-special-characters",exports:["SpecialCharacters","SpecialCharactersEssentials"]},{module:"@ckeditor/ckeditor5-horizontal-line",exports:["HorizontalLine"]}];let CKEditor5Element=class extends LitElement{constructor(){super(...arguments),this.options={},this.formEngine={},this.styleSheets=new Map}connectedCallback(){if(super.connectedCallback(),Array.isArray(this.options.contentsCss))for(const t of this.options.contentsCss)this.prefixAndLoadContentsCss(t,this.getAttribute("id"))}disconnectedCallback(){super.disconnectedCallback(),document.adoptedStyleSheets=document.adoptedStyleSheets.filter((t=>!this.styleSheets.has(t))),this.styleSheets.clear()}async firstUpdated(){if(!(this.target instanceof HTMLElement))throw new Error("No rich-text content target found.");const t=normalizeImportModules(this.options.removeImportModules||[]),e=normalizeImportModules([...defaultPlugins,...this.options.importModules||[]]).map((e=>{const{module:o}=e;let{exports:r}=e;for(const e of t)e.module===o&&(r=r.filter((t=>!e.exports.includes(t))));return{module:o,exports:r}})),o=await Promise.all(e.map((async t=>{try{return{module:await import(t.module),exports:t.exports}}catch(e){return console.error(`Failed to load CKEditor5 module ${t.module}`,e),{module:null,exports:[]}}}))),r=[];o.forEach((({module:t,exports:e})=>{for(const o of e)o in t?r.push(t[o]):console.error(`CKEditor5 plugin export "${o}" not available in`,t)}));const i=r.filter((t=>t.overrides?.length>0)).map((t=>t.overrides)).flat(1),n=r.filter((t=>!i.includes(t))),s={toolbar:this.options.toolbar,plugins:n,typo3link:this.options.typo3link||null,removePlugins:this.options.removePlugins||[]};this.options.language&&(s.language=this.options.language),this.options.style&&(s.style=this.options.style),this.options.wordCount&&(s.wordCount=this.options.wordCount),this.options.table&&(s.table=this.options.table),this.options.heading&&(s.heading=this.options.heading),this.options.alignment&&(s.alignment=this.options.alignment),this.options.ui&&(s.ui=this.options.ui),this.options.htmlSupport&&(s.htmlSupport=convertPseudoRegExp(this.options.htmlSupport)),ClassicEditor.create(this.target,s).then((t=>{if(this.applyEditableElementStyles(t),this.handleWordCountPlugin(t),this.applyReadOnly(t),t.plugins.has("SourceEditing")){const e=t.plugins.get("SourceEditing");t.model.document.on("change:data",(()=>{e.isSourceEditingMode||t.updateSourceElement(),this.target.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0}))}))}this.options.debug&&import("@ckeditor/ckeditor5-inspector").then((({default:e})=>e.attach(t,{isCollapsed:!0})))}))}createRenderRoot(){return this}render(){return html` +var __decorate=function(e,t,o,r){var i,n=arguments.length,s=n<3?t:null===r?r=Object.getOwnPropertyDescriptor(t,o):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,o,r);else for(var l=e.length-1;l>=0;l--)(i=e[l])&&(s=(n<3?i(s):n>3?i(t,o,s):i(t,o))||s);return n>3&&s&&Object.defineProperty(t,o,s),s};import{html,LitElement}from"lit";import{customElement,property,query}from"lit/decorators.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import{prefixAndRebaseCss}from"@typo3/rte-ckeditor/css-prefixer.js";import{ClassicEditor}from"@ckeditor/ckeditor5-editor-classic";const defaultPlugins=[{module:"@ckeditor/ckeditor5-block-quote",exports:["BlockQuote"]},{module:"@ckeditor/ckeditor5-essentials",exports:["Essentials"]},{module:"@ckeditor/ckeditor5-find-and-replace",exports:["FindAndReplace"]},{module:"@ckeditor/ckeditor5-heading",exports:["Heading"]},{module:"@ckeditor/ckeditor5-indent",exports:["Indent"]},{module:"@ckeditor/ckeditor5-link",exports:["Link"]},{module:"@ckeditor/ckeditor5-list",exports:["DocumentList"]},{module:"@ckeditor/ckeditor5-paragraph",exports:["Paragraph"]},{module:"@ckeditor/ckeditor5-clipboard",exports:["PastePlainText"]},{module:"@ckeditor/ckeditor5-paste-from-office",exports:["PasteFromOffice"]},{module:"@ckeditor/ckeditor5-remove-format",exports:["RemoveFormat"]},{module:"@ckeditor/ckeditor5-table",exports:["Table","TableToolbar","TableProperties","TableCellProperties"]},{module:"@ckeditor/ckeditor5-typing",exports:["TextTransformation"]},{module:"@ckeditor/ckeditor5-source-editing",exports:["SourceEditing"]},{module:"@ckeditor/ckeditor5-alignment",exports:["Alignment"]},{module:"@ckeditor/ckeditor5-style",exports:["Style"]},{module:"@ckeditor/ckeditor5-html-support",exports:["GeneralHtmlSupport"]},{module:"@ckeditor/ckeditor5-basic-styles",exports:["Bold","Italic","Subscript","Superscript","Strikethrough","Underline"]},{module:"@ckeditor/ckeditor5-special-characters",exports:["SpecialCharacters","SpecialCharactersEssentials"]},{module:"@ckeditor/ckeditor5-horizontal-line",exports:["HorizontalLine"]}];let CKEditor5Element=class extends LitElement{constructor(){super(...arguments),this.options={},this.formEngine={},this.styleSheets=new Map}connectedCallback(){if(super.connectedCallback(),Array.isArray(this.options.contentsCss))for(const e of this.options.contentsCss)this.prefixAndLoadContentsCss(e,this.getAttribute("id"))}disconnectedCallback(){super.disconnectedCallback(),document.adoptedStyleSheets=document.adoptedStyleSheets.filter((e=>!this.styleSheets.has(e))),this.styleSheets.clear()}async firstUpdated(){if(!(this.target instanceof HTMLElement))throw new Error("No rich-text content target found.");const{importModules:e,removeImportModules:t,width:o,height:r,readOnly:i,debug:n,toolbar:s,placeholder:l,htmlSupport:d,wordCount:a,typo3link:c,removePlugins:p,...u}=this.options;"extraPlugins"in u&&delete u.extraPlugins,"contentsCss"in u&&delete u.contentsCss;const m=await this.resolvePlugins(defaultPlugins,e,t),h={...u,toolbar:s,plugins:m,placeholder:l,wordCount:a,typo3link:c||null,removePlugins:p||[]};void 0!==d&&(h.htmlSupport=convertPseudoRegExp(d)),ClassicEditor.create(this.target,h).then((e=>{if(this.applyEditableElementStyles(e,o,r),this.handleWordCountPlugin(e,a),this.applyReadOnly(e,i),e.plugins.has("SourceEditing")){const t=e.plugins.get("SourceEditing");e.model.document.on("change:data",(()=>{t.isSourceEditingMode||e.updateSourceElement(),this.target.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0}))}))}n&&import("@ckeditor/ckeditor5-inspector").then((({default:t})=>t.attach(e,{isCollapsed:!0})))}))}createRenderRoot(){return this}render(){return html` - `}async prefixAndLoadContentsCss(t,e){let o;try{const e=await new AjaxRequest(t).get();o=await e.resolve()}catch{return}const r=prefixAndRebaseCss(o,t,`#${e} .ck-content`),i=new CSSStyleSheet;await i.replace(r),this.styleSheets.set(i,!0),document.adoptedStyleSheets=[...document.adoptedStyleSheets,i]}applyEditableElementStyles(t){const e=t.editing.view,o={"min-height":this.options.height,"min-width":this.options.width};Object.keys(o).forEach((t=>{let r=o[t];r&&(isFinite(r)&&!Number.isNaN(parseFloat(r))&&(r+="px"),e.change((o=>{o.setStyle(t,r,e.document.getRoot())})))}))}handleWordCountPlugin(t){if(t.plugins.has("WordCount")&&(this.options?.wordCount?.displayWords||this.options?.wordCount?.displayCharacters)){const e=t.plugins.get("WordCount");this.renderRoot.appendChild(e.wordCountContainer)}}applyReadOnly(t){this.options.readOnly&&t.enableReadOnlyMode("typo3-lock")}};__decorate([property({type:Object})],CKEditor5Element.prototype,"options",void 0),__decorate([property({type:Object,attribute:"form-engine"})],CKEditor5Element.prototype,"formEngine",void 0),__decorate([query("textarea")],CKEditor5Element.prototype,"target",void 0),CKEditor5Element=__decorate([customElement("typo3-rte-ckeditor-ckeditor5")],CKEditor5Element);export{CKEditor5Element};function walkObj(t,e){if("object"==typeof t){if(Array.isArray(t))return t.map((t=>e(t)??walkObj(t,e)));const o={};for(const[r,i]of Object.entries(t))o[r]=e(i)??walkObj(i,e);return o}return t}function convertPseudoRegExp(t){return walkObj(t,(t=>{if("object"==typeof t&&"pattern"in t&&"string"==typeof t.pattern){const e=t;return new RegExp(e.pattern,e.flags||void 0)}return null}))}function normalizeImportModules(t){return t.map((t=>"string"==typeof t?{module:t,exports:["default"]}:t))} \ No newline at end of file + `}async resolvePlugins(e,t,o){const r=normalizeImportModules(o||[]),i=normalizeImportModules([...e,...t||[]]).map((e=>{const{module:t}=e;let{exports:o}=e;for(const e of r)e.module===t&&(o=o.filter((t=>!e.exports.includes(t))));return{module:t,exports:o}})),n=await Promise.all(i.map((async e=>{try{return{module:await import(e.module),exports:e.exports}}catch(t){return console.error(`Failed to load CKEditor5 module ${e.module}`,t),{module:null,exports:[]}}}))),s=[];n.forEach((({module:e,exports:t})=>{for(const o of t)o in e?s.push(e[o]):console.error(`CKEditor5 plugin export "${o}" not available in`,e)}));const l=s.filter((e=>e.overrides?.length>0)).map((e=>e.overrides)).flat(1);return s.filter((e=>!l.includes(e)))}async prefixAndLoadContentsCss(e,t){let o;try{const t=await new AjaxRequest(e).get();o=await t.resolve()}catch{return}const r=prefixAndRebaseCss(o,e,`#${t} .ck-content`),i=new CSSStyleSheet;await i.replace(r),this.styleSheets.set(i,!0),document.adoptedStyleSheets=[...document.adoptedStyleSheets,i]}applyEditableElementStyles(e,t,o){const r=e.editing.view,i={"min-height":o,"min-width":t};Object.keys(i).forEach((e=>{const t=i[e];if(!t)return;let o;o="number"!=typeof t&&Number.isNaN(Number(o))?t:`${t}px`,r.change((t=>{t.setStyle(e,o,r.document.getRoot())}))}))}handleWordCountPlugin(e,t){if(e.plugins.has("WordCount")&&(t?.displayWords||t?.displayCharacters)){const t=e.plugins.get("WordCount");this.renderRoot.appendChild(t.wordCountContainer)}}applyReadOnly(e,t){t&&e.enableReadOnlyMode("typo3-lock")}};__decorate([property({type:Object})],CKEditor5Element.prototype,"options",void 0),__decorate([property({type:Object,attribute:"form-engine"})],CKEditor5Element.prototype,"formEngine",void 0),__decorate([query("textarea")],CKEditor5Element.prototype,"target",void 0),CKEditor5Element=__decorate([customElement("typo3-rte-ckeditor-ckeditor5")],CKEditor5Element);export{CKEditor5Element};function walkObj(e,t){if("object"==typeof e){if(Array.isArray(e))return e.map((e=>t(e)??walkObj(e,t)));const o={};for(const[r,i]of Object.entries(e))o[r]=t(i)??walkObj(i,t);return o}return e}function convertPseudoRegExp(e){return walkObj(e,(e=>{if("object"==typeof e&&"pattern"in e&&"string"==typeof e.pattern){const t=e;return new RegExp(t.pattern,t.flags||void 0)}return null}))}function normalizeImportModules(e){return e.map((e=>"string"==typeof e?{module:e,exports:["default"]}:e))} \ No newline at end of file