Skip to content

Commit

Permalink
Merge pull request #10697 from ckeditor/ck/10505
Browse files Browse the repository at this point in the history
Fix (source-editing): Calling `editor.getData()` while in the source editing mode should return the data from the source editor passed through the model. Closes #10505.

Feature (engine): The `DataController#get()` is now decorated and fires a `get` event on the method call. See #10505.
  • Loading branch information
oleq authored Oct 15, 2021
2 parents 5189087 + 5cbac0b commit 971763c
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 16 deletions.
1 change: 1 addition & 0 deletions packages/ckeditor5-autosave/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@ckeditor/ckeditor5-dev-utils": "^25.4.0",
"@ckeditor/ckeditor5-editor-classic": "^30.0.0",
"@ckeditor/ckeditor5-paragraph": "^30.0.0",
"@ckeditor/ckeditor5-source-editing": "^30.0.0",
"@ckeditor/ckeditor5-theme-lark": "^30.0.0",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
Expand Down
7 changes: 5 additions & 2 deletions packages/ckeditor5-autosave/tests/manual/autosave.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@

import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';

import SourceEditing from '@ckeditor/ckeditor5-source-editing/src/sourceediting';
import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset';
import Autosave from '../../src/autosave';

ClassicEditor
.create( document.querySelector( '#editor' ), {
plugins: [ ArticlePluginSet, Autosave ],
toolbar: [ 'heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' ],
plugins: [ ArticlePluginSet, Autosave, SourceEditing ],
toolbar: [
'heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo', '|', 'sourceEditing'
],
image: {
toolbar: [ 'imageStyle:block', 'imageStyle:side', '|', 'imageTextAlternative' ]
},
Expand Down
11 changes: 11 additions & 0 deletions packages/ckeditor5-engine/src/controller/datacontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ export default class DataController {

this.decorate( 'init' );
this.decorate( 'set' );
this.decorate( 'get' );

// Fire the `ready` event when the initialization has completed. Such low-level listener gives possibility
// to plug into the initialization pipeline without interrupting the initialization flow.
Expand All @@ -163,6 +164,7 @@ export default class DataController {
* Returns the model's data converted by downcast dispatchers attached to {@link #downcastDispatcher} and
* formatted by the {@link #processor data processor}.
*
* @fires get
* @param {Object} [options] Additional configuration for the retrieved data. `DataController` provides two optional
* properties: `rootName` and `trim`. Other properties of this object are specified by various editor features.
* @param {String} [options.rootName='main'] Root name.
Expand Down Expand Up @@ -523,6 +525,15 @@ export default class DataController {
*
* @event set
*/

/**
* Event fired after {@link #get get() method} has been run.
*
* The `get` event is fired by decorated {@link #get} method.
* See {@link module:utils/observablemixin~ObservableMixin#decorate} for more information and samples.
*
* @event get
*/
}

mix( DataController, ObservableMixin );
Expand Down
10 changes: 10 additions & 0 deletions packages/ckeditor5-engine/tests/controller/datacontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,16 @@ describe( 'DataController', () => {
downcastHelpers.elementToElement( { model: 'paragraph', view: 'p' } );
} );

it( 'should be decorated', () => {
const spy = sinon.spy();

data.on( 'get', spy );

data.get();

sinon.assert.calledWithExactly( spy, sinon.match.any, [] );
} );

it( 'should get paragraph with text', () => {
setData( model, '<paragraph>foo</paragraph>' );

Expand Down
44 changes: 30 additions & 14 deletions packages/ckeditor5-source-editing/src/sourceediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,13 @@ export default class SourceEditing extends Plugin {

this.listenTo( editor, 'change:isReadOnly', ( evt, name, isReadOnly ) => this._handleReadOnlyMode( isReadOnly ) );
}

// Update the editor data while calling editor.getData() in the source editing mode.
editor.data.on( 'get', () => {
if ( this.isSourceEditingMode ) {
this._updateEditorData();
}
}, { priority: 'high' } );
}

/**
Expand Down Expand Up @@ -261,6 +268,29 @@ export default class SourceEditing extends Plugin {
const editor = this.editor;
const editingView = editor.editing.view;

this._updateEditorData();

editingView.change( writer => {
for ( const [ rootName ] of this._replacedRoots ) {
writer.removeClass( 'ck-hidden', editingView.document.getRoot( rootName ) );
}
} );

this._elementReplacer.restore();

this._replacedRoots.clear();
this._dataFromRoots.clear();

editingView.focus();
}

/**
* Updates the source data in all hidden editing roots.
*
* @private
*/
_updateEditorData() {
const editor = this.editor;
const data = {};

for ( const [ rootName, domSourceEditingElementWrapper ] of this._replacedRoots ) {
Expand All @@ -272,25 +302,11 @@ export default class SourceEditing extends Plugin {
if ( oldData !== newData ) {
data[ rootName ] = newData;
}

editingView.change( writer => {
const viewRoot = editingView.document.getRoot( rootName );

writer.removeClass( 'ck-hidden', viewRoot );
} );
}

this._elementReplacer.restore();

this._replacedRoots.clear();

this._dataFromRoots.clear();

if ( Object.keys( data ).length ) {
editor.data.set( data, { batchType: 'default' } );
}

editor.editing.view.focus();
}

/**
Expand Down
34 changes: 34 additions & 0 deletions packages/ckeditor5-source-editing/tests/sourceediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,40 @@ describe( 'SourceEditing', () => {
expect( editor.data.get() ).to.equal( '<p>Foo</p>' );
} );

it( 'should update the editor data after calling editor.getData() in the source editing mode', () => {
const setDataSpy = sinon.spy();

editor.data.on( 'set', setDataSpy );

button.fire( 'execute' );

const domRoot = editor.editing.view.getDomRoot();
const textarea = domRoot.nextSibling.children[ 0 ];

textarea.value = 'foo';
textarea.dispatchEvent( new Event( 'input' ) );

// Trigger getData() while in the source editing mode.
expect( editor.getData() ).to.equal( '<p>foo</p>' );

textarea.value = 'bar';
textarea.dispatchEvent( new Event( 'input' ) );

// Exit source editing mode.
button.fire( 'execute' );

expect( setDataSpy.calledTwice ).to.be.true;
expect( setDataSpy.firstCall.args[ 1 ] ).to.deep.equal( [
{ main: 'foo' },
{ batchType: 'default' }
] );
expect( setDataSpy.secondCall.args[ 1 ] ).to.deep.equal( [
{ main: 'bar' },
{ batchType: 'default' }
] );
expect( editor.data.get() ).to.equal( '<p>bar</p>' );
} );

it( 'should insert the formatted HTML source (editor output) into the textarea', () => {
button.fire( 'execute' );

Expand Down

0 comments on commit 971763c

Please sign in to comment.