Loading core/modules/ckeditor5/ckeditor5.ckeditor5.yml +11 −0 Original line number Diff line number Diff line Loading @@ -83,6 +83,17 @@ ckeditor5_style: elements: - <$any-html5-element class> ckeditor5_htmlComments: ckeditor5: plugins: - htmlSupport.HtmlComment drupal: label: HTML Comment support elements: false library: core/ckeditor5.htmlSupport # @see \Drupal\ckeditor5\Plugin\CKEditor5PluginManagerInterface::getEnabledDefinitions() conditions: [] ckeditor5_arbitraryHtmlSupport: ckeditor5: plugins: [htmlSupport.GeneralHtmlSupport] Loading core/modules/ckeditor5/js/build/drupalHtmlEngine.js +1 −1 Original line number Diff line number Diff line !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.CKEditor5=t():(e.CKEditor5=e.CKEditor5||{},e.CKEditor5.drupalHtmlEngine=t())}(globalThis,(()=>(()=>{var e={"ckeditor5/src/core.js":(e,t,r)=>{e.exports=r("dll-reference CKEditor5.dll")("./src/core.js")},"dll-reference CKEditor5.dll":e=>{"use strict";e.exports=CKEditor5.dll}},t={};function r(n){var p=t[n];if(void 0!==p)return p.exports;var s=t[n]={exports:{}};return e[n](s,s.exports,r),s.exports}r.d=(e,t)=>{for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var n={};return(()=>{"use strict";r.d(n,{default:()=>o});var e=r("ckeditor5/src/core.js");class t{constructor(){this.chunks=[],this.selfClosingTags=["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]}build(){return this.chunks.join("")}appendNode(e){e.nodeType===Node.TEXT_NODE?this._appendText(e):e.nodeType===Node.ELEMENT_NODE?this._appendElement(e):e.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&this._appendChildren(e)}_appendElement(e){const t=e.nodeName.toLowerCase();this._append("<"),this._append(t),this._appendAttributes(e),this._append(">"),this.selfClosingTags.includes(t)||(this._appendChildren(e),this._append("</"),this._append(t),this._append(">"))}_appendChildren(e){Object.keys(e.childNodes).forEach((t=>{this.appendNode(e.childNodes[t])}))}_appendAttributes(e){Object.keys(e.attributes).forEach((t=>{this._append(" "),this._append(e.attributes[t].name),this._append('="'),this._append(this.constructor._escapeAttribute(e.attributes[t].value)),this._append('"')}))}_appendText(e){const t=document.implementation.createHTMLDocument("").createElement("p");t.textContent=e.textContent,this._append(t.innerHTML)}_append(e){this.chunks.push(e)}static _escapeAttribute(e){return e.replace(/&/g,"&").replace(/'/g,"'").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">").replace(/\r\n/g," ").replace(/[\r\n]/g," ")}}class p{getHtml(e){const r=new t;return r.appendNode(e),r.build()}}class s extends e.Plugin{init(){this.editor.data.processor.htmlWriter=new p}static get pluginName(){return"DrupalHtmlEngine"}}const o={DrupalHtmlEngine:s}})(),n=n.default})())); No newline at end of file !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.CKEditor5=t():(e.CKEditor5=e.CKEditor5||{},e.CKEditor5.drupalHtmlEngine=t())}(globalThis,(()=>(()=>{var e={"ckeditor5/src/core.js":(e,t,n)=>{e.exports=n("dll-reference CKEditor5.dll")("./src/core.js")},"dll-reference CKEditor5.dll":e=>{"use strict";e.exports=CKEditor5.dll}},t={};function n(p){var r=t[p];if(void 0!==r)return r.exports;var s=t[p]={exports:{}};return e[p](s,s.exports,n),s.exports}n.d=(e,t)=>{for(var p in t)n.o(t,p)&&!n.o(e,p)&&Object.defineProperty(e,p,{enumerable:!0,get:t[p]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var p={};return(()=>{"use strict";n.d(p,{default:()=>o});var e=n("ckeditor5/src/core.js");class t{constructor(){this.chunks=[],this.selfClosingTags=["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]}build(){return this.chunks.join("")}appendNode(e){e.nodeType===Node.TEXT_NODE?this._appendText(e):e.nodeType===Node.ELEMENT_NODE?this._appendElement(e):e.nodeType===Node.DOCUMENT_FRAGMENT_NODE?this._appendChildren(e):e.nodeType===Node.COMMENT_NODE&&this._appendComment(e)}_appendElement(e){const t=e.nodeName.toLowerCase();this._append("<"),this._append(t),this._appendAttributes(e),this._append(">"),this.selfClosingTags.includes(t)||(this._appendChildren(e),this._append("</"),this._append(t),this._append(">"))}_appendChildren(e){Object.keys(e.childNodes).forEach((t=>{this.appendNode(e.childNodes[t])}))}_appendAttributes(e){Object.keys(e.attributes).forEach((t=>{this._append(" "),this._append(e.attributes[t].name),this._append('="'),this._append(this.constructor._escapeAttribute(e.attributes[t].value)),this._append('"')}))}_appendText(e){const t=document.implementation.createHTMLDocument("").createElement("p");t.textContent=e.textContent,this._append(t.innerHTML)}_appendComment(e){this._append("\x3c!--"),this._append(e.textContent),this._append("--\x3e")}_append(e){this.chunks.push(e)}static _escapeAttribute(e){return e.replace(/&/g,"&").replace(/'/g,"'").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">").replace(/\r\n/g," ").replace(/[\r\n]/g," ")}}class r{getHtml(e){const n=new t;return n.appendNode(e),n.build()}}class s extends e.Plugin{init(){this.editor.data.processor.htmlWriter=new r}static get pluginName(){return"DrupalHtmlEngine"}}const o={DrupalHtmlEngine:s}})(),p=p.default})())); No newline at end of file core/modules/ckeditor5/js/ckeditor5_plugins/drupalHtmlEngine/src/drupalhtmlbuilder.js +16 −0 Original line number Diff line number Diff line Loading @@ -61,6 +61,8 @@ export default class DrupalHtmlBuilder { this._appendElement(node); } else if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { this._appendChildren(node); } else if (node.nodeType === Node.COMMENT_NODE) { this._appendComment(node); } } Loading Loading @@ -142,6 +144,20 @@ export default class DrupalHtmlBuilder { this._append(container.innerHTML); } /** * Appends a comment to the value. * * @param {DocumentFragment} node * A document fragment to be appended to the value. * * @private */ _appendComment(node) { this._append('<!--'); this._append(node.textContent); this._append('-->'); } /** * Appends a string to the value. * Loading core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5Test.php +43 −0 Original line number Diff line number Diff line Loading @@ -745,4 +745,47 @@ public function testFilterHtmlAllowedGlobalAttributes(): void { $assert_session->responseContains('<p dir="ltr" lang="en">Hello World</p><p dir="rtl" lang="ar">مرحبا بالعالم</p>'); } /** * Ensures that HTML comments are preserved in CKEditor 5. */ public function testComments(): void { $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); // Add a node with text rendered via the Plain Text format. $this->drupalGet('node/add'); $page->fillField('title[0][value]', 'My test content'); $page->fillField('body[0][value]', '<!-- Hamsters, alpacas, llamas, and kittens are cute! --><p>This is a <em>test!</em></p>'); $page->pressButton('Save'); FilterFormat::create([ 'format' => 'ckeditor5', 'name' => 'CKEditor 5 HTML comments test', 'roles' => [RoleInterface::AUTHENTICATED_ID], ])->save(); Editor::create([ 'format' => 'ckeditor5', 'editor' => 'ckeditor5', ])->save(); $this->assertSame([], array_map( function (ConstraintViolation $v) { return (string) $v->getMessage(); }, iterator_to_array(CKEditor5::validatePair( Editor::load('ckeditor5'), FilterFormat::load('ckeditor5') )) )); $this->drupalGet('node/1/edit'); $page->selectFieldOption('body[0][format]', 'ckeditor5'); $this->assertNotEmpty($assert_session->waitForText('Change text format?')); $page->pressButton('Continue'); $this->assertNotEmpty($assert_session->waitForElement('css', '.ck-editor')); $page->pressButton('Save'); $assert_session->responseContains('<!-- Hamsters, alpacas, llamas, and kittens are cute! --><p>This is a <em>test!</em></p>'); } } core/modules/ckeditor5/tests/src/Kernel/CKEditor5PluginManagerTest.php +2 −0 Original line number Diff line number Diff line Loading @@ -1092,6 +1092,7 @@ public function testEnabledPlugins() { 'ckeditor5_globalAttributeDir', 'ckeditor5_globalAttributeLang', 'ckeditor5_heading', 'ckeditor5_htmlComments', 'ckeditor5_paragraph', 'ckeditor5_pasteFromOffice', ]; Loading Loading @@ -1214,6 +1215,7 @@ public function testEnabledPlugins() { 'ckeditor5_emphasis', 'ckeditor5_essentials', 'ckeditor5_heading', 'ckeditor5_htmlComments', 'ckeditor5_paragraph', 'ckeditor5_pasteFromOffice', ]; Loading Loading
core/modules/ckeditor5/ckeditor5.ckeditor5.yml +11 −0 Original line number Diff line number Diff line Loading @@ -83,6 +83,17 @@ ckeditor5_style: elements: - <$any-html5-element class> ckeditor5_htmlComments: ckeditor5: plugins: - htmlSupport.HtmlComment drupal: label: HTML Comment support elements: false library: core/ckeditor5.htmlSupport # @see \Drupal\ckeditor5\Plugin\CKEditor5PluginManagerInterface::getEnabledDefinitions() conditions: [] ckeditor5_arbitraryHtmlSupport: ckeditor5: plugins: [htmlSupport.GeneralHtmlSupport] Loading
core/modules/ckeditor5/js/build/drupalHtmlEngine.js +1 −1 Original line number Diff line number Diff line !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.CKEditor5=t():(e.CKEditor5=e.CKEditor5||{},e.CKEditor5.drupalHtmlEngine=t())}(globalThis,(()=>(()=>{var e={"ckeditor5/src/core.js":(e,t,r)=>{e.exports=r("dll-reference CKEditor5.dll")("./src/core.js")},"dll-reference CKEditor5.dll":e=>{"use strict";e.exports=CKEditor5.dll}},t={};function r(n){var p=t[n];if(void 0!==p)return p.exports;var s=t[n]={exports:{}};return e[n](s,s.exports,r),s.exports}r.d=(e,t)=>{for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var n={};return(()=>{"use strict";r.d(n,{default:()=>o});var e=r("ckeditor5/src/core.js");class t{constructor(){this.chunks=[],this.selfClosingTags=["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]}build(){return this.chunks.join("")}appendNode(e){e.nodeType===Node.TEXT_NODE?this._appendText(e):e.nodeType===Node.ELEMENT_NODE?this._appendElement(e):e.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&this._appendChildren(e)}_appendElement(e){const t=e.nodeName.toLowerCase();this._append("<"),this._append(t),this._appendAttributes(e),this._append(">"),this.selfClosingTags.includes(t)||(this._appendChildren(e),this._append("</"),this._append(t),this._append(">"))}_appendChildren(e){Object.keys(e.childNodes).forEach((t=>{this.appendNode(e.childNodes[t])}))}_appendAttributes(e){Object.keys(e.attributes).forEach((t=>{this._append(" "),this._append(e.attributes[t].name),this._append('="'),this._append(this.constructor._escapeAttribute(e.attributes[t].value)),this._append('"')}))}_appendText(e){const t=document.implementation.createHTMLDocument("").createElement("p");t.textContent=e.textContent,this._append(t.innerHTML)}_append(e){this.chunks.push(e)}static _escapeAttribute(e){return e.replace(/&/g,"&").replace(/'/g,"'").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">").replace(/\r\n/g," ").replace(/[\r\n]/g," ")}}class p{getHtml(e){const r=new t;return r.appendNode(e),r.build()}}class s extends e.Plugin{init(){this.editor.data.processor.htmlWriter=new p}static get pluginName(){return"DrupalHtmlEngine"}}const o={DrupalHtmlEngine:s}})(),n=n.default})())); No newline at end of file !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.CKEditor5=t():(e.CKEditor5=e.CKEditor5||{},e.CKEditor5.drupalHtmlEngine=t())}(globalThis,(()=>(()=>{var e={"ckeditor5/src/core.js":(e,t,n)=>{e.exports=n("dll-reference CKEditor5.dll")("./src/core.js")},"dll-reference CKEditor5.dll":e=>{"use strict";e.exports=CKEditor5.dll}},t={};function n(p){var r=t[p];if(void 0!==r)return r.exports;var s=t[p]={exports:{}};return e[p](s,s.exports,n),s.exports}n.d=(e,t)=>{for(var p in t)n.o(t,p)&&!n.o(e,p)&&Object.defineProperty(e,p,{enumerable:!0,get:t[p]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var p={};return(()=>{"use strict";n.d(p,{default:()=>o});var e=n("ckeditor5/src/core.js");class t{constructor(){this.chunks=[],this.selfClosingTags=["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]}build(){return this.chunks.join("")}appendNode(e){e.nodeType===Node.TEXT_NODE?this._appendText(e):e.nodeType===Node.ELEMENT_NODE?this._appendElement(e):e.nodeType===Node.DOCUMENT_FRAGMENT_NODE?this._appendChildren(e):e.nodeType===Node.COMMENT_NODE&&this._appendComment(e)}_appendElement(e){const t=e.nodeName.toLowerCase();this._append("<"),this._append(t),this._appendAttributes(e),this._append(">"),this.selfClosingTags.includes(t)||(this._appendChildren(e),this._append("</"),this._append(t),this._append(">"))}_appendChildren(e){Object.keys(e.childNodes).forEach((t=>{this.appendNode(e.childNodes[t])}))}_appendAttributes(e){Object.keys(e.attributes).forEach((t=>{this._append(" "),this._append(e.attributes[t].name),this._append('="'),this._append(this.constructor._escapeAttribute(e.attributes[t].value)),this._append('"')}))}_appendText(e){const t=document.implementation.createHTMLDocument("").createElement("p");t.textContent=e.textContent,this._append(t.innerHTML)}_appendComment(e){this._append("\x3c!--"),this._append(e.textContent),this._append("--\x3e")}_append(e){this.chunks.push(e)}static _escapeAttribute(e){return e.replace(/&/g,"&").replace(/'/g,"'").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">").replace(/\r\n/g," ").replace(/[\r\n]/g," ")}}class r{getHtml(e){const n=new t;return n.appendNode(e),n.build()}}class s extends e.Plugin{init(){this.editor.data.processor.htmlWriter=new r}static get pluginName(){return"DrupalHtmlEngine"}}const o={DrupalHtmlEngine:s}})(),p=p.default})())); No newline at end of file
core/modules/ckeditor5/js/ckeditor5_plugins/drupalHtmlEngine/src/drupalhtmlbuilder.js +16 −0 Original line number Diff line number Diff line Loading @@ -61,6 +61,8 @@ export default class DrupalHtmlBuilder { this._appendElement(node); } else if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { this._appendChildren(node); } else if (node.nodeType === Node.COMMENT_NODE) { this._appendComment(node); } } Loading Loading @@ -142,6 +144,20 @@ export default class DrupalHtmlBuilder { this._append(container.innerHTML); } /** * Appends a comment to the value. * * @param {DocumentFragment} node * A document fragment to be appended to the value. * * @private */ _appendComment(node) { this._append('<!--'); this._append(node.textContent); this._append('-->'); } /** * Appends a string to the value. * Loading
core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5Test.php +43 −0 Original line number Diff line number Diff line Loading @@ -745,4 +745,47 @@ public function testFilterHtmlAllowedGlobalAttributes(): void { $assert_session->responseContains('<p dir="ltr" lang="en">Hello World</p><p dir="rtl" lang="ar">مرحبا بالعالم</p>'); } /** * Ensures that HTML comments are preserved in CKEditor 5. */ public function testComments(): void { $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); // Add a node with text rendered via the Plain Text format. $this->drupalGet('node/add'); $page->fillField('title[0][value]', 'My test content'); $page->fillField('body[0][value]', '<!-- Hamsters, alpacas, llamas, and kittens are cute! --><p>This is a <em>test!</em></p>'); $page->pressButton('Save'); FilterFormat::create([ 'format' => 'ckeditor5', 'name' => 'CKEditor 5 HTML comments test', 'roles' => [RoleInterface::AUTHENTICATED_ID], ])->save(); Editor::create([ 'format' => 'ckeditor5', 'editor' => 'ckeditor5', ])->save(); $this->assertSame([], array_map( function (ConstraintViolation $v) { return (string) $v->getMessage(); }, iterator_to_array(CKEditor5::validatePair( Editor::load('ckeditor5'), FilterFormat::load('ckeditor5') )) )); $this->drupalGet('node/1/edit'); $page->selectFieldOption('body[0][format]', 'ckeditor5'); $this->assertNotEmpty($assert_session->waitForText('Change text format?')); $page->pressButton('Continue'); $this->assertNotEmpty($assert_session->waitForElement('css', '.ck-editor')); $page->pressButton('Save'); $assert_session->responseContains('<!-- Hamsters, alpacas, llamas, and kittens are cute! --><p>This is a <em>test!</em></p>'); } }
core/modules/ckeditor5/tests/src/Kernel/CKEditor5PluginManagerTest.php +2 −0 Original line number Diff line number Diff line Loading @@ -1092,6 +1092,7 @@ public function testEnabledPlugins() { 'ckeditor5_globalAttributeDir', 'ckeditor5_globalAttributeLang', 'ckeditor5_heading', 'ckeditor5_htmlComments', 'ckeditor5_paragraph', 'ckeditor5_pasteFromOffice', ]; Loading Loading @@ -1214,6 +1215,7 @@ public function testEnabledPlugins() { 'ckeditor5_emphasis', 'ckeditor5_essentials', 'ckeditor5_heading', 'ckeditor5_htmlComments', 'ckeditor5_paragraph', 'ckeditor5_pasteFromOffice', ]; Loading