diff --git a/.cspell-project-words.txt b/.cspell-project-words.txt index 5fd7ad8..469ae38 100644 --- a/.cspell-project-words.txt +++ b/.cspell-project-words.txt @@ -30,3 +30,4 @@ unusedcode wapmorgan Wenger wengerk +dolo diff --git a/js/build/nbsp.js b/js/build/nbsp.js index 5918b4a..b634413 100644 --- a/js/build/nbsp.js +++ b/js/build/nbsp.js @@ -1 +1 @@ -!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.nbsp=t())}(self,(()=>(()=>{var e={"ckeditor5/src/core.js":(e,t,s)=>{e.exports=s("dll-reference CKEditor5.dll")("./src/core.js")},"ckeditor5/src/ui.js":(e,t,s)=>{e.exports=s("dll-reference CKEditor5.dll")("./src/ui.js")},"dll-reference CKEditor5.dll":e=>{"use strict";e.exports=CKEditor5.dll}},t={};function s(r){var o=t[r];if(void 0!==o)return o.exports;var n=t[r]={exports:{}};return e[r](n,n.exports,s),n.exports}s.d=(e,t)=>{for(var r in t)s.o(t,r)&&!s.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},s.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var r={};return(()=>{"use strict";s.d(r,{default:()=>d});var e=s("ckeditor5/src/core.js");class t extends e.Command{execute(){this.editor.model.change((e=>{const t=this.editor.data.processor.toView(" "),s=this.editor.data.toModel(t);this.editor.model.insertContent(s)}))}}class o extends e.Plugin{_defineSchema(){this.editor.model.schema.register("nbsp",{allowWhere:"$text",allowAttributesOf:"$text",isInline:!0,isObject:!0})}_defineConverters(){this.editor.conversion.elementToElement({model:"nbsp",view:"nbsp"})}init(){const e=this.editor;this.editor.commands.add("nbsp",new t(this.editor)),this._defineSchema(),this._defineConverters(),e.keystrokes.set(["ctrl",32],((t,s)=>{e.commands.execute("nbsp"),s()}))}}var n=s("ckeditor5/src/ui.js");class i extends e.Plugin{init(){const e=this.editor;e.ui.componentFactory.add("nbsp",(t=>{const s=new n.ButtonView(t);return s.set({label:"Insert non-breaking space",icon:'\n\n\n\n\n\n\n',tooltip:!0}),s.on("execute",(()=>{e.model.change((t=>{e.commands.execute("nbsp")}))})),s}))}}class c extends e.Plugin{static get pluginName(){return"Nbsp"}static get requires(){return[o,i]}}const d={Nbsp:c}})(),r=r.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.nbsp=t())}(self,(()=>(()=>{var e={"ckeditor5/src/core.js":(e,t,r)=>{e.exports=r("dll-reference CKEditor5.dll")("./src/core.js")},"ckeditor5/src/ui.js":(e,t,r)=>{e.exports=r("dll-reference CKEditor5.dll")("./src/ui.js")},"dll-reference CKEditor5.dll":e=>{"use strict";e.exports=CKEditor5.dll}},t={};function r(s){var o=t[s];if(void 0!==o)return o.exports;var n=t[s]={exports:{}};return e[s](n,n.exports,r),n.exports}r.d=(e,t)=>{for(var s in t)r.o(t,s)&&!r.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:t[s]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var s={};return(()=>{"use strict";r.d(s,{default:()=>d});var e=r("ckeditor5/src/core.js");class t extends e.Command{execute(){const e=this.editor,t=e.model.document.selection;e.model.change((r=>{const s=r.createElement("nbsp",{...Object.fromEntries(t.getAttributes())});e.model.insertObject(s,null,null,{setSelection:"on"})}))}}class o extends e.Plugin{_defineSchema(){this.editor.model.schema.register("nbsp",{allowWhere:"$text",allowAttributesOf:"$text",isInline:!0,isObject:!0})}_defineConverters(){this.editor.conversion.elementToElement({model:"nbsp",view:"nbsp"})}init(){const e=this.editor;this.editor.commands.add("nbsp",new t(this.editor)),this._defineSchema(),this._defineConverters(),e.keystrokes.set(["ctrl",32],((t,r)=>{e.commands.execute("nbsp"),r()}))}}var n=r("ckeditor5/src/ui.js");class i extends e.Plugin{init(){const e=this.editor;e.ui.componentFactory.add("nbsp",(t=>{const r=new n.ButtonView(t);return r.set({label:"Insert non-breaking space",icon:'\n\n\n\n\n\n\n',tooltip:!0}),r.on("execute",(()=>{e.model.change((t=>{e.commands.execute("nbsp")}))})),r}))}}class c extends e.Plugin{static get pluginName(){return"Nbsp"}static get requires(){return[o,i]}}const d={Nbsp:c}})(),s=s.default})())); \ No newline at end of file diff --git a/js/ckeditor5_plugins/nbsp/src/nbspCommand.js b/js/ckeditor5_plugins/nbsp/src/nbspCommand.js index 7fd75cc..7e9588e 100644 --- a/js/ckeditor5_plugins/nbsp/src/nbspCommand.js +++ b/js/ckeditor5_plugins/nbsp/src/nbspCommand.js @@ -2,12 +2,20 @@ import { Command } from "ckeditor5/src/core"; export default class NbspCommand extends Command { execute() { + const editor = this.editor; + const selection = editor.model.document.selection; + // Insert tag in the current selection location. - this.editor.model.change((writer) => { - const nbspElement = " "; - const nbspViewFragment = this.editor.data.processor.toView(nbspElement); - const nbspModelFragment = this.editor.data.toModel(nbspViewFragment); - this.editor.model.insertContent(nbspModelFragment); + editor.model.change((writer) => { + // Create a element with all the selection attributes. + const placeholder = writer.createElement("nbsp", { + ...Object.fromEntries(selection.getAttributes()), + }); + + // Insert it into the document. Put the selection on the inserted element. + editor.model.insertObject(placeholder, null, null, { + setSelection: "on", + }); }); } } diff --git a/tests/src/FunctionalJavascript/DrupalCKEditor5NbspTest.php b/tests/src/FunctionalJavascript/DrupalCKEditor5NbspTest.php index 7c4c50f..1652f27 100644 --- a/tests/src/FunctionalJavascript/DrupalCKEditor5NbspTest.php +++ b/tests/src/FunctionalJavascript/DrupalCKEditor5NbspTest.php @@ -199,4 +199,41 @@ public function testNbspInsideLinkTag() { $this->assertEquals("dolore sit", $link[0]->textContent); } + /** + * Tests using Drupal Nbsp button to add non-breaking space into Link via Btn. + * + * @group kevin + */ + public function testNbspInsideLinkTagWithButton() { + $this->drupalGet('node/add/page'); + $this->waitForEditor(); + $assert_session = $this->assertSession(); + $editor = $assert_session->waitForElementVisible('css', '.ck-editor__editable', 1000); + + // Emulate the user typing a link element. + $this->pressEditorButton('Source'); + $source_text_area = $assert_session->waitForElement('css', '.ck-source-editing-area textarea'); + $source_text_area->setValue('lorem ipsum doloresitdolo amet.'); + + // Click source again to make source inactive and have the Schema refreshed. + $this->pressEditorButton('Source'); + + // Place an NBSP element inside the link by replacing the content of the + // element. + $this->selectTextInsideElement('.ck-editor__editable a em'); + $this->pressEditorButton('Insert non-breaking space'); + + $this->assertNotEmpty($assert_session->waitForElement('css', '.ck-editor__editable a em > nbsp')); + $this->assertEquals('

lorem ipsum dolore
dolo
amet.

', $editor->getHtml()); + + // The link should be left intact and we should have 1 NBSP element inside. + $xpath = new \DOMXPath($this->getEditorDataAsDom()); + $nbsp = $xpath->query('//nbsp'); + $this->assertCount(1, $nbsp); + $this->assertEquals(" ", $nbsp[0]->firstChild->nodeValue); + $link = $xpath->query('//a'); + $this->assertCount(1, $link); + $this->assertEquals("dolore dolo", $link[0]->textContent); + } + }