Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Used ObservableMixin.decorate() for methods which already fire events #966

Merged
merged 3 commits into from
Jun 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 22 additions & 37 deletions src/controller/datacontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/

import mix from '@ckeditor/ckeditor5-utils/src/mix';
import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';

import Mapper from '../conversion/mapper';

Expand Down Expand Up @@ -38,7 +38,7 @@ import getSelectedContent from './getselectedcontent';
* * {@link module:engine/conversion/modelconversiondispatcher~ModelConversionDispatcher model to view} and
* * {@link module:engine/conversion/viewconversiondispatcher~ViewConversionDispatcher view to model} converters.
*
* @mixes module:utils/emittermixin~EmitterMixin
* @mixes module:utils/emittermixin~ObservableMixin
*/
export default class DataController {
/**
Expand Down Expand Up @@ -117,12 +117,8 @@ export default class DataController {
this.viewToModel.on( 'element', convertToModelFragment(), { priority: 'lowest' } );
this.viewToModel.on( 'documentFragment', convertToModelFragment(), { priority: 'lowest' } );

this.on( 'insertContent', ( evt, data ) => insertContent( this, data.content, data.selection, data.batch ) );
this.on( 'deleteContent', ( evt, data ) => deleteContent( data.selection, data.batch, data.options ) );
this.on( 'modifySelection', ( evt, data ) => modifySelection( this, data.selection, data.options ) );
this.on( 'getSelectedContent', ( evt, data ) => {
data.content = getSelectedContent( data.selection );
} );
[ 'insertContent', 'deleteContent', 'modifySelection', 'getSelectedContent' ]
.forEach( methodName => this.decorate( methodName ) );
}

/**
Expand Down Expand Up @@ -257,7 +253,7 @@ export default class DataController {
* changes will be added to a new batch.
*/
insertContent( content, selection, batch ) {
this.fire( 'insertContent', { content, selection, batch } );
insertContent( this, content, selection, batch );
}

/**
Expand All @@ -277,7 +273,7 @@ export default class DataController {
* @param {Object} options See {@link module:engine/controller/deletecontent~deleteContent}'s options.
*/
deleteContent( selection, batch, options ) {
this.fire( 'deleteContent', { batch, selection, options } );
deleteContent( selection, batch, options );
}

/**
Expand All @@ -288,7 +284,7 @@ export default class DataController {
* @param {Object} options See {@link module:engine/controller/modifyselection~modifySelection}'s options.
*/
modifySelection( selection, options ) {
this.fire( 'modifySelection', { selection, options } );
modifySelection( this, selection, options );
}

/**
Expand All @@ -299,59 +295,48 @@ export default class DataController {
* @returns {module:engine/model/documentfragment~DocumentFragment} Document fragment holding the clone of the selected content.
*/
getSelectedContent( selection ) {
const evtData = { selection };

this.fire( 'getSelectedContent', evtData );

return evtData.content;
return getSelectedContent( selection );
}
}

mix( DataController, EmitterMixin );
mix( DataController, ObservableMixin );

/**
* Event fired when {@link #insertContent} method is called.
*
* The {@link #insertContent default action of that method} is implemented as a
* listener to this event so it can be fully customized by the features.
*
* @event insertContent
* @param {Object} data
* @param {module:engine/view/documentfragment~DocumentFragment|module:engine/model/item~Item} data.content The content to insert.
* @param {module:engine/model/selection~Selection} data.selection Selection into which the content should be inserted.
* @param {module:engine/model/batch~Batch} [data.batch] Batch to which deltas will be added.
* @param {Array} args The arguments passed to the original method.
*/

/**
* Event fired when {@link #deleteContent} method is called.
* The {@link module:engine/controller/deletecontent~deleteContent default action of that method} is implemented as a
*
* The {@link #deleteContent default action of that method} is implemented as a
* listener to this event so it can be fully customized by the features.
*
* @event deleteContent
* @param {Object} data
* @param {module:engine/model/batch~Batch} data.batch
* @param {module:engine/model/selection~Selection} data.selection
* @param {Object} data.options See {@link module:engine/controller/deletecontent~deleteContent}'s options.
* @param {Array} args The arguments passed to the original method.
*/

/**
* Event fired when {@link #modifySelection} method is called.
* The {@link module:engine/controller/modifyselection~modifySelection default action of that method} is implemented as a
*
* The {@link #modifySelection default action of that method} is implemented as a
* listener to this event so it can be fully customized by the features.
*
* @event modifySelection
* @param {Object} data
* @param {module:engine/model/selection~Selection} data.selection
* @param {Object} data.options See {@link module:engine/controller/modifyselection~modifySelection}'s options.
* @param {Array} args The arguments passed to the original method.
*/

/**
* Event fired when {@link module:engine/controller/datacontroller~DataController#getSelectedContent} method is called.
* The {@link module:engine/controller/getselectedcontent~getSelectedContent default action of that method} is implemented as a
* Event fired when {@link #getSelectedContent} method is called.
*
* The {@link #getSelectedContent default action of that method} is implemented as a
* listener to this event so it can be fully customized by the features.
*
* @event module:engine/controller/datacontroller~DataController#getSelectedContent
* @param {Object} data
* @param {module:engine/model/selection~Selection} data.selection
* @param {module:engine/model/documentfragment~DocumentFragment} data.content The document fragment to return
* (holding a clone of the selected content).
* @event getSelectedContent
* @param {Array} args The arguments passed to the original method.
*/
12 changes: 4 additions & 8 deletions src/view/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,7 @@ export default class Document {

injectQuirksHandling( this );

// Listens `render` event on default priority.
// This way we can attach other listeners before or after rendering execution.
this.on( 'render', () => {
this.disableObservers();
this.renderer.render();
this.enableObservers();
} );
this.decorate( 'render' );
}

/**
Expand Down Expand Up @@ -266,7 +260,9 @@ export default class Document {
* @fires render
*/
render() {
this.fire( 'render' );
this.disableObservers();
this.renderer.render();
this.enableObservers();
}

/**
Expand Down
180 changes: 59 additions & 121 deletions tests/controller/datacontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,94 +42,6 @@ describe( 'DataController', () => {

expect( data.processor ).to.be.undefined;
} );

it( 'should add insertContent listener and inserts document fragment when event fires', () => {
const batch = modelDocument.batch();
const content = new ModelDocumentFragment( [ new ModelText( 'x' ) ] );

schema.registerItem( 'paragraph', '$block' );

setData( modelDocument, '<paragraph>a[]b</paragraph>' );

data.fire( 'insertContent', { content, selection: modelDocument.selection, batch } );

expect( getData( modelDocument ) ).to.equal( '<paragraph>ax[]b</paragraph>' );
expect( batch.deltas.length ).to.be.above( 0 );
} );

it( 'should add insertContent listener and inserts an item when event fires', () => {
const batch = modelDocument.batch();
const content = new ModelText( 'x' );

schema.registerItem( 'paragraph', '$block' );

setData( modelDocument, '<paragraph>a[]b</paragraph>' );

data.fire( 'insertContent', { content, selection: modelDocument.selection, batch } );

expect( getData( modelDocument ) ).to.equal( '<paragraph>ax[]b</paragraph>' );
expect( batch.deltas.length ).to.be.above( 0 );
} );

it( 'should add deleteContent listener', () => {
schema.registerItem( 'paragraph', '$block' );

setData( modelDocument, '<paragraph>f[oo</paragraph><paragraph>ba]r</paragraph>' );

const batch = modelDocument.batch();

data.fire( 'deleteContent', { batch, selection: modelDocument.selection } );

expect( getData( modelDocument ) ).to.equal( '<paragraph>f[]</paragraph><paragraph>r</paragraph>' );
expect( batch.deltas ).to.not.be.empty;
} );

it( 'should add deleteContent listener which passes ', () => {
schema.registerItem( 'paragraph', '$block' );

setData( modelDocument, '<paragraph>f[oo</paragraph><paragraph>ba]r</paragraph>' );

const batch = modelDocument.batch();

data.fire( 'deleteContent', {
batch,
selection: modelDocument.selection,
options: { merge: true }
} );

expect( getData( modelDocument ) ).to.equal( '<paragraph>f[]r</paragraph>' );
} );

it( 'should add modifySelection listener', () => {
schema.registerItem( 'paragraph', '$block' );

setData( modelDocument, '<paragraph>foo[]bar</paragraph>' );

data.fire( 'modifySelection', {
selection: modelDocument.selection,
options: {
direction: 'backward'
}
} );

expect( getData( modelDocument ) )
.to.equal( '<paragraph>fo[o]bar</paragraph>' );
expect( modelDocument.selection.isBackward ).to.true;
} );

it( 'should add getSelectedContent listener', () => {
schema.registerItem( 'paragraph', '$block' );

setData( modelDocument, '<paragraph>fo[ob]ar</paragraph>' );

const evtData = {
selection: modelDocument.selection
};

data.fire( 'getSelectedContent', evtData );

expect( stringify( evtData.content ) ).to.equal( 'ob' );
} );
} );

describe( 'parse', () => {
Expand Down Expand Up @@ -425,80 +337,106 @@ describe( 'DataController', () => {
} );

describe( 'insertContent', () => {
it( 'should fire the insertContent event', () => {
it( 'should be decorated', () => {
schema.allow( { name: '$text', inside: '$root' } ); // To surpress warnings.

const spy = sinon.spy();
const content = new ModelDocumentFragment( [ new ModelText( 'x' ) ] );
const batch = modelDocument.batch();
schema.allow( { name: '$text', inside: '$root' } );

data.on( 'insertContent', spy );

data.insertContent( content, modelDocument.selection, batch );
data.insertContent( new ModelText( 'a' ), modelDocument.selection );

expect( spy.calledOnce ).to.be.true;
} );

it( 'should insert content (item)', () => {
schema.registerItem( 'paragraph', '$block' );

setData( modelDocument, '<paragraph>fo[]ar</paragraph>' );

data.insertContent( new ModelText( 'ob' ), modelDocument.selection );

expect( getData( modelDocument ) ).to.equal( '<paragraph>foob[]ar</paragraph>' );
} );

it( 'should insert content (document fragment)', () => {
schema.registerItem( 'paragraph', '$block' );

setData( modelDocument, '<paragraph>fo[]ar</paragraph>' );

data.insertContent( new ModelDocumentFragment( [ new ModelText( 'ob' ) ] ), modelDocument.selection );

expect( spy.args[ 0 ][ 1 ] ).to.deep.equal( {
batch,
selection: modelDocument.selection,
content
} );
expect( getData( modelDocument ) ).to.equal( '<paragraph>foob[]ar</paragraph>' );
} );
} );

describe( 'deleteContent', () => {
it( 'should fire the deleteContent event', () => {
it( 'should be decorated', () => {
const spy = sinon.spy();
const batch = modelDocument.batch();

data.on( 'deleteContent', spy );

data.deleteContent( modelDocument.selection, batch );
data.deleteContent( modelDocument.selection );

const evtData = spy.args[ 0 ][ 1 ];
expect( spy.calledOnce ).to.be.true;
} );

expect( evtData.batch ).to.equal( batch );
expect( evtData.selection ).to.equal( modelDocument.selection );
it( 'should delete selected content', () => {
schema.registerItem( 'paragraph', '$block' );

setData( modelDocument, '<paragraph>fo[ob]ar</paragraph>' );

data.deleteContent( modelDocument.selection, modelDocument.batch() );

expect( getData( modelDocument ) ).to.equal( '<paragraph>fo[]ar</paragraph>' );
} );
} );

describe( 'modifySelection', () => {
it( 'should fire the deleteContent event', () => {
it( 'should be decorated', () => {
schema.registerItem( 'paragraph', '$block' );
setData( modelDocument, '<paragraph>fo[ob]ar</paragraph>' );

const spy = sinon.spy();
const opts = { direction: 'backward' };

data.on( 'modifySelection', spy );

data.modifySelection( modelDocument.selection, opts );
data.modifySelection( modelDocument.selection );

const evtData = spy.args[ 0 ][ 1 ];
expect( spy.calledOnce ).to.be.true;
} );

expect( evtData.selection ).to.equal( modelDocument.selection );
expect( evtData.options ).to.equal( opts );
it( 'should modify a selection', () => {
schema.registerItem( 'paragraph', '$block' );

setData( modelDocument, '<paragraph>fo[ob]ar</paragraph>' );

data.modifySelection( modelDocument.selection, { direction: 'backward' } );

expect( getData( modelDocument ) ).to.equal( '<paragraph>fo[o]bar</paragraph>' );
} );
} );

describe( 'getSelectedContent', () => {
it( 'should fire the getSelectedContent event', () => {
it( 'should be decorated', () => {
const spy = sinon.spy();
const sel = new ModelSelection();

data.on( 'getSelectedContent', spy );

data.getSelectedContent( sel );

const evtData = spy.args[ 0 ][ 1 ];

expect( evtData.selection ).to.equal( sel );
expect( spy.calledOnce ).to.be.true;
} );

it( 'should return the evtData.content of the getSelectedContent event', () => {
const frag = new ModelDocumentFragment();
it( 'should return selected content', () => {
schema.registerItem( 'paragraph', '$block' );

data.on( 'getSelectedContent', ( evt, data ) => {
data.content = frag;
setData( modelDocument, '<paragraph>fo[ob]ar</paragraph>' );

evt.stop();
}, { priority: 'high' } );
const content = data.getSelectedContent( modelDocument.selection );

expect( data.getSelectedContent() ).to.equal( frag );
expect( stringify( content ) ).to.equal( 'ob' );
} );
} );
} );
Loading