From 4c0a78512ae1955414840ad4c8297981d99ae862 Mon Sep 17 00:00:00 2001 From: Benjamin Franzke <bfr@qbus.de> Date: Mon, 20 Dec 2021 07:42:02 +0100 Subject: [PATCH] [TASK] Use plain script-loading for CKEditor v4 CKEditor is now loaded via a plain <script async> tag in preparation for a transition from requirejs to ES6 modules. CKEditor v4 is not available as ES6 module and can not easily be transformed into a strict-mode compatible module. CKEditor v4 requires `this` to be the global window object in various functions, but `this` is not bound to the global window object in strict-mode (as implied by <script type="module">). Note: CKEditor is not a real AMD module anyway, but is configured to be shimed by requirejs to be loadable as AMD module, therefore it can safely be loaded using good old <script>. Releases: main Resolves: #96394 Related: #96323 Change-Id: Ife0b476ba29b85f85dfd654ea096cdf30c47b6ef Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/72733 Tested-by: core-ci <typo3@b13.com> Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de> Tested-by: Benni Mack <benni@typo3.org> Tested-by: Benjamin Franzke <bfr@qbus.de> Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de> Reviewed-by: Benni Mack <benni@typo3.org> Reviewed-by: Benjamin Franzke <bfr@qbus.de> --- .../Public/TypeScript/CKEditorLoader.ts | 22 +++++++++++++++++++ .../TypeScript/FormEngineInitializer.ts | 3 ++- .../Public/TypeScript/RteLinkBrowser.ts | 1 - Build/types/TYPO3/index.d.ts | 1 - .../Hook/PageRendererRenderPreProcess.php | 2 ++ .../Public/JavaScript/CKEditorLoader.js | 13 +++++++++++ .../JavaScript/FormEngineInitializer.js | 2 +- .../Public/JavaScript/RteLinkBrowser.js | 2 +- 8 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 Build/Sources/TypeScript/rte_ckeditor/Resources/Public/TypeScript/CKEditorLoader.ts create mode 100644 typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/CKEditorLoader.js diff --git a/Build/Sources/TypeScript/rte_ckeditor/Resources/Public/TypeScript/CKEditorLoader.ts b/Build/Sources/TypeScript/rte_ckeditor/Resources/Public/TypeScript/CKEditorLoader.ts new file mode 100644 index 000000000000..5d9c3637300b --- /dev/null +++ b/Build/Sources/TypeScript/rte_ckeditor/Resources/Public/TypeScript/CKEditorLoader.ts @@ -0,0 +1,22 @@ +import module from 'module'; + +let ckeditorPromise: Promise<typeof window.CKEDITOR>|null = null; + +function loadScript(url: string): Promise<Event> { + return new Promise((resolve, reject) => { + const newScript = document.createElement('script'); + newScript.async = true + newScript.onerror = reject; + newScript.onload = (ev: Event) => resolve(ev); + newScript.src = url; + document.head.appendChild(newScript); + }); +} + +export function loadCKEditor(): Promise<typeof window.CKEDITOR> { + if (ckeditorPromise === null) { + const scriptUrl = module.uri.replace(/\/[^\/]+\.js/, '/Contrib/ckeditor.js') + ckeditorPromise = loadScript(scriptUrl).then(() => window.CKEDITOR); + } + return ckeditorPromise; +} diff --git a/Build/Sources/TypeScript/rte_ckeditor/Resources/Public/TypeScript/FormEngineInitializer.ts b/Build/Sources/TypeScript/rte_ckeditor/Resources/Public/TypeScript/FormEngineInitializer.ts index d58eee634ff9..4ee3f57696ff 100644 --- a/Build/Sources/TypeScript/rte_ckeditor/Resources/Public/TypeScript/FormEngineInitializer.ts +++ b/Build/Sources/TypeScript/rte_ckeditor/Resources/Public/TypeScript/FormEngineInitializer.ts @@ -11,6 +11,7 @@ * The TYPO3 project - inspiring people to share! */ +import {loadCKEditor} from 'TYPO3/CMS/RteCkeditor/CKEditorLoader'; import $ from 'jquery'; import FormEngine = require('TYPO3/CMS/Backend/FormEngine'); @@ -31,7 +32,7 @@ interface CKEditorExternalPlugin { */ export class FormEngineInitializer { public static initializeCKEditor(options: CKEditorOptions): void { - import('ckeditor').then(({default: CKEDITOR}) => { + loadCKEditor().then((CKEDITOR) => { CKEDITOR.timestamp += '-' + options.configurationHash; options.externalPlugins .forEach((item: CKEditorExternalPlugin) => CKEDITOR.plugins.addExternal(item.name, item.resource, '')); diff --git a/Build/Sources/TypeScript/rte_ckeditor/Resources/Public/TypeScript/RteLinkBrowser.ts b/Build/Sources/TypeScript/rte_ckeditor/Resources/Public/TypeScript/RteLinkBrowser.ts index dccaadfd6eb7..522e0c158035 100644 --- a/Build/Sources/TypeScript/rte_ckeditor/Resources/Public/TypeScript/RteLinkBrowser.ts +++ b/Build/Sources/TypeScript/rte_ckeditor/Resources/Public/TypeScript/RteLinkBrowser.ts @@ -13,7 +13,6 @@ import $ from 'jquery'; import LinkBrowser = require('TYPO3/CMS/Recordlist/LinkBrowser'); -import 'ckeditor'; import Modal = require('TYPO3/CMS/Backend/Modal'); /** diff --git a/Build/types/TYPO3/index.d.ts b/Build/types/TYPO3/index.d.ts index db9ff14ed110..ca20acb1d028 100644 --- a/Build/types/TYPO3/index.d.ts +++ b/Build/types/TYPO3/index.d.ts @@ -138,7 +138,6 @@ interface Window { * Needed type declarations for provided libs */ declare module 'muuri'; -declare module 'ckeditor'; declare module 'codemirror'; declare module 'flatpickr/flatpickr.min'; declare module 'flatpickr/locales'; diff --git a/typo3/sysext/rte_ckeditor/Classes/Hook/PageRendererRenderPreProcess.php b/typo3/sysext/rte_ckeditor/Classes/Hook/PageRendererRenderPreProcess.php index 0534522eb2db..d178bd227654 100644 --- a/typo3/sysext/rte_ckeditor/Classes/Hook/PageRendererRenderPreProcess.php +++ b/typo3/sysext/rte_ckeditor/Classes/Hook/PageRendererRenderPreProcess.php @@ -31,6 +31,8 @@ final class PageRendererRenderPreProcess { // @todo: Add an event to PageRenderer for registration of RequireJS configuration, see #93236 if ($pageRenderer->getApplicationType() === 'BE') { + // @todo: Unused in TYPO3 core, but kept for requirejs compatibility in backend modules. + // Remove/deprecate when requirejs is deprecated. $pageRenderer->addRequireJsConfiguration([ 'shim' => [ 'ckeditor' => ['exports' => 'CKEDITOR'], diff --git a/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/CKEditorLoader.js b/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/CKEditorLoader.js new file mode 100644 index 000000000000..7541a6d73696 --- /dev/null +++ b/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/CKEditorLoader.js @@ -0,0 +1,13 @@ +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ +var __importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};define(["require","exports","module"],(function(e,t,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.loadCKEditor=void 0,o=__importDefault(o);let r=null;t.loadCKEditor=function(){if(null===r){const t=o.default.uri.replace(/\/[^\/]+\.js/,"/Contrib/ckeditor.js");r=(e=t,new Promise((t,o)=>{const r=document.createElement("script");r.async=!0,r.onerror=o,r.onload=e=>t(e),r.src=e,document.head.appendChild(r)})).then(()=>window.CKEDITOR)}var e;return r}})); \ No newline at end of file diff --git a/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/FormEngineInitializer.js b/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/FormEngineInitializer.js index 293254bdba95..27ced9c13cce 100644 --- a/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/FormEngineInitializer.js +++ b/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/FormEngineInitializer.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -var __createBinding=this&&this.__createBinding||(Object.create?function(e,t,i,n){void 0===n&&(n=i),Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[i]}})}:function(e,t,i,n){void 0===n&&(n=i),e[n]=t[i]}),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),__importStar=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.prototype.hasOwnProperty.call(e,i)&&__createBinding(t,e,i);return __setModuleDefault(t,e),t},__importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};define(["require","exports","jquery","TYPO3/CMS/Backend/FormEngine"],(function(e,t,i,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.FormEngineInitializer=void 0,i=__importDefault(i);t.FormEngineInitializer=class{static initializeCKEditor(t){new Promise((t,i)=>{e(["ckeditor"],t,i)}).then(__importStar).then(({default:e})=>{e.timestamp+="-"+t.configurationHash,t.externalPlugins.forEach(t=>e.plugins.addExternal(t.name,t.resource,"")),i.default(()=>{const a=t.fieldId,r="#"+i.default.escapeSelector(a);e.replace(a,t.configuration);const o=e.instances[a];o.on("change",e=>{let t=e.sender.commands;o.updateElement(),n.Validation.validateField(i.default(r)),n.Validation.markFieldAsChanged(i.default(r)),void 0!==t.maximize&&1===t.maximize.state&&o.on("maximize",e=>{i.default(this).off("maximize"),n.Validation.markFieldAsChanged(i.default(r))})}),o.on("mode",e=>{if("source"===e.editor.mode){const e=o.editable();e.attachListener(e,"change",()=>{n.Validation.markFieldAsChanged(i.default(r))})}}),document.addEventListener("inline:sorting-changed",()=>{o.destroy(),e.replace(a,t.configuration)}),document.addEventListener("formengine:flexform:sorting-changed",()=>{o.destroy(),e.replace(a,t.configuration)})})})}}})); \ No newline at end of file +var __importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};define(["require","exports","TYPO3/CMS/RteCkeditor/CKEditorLoader","jquery","TYPO3/CMS/Backend/FormEngine"],(function(e,i,t,a,n){"use strict";Object.defineProperty(i,"__esModule",{value:!0}),i.FormEngineInitializer=void 0,a=__importDefault(a);i.FormEngineInitializer=class{static initializeCKEditor(e){t.loadCKEditor().then(i=>{i.timestamp+="-"+e.configurationHash,e.externalPlugins.forEach(e=>i.plugins.addExternal(e.name,e.resource,"")),a.default(()=>{const t=e.fieldId,o="#"+a.default.escapeSelector(t);i.replace(t,e.configuration);const d=i.instances[t];d.on("change",e=>{let i=e.sender.commands;d.updateElement(),n.Validation.validateField(a.default(o)),n.Validation.markFieldAsChanged(a.default(o)),void 0!==i.maximize&&1===i.maximize.state&&d.on("maximize",e=>{a.default(this).off("maximize"),n.Validation.markFieldAsChanged(a.default(o))})}),d.on("mode",e=>{if("source"===e.editor.mode){const e=d.editable();e.attachListener(e,"change",()=>{n.Validation.markFieldAsChanged(a.default(o))})}}),document.addEventListener("inline:sorting-changed",()=>{d.destroy(),i.replace(t,e.configuration)}),document.addEventListener("formengine:flexform:sorting-changed",()=>{d.destroy(),i.replace(t,e.configuration)})})})}}})); \ No newline at end of file diff --git a/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/RteLinkBrowser.js b/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/RteLinkBrowser.js index 88f4a7f5e503..eabdbe30f496 100644 --- a/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/RteLinkBrowser.js +++ b/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/RteLinkBrowser.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -var __importDefault=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};define(["require","exports","jquery","TYPO3/CMS/Recordlist/LinkBrowser","TYPO3/CMS/Backend/Modal","ckeditor"],(function(t,e,i,n,s){"use strict";i=__importDefault(i);class l{constructor(){this.plugin=null,this.CKEditor=null,this.ranges=[],this.siteUrl=""}initialize(t){let e=s.currentModal.data("ckeditor");if(void 0!==e)this.CKEditor=e;else{let e;e=void 0!==top.TYPO3.Backend&&void 0!==top.TYPO3.Backend.ContentContainer.get()?top.TYPO3.Backend.ContentContainer.get():window.parent,i.default.each(e.CKEDITOR.instances,(e,i)=>{i.id===t&&(this.CKEditor=i)})}window.addEventListener("beforeunload",()=>{this.CKEditor.getSelection().selectRanges(this.ranges)}),this.ranges=this.CKEditor.getSelection().getRanges(),i.default.extend(l,i.default("body").data()),i.default(".t3js-class-selector").on("change",()=>{i.default("option:selected",this).data("linkTitle")&&i.default(".t3js-linkTitle").val(i.default("option:selected",this).data("linkTitle"))}),i.default(".t3js-removeCurrentLink").on("click",t=>{t.preventDefault(),this.CKEditor.execCommand("unlink"),s.dismiss()})}finalizeFunction(t){const e=this.CKEditor.document.createElement("a"),l=n.getLinkAttributeValues();let a=l.params?l.params:"";l.target&&e.setAttribute("target",l.target),l.class&&e.setAttribute("class",l.class),l.title&&e.setAttribute("title",l.title),delete l.title,delete l.class,delete l.target,delete l.params,i.default.each(l,(t,i)=>{e.setAttribute(t,i)});const r=t.match(/^([a-z0-9]+:\/\/[^:\/?#]+(?:\/?[^?#]*)?)(\??[^#]*)(#?.*)$/);if(r&&r.length>0){t=r[1]+r[2];const e=r[2].length>0?"&":"?";a.length>0&&("&"===a[0]&&(a=a.substr(1)),a.length>0&&(t+=e+a)),t+=r[3]}e.setAttribute("href",t);const o=this.CKEditor.getSelection();o.selectRanges(this.ranges),o&&""===o.getSelectedText()&&o.selectElement(o.getStartElement()),o&&o.getSelectedText()?e.setText(o.getSelectedText()):e.setText(e.getAttribute("href")),this.CKEditor.insertElement(e),s.dismiss()}}let a=new l;return n.finalizeFunction=t=>{a.finalizeFunction(t)},a})); \ No newline at end of file +var __importDefault=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};define(["require","exports","jquery","TYPO3/CMS/Recordlist/LinkBrowser","TYPO3/CMS/Backend/Modal"],(function(t,e,i,n,s){"use strict";i=__importDefault(i);class l{constructor(){this.plugin=null,this.CKEditor=null,this.ranges=[],this.siteUrl=""}initialize(t){let e=s.currentModal.data("ckeditor");if(void 0!==e)this.CKEditor=e;else{let e;e=void 0!==top.TYPO3.Backend&&void 0!==top.TYPO3.Backend.ContentContainer.get()?top.TYPO3.Backend.ContentContainer.get():window.parent,i.default.each(e.CKEDITOR.instances,(e,i)=>{i.id===t&&(this.CKEditor=i)})}window.addEventListener("beforeunload",()=>{this.CKEditor.getSelection().selectRanges(this.ranges)}),this.ranges=this.CKEditor.getSelection().getRanges(),i.default.extend(l,i.default("body").data()),i.default(".t3js-class-selector").on("change",()=>{i.default("option:selected",this).data("linkTitle")&&i.default(".t3js-linkTitle").val(i.default("option:selected",this).data("linkTitle"))}),i.default(".t3js-removeCurrentLink").on("click",t=>{t.preventDefault(),this.CKEditor.execCommand("unlink"),s.dismiss()})}finalizeFunction(t){const e=this.CKEditor.document.createElement("a"),l=n.getLinkAttributeValues();let a=l.params?l.params:"";l.target&&e.setAttribute("target",l.target),l.class&&e.setAttribute("class",l.class),l.title&&e.setAttribute("title",l.title),delete l.title,delete l.class,delete l.target,delete l.params,i.default.each(l,(t,i)=>{e.setAttribute(t,i)});const r=t.match(/^([a-z0-9]+:\/\/[^:\/?#]+(?:\/?[^?#]*)?)(\??[^#]*)(#?.*)$/);if(r&&r.length>0){t=r[1]+r[2];const e=r[2].length>0?"&":"?";a.length>0&&("&"===a[0]&&(a=a.substr(1)),a.length>0&&(t+=e+a)),t+=r[3]}e.setAttribute("href",t);const o=this.CKEditor.getSelection();o.selectRanges(this.ranges),o&&""===o.getSelectedText()&&o.selectElement(o.getStartElement()),o&&o.getSelectedText()?e.setText(o.getSelectedText()):e.setText(e.getAttribute("href")),this.CKEditor.insertElement(e),s.dismiss()}}let a=new l;return n.finalizeFunction=t=>{a.finalizeFunction(t)},a})); \ No newline at end of file -- GitLab