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

Commit

Permalink
Merge branch 'master' into t/ckeditor5-alignment/16
Browse files Browse the repository at this point in the history
  • Loading branch information
Reinmar committed Mar 11, 2018
2 parents 10b50a6 + d20d660 commit 4cb0c34
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 2 deletions.
45 changes: 43 additions & 2 deletions src/controller/datacontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import mix from '@ckeditor/ckeditor5-utils/src/mix';
import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';

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

Expand Down Expand Up @@ -100,6 +101,8 @@ export default class DataController {
this.upcastDispatcher.on( 'text', convertText(), { priority: 'lowest' } );
this.upcastDispatcher.on( 'element', convertToModelFragment(), { priority: 'lowest' } );
this.upcastDispatcher.on( 'documentFragment', convertToModelFragment(), { priority: 'lowest' } );

this.decorate( 'init' );
}

/**
Expand Down Expand Up @@ -170,9 +173,42 @@ export default class DataController {
return viewDocumentFragment;
}

/**
* Sets initial input data parsed by the {@link #processor data processor} and
* converted by the {@link #upcastDispatcher view-to-model converters}.
* Initial data can be set only to document that {@link module:engine/model/document~Document#version} is equal 0.
*
* **Note** This method is {@link module:utils/observablemixin~ObservableMixin#decorate decorated} which is
* used by e.g. collaborative editing plugin that syncs remote data on init.
*
* @fires init
* @param {String} data Input data.
* @param {String} [rootName='main'] Root name.
*/
init( data, rootName = 'main' ) {
if ( this.model.document.version ) {
/**
* Cannot set initial data to not empty {@link module:engine/model/document~Document}.
* Initial data should be set once, during {@link module:core/editor/editor~Editor} initialization,
* when the {@link module:engine/model/document~Document#version} is equal 0.
*
* @error datacontroller-init-document-not-empty
*/
throw new CKEditorError( 'datacontroller-init-document-not-empty: Trying to set initial data to not empty document.' );
}

const modelRoot = this.model.document.getRoot( rootName );

this.model.enqueueChange( 'transparent', writer => {
writer.insert( this.parse( data, modelRoot ), modelRoot );
} );
}

/**
* Sets input data parsed by the {@link #processor data processor} and
* converted by the {@link #upcastDispatcher view-to-model converters}.
* This method can be used any time to replace existing editor data by the new one without clearing the
* {@link module:engine/model/document~Document#history document history}.
*
* This method also creates a batch with all the changes applied. If all you need is to parse data, use
* the {@link #parse} method.
Expand All @@ -185,8 +221,6 @@ export default class DataController {
const modelRoot = this.model.document.getRoot( rootName );

this.model.enqueueChange( 'transparent', writer => {
// Clearing selection is a workaround for ticket #569 (LiveRange loses position after removing data from document).
// After fixing it this code should be removed.
writer.setSelection( null );
writer.removeSelectionAttribute( this.model.document.selection.getAttributeKeys() );

Expand Down Expand Up @@ -237,6 +271,13 @@ export default class DataController {
* Removes all event listeners set by the DataController.
*/
destroy() {}

/**
* Event fired by decorated {@link #init} method.
* See {@link module:utils/observablemixin~ObservableMixin.decorate} for more information and samples.
*
* @event init
*/
}

mix( DataController, ObservableMixin );
Expand Down
56 changes: 56 additions & 0 deletions tests/controller/datacontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Model from '../../src/model/model';
import Range from '../../src/model/range';
import DataController from '../../src/controller/datacontroller';
import HtmlDataProcessor from '../../src/dataprocessor/htmldataprocessor';
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';

import ModelDocumentFragment from '../../src/model/documentfragment';
import ViewDocumentFragment from '../../src/view/documentfragment';
Expand Down Expand Up @@ -152,6 +153,61 @@ describe( 'DataController', () => {
} );
} );

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

data.on( 'init', spy );

data.init( 'foo bar' );

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

it( 'should throw an error when document data is already initialized', () => {
data.init( '<p>Foo</p>' );

expect( () => {
data.init( '<p>Bar</p>' );
} ).to.throw(
CKEditorError,
'datacontroller-init-document-not-empty: Trying to set initial data to not empty document.'
);
} );

it( 'should set data to default main root', () => {
schema.extend( '$text', { allowIn: '$root' } );
data.init( 'foo' );

expect( getData( model, { withoutSelection: true } ) ).to.equal( 'foo' );
} );

it( 'should get root name as a parameter', () => {
schema.extend( '$text', { allowIn: '$root' } );
data.init( 'foo', 'title' );

expect( getData( model, { withoutSelection: true, rootName: 'title' } ) ).to.equal( 'foo' );
} );

it( 'should create a batch', () => {
schema.extend( '$text', { allowIn: '$root' } );
data.init( 'foo' );

expect( count( modelDocument.history.getDeltas() ) ).to.equal( 1 );
} );

it( 'should cause firing change event', () => {
const spy = sinon.spy();

schema.extend( '$text', { allowIn: '$root' } );
model.document.on( 'change', spy );

data.init( 'foo' );

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

describe( 'set()', () => {
it( 'should set data to default main root', () => {
schema.extend( '$text', { allowIn: '$root' } );
Expand Down

0 comments on commit 4cb0c34

Please sign in to comment.