From 4d6da2299c0481b0fb34960054afd4649780dbf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Koszuli=C5=84ski?= Date: Tue, 15 Nov 2016 13:42:09 +0100 Subject: [PATCH 1/2] Renamed insert to insertContent and changed it param to model's doc frag. --- src/controller/datacontroller.js | 14 +++---- .../{insert.js => insertcontent.js} | 37 +++++++++---------- tests/controller/datacontroller.js | 20 +++++----- tests/controller/editingcontroller.js | 1 - .../{insert.js => insertcontent.js} | 24 ++++-------- 5 files changed, 41 insertions(+), 55 deletions(-) rename src/controller/{insert.js => insertcontent.js} (93%) rename tests/controller/{insert.js => insertcontent.js} (96%) diff --git a/src/controller/datacontroller.js b/src/controller/datacontroller.js index 3d9be1f34..b36ba77dd 100644 --- a/src/controller/datacontroller.js +++ b/src/controller/datacontroller.js @@ -19,7 +19,7 @@ import ViewDocumentFragment from '../view/documentfragment.js'; import ModelRange from '../model/range.js'; import ModelPosition from '../model/position.js'; -import insert from './insert.js'; +import insertContent from './insertcontent.js'; /** * Controller for the data pipeline. The data pipeline controls how data is retrieved from the document @@ -111,7 +111,7 @@ export default class DataController { this.viewToModel.on( 'element', convertToModelFragment(), { priority: 'lowest' } ); this.viewToModel.on( 'documentFragment', convertToModelFragment(), { priority: 'lowest' } ); - this.on( 'insert', ( evt, data ) => insert( this, data.content, data.selection, data.batch ) ); + this.on( 'insertContent', ( evt, data ) => insertContent( this, data.content, data.selection, data.batch ) ); } /** @@ -214,16 +214,16 @@ export default class DataController { destroy() {} /** - * See {@link engine.controller.insert}. + * See {@link engine.controller.insertContent}. * - * @fires engine.controller.DataController#insert - * @param {engine.view.DocumentFragment} content The content to insert. + * @fires engine.controller.DataController#insertContent + * @param {engine.model.DocumentFragment} content The content to insert. * @param {engine.model.Selection} selection Selection into which the content should be inserted. * @param {engine.model.Batch} [batch] Batch to which deltas will be added. If not specified, then * changes will be added to a new batch. */ - insert( content, selection, batch ) { - this.fire( 'insert', { content, selection, batch } ); + insertContent( content, selection, batch ) { + this.fire( 'insertContent', { content, selection, batch } ); } } diff --git a/src/controller/insert.js b/src/controller/insertcontent.js similarity index 93% rename from src/controller/insert.js rename to src/controller/insertcontent.js index b26027218..49359861b 100644 --- a/src/controller/insert.js +++ b/src/controller/insertcontent.js @@ -10,25 +10,23 @@ import Element from '../model/element.js'; import Range from '../model/range.js'; import log from '../../utils/log.js'; -// import { stringify as stringifyModel } from '../dev-utils/model.js'; - /** * Inserts content into the editor (specified selection) as one would expect the paste * functionality to work. * - * **Note:** Use {@link engine.controller.DataController#insert} instead of this function. - * This function is only exposed to be reusable in algorithms which change the {@link engine.controller.DataController#insert} + * **Note:** Use {@link engine.controller.DataController#insertContent} instead of this function. + * This function is only exposed to be reusable in algorithms which change the {@link engine.controller.DataController#insertContent} * method's behavior. * - * @method engine.controller.insert + * @method engine.controller.insertContent * @param {engine.controller.DataController} dataController The data controller in context of which the insertion * should be performed. - * @param {engine.view.DocumentFragment} content The content to insert. + * @param {engine.model.DocumentFragment} content The content to insert. * @param {engine.model.Selection} selection Selection into which the content should be inserted. * @param {engine.model.Batch} [batch] Batch to which deltas will be added. If not specified, then * changes will be added to a new batch. */ -export default function insert( dataController, content, selection, batch ) { +export default function insertContent( dataController, content, selection, batch ) { if ( !batch ) { batch = dataController.model.batch(); } @@ -39,19 +37,9 @@ export default function insert( dataController, content, selection, batch ) { } ); } - // Convert the pasted content to a model document fragment. - // Convertion is contextual, but in this case we need an "all allowed" context and for that - // we use the $clipboardHolder item. - const modelFragment = dataController.viewToModel.convert( content, { - context: [ '$clipboardHolder' ] - } ); - - // We'll be debugging this dozens of times still. - // console.log( stringifyModel( modelFragment ) ); - const insertion = new Insertion( dataController, batch, selection.anchor ); - insertion.handleNodes( modelFragment.getChildren(), { + insertion.handleNodes( content.getChildren(), { // The set of children being inserted is the only set in this context // so it's the first and last (it's a hack ;)). isFirst: true, @@ -71,16 +59,22 @@ class Insertion { constructor( dataController, batch, position ) { /** * The data controller in context of which the insertion should be performed. + * + * @member {engine.controller.DataController} #dataController */ this.dataController = dataController; /** * Batch to which deltas will be added. + * + * @member {engine.model.Batch} #batch */ this.batch = batch; /** * The position at which (or near which) the next node will be inserted. + * + * @member {engine.model.Position} #position */ this.position = position; @@ -91,11 +85,16 @@ class Insertion { *

x

^y

+

z

(can merge to

y

) *

x^y

+

z

(can merge to

xy

which will be split during the action, * so both its pieces will be added to this set) + * + * + * @member {Set} #canMergeWith */ this.canMergeWith = new Set( [ this.position.parent ] ); /** * Schema of the model. + * + * @member {engine.model.Schema} #schema */ this.schema = dataController.model.schema; } @@ -152,7 +151,7 @@ class Insertion { * @param {Boolean} context.isFirst Whether the given node is the first one in the content to be inserted. * @param {Boolean} context.isLast Whether the given node is the last one in the content to be inserted. */ - _handleNode( node, context = {} ) { + _handleNode( node, context ) { // Let's handle object in a special way. // * They should never be merged with other elements. // * If they are not allowed in any of the selection ancestors, they could be either autoparagraphed or totally removed. diff --git a/tests/controller/datacontroller.js b/tests/controller/datacontroller.js index f64c7eb80..c72275377 100644 --- a/tests/controller/datacontroller.js +++ b/tests/controller/datacontroller.js @@ -3,8 +3,6 @@ * For licensing, see LICENSE.md. */ -/* bender-tags: view */ - import ModelDocument from 'ckeditor5/engine/model/document.js'; import DataController from 'ckeditor5/engine/controller/datacontroller.js'; import HtmlDataProcessor from 'ckeditor5/engine/dataprocessor/htmldataprocessor.js'; @@ -12,8 +10,8 @@ import HtmlDataProcessor from 'ckeditor5/engine/dataprocessor/htmldataprocessor. import buildViewConverter from 'ckeditor5/engine/conversion/buildviewconverter.js'; import buildModelConverter from 'ckeditor5/engine/conversion/buildmodelconverter.js'; -import ViewDocumentFragment from 'ckeditor5/engine/view/documentfragment.js'; -import ViewText from 'ckeditor5/engine/view/text.js'; +import ModelDocumentFragment from 'ckeditor5/engine/model/documentfragment.js'; +import ModelText from 'ckeditor5/engine/model/text.js'; import { getData, setData, stringify, parse } from 'ckeditor5/engine/dev-utils/model.js'; @@ -43,13 +41,13 @@ describe( 'DataController', () => { it( 'should add insertContent listener', () => { const batch = modelDocument.batch(); - const content = new ViewDocumentFragment( [ new ViewText( 'x' ) ] ); + const content = new ModelDocumentFragment( [ new ModelText( 'x' ) ] ); schema.registerItem( 'paragraph', '$block' ); setData( modelDocument, 'a[]b' ); - data.fire( 'insert', { content, selection: modelDocument.selection, batch } ); + data.fire( 'insertContent', { content, selection: modelDocument.selection, batch } ); expect( getData( modelDocument ) ).to.equal( 'ax[]b' ); expect( batch.deltas.length ).to.be.above( 0 ); @@ -270,15 +268,15 @@ describe( 'DataController', () => { } ); } ); - describe( 'insert', () => { - it( 'should fire the insert event', () => { + describe( 'insertContent', () => { + it( 'should fire the insertContent event', () => { const spy = sinon.spy(); - const content = new ViewDocumentFragment( [ new ViewText( 'x' ) ] ); + const content = new ModelDocumentFragment( [ new ModelText( 'x' ) ] ); const batch = modelDocument.batch(); - data.on( 'insert', spy ); + data.on( 'insertContent', spy ); - data.insert( content, modelDocument.selection, batch ); + data.insertContent( content, modelDocument.selection, batch ); expect( spy.args[ 0 ][ 1 ] ).to.deep.equal( { batch: batch, diff --git a/tests/controller/editingcontroller.js b/tests/controller/editingcontroller.js index 47e800730..6691780a0 100644 --- a/tests/controller/editingcontroller.js +++ b/tests/controller/editingcontroller.js @@ -4,7 +4,6 @@ */ /* globals setTimeout, Range, document */ -/* bender-tags: view */ import EmitterMixin from 'ckeditor5/utils/emittermixin.js'; diff --git a/tests/controller/insert.js b/tests/controller/insertcontent.js similarity index 96% rename from tests/controller/insert.js rename to tests/controller/insertcontent.js index a7d710494..af78f91f7 100644 --- a/tests/controller/insert.js +++ b/tests/controller/insertcontent.js @@ -3,15 +3,11 @@ * For licensing, see LICENSE.md. */ -/* bender-tags: model */ - import Document from 'ckeditor5/engine/model/document.js'; import DataController from 'ckeditor5/engine/controller/datacontroller.js'; -import insert from 'ckeditor5/engine/controller/insert.js'; +import insertContent from 'ckeditor5/engine/controller/insertcontent.js'; -import ViewDocumentFragment from 'ckeditor5/engine/view/documentfragment.js'; -import ViewText from 'ckeditor5/engine/view/text.js'; -import ModelDocumentFragment from 'ckeditor5/engine/model/documentfragment.js'; +import DocumentFragment from 'ckeditor5/engine/model/documentfragment.js'; import Text from 'ckeditor5/engine/model/text.js'; import { setData, getData, parse } from 'ckeditor5/engine/dev-utils/model.js'; @@ -19,7 +15,7 @@ import { setData, getData, parse } from 'ckeditor5/engine/dev-utils/model.js'; describe( 'DataController', () => { let doc, dataController; - describe( 'insert', () => { + describe( 'insertContent', () => { it( 'uses the passed batch', () => { doc = new Document(); doc.createRoot(); @@ -31,7 +27,7 @@ describe( 'DataController', () => { setData( doc, 'x[]x' ); - insert( dataController, new ViewDocumentFragment( [ new ViewText( 'a' ) ] ), doc.selection, batch ); + insertContent( dataController, new DocumentFragment( [ new Text( 'a' ) ] ), doc.selection, batch ); expect( batch.deltas.length ).to.be.above( 0 ); } ); @@ -540,16 +536,10 @@ describe( 'DataController', () => { } ); } - if ( !( content instanceof ModelDocumentFragment ) ) { - content = new ModelDocumentFragment( [ content ] ); + if ( !( content instanceof DocumentFragment ) ) { + content = new DocumentFragment( [ content ] ); } - // Override the convertion so we get exactly the model that we defined in the content param. - // This way we avoid the need to write converters for everything we want to test. - dataController.viewToModel.convert = () => { - return content; - }; - - insert( dataController, new ViewDocumentFragment(), doc.selection ); + insertContent( dataController, content, doc.selection ); } } ); From f8af45e2fd5a53122d8e348408efc9ca63786257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Koszuli=C5=84ski?= Date: Tue, 15 Nov 2016 15:53:45 +0100 Subject: [PATCH 2/2] Removed the composer. --- src/controller/datacontroller.js | 65 ++++++++++++- .../deletecontent.js} | 16 ++-- src/controller/insertcontent.js | 2 +- .../modifyselection.js | 12 +-- src/model/composer/composer.js | 87 ----------------- src/model/document.js | 10 -- tests/controller/datacontroller.js | 78 +++++++++++++++ .../deletecontent.js} | 34 ++++--- .../modifyselection.js | 4 +- tests/model/composer/composer.js | 96 ------------------- tests/model/document/document.js | 2 - 11 files changed, 171 insertions(+), 235 deletions(-) rename src/{model/composer/deletecontents.js => controller/deletecontent.js} (87%) rename src/{model/composer => controller}/modifyselection.js (94%) delete mode 100644 src/model/composer/composer.js rename tests/{model/composer/deletecontents.js => controller/deletecontent.js} (93%) rename tests/{model/composer => controller}/modifyselection.js (99%) delete mode 100644 tests/model/composer/composer.js diff --git a/src/controller/datacontroller.js b/src/controller/datacontroller.js index b36ba77dd..8c6acc287 100644 --- a/src/controller/datacontroller.js +++ b/src/controller/datacontroller.js @@ -20,6 +20,8 @@ import ModelRange from '../model/range.js'; import ModelPosition from '../model/position.js'; import insertContent from './insertcontent.js'; +import deleteContent from './deletecontent.js'; +import modifySelection from './modifyselection.js'; /** * Controller for the data pipeline. The data pipeline controls how data is retrieved from the document @@ -112,6 +114,8 @@ export default class DataController { 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( data.selection, data.options ) ); } /** @@ -225,19 +229,72 @@ export default class DataController { insertContent( content, selection, batch ) { this.fire( 'insertContent', { content, selection, batch } ); } + + /** + * See {@link engine.controller.deleteContent}. + * + * Note: For the sake of predictability, the resulting selection should always be collapsed. + * In cases where a feature wants to modify deleting behavior so selection isn't collapsed + * (e.g. a table feature may want to keep row selection after pressing Backspace), + * then that behavior should be implemented in the view's listener. At the same time, the table feature + * will need to modify this method's behavior too, e.g. to "delete contents and then collapse + * the selection inside the last selected cell" or "delete the row and collapse selection somewhere near". + * That needs to be done in order to ensure that other features which use `deleteContent()` will work well with tables. + * + * @fires engine.controller.DataController#deleteContent + * @param {engine.model.Selection} selection Selection of which the content should be deleted. + * @param {engine.model.Batch} batch Batch to which deltas will be added. + * @param {Object} options See {@link engine.controller.deleteContent}'s options. + */ + deleteContent( selection, batch, options ) { + this.fire( 'deleteContent', { batch, selection, options } ); + } + + /** + * See {@link engine.controller.modifySelection}. + * + * @fires engine.controller.DataController#modifySelection + * @param {engine.model.Selection} The selection to modify. + * @param {Object} options See {@link engine.controller.modifySelection}'s options. + */ + modifySelection( selection, options ) { + this.fire( 'modifySelection', { selection, options } ); + } } mix( DataController, EmitterMixin ); /** - * Event fired when {@link engine.controller.DataController#insert} method is called. - * The {@link engine.controller.dataController.insert default action of the composer} is implemented as a - * listener to that event so it can be fully customized by the features. + * Event fired when {@link engine.controller.DataController#insertContent} method is called. + * The {@link engine.controller.dataController.insertContent default action of that method} is implemented as a + * listener to this event so it can be fully customized by the features. * - * @event engine.controller.DataController#insert + * @event engine.controller.DataController#insertContent * @param {Object} data * @param {engine.view.DocumentFragment} data.content The content to insert. * @param {engine.model.Selection} data.selection Selection into which the content should be inserted. * @param {engine.model.Batch} [data.batch] Batch to which deltas will be added. */ +/** + * Event fired when {@link engine.controller.DataController#deleteContent} method is called. + * The {@link engine.controller.deleteContent default action of that method} is implemented as a + * listener to this event so it can be fully customized by the features. + * + * @event engine.controller.DataController#deleteContent + * @param {Object} data + * @param {engine.model.Batch} data.batch + * @param {engine.model.Selection} data.selection + * @param {Object} data.options See {@link engine.controller.deleteContent}'s options. + */ + +/** + * Event fired when {@link engine.controller.DataController#modifySelection} method is called. + * The {@link engine.controller.modifySelection default action of that method} is implemented as a + * listener to this event so it can be fully customized by the features. + * + * @event engine.controller.DataController#modifySelection + * @param {Object} data + * @param {engine.model.Selection} data.selection + * @param {Object} data.options See {@link engine.controller.modifySelection}'s options. + */ diff --git a/src/model/composer/deletecontents.js b/src/controller/deletecontent.js similarity index 87% rename from src/model/composer/deletecontents.js rename to src/controller/deletecontent.js index 77e9285b8..013b52f49 100644 --- a/src/model/composer/deletecontents.js +++ b/src/controller/deletecontent.js @@ -3,23 +3,23 @@ * For licensing, see LICENSE.md. */ -import LivePosition from '../liveposition.js'; -import Position from '../position.js'; -import Element from '../element.js'; -import compareArrays from '../../../utils/comparearrays.js'; +import LivePosition from '../model/liveposition.js'; +import Position from '../model/position.js'; +import Element from '../model/element.js'; +import compareArrays from '../../utils/comparearrays.js'; /** - * Delete contents of the selection and merge siblings. The resulting selection is always collapsed. + * Deletes content of the selection and merge siblings. The resulting selection is always collapsed. * - * @method engine.model.composer.deleteContents - * @param {engine.model.Batch} batch Batch to which the deltas will be added. + * @method engine.controller.deleteContent * @param {engine.model.Selection} selection Selection of which the content should be deleted. + * @param {engine.model.Batch} batch Batch to which the deltas will be added. * @param {Object} [options] * @param {Boolean} [options.merge=false] Merge elements after removing the contents of the selection. * For example, `x[x

y]y

` will become: `x^y` with the option enabled * and: `x^

y

` without it. */ -export default function deleteContents( batch, selection, options = {} ) { +export default function deleteContent( selection, batch, options = {} ) { if ( selection.isCollapsed ) { return; } diff --git a/src/controller/insertcontent.js b/src/controller/insertcontent.js index 49359861b..18c0c6cc6 100644 --- a/src/controller/insertcontent.js +++ b/src/controller/insertcontent.js @@ -32,7 +32,7 @@ export default function insertContent( dataController, content, selection, batch } if ( !selection.isCollapsed ) { - dataController.model.composer.deleteContents( batch, selection, { + dataController.deleteContent( selection, batch, { merge: true } ); } diff --git a/src/model/composer/modifyselection.js b/src/controller/modifyselection.js similarity index 94% rename from src/model/composer/modifyselection.js rename to src/controller/modifyselection.js index b618d5d76..c136c0848 100644 --- a/src/model/composer/modifyselection.js +++ b/src/controller/modifyselection.js @@ -3,13 +3,13 @@ * For licensing, see LICENSE.md. */ -import Position from '../position.js'; -import TreeWalker from '../treewalker.js'; -import Range from '../range.js'; -import { isInsideSurrogatePair, isInsideCombinedSymbol } from '../../../utils/unicode.js'; +import Position from '../model/position.js'; +import TreeWalker from '../model/treewalker.js'; +import Range from '../model/range.js'; +import { isInsideSurrogatePair, isInsideCombinedSymbol } from '../../utils/unicode.js'; /** - * Modifies the selection. Currently the supported modifications are: + * Modifies the selection. Currently, the supported modifications are: * * * Extending. The selection focus is moved in the specified `options.direction` with a step specified in `options.unit`. * Possible values for `unit` are: @@ -29,7 +29,7 @@ import { isInsideSurrogatePair, isInsideCombinedSymbol } from '../../../utils/un * * **Note:** if you extend a forward selection in a backward direction you will in fact shrink it. * - * @method engine.model.composer.modifySelection + * @method engine.controller.modifySelection * @param {engine.model.Selection} selection The selection to modify. * @param {Object} [options] * @param {'forward'|'backward'} [options.direction='forward'] The direction in which the selection should be modified. diff --git a/src/model/composer/composer.js b/src/model/composer/composer.js deleted file mode 100644 index 7f5b1fe26..000000000 --- a/src/model/composer/composer.js +++ /dev/null @@ -1,87 +0,0 @@ -/** - * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. - * For licensing, see LICENSE.md. - */ - -import mix from '../../../utils/mix.js'; -import EmitterMixin from '../../../utils/emittermixin.js'; -import deleteContents from './deletecontents.js'; -import modifySelection from './modifyselection.js'; - -/** - * Set of frequently used tools to work with a document. - * The instance of composer is available in {@link engine.model.Document#composer}. - * - * By default this class implements only a very basic version of those algorithms. However, all its methods can be extended - * by features by listening to related events. The default action of those events are implemented - * by functions available in the {@link engine.model.composer} namespace, so they can be reused - * in the algorithms implemented by features. - * - * @member engine.model.composer - * @mixes utils.EmitterMixin - */ -export default class Composer { - /** - * Creates an instance of the composer. - */ - constructor() { - this.on( 'deleteContents', ( evt, data ) => deleteContents( data.batch, data.selection, data.options ) ); - this.on( 'modifySelection', ( evt, data ) => modifySelection( data.selection, data.options ) ); - } - - /** - * See {@link engine.model.composer.deleteContents}. - * - * Note: For the sake of predictability, the resulting selection should always be collapsed. - * In cases where a feature wants to modify deleting behavior so selection isn't collapsed - * (e.g. a table feature may want to keep row selection after pressing Backspace), - * then that behavior should be implemented in the view's listener. At the same time, the table feature - * will need to modify this method's behavior too, e.g. to "delete contents and then collapse - * the selection inside the last selected cell" or "delete the row and collapse selection somewhere near". - * That needs to be done in order to ensure that other features which use `deleteContents()` will work well with tables. - * - * @fires engine.model.composer.Composer#deleteContents - * @param {engine.model.Batch} batch Batch to which deltas will be added. - * @param {engine.model.Selection} selection Selection of which the content should be deleted. - * @param {Object} options See {@link engine.model.composer.deleteContents}'s options. - */ - deleteContents( batch, selection, options ) { - this.fire( 'deleteContents', { batch, selection, options } ); - } - - /** - * See {@link engine.model.composer.modifySelection}. - * - * @fires engine.model.composer.Composer#modifySelection - * @param {engine.model.Selection} The selection to modify. - * @param {Object} options See {@link engine.model.composer.modifySelection}'s options. - */ - modifySelection( selection, options ) { - this.fire( 'modifySelection', { selection, options } ); - } -} - -mix( Composer, EmitterMixin ); - -/** - * Event fired when {@link engine.model.composer.Composer#deleteContents} method is called. - * The {@link engine.model.composer.deleteContents default action of the composer} is implemented as a - * listener to that event so it can be fully customized by the features. - * - * @event engine.model.composer.Composer#deleteContents - * @param {Object} data - * @param {engine.model.Batch} data.batch - * @param {engine.model.Selection} data.selection - * @param {Object} data.options See {@link engine.model.composer.deleteContents}'s options. - */ - -/** - * Event fired when {@link engine.model.composer.Composer#modifySelection} method is called. - * The {@link engine.model.composer.modifySelection default action of the composer} is implemented as a - * listener to that event so it can be fully customized by the features. - * - * @event engine.model.composer.Composer#modifySelection - * @param {Object} data - * @param {engine.model.Selection} data.selection - * @param {Object} data.options See {@link engine.model.composer.modifySelection}'s options. - */ diff --git a/src/model/document.js b/src/model/document.js index 3b046f282..08f56930f 100644 --- a/src/model/document.js +++ b/src/model/document.js @@ -14,7 +14,6 @@ import Batch from './batch.js'; import History from './history.js'; import LiveSelection from './liveselection.js'; import Schema from './schema.js'; -import Composer from './composer/composer.js'; import clone from '../../utils/lib/lodash/clone.js'; import EmitterMixin from '../../utils/emittermixin.js'; import CKEditorError from '../../utils/ckeditorerror.js'; @@ -78,15 +77,6 @@ export default class Document { */ this.history = new History( this ); - /** - * Composer for this document. Set of tools to work with the document. - * - * The features can tune up these tools to better work on their specific cases. - * - * @member {engine.model.composer.Composer} engine.model.Document#composer - */ - this.composer = new Composer(); - /** * Array of pending changes. See: {@link engine.model.Document#enqueueChanges}. * diff --git a/tests/controller/datacontroller.js b/tests/controller/datacontroller.js index c72275377..eda862d7f 100644 --- a/tests/controller/datacontroller.js +++ b/tests/controller/datacontroller.js @@ -52,6 +52,52 @@ describe( 'DataController', () => { expect( getData( modelDocument ) ).to.equal( 'ax[]b' ); expect( batch.deltas.length ).to.be.above( 0 ); } ); + + it( 'should add deleteContent listener', () => { + schema.registerItem( 'paragraph', '$block' ); + + setData( modelDocument, 'f[ooba]r' ); + + const batch = modelDocument.batch(); + + data.fire( 'deleteContent', { batch, selection: modelDocument.selection } ); + + expect( getData( modelDocument ) ).to.equal( 'f[]r' ); + expect( batch.deltas ).to.not.be.empty; + } ); + + it( 'should add deleteContent listener which passes ', () => { + schema.registerItem( 'paragraph', '$block' ); + + setData( modelDocument, 'f[ooba]r' ); + + const batch = modelDocument.batch(); + + data.fire( 'deleteContent', { + batch, + selection: modelDocument.selection, + options: { merge: true } + } ); + + expect( getData( modelDocument ) ).to.equal( 'f[]r' ); + } ); + + it( 'should add modifySelection listener', () => { + schema.registerItem( 'paragraph', '$block' ); + + setData( modelDocument, 'foo[]bar' ); + + data.fire( 'modifySelection', { + selection: modelDocument.selection, + options: { + direction: 'backward' + } + } ); + + expect( getData( modelDocument ) ) + .to.equal( 'fo[o]bar' ); + expect( modelDocument.selection.isBackward ).to.true; + } ); } ); describe( 'parse', () => { @@ -285,4 +331,36 @@ describe( 'DataController', () => { } ); } ); } ); + + describe( 'deleteContent', () => { + it( 'should fire the deleteContent event', () => { + const spy = sinon.spy(); + const batch = modelDocument.batch(); + + data.on( 'deleteContent', spy ); + + data.deleteContent( modelDocument.selection, batch ); + + const evtData = spy.args[ 0 ][ 1 ]; + + expect( evtData.batch ).to.equal( batch ); + expect( evtData.selection ).to.equal( modelDocument.selection ); + } ); + } ); + + describe( 'modifySelection', () => { + it( 'should fire the deleteContent event', () => { + const spy = sinon.spy(); + const opts = { direction: 'backward' }; + + data.on( 'modifySelection', spy ); + + data.modifySelection( modelDocument.selection, opts ); + + const evtData = spy.args[ 0 ][ 1 ]; + + expect( evtData.selection ).to.equal( modelDocument.selection ); + expect( evtData.options ).to.equal( opts ); + } ); + } ); } ); diff --git a/tests/model/composer/deletecontents.js b/tests/controller/deletecontent.js similarity index 93% rename from tests/model/composer/deletecontents.js rename to tests/controller/deletecontent.js index be5e3ae61..9c89c11c2 100644 --- a/tests/model/composer/deletecontents.js +++ b/tests/controller/deletecontent.js @@ -3,16 +3,14 @@ * For licensing, see LICENSE.md. */ -/* bender-tags: model, composer */ - import Document from 'ckeditor5/engine/model/document.js'; -import deleteContents from 'ckeditor5/engine/model/composer/deletecontents.js'; +import deleteContent from 'ckeditor5/engine/controller/deletecontent.js'; import { setData, getData } from 'ckeditor5/engine/dev-utils/model.js'; describe( 'Delete utils', () => { let doc; - describe( 'deleteContents', () => { + describe( 'deleteContent', () => { describe( 'in simple scenarios', () => { beforeEach( () => { doc = new Document(); @@ -41,7 +39,7 @@ describe( 'Delete utils', () => { it( 'deletes single character (backward selection)' , () => { setData( doc, 'f[o]o', { lastRangeBackward: true } ); - deleteContents( doc.batch(), doc.selection ); + deleteContent( doc.selection, doc.batch() ); expect( getData( doc ) ).to.equal( 'f[]o' ); } ); @@ -95,7 +93,7 @@ describe( 'Delete utils', () => { it( 'deletes characters (first half has attrs)', () => { setData( doc, '<$text bold="true">fo[ob]ar' ); - deleteContents( doc.batch(), doc.selection ); + deleteContent( doc.selection, doc.batch() ); expect( getData( doc ) ).to.equal( '<$text bold="true">fo[]ar' ); expect( doc.selection.getAttribute( 'bold' ) ).to.equal( true ); @@ -104,7 +102,7 @@ describe( 'Delete utils', () => { it( 'deletes characters (2nd half has attrs)', () => { setData( doc, 'fo[o<$text bold="true">b]ar' ); - deleteContents( doc.batch(), doc.selection ); + deleteContent( doc.selection, doc.batch() ); expect( getData( doc ) ).to.equal( 'fo[]<$text bold="true">ar' ); expect( doc.selection.getAttribute( 'bold' ) ).to.undefined; @@ -113,7 +111,7 @@ describe( 'Delete utils', () => { it( 'clears selection attrs when emptied content', () => { setData( doc, 'x[<$text bold="true">foo]y' ); - deleteContents( doc.batch(), doc.selection ); + deleteContent( doc.selection, doc.batch() ); expect( getData( doc ) ).to.equal( 'x[]y' ); expect( doc.selection.getAttribute( 'bold' ) ).to.undefined; @@ -130,7 +128,7 @@ describe( 'Delete utils', () => { } ); - deleteContents( doc.batch(), doc.selection ); + deleteContent( doc.selection, doc.batch() ); expect( getData( doc ) ).to.equal( 'x<$text bold="true">a[]by' ); expect( doc.selection.getAttribute( 'bold' ) ).to.equal( true ); @@ -203,7 +201,7 @@ describe( 'Delete utils', () => { { lastRangeBackward: true } ); - deleteContents( doc.batch(), doc.selection, { merge: true } ); + deleteContent( doc.selection, doc.batch(), { merge: true } ); expect( getData( doc ) ).to.equal( 'xfo[]ary' ); } ); @@ -301,7 +299,7 @@ describe( 'Delete utils', () => { { rootName: 'paragraphRoot' } ); - deleteContents( doc.batch(), doc.selection ); + deleteContent( doc.selection, doc.batch() ); expect( getData( doc, { rootName: 'paragraphRoot' } ) ) .to.equal( 'x[]z' ); @@ -314,7 +312,7 @@ describe( 'Delete utils', () => { { rootName: 'bodyRoot' } ); - deleteContents( doc.batch(), doc.selection ); + deleteContent( doc.selection, doc.batch() ); expect( getData( doc, { rootName: 'bodyRoot' } ) ) .to.equal( 'x[]z' ); @@ -327,7 +325,7 @@ describe( 'Delete utils', () => { { rootName: 'bodyRoot' } ); - deleteContents( doc.batch(), doc.selection ); + deleteContent( doc.selection, doc.batch() ); expect( getData( doc, { rootName: 'bodyRoot' } ) ) .to.equal( 'x[]z' ); @@ -340,7 +338,7 @@ describe( 'Delete utils', () => { { rootName: 'bodyRoot' } ); - deleteContents( doc.batch(), doc.selection ); + deleteContent( doc.selection, doc.batch() ); expect( getData( doc, { rootName: 'bodyRoot' } ) ) .to.equal( 'x[]z' ); @@ -353,7 +351,7 @@ describe( 'Delete utils', () => { { rootName: 'bodyRoot' } ); - deleteContents( doc.batch(), doc.selection ); + deleteContent( doc.selection, doc.batch() ); expect( getData( doc, { rootName: 'bodyRoot' } ) ) .to.equal( 'x[]z' ); @@ -366,7 +364,7 @@ describe( 'Delete utils', () => { { rootName: 'bodyRoot' } ); - deleteContents( doc.batch(), doc.selection ); + deleteContent( doc.selection, doc.batch() ); expect( getData( doc, { rootName: 'bodyRoot' } ) ) .to.equal( '[]' ); @@ -379,7 +377,7 @@ describe( 'Delete utils', () => { { rootName: 'restrictedRoot' } ); - deleteContents( doc.batch(), doc.selection ); + deleteContent( doc.selection, doc.batch() ); expect( getData( doc, { rootName: 'restrictedRoot' } ) ) .to.equal( '[]' ); @@ -390,7 +388,7 @@ describe( 'Delete utils', () => { it( title, () => { setData( doc, input ); - deleteContents( doc.batch(), doc.selection, options ); + deleteContent( doc.selection, doc.batch(), options ); expect( getData( doc ) ).to.equal( output ); } ); diff --git a/tests/model/composer/modifyselection.js b/tests/controller/modifyselection.js similarity index 99% rename from tests/model/composer/modifyselection.js rename to tests/controller/modifyselection.js index 5545411e3..0360bc884 100644 --- a/tests/model/composer/modifyselection.js +++ b/tests/controller/modifyselection.js @@ -3,11 +3,9 @@ * For licensing, see LICENSE.md. */ -/* bender-tags: model, composer */ - import Document from 'ckeditor5/engine/model/document.js'; import Selection from 'ckeditor5/engine/model/selection.js'; -import modifySelection from 'ckeditor5/engine/model/composer/modifyselection.js'; +import modifySelection from 'ckeditor5/engine/controller/modifyselection.js'; import { setData, stringify } from 'ckeditor5/engine/dev-utils/model.js'; describe( 'Delete utils', () => { diff --git a/tests/model/composer/composer.js b/tests/model/composer/composer.js deleted file mode 100644 index 8243ee674..000000000 --- a/tests/model/composer/composer.js +++ /dev/null @@ -1,96 +0,0 @@ -/** - * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. - * For licensing, see LICENSE.md. - */ - -/* bender-tags: model, composer */ - -import Document from 'ckeditor5/engine/model/document.js'; -import Composer from 'ckeditor5/engine/model/composer/composer.js'; -import { setData, getData } from 'ckeditor5/engine/dev-utils/model.js'; - -describe( 'Composer', () => { - let document, composer; - - beforeEach( () => { - document = new Document(); - document.schema.registerItem( 'p', '$block' ); - document.createRoot(); - - composer = new Composer(); - } ); - - describe( 'constructor()', () => { - it( 'attaches deleteContents default listener', () => { - setData( document, '

f[oo

ba]r

' ); - - const batch = document.batch(); - - composer.fire( 'deleteContents', { batch, selection: document.selection } ); - - expect( getData( document ) ).to.equal( '

f[]

r

' ); - expect( batch.deltas ).to.not.be.empty; - } ); - - it( 'attaches deleteContents default listener which passes options', () => { - setData( document, '

f[oo

ba]r

' ); - - const batch = document.batch(); - - composer.fire( 'deleteContents', { - batch, - selection: document.selection, - options: { merge: true } - } ); - - expect( getData( document ) ).to.equal( '

f[]r

' ); - } ); - - it( 'attaches modifySelection default listener', () => { - setData( document, '

foo[]bar

' ); - - composer.fire( 'modifySelection', { - selection: document.selection, - options: { - direction: 'backward' - } - } ); - - expect( getData( document ) ) - .to.equal( '

fo[o]bar

' ); - expect( document.selection.isBackward ).to.true; - } ); - } ); - - describe( 'deleteContents', () => { - it( 'fires deleteContents event', () => { - const spy = sinon.spy(); - const batch = document.batch(); - - composer.on( 'deleteContents', spy ); - - composer.deleteContents( batch, document.selection ); - - const data = spy.args[ 0 ][ 1 ]; - - expect( data.batch ).to.equal( batch ); - expect( data.selection ).to.equal( document.selection ); - } ); - } ); - - describe( 'modifySelection', () => { - it( 'fires deleteContents event', () => { - const spy = sinon.spy(); - const opts = { direction: 'backward' }; - - composer.on( 'modifySelection', spy ); - - composer.modifySelection( document.selection, opts ); - - const data = spy.args[ 0 ][ 1 ]; - - expect( data.selection ).to.equal( document.selection ); - expect( data.options ).to.equal( opts ); - } ); - } ); -} ); diff --git a/tests/model/document/document.js b/tests/model/document/document.js index ffbfa897a..e2877a0ce 100644 --- a/tests/model/document/document.js +++ b/tests/model/document/document.js @@ -7,7 +7,6 @@ import Document from 'ckeditor5/engine/model/document.js'; import Schema from 'ckeditor5/engine/model/schema.js'; -import Composer from 'ckeditor5/engine/model/composer/composer.js'; import RootElement from 'ckeditor5/engine/model/rootelement.js'; import Batch from 'ckeditor5/engine/model/batch.js'; import Delta from 'ckeditor5/engine/model/delta/delta.js'; @@ -31,7 +30,6 @@ describe( 'Document', () => { expect( doc.graveyard.maxOffset ).to.equal( 0 ); expect( count( doc.selection.getRanges() ) ).to.equal( 1 ); - expect( doc.composer ).to.be.instanceof( Composer ); expect( doc.schema ).to.be.instanceof( Schema ); } ); } );