} return.deltasB The second set of deltas transformed
+ * by the first set of deltas.
*/
- destroy() {
- this.document.destroy();
- this.stopListening();
+ transformDeltas( deltasA, deltasB, useContext = false ) {
+ return deltaTransform.transformDeltaSets( deltasA, deltasB, useContext ? this.document : null );
}
/**
- * Fired after leaving each {@link module:engine/model/model~Model#enqueueChange} block or outermost
- * {@link module:engine/model/model~Model#change} block.
- * Have the same parameters as {@link module:engine/model/model~Model#change}.
+ * See {@link module:engine/model/utils/insertcontent~insertContent}.
*
- * @event change
+ * @fires insertContent
+ * @param {module:engine/model/documentfragment~DocumentFragment|module:engine/model/item~Item} content The content to insert.
+ * @param {module:engine/model/selection~Selection} selection Selection into which the content should be inserted.
*/
+ insertContent( content, selection ) {
+ insertContent( this, content, selection );
+ }
/**
- * Fired when all queued model changes are done.
+ * See {@link module:engine/model/utils/deletecontent.deleteContent}.
*
- * @see #change
- * @see #enqueueChange
- * @event changesDone
+ * 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 deleteContent
+ * @param {module:engine/model/selection~Selection} selection Selection of which the content should be deleted.
+ * @param {Object} options See {@link module:engine/model/utils/deletecontent~deleteContent}'s options.
*/
+ deleteContent( selection, options ) {
+ deleteContent( this, selection, options );
+ }
/**
- * Fired every time any {@link module:engine/model/operation/operation~Operation operation} is applied on the model
- * using {@link #applyOperation}.
+ * See {@link module:engine/model/utils/modifyselection~modifySelection}.
*
- * Note that this is an internal event for the specific use-cases. You can use it if you need to know about each operation
- * applied on the document, but in most cases {@link #change} event which is fired when all changes in a
- * {@link module:engine/model/batch~Batch} are applied, is a better choice.
- *
- * With the high priority operation is validated.
+ * @fires modifySelection
+ * @param {module:engine/model/selection~Selection} selection The selection to modify.
+ * @param {Object} options See {@link module:engine/model/utils/modifyselection.modifySelection}'s options.
+ */
+ modifySelection( selection, options ) {
+ modifySelection( this, selection, options );
+ }
+
+ /**
+ * See {@link module:engine/model/utils/getselectedcontent~getSelectedContent}.
*
- * With the normal priority operation is executed. After that priority you will be able to get additional
- * information about the applied changes returned by {@link module:engine/model/operation/operation~Operation#_execute}
- * as `evt.return`.
+ * @fires getSelectedContent
+ * @param {module:engine/model/selection~Selection} selection The selection of which content will be retrieved.
+ * @returns {module:engine/model/documentfragment~DocumentFragment} Document fragment holding the clone of the selected content.
+ */
+ getSelectedContent( selection ) {
+ return getSelectedContent( this, selection );
+ }
+
+ /**
+ * Checks whether given {@link module:engine/model/range~Range range} or {@link module:engine/model/element~Element element}
+ * has any content.
*
- * With the low priority the {@link module:engine/model/document~Document} listen on this event and updates its version.
+ * Content is any text node or element which is registered in {@link module:engine/model/schema~Schema schema}.
*
- * @event applyOperation
- * @param {Array} args Arguments of the `applyOperation` which are an array with a single element:
- * {@link module:engine/model/operation/operation~Operation operation}.
+ * @param {module:engine/model/range~Range|module:engine/model/element~Element} rangeOrElement Range or element to check.
+ * @returns {Boolean}
+ */
+ hasContent( rangeOrElement ) {
+ if ( rangeOrElement instanceof ModelElement ) {
+ rangeOrElement = ModelRange.createIn( rangeOrElement );
+ }
+
+ if ( rangeOrElement.isCollapsed ) {
+ return false;
+ }
+
+ for ( const item of rangeOrElement.getItems() ) {
+ // Remember, `TreeWalker` returns always `textProxy` nodes.
+ if ( item.is( 'textProxy' ) || this.schema.objects.has( item.name ) ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Removes all events listeners set by model instance and destroy Document.
*/
+ destroy() {
+ this.document.destroy();
+ this.stopListening();
+ }
}
mix( Model, ObservableMixin );
+
+/**
+ * Fired after leaving each {@link module:engine/model/model~Model#enqueueChange} block or outermost
+ * {@link module:engine/model/model~Model#change} block.
+ * Have the same parameters as {@link module:engine/model/model~Model#change}.
+ *
+ * @event change
+ */
+
+/**
+ * Fired when all queued model changes are done.
+ *
+ * @see #change
+ * @see #enqueueChange
+ * @event changesDone
+ */
+
+/**
+ * Fired every time any {@link module:engine/model/operation/operation~Operation operation} is applied on the model
+ * using {@link #applyOperation}.
+ *
+ * Note that this is an internal event for the specific use-cases. You can use it if you need to know about each operation
+ * applied on the document, but in most cases {@link #change} event which is fired when all changes in a
+ * {@link module:engine/model/batch~Batch} are applied, is a better choice.
+ *
+ * With the high priority operation is validated.
+ *
+ * With the normal priority operation is executed. After that priority you will be able to get additional
+ * information about the applied changes returned by {@link module:engine/model/operation/operation~Operation#_execute}
+ * as `evt.return`.
+ *
+ * With the low priority the {@link module:engine/model/document~Document} listen on this event and updates its version.
+ *
+ * @event applyOperation
+ * @param {Array} args Arguments of the `applyOperation` which are an array with a single element:
+ * {@link module:engine/model/operation/operation~Operation operation}.
+ */
+
+/**
+ * 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 {Array} args The arguments passed to the original method.
+ */
+
+/**
+ * Event fired when {@link #deleteContent} method is called.
+ *
+ * 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 {Array} args The arguments passed to the original method.
+ */
+
+/**
+ * Event fired when {@link #modifySelection} method is called.
+ *
+ * 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 {Array} args The arguments passed to the original method.
+ */
+
+/**
+ * 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 getSelectedContent
+ * @param {Array} args The arguments passed to the original method.
+ */
diff --git a/src/controller/deletecontent.js b/src/model/utils/deletecontent.js
similarity index 94%
rename from src/controller/deletecontent.js
rename to src/model/utils/deletecontent.js
index 7bf12e7f5..74193d2a8 100644
--- a/src/controller/deletecontent.js
+++ b/src/model/utils/deletecontent.js
@@ -4,17 +4,17 @@
*/
/**
- * @module engine/controller/deletecontent
+ * @module engine/model/utils/deletecontent
*/
-import LivePosition from '../model/liveposition';
-import Position from '../model/position';
-import Range from '../model/range';
+import LivePosition from '../liveposition';
+import Position from '../position';
+import Range from '../range';
/**
* Deletes content of the selection and merge siblings. The resulting selection is always collapsed.
*
- * @param {module:engine/controller/datacontroller~DataController} dataController The data controller in context of which the insertion
+ * @param {module:engine/model/model~Model} model The model in context of which the insertion
* should be performed.
* @param {module:engine/model/selection~Selection} selection Selection of which the content should be deleted.
* @param {module:engine/model/batch~Batch} batch Batch to which the deltas will be added.
@@ -37,14 +37,14 @@ import Range from '../model/range';
* * `^` with the option disabled (`doNotResetEntireContent == false`)
* * `^` with enabled (`doNotResetEntireContent == true`).
*/
-export default function deleteContent( dataController, selection, options = {} ) {
+export default function deleteContent( model, selection, options = {} ) {
if ( selection.isCollapsed ) {
return;
}
- const schema = dataController.model.schema;
+ const schema = model.schema;
- dataController.model.change( writer => {
+ model.change( writer => {
// 1. Replace the entire content with paragraph.
// See: https://github.com/ckeditor/ckeditor5-engine/issues/1012#issuecomment-315017594.
if ( !options.doNotResetEntireContent && shouldEntireContentBeReplacedWithParagraph( schema, selection ) ) {
diff --git a/src/controller/getselectedcontent.js b/src/model/utils/getselectedcontent.js
similarity index 92%
rename from src/controller/getselectedcontent.js
rename to src/model/utils/getselectedcontent.js
index dd5bc9b99..cd20cf906 100644
--- a/src/controller/getselectedcontent.js
+++ b/src/model/utils/getselectedcontent.js
@@ -4,11 +4,11 @@
*/
/**
- * @module engine/controller/getselectedcontent
+ * @module engine/model/utils/getselectedcontent
*/
-import Range from '../model/range';
-import Position from '../model/position';
+import Range from '../range';
+import Position from '../position';
/**
* Gets a clone of the selected content.
@@ -21,13 +21,13 @@ import Position from '../model/position';
*
* st
se
*
- * @param {module:engine/controller/datacontroller~DataController} dataController The data controller in context of which
+ * @param {module:engine/model/model~Model} model The model in context of which
* the selection modification should be performed.
* @param {module:engine/model/selection~Selection} selection The selection of which content will be returned.
* @returns {module:engine/model/documentfragment~DocumentFragment}
*/
-export default function getSelectedContent( dataController, selection ) {
- return dataController.model.change( writer => {
+export default function getSelectedContent( model, selection ) {
+ return model.change( writer => {
const frag = writer.createDocumentFragment();
const range = selection.getFirstRange();
diff --git a/src/controller/insertcontent.js b/src/model/utils/insertcontent.js
similarity index 92%
rename from src/controller/insertcontent.js
rename to src/model/utils/insertcontent.js
index 76196678e..204115061 100644
--- a/src/controller/insertcontent.js
+++ b/src/model/utils/insertcontent.js
@@ -4,36 +4,36 @@
*/
/**
- * @module engine/controller/insertcontent
+ * @module engine/model/utils/insertcontent
*/
-import Position from '../model/position';
-import LivePosition from '../model/liveposition';
-import Element from '../model/element';
-import Range from '../model/range';
+import Position from '../position';
+import LivePosition from '../liveposition';
+import Element from '../element';
+import Range from '../range';
import log from '@ckeditor/ckeditor5-utils/src/log';
/**
* Inserts content into the editor (specified selection) as one would expect the paste
* functionality to work.
*
- * **Note:** Use {@link module:engine/controller/datacontroller~DataController#insertContent} instead of this function.
+ * **Note:** Use {@link module:engine/model/model~Model#insertContent} instead of this function.
* This function is only exposed to be reusable in algorithms
- * which change the {@link module:engine/controller/datacontroller~DataController#insertContent}
+ * which change the {@link module:engine/model/model~Model#insertContent}
* method's behavior.
*
- * @param {module:engine/controller/datacontroller~DataController} dataController The data controller in context of which the insertion
+ * @param {module:engine/model/model~Model} model The model in context of which the insertion
* should be performed.
* @param {module:engine/model/documentfragment~DocumentFragment|module:engine/model/item~Item} content The content to insert.
* @param {module:engine/model/selection~Selection} selection Selection into which the content should be inserted.
*/
-export default function insertContent( dataController, content, selection ) {
- dataController.model.change( writer => {
+export default function insertContent( model, content, selection ) {
+ model.change( writer => {
if ( !selection.isCollapsed ) {
- dataController.deleteContent( selection );
+ model.deleteContent( selection );
}
- const insertion = new Insertion( dataController, writer, selection.anchor );
+ const insertion = new Insertion( model, writer, selection.anchor );
let nodesToInsert;
@@ -75,13 +75,13 @@ export default function insertContent( dataController, content, selection ) {
* @private
*/
class Insertion {
- constructor( dataController, writer, position ) {
+ constructor( model, writer, position ) {
/**
- * The data controller in context of which the insertion should be performed.
+ * The model in context of which the insertion should be performed.
*
- * @member {module:engine/controller/datacontroller~DataController} #dataController
+ * @member {module:engine/model~Model} #model
*/
- this.dataController = dataController;
+ this.model = model;
/**
* Batch to which deltas will be added.
@@ -115,7 +115,7 @@ class Insertion {
*
* @member {module:engine/model/schema~Schema} #schema
*/
- this.schema = dataController.model.schema;
+ this.schema = model.schema;
}
/**
@@ -149,7 +149,7 @@ class Insertion {
return Range.createOn( this.nodeToSelect );
}
- return this.dataController.model.document.getNearestSelectionRange( this.position );
+ return this.model.document.getNearestSelectionRange( this.position );
}
/**
diff --git a/src/controller/modifyselection.js b/src/model/utils/modifyselection.js
similarity index 92%
rename from src/controller/modifyselection.js
rename to src/model/utils/modifyselection.js
index ac36d3275..83f77b380 100644
--- a/src/controller/modifyselection.js
+++ b/src/model/utils/modifyselection.js
@@ -4,12 +4,12 @@
*/
/**
- * @module engine/controller/modifyselection
+ * @module engine/model/utils/modifyselection
*/
-import Position from '../model/position';
-import TreeWalker from '../model/treewalker';
-import Range from '../model/range';
+import Position from '../position';
+import TreeWalker from '../treewalker';
+import Range from '../range';
import { isInsideSurrogatePair, isInsideCombinedSymbol } from '@ckeditor/ckeditor5-utils/src/unicode';
/**
@@ -33,15 +33,15 @@ import { isInsideSurrogatePair, isInsideCombinedSymbol } from '@ckeditor/ckedito
*
* **Note:** if you extend a forward selection in a backward direction you will in fact shrink it.
*
- * @param {module:engine/controller/datacontroller~DataController} dataController The data controller in context of which
+ * @param {module:engine/model/model~Model} model The model in context of which
* the selection modification should be performed.
* @param {module:engine/model/selection~Selection} selection The selection to modify.
* @param {Object} [options]
* @param {'forward'|'backward'} [options.direction='forward'] The direction in which the selection should be modified.
* @param {'character'|'codePoint'} [options.unit='character'] The unit by which selection should be modified.
*/
-export default function modifySelection( dataController, selection, options = {} ) {
- const schema = dataController.model.schema;
+export default function modifySelection( model, selection, options = {} ) {
+ const schema = model.schema;
const isForward = options.direction != 'backward';
const unit = options.unit ? options.unit : 'character';
diff --git a/tests/controller/datacontroller.js b/tests/controller/datacontroller.js
index 18d428abe..a051a6276 100644
--- a/tests/controller/datacontroller.js
+++ b/tests/controller/datacontroller.js
@@ -11,9 +11,6 @@ import buildViewConverter from '../../src/conversion/buildviewconverter';
import buildModelConverter from '../../src/conversion/buildmodelconverter';
import ModelDocumentFragment from '../../src/model/documentfragment';
-import ModelText from '../../src/model/text';
-import ModelRange from '../../src/model/range';
-import ModelSelection from '../../src/model/selection';
import ViewDocumentFragment from '../../src/view/documentfragment';
@@ -336,223 +333,4 @@ describe( 'DataController', () => {
expect( data ).to.respondTo( 'destroy' );
} );
} );
-
- describe( 'insertContent()', () => {
- it( 'should be decorated', () => {
- schema.allow( { name: '$text', inside: '$root' } ); // To surpress warnings.
-
- const spy = sinon.spy();
-
- data.on( 'insertContent', spy );
-
- data.insertContent( new ModelText( 'a' ), modelDocument.selection );
-
- expect( spy.calledOnce ).to.be.true;
- } );
-
- it( 'should insert content (item)', () => {
- schema.registerItem( 'paragraph', '$block' );
-
- setData( model, 'fo[]ar' );
-
- data.insertContent( new ModelText( 'ob' ), modelDocument.selection );
-
- expect( getData( model ) ).to.equal( 'foob[]ar' );
- } );
-
- it( 'should insert content (document fragment)', () => {
- schema.registerItem( 'paragraph', '$block' );
-
- setData( model, 'fo[]ar' );
-
- data.insertContent( new ModelDocumentFragment( [ new ModelText( 'ob' ) ] ), modelDocument.selection );
-
- expect( getData( model ) ).to.equal( 'foob[]ar' );
- } );
-
- it( 'should use parent batch', () => {
- schema.registerItem( 'paragraph', '$block' );
- setData( model, '[]' );
-
- model.change( writer => {
- data.insertContent( new ModelText( 'abc' ), modelDocument.selection );
- expect( writer.batch.deltas ).to.length( 1 );
- } );
- } );
- } );
-
- describe( 'deleteContent()', () => {
- it( 'should be decorated', () => {
- const spy = sinon.spy();
-
- data.on( 'deleteContent', spy );
-
- data.deleteContent( modelDocument.selection );
-
- expect( spy.calledOnce ).to.be.true;
- } );
-
- it( 'should delete selected content', () => {
- schema.registerItem( 'paragraph', '$block' );
-
- setData( model, 'fo[ob]ar' );
-
- data.deleteContent( modelDocument.selection );
-
- expect( getData( model ) ).to.equal( 'fo[]ar' );
- } );
-
- it( 'should use parent batch', () => {
- schema.registerItem( 'paragraph', '$block' );
-
- setData( model, 'fo[ob]ar' );
-
- model.change( writer => {
- data.deleteContent( modelDocument.selection );
- expect( writer.batch.deltas ).to.length( 1 );
- } );
- } );
- } );
-
- describe( 'modifySelection()', () => {
- it( 'should be decorated', () => {
- schema.registerItem( 'paragraph', '$block' );
- setData( model, 'fo[ob]ar' );
-
- const spy = sinon.spy();
-
- data.on( 'modifySelection', spy );
-
- data.modifySelection( modelDocument.selection );
-
- expect( spy.calledOnce ).to.be.true;
- } );
-
- it( 'should modify a selection', () => {
- schema.registerItem( 'paragraph', '$block' );
-
- setData( model, 'fo[ob]ar' );
-
- data.modifySelection( modelDocument.selection, { direction: 'backward' } );
-
- expect( getData( model ) ).to.equal( 'fo[o]bar' );
- } );
- } );
-
- describe( 'getSelectedContent()', () => {
- it( 'should be decorated', () => {
- const spy = sinon.spy();
- const sel = new ModelSelection();
-
- data.on( 'getSelectedContent', spy );
-
- data.getSelectedContent( sel );
-
- expect( spy.calledOnce ).to.be.true;
- } );
-
- it( 'should return selected content', () => {
- schema.registerItem( 'paragraph', '$block' );
-
- setData( model, 'fo[ob]ar' );
-
- const content = data.getSelectedContent( modelDocument.selection );
-
- expect( stringify( content ) ).to.equal( 'ob' );
- } );
-
- it( 'should use parent batch', () => {
- schema.registerItem( 'paragraph', '$block' );
-
- setData( model, 'fo[ob]ar' );
-
- model.change( writer => {
- data.getSelectedContent( modelDocument.selection );
- expect( writer.batch.deltas ).to.length( 1 );
- } );
- } );
- } );
-
- describe( 'hasContent()', () => {
- let root;
-
- beforeEach( () => {
- schema.registerItem( 'paragraph', '$block' );
- schema.registerItem( 'div', '$block' );
- schema.allow( { name: '$block', inside: 'div' } );
- schema.registerItem( 'image' );
- schema.allow( { name: 'image', inside: 'div' } );
- schema.objects.add( 'image' );
-
- setData(
- model,
-
- '' +
- 'foo' +
- '' +
- '' +
- '
'
- );
-
- root = modelDocument.getRoot();
- } );
-
- it( 'should return true if given element has text node', () => {
- const pFoo = root.getChild( 1 );
-
- expect( data.hasContent( pFoo ) ).to.be.true;
- } );
-
- it( 'should return true if given element has element that is an object', () => {
- const divImg = root.getChild( 2 );
-
- expect( data.hasContent( divImg ) ).to.be.true;
- } );
-
- it( 'should return false if given element has no elements', () => {
- const pEmpty = root.getChild( 0 ).getChild( 0 );
-
- expect( data.hasContent( pEmpty ) ).to.be.false;
- } );
-
- it( 'should return false if given element has only elements that are not objects', () => {
- const divP = root.getChild( 0 );
-
- expect( data.hasContent( divP ) ).to.be.false;
- } );
-
- it( 'should return true if there is a text node in given range', () => {
- const range = ModelRange.createFromParentsAndOffsets( root, 1, root, 2 );
-
- expect( data.hasContent( range ) ).to.be.true;
- } );
-
- it( 'should return true if there is a part of text node in given range', () => {
- const pFoo = root.getChild( 1 );
- const range = ModelRange.createFromParentsAndOffsets( pFoo, 1, pFoo, 2 );
-
- expect( data.hasContent( range ) ).to.be.true;
- } );
-
- it( 'should return true if there is element that is an object in given range', () => {
- const divImg = root.getChild( 2 );
- const range = ModelRange.createFromParentsAndOffsets( divImg, 0, divImg, 1 );
-
- expect( data.hasContent( range ) ).to.be.true;
- } );
-
- it( 'should return false if range is collapsed', () => {
- const range = ModelRange.createFromParentsAndOffsets( root, 1, root, 1 );
-
- expect( data.hasContent( range ) ).to.be.false;
- } );
-
- it( 'should return false if range has only elements that are not objects', () => {
- const range = ModelRange.createFromParentsAndOffsets( root, 0, root, 1 );
-
- expect( data.hasContent( range ) ).to.be.false;
- } );
- } );
} );
diff --git a/tests/model/document/document.js b/tests/model/document/document.js
index 55a00f1de..9a5efb116 100644
--- a/tests/model/document/document.js
+++ b/tests/model/document/document.js
@@ -8,8 +8,6 @@ import Document from '../../../src/model/document';
import RootElement from '../../../src/model/rootelement';
import Batch from '../../../src/model/batch';
import Delta from '../../../src/model/delta/delta';
-import NoOperation from '../../../src/model/operation/nooperation';
-import deltaTransform from '../../../src/model/delta/transform';
import Range from '../../../src/model/range';
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
import count from '@ckeditor/ckeditor5-utils/src/count';
@@ -472,44 +470,6 @@ describe( 'Document', () => {
}
} );
- describe( 'transformDeltas', () => {
- it( 'should use deltaTransform.transformDeltaSets', () => {
- sinon.spy( deltaTransform, 'transformDeltaSets' );
-
- // Prepare some empty-ish deltas so the transformation won't throw an error.
- const deltasA = [ new Delta() ];
- deltasA[ 0 ].addOperation( new NoOperation( 0 ) );
-
- const deltasB = [ new Delta() ];
- deltasB[ 0 ].addOperation( new NoOperation( 0 ) );
-
- doc.transformDeltas( deltasA, deltasB );
-
- expect( deltaTransform.transformDeltaSets.calledOnce ).to.be.true;
- expect( deltaTransform.transformDeltaSets.calledWith( deltasA, deltasB, null ) ).to.be.true;
-
- deltaTransform.transformDeltaSets.restore();
- } );
-
- it( 'should pass itself to transformDeltaSets if useContext was set to true', () => {
- sinon.spy( deltaTransform, 'transformDeltaSets' );
-
- // Prepare some empty-ish deltas so the transformation won't throw an error.
- const deltasA = [ new Delta() ];
- deltasA[ 0 ].addOperation( new NoOperation( 0 ) );
-
- const deltasB = [ new Delta() ];
- deltasB[ 0 ].addOperation( new NoOperation( 0 ) );
-
- doc.transformDeltas( deltasA, deltasB, true );
-
- expect( deltaTransform.transformDeltaSets.calledOnce ).to.be.true;
- expect( deltaTransform.transformDeltaSets.calledWith( deltasA, deltasB, doc ) ).to.be.true;
-
- deltaTransform.transformDeltaSets.restore();
- } );
- } );
-
describe( '_getDefaultRoot()', () => {
it( 'should return graveyard root if there are no other roots in the document', () => {
expect( doc._getDefaultRoot() ).to.equal( doc.graveyard );
diff --git a/tests/model/model.js b/tests/model/model.js
index 7781c1972..865caae1a 100644
--- a/tests/model/model.js
+++ b/tests/model/model.js
@@ -3,15 +3,27 @@
* For licensing, see LICENSE.md.
*/
-import Model from '../../src/model/model';
import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
+import Model from '../../src/model/model';
+import NoOperation from '../../src/model/operation/nooperation';
+import deltaTransform from '../../src/model/delta/transform';
+import Delta from '../../src/model/delta/delta';
+import ModelText from '../../src/model/text';
+import ModelRange from '../../src/model/range';
+import ModelSelection from '../../src/model/selection';
+import ModelDocumentFragment from '../../src/model/documentfragment';
+import { getData, setData, stringify } from '../../src/dev-utils/model';
describe( 'Model', () => {
- let model;
- let changes = '';
+ let model, schema, changes;
beforeEach( () => {
model = new Model();
+ model.document.createRoot();
+ model.document.createRoot( '$root', 'title' );
+
+ schema = model.schema;
+
changes = '';
} );
@@ -322,6 +334,263 @@ describe( 'Model', () => {
} );
} );
+ describe( 'transformDeltas', () => {
+ it( 'should use deltaTransform.transformDeltaSets', () => {
+ sinon.spy( deltaTransform, 'transformDeltaSets' );
+
+ // Prepare some empty-ish deltas so the transformation won't throw an error.
+ const deltasA = [ new Delta() ];
+ deltasA[ 0 ].addOperation( new NoOperation( 0 ) );
+
+ const deltasB = [ new Delta() ];
+ deltasB[ 0 ].addOperation( new NoOperation( 0 ) );
+
+ model.transformDeltas( deltasA, deltasB );
+
+ expect( deltaTransform.transformDeltaSets.calledOnce ).to.be.true;
+ expect( deltaTransform.transformDeltaSets.calledWith( deltasA, deltasB, null ) ).to.be.true;
+
+ deltaTransform.transformDeltaSets.restore();
+ } );
+
+ it( 'should pass itself to transformDeltaSets if useContext was set to true', () => {
+ sinon.spy( deltaTransform, 'transformDeltaSets' );
+
+ // Prepare some empty-ish deltas so the transformation won't throw an error.
+ const deltasA = [ new Delta() ];
+ deltasA[ 0 ].addOperation( new NoOperation( 0 ) );
+
+ const deltasB = [ new Delta() ];
+ deltasB[ 0 ].addOperation( new NoOperation( 0 ) );
+
+ model.transformDeltas( deltasA, deltasB, true );
+
+ expect( deltaTransform.transformDeltaSets.calledOnce ).to.be.true;
+ expect( deltaTransform.transformDeltaSets.calledWith( deltasA, deltasB, model.document ) ).to.be.true;
+
+ deltaTransform.transformDeltaSets.restore();
+ } );
+ } );
+
+ describe( 'insertContent()', () => {
+ it( 'should be decorated', () => {
+ schema.allow( { name: '$text', inside: '$root' } ); // To surpress warnings.
+
+ const spy = sinon.spy();
+
+ model.on( 'insertContent', spy );
+
+ model.insertContent( new ModelText( 'a' ), model.document.selection );
+
+ expect( spy.calledOnce ).to.be.true;
+ } );
+
+ it( 'should insert content (item)', () => {
+ schema.registerItem( 'paragraph', '$block' );
+
+ setData( model, 'fo[]ar' );
+
+ model.insertContent( new ModelText( 'ob' ), model.document.selection );
+
+ expect( getData( model ) ).to.equal( 'foob[]ar' );
+ } );
+
+ it( 'should insert content (document fragment)', () => {
+ schema.registerItem( 'paragraph', '$block' );
+
+ setData( model, 'fo[]ar' );
+
+ model.insertContent( new ModelDocumentFragment( [ new ModelText( 'ob' ) ] ), model.document.selection );
+
+ expect( getData( model ) ).to.equal( 'foob[]ar' );
+ } );
+
+ it( 'should use parent batch', () => {
+ schema.registerItem( 'paragraph', '$block' );
+ setData( model, '[]' );
+
+ model.change( writer => {
+ model.insertContent( new ModelText( 'abc' ), model.document.selection );
+ expect( writer.batch.deltas ).to.length( 1 );
+ } );
+ } );
+ } );
+
+ describe( 'deleteContent()', () => {
+ it( 'should be decorated', () => {
+ const spy = sinon.spy();
+
+ model.on( 'deleteContent', spy );
+
+ model.deleteContent( model.document.selection );
+
+ expect( spy.calledOnce ).to.be.true;
+ } );
+
+ it( 'should delete selected content', () => {
+ schema.registerItem( 'paragraph', '$block' );
+
+ setData( model, 'fo[ob]ar' );
+
+ model.deleteContent( model.document.selection );
+
+ expect( getData( model ) ).to.equal( 'fo[]ar' );
+ } );
+
+ it( 'should use parent batch', () => {
+ schema.registerItem( 'paragraph', '$block' );
+
+ setData( model, 'fo[ob]ar' );
+
+ model.change( writer => {
+ model.deleteContent( model.document.selection );
+ expect( writer.batch.deltas ).to.length( 1 );
+ } );
+ } );
+ } );
+
+ describe( 'modifySelection()', () => {
+ it( 'should be decorated', () => {
+ schema.registerItem( 'paragraph', '$block' );
+ setData( model, 'fo[ob]ar' );
+
+ const spy = sinon.spy();
+
+ model.on( 'modifySelection', spy );
+
+ model.modifySelection( model.document.selection );
+
+ expect( spy.calledOnce ).to.be.true;
+ } );
+
+ it( 'should modify a selection', () => {
+ schema.registerItem( 'paragraph', '$block' );
+
+ setData( model, 'fo[ob]ar' );
+
+ model.modifySelection( model.document.selection, { direction: 'backward' } );
+
+ expect( getData( model ) ).to.equal( 'fo[o]bar' );
+ } );
+ } );
+
+ describe( 'getSelectedContent()', () => {
+ it( 'should be decorated', () => {
+ const spy = sinon.spy();
+ const sel = new ModelSelection();
+
+ model.on( 'getSelectedContent', spy );
+
+ model.getSelectedContent( sel );
+
+ expect( spy.calledOnce ).to.be.true;
+ } );
+
+ it( 'should return selected content', () => {
+ schema.registerItem( 'paragraph', '$block' );
+
+ setData( model, 'fo[ob]ar' );
+
+ const content = model.getSelectedContent( model.document.selection );
+
+ expect( stringify( content ) ).to.equal( 'ob' );
+ } );
+
+ it( 'should use parent batch', () => {
+ schema.registerItem( 'paragraph', '$block' );
+
+ setData( model, 'fo[ob]ar' );
+
+ model.change( writer => {
+ model.getSelectedContent( model.document.selection );
+ expect( writer.batch.deltas ).to.length( 1 );
+ } );
+ } );
+ } );
+
+ describe( 'hasContent()', () => {
+ let root;
+
+ beforeEach( () => {
+ schema.registerItem( 'paragraph', '$block' );
+ schema.registerItem( 'div', '$block' );
+ schema.allow( { name: '$block', inside: 'div' } );
+ schema.registerItem( 'image' );
+ schema.allow( { name: 'image', inside: 'div' } );
+ schema.objects.add( 'image' );
+
+ setData(
+ model,
+
+ '' +
+ 'foo' +
+ '' +
+ '' +
+ '
'
+ );
+
+ root = model.document.getRoot();
+ } );
+
+ it( 'should return true if given element has text node', () => {
+ const pFoo = root.getChild( 1 );
+
+ expect( model.hasContent( pFoo ) ).to.be.true;
+ } );
+
+ it( 'should return true if given element has element that is an object', () => {
+ const divImg = root.getChild( 2 );
+
+ expect( model.hasContent( divImg ) ).to.be.true;
+ } );
+
+ it( 'should return false if given element has no elements', () => {
+ const pEmpty = root.getChild( 0 ).getChild( 0 );
+
+ expect( model.hasContent( pEmpty ) ).to.be.false;
+ } );
+
+ it( 'should return false if given element has only elements that are not objects', () => {
+ const divP = root.getChild( 0 );
+
+ expect( model.hasContent( divP ) ).to.be.false;
+ } );
+
+ it( 'should return true if there is a text node in given range', () => {
+ const range = ModelRange.createFromParentsAndOffsets( root, 1, root, 2 );
+
+ expect( model.hasContent( range ) ).to.be.true;
+ } );
+
+ it( 'should return true if there is a part of text node in given range', () => {
+ const pFoo = root.getChild( 1 );
+ const range = ModelRange.createFromParentsAndOffsets( pFoo, 1, pFoo, 2 );
+
+ expect( model.hasContent( range ) ).to.be.true;
+ } );
+
+ it( 'should return true if there is element that is an object in given range', () => {
+ const divImg = root.getChild( 2 );
+ const range = ModelRange.createFromParentsAndOffsets( divImg, 0, divImg, 1 );
+
+ expect( model.hasContent( range ) ).to.be.true;
+ } );
+
+ it( 'should return false if range is collapsed', () => {
+ const range = ModelRange.createFromParentsAndOffsets( root, 1, root, 1 );
+
+ expect( model.hasContent( range ) ).to.be.false;
+ } );
+
+ it( 'should return false if range has only elements that are not objects', () => {
+ const range = ModelRange.createFromParentsAndOffsets( root, 0, root, 1 );
+
+ expect( model.hasContent( range ) ).to.be.false;
+ } );
+ } );
+
describe( 'destroy()', () => {
it( 'should destroy document', () => {
sinon.spy( model.document, 'destroy' );
diff --git a/tests/controller/deletecontent.js b/tests/model/utils/deletecontent.js
similarity index 94%
rename from tests/controller/deletecontent.js
rename to tests/model/utils/deletecontent.js
index 30afdd617..d9eb1ed77 100644
--- a/tests/controller/deletecontent.js
+++ b/tests/model/utils/deletecontent.js
@@ -3,29 +3,27 @@
* For licensing, see LICENSE.md.
*/
-import Model from '../../src/model/model';
-import Position from '../../src/model/position';
-import Range from '../../src/model/range';
-import Element from '../../src/model/element';
-import DataController from '../../src/controller/datacontroller';
-import deleteContent from '../../src/controller/deletecontent';
-import { setData, getData } from '../../src/dev-utils/model';
+import Model from '../../../src/model/model';
+import Position from '../../../src/model/position';
+import Range from '../../../src/model/range';
+import Element from '../../../src/model/element';
+import deleteContent from '../../../src/model/utils/deletecontent';
+import { setData, getData } from '../../../src/dev-utils/model';
describe( 'DataController utils', () => {
- let model, doc, data;
+ let model, doc;
describe( 'deleteContent', () => {
it( 'should use parent batch', () => {
model = new Model();
doc = model.document;
doc.createRoot();
- data = new DataController( model );
model.schema.allow( { name: '$text', inside: '$root' } );
setData( model, 'x[abc]x' );
model.change( writer => {
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
expect( writer.batch.deltas ).to.length( 1 );
} );
} );
@@ -35,7 +33,6 @@ describe( 'DataController utils', () => {
model = new Model();
doc = model.document;
doc.createRoot();
- data = new DataController( model );
const schema = model.schema;
@@ -60,7 +57,7 @@ describe( 'DataController utils', () => {
it( 'deletes single character (backward selection)', () => {
setData( model, 'f[o]o', { lastRangeBackward: true } );
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
expect( getData( model ) ).to.equal( 'f[]o' );
} );
@@ -101,7 +98,6 @@ describe( 'DataController utils', () => {
model = new Model();
doc = model.document;
doc.createRoot();
- data = new DataController( model );
const schema = model.schema;
@@ -115,7 +111,7 @@ describe( 'DataController utils', () => {
it( 'deletes characters (first half has attrs)', () => {
setData( model, '<$text bold="true">fo[o$text>b]ar' );
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
expect( getData( model ) ).to.equal( '<$text bold="true">fo[]$text>ar' );
expect( doc.selection.getAttribute( 'bold' ) ).to.equal( true );
@@ -124,7 +120,7 @@ describe( 'DataController utils', () => {
it( 'deletes characters (2nd half has attrs)', () => {
setData( model, 'fo[o<$text bold="true">b]ar$text>' );
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
expect( getData( model ) ).to.equal( 'fo[]<$text bold="true">ar$text>' );
expect( doc.selection.getAttribute( 'bold' ) ).to.undefined;
@@ -133,7 +129,7 @@ describe( 'DataController utils', () => {
it( 'clears selection attrs when emptied content', () => {
setData( model, 'x[<$text bold="true">foo$text>]y' );
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
expect( getData( model ) ).to.equal( 'x[]y' );
expect( doc.selection.getAttribute( 'bold' ) ).to.undefined;
@@ -150,7 +146,7 @@ describe( 'DataController utils', () => {
}
);
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
expect( getData( model ) ).to.equal( 'x<$text bold="true">a[]b$text>y' );
expect( doc.selection.getAttribute( 'bold' ) ).to.equal( true );
@@ -171,7 +167,6 @@ describe( 'DataController utils', () => {
model = new Model();
doc = model.document;
doc.createRoot();
- data = new DataController( model );
const schema = model.schema;
@@ -226,7 +221,7 @@ describe( 'DataController utils', () => {
{ lastRangeBackward: true }
);
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
expect( getData( model ) ).to.equal( 'xfo[]ary' );
} );
@@ -262,7 +257,7 @@ describe( 'DataController utils', () => {
model.change( writer => {
mergeSpy = sinon.spy( writer, 'merge' );
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
} );
expect( getData( model ) ).to.equal( 'ab[]' );
@@ -277,7 +272,7 @@ describe( 'DataController utils', () => {
model.change( writer => {
mergeSpy = sinon.spy( writer, 'merge' );
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
} );
expect( getData( model ) ).to.equal( 'ab[]' );
@@ -293,7 +288,7 @@ describe( 'DataController utils', () => {
model.change( writer => {
mergeSpy = sinon.spy( writer, 'merge' );
moveSpy = sinon.spy( writer, 'move' );
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
} );
expect( getData( model ) ).to.equal( 'ab[]gh' );
@@ -346,7 +341,7 @@ describe( 'DataController utils', () => {
doc.selection.setRanges( [ range ] );
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
expect( getData( model ) )
.to.equal( 'xxfo[]aryy' );
@@ -391,7 +386,7 @@ describe( 'DataController utils', () => {
doc.selection.setRanges( [ range ] );
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
expect( getData( model ) )
.to.equal( 'xfooba[]om' );
@@ -434,7 +429,7 @@ describe( 'DataController utils', () => {
doc.selection.setRanges( [ range ] );
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
expect( getData( model ) )
.to.equal( 'fo[]' );
@@ -528,8 +523,6 @@ describe( 'DataController utils', () => {
// Special root which allows only blockWidgets inside itself.
doc.createRoot( 'restrictedRoot', 'restrictedRoot' );
- data = new DataController( model );
-
const schema = model.schema;
schema.limits.add( 'restrictedRoot' );
@@ -557,7 +550,7 @@ describe( 'DataController utils', () => {
{ rootName: 'paragraphRoot' }
);
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
expect( getData( model, { rootName: 'paragraphRoot' } ) )
.to.equal( 'x[]z' );
@@ -570,7 +563,7 @@ describe( 'DataController utils', () => {
{ rootName: 'bodyRoot' }
);
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
expect( getData( model, { rootName: 'bodyRoot' } ) )
.to.equal( 'x[]z' );
@@ -583,7 +576,7 @@ describe( 'DataController utils', () => {
{ rootName: 'bodyRoot' }
);
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
expect( getData( model, { rootName: 'bodyRoot' } ) )
.to.equal( 'x[]z' );
@@ -596,7 +589,7 @@ describe( 'DataController utils', () => {
{ rootName: 'bodyRoot' }
);
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
expect( getData( model, { rootName: 'bodyRoot' } ) )
.to.equal( 'x[]z' );
@@ -609,7 +602,7 @@ describe( 'DataController utils', () => {
{ rootName: 'bodyRoot' }
);
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
expect( getData( model, { rootName: 'bodyRoot' } ) )
.to.equal( 'x[]z' );
@@ -622,7 +615,7 @@ describe( 'DataController utils', () => {
{ rootName: 'bodyRoot' }
);
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
expect( getData( model, { rootName: 'bodyRoot' } ) )
.to.equal( '[]' );
@@ -635,7 +628,7 @@ describe( 'DataController utils', () => {
{ rootName: 'restrictedRoot' }
);
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
expect( getData( model, { rootName: 'restrictedRoot' } ) )
.to.equal( '[]' );
@@ -647,7 +640,6 @@ describe( 'DataController utils', () => {
model = new Model();
doc = model.document;
doc.createRoot();
- data = new DataController( model );
const schema = model.schema;
@@ -705,7 +697,6 @@ describe( 'DataController utils', () => {
model = new Model();
doc = model.document;
doc.createRoot();
- data = new DataController( model );
const schema = model.schema;
@@ -754,7 +745,6 @@ describe( 'DataController utils', () => {
model = new Model();
doc = model.document;
doc.createRoot();
- data = new DataController( model );
const schema = model.schema;
@@ -832,7 +822,7 @@ describe( 'DataController utils', () => {
{ rootName: 'paragraphRoot' }
);
- deleteContent( data, doc.selection );
+ deleteContent( model, doc.selection );
expect( getData( model, { rootName: 'paragraphRoot' } ) )
.to.equal( 'x[]z' );
@@ -852,7 +842,7 @@ describe( 'DataController utils', () => {
it( title, () => {
setData( model, input );
- deleteContent( data, doc.selection, options );
+ deleteContent( model, doc.selection, options );
expect( getData( model ) ).to.equal( output );
} );
diff --git a/tests/controller/getselectedcontent.js b/tests/model/utils/getselectedcontent.js
similarity index 77%
rename from tests/controller/getselectedcontent.js
rename to tests/model/utils/getselectedcontent.js
index a2aa5dbba..7f28d161b 100644
--- a/tests/controller/getselectedcontent.js
+++ b/tests/model/utils/getselectedcontent.js
@@ -3,27 +3,25 @@
* For licensing, see LICENSE.md.
*/
-import Model from '../../src/model/model';
-import DataController from '../../src/controller/datacontroller';
-import DocumentFragment from '../../src/model/documentfragment';
-import getSelectedContent from '../../src/controller/getselectedcontent';
-import { setData, stringify } from '../../src/dev-utils/model';
+import Model from '../../../src/model/model';
+import DocumentFragment from '../../../src/model/documentfragment';
+import getSelectedContent from '../../../src/model/utils/getselectedcontent';
+import { setData, stringify } from '../../../src/dev-utils/model';
describe( 'DataController utils', () => {
- let model, doc, data;
+ let model, doc;
describe( 'getSelectedContent', () => {
it( 'should use parent batch', () => {
model = new Model();
doc = model.document;
doc.createRoot();
- data = new DataController( model );
model.schema.allow( { name: '$text', inside: '$root' } );
setData( model, 'x[abc]x' );
model.change( writer => {
- getSelectedContent( data, doc.selection );
+ getSelectedContent( model, doc.selection );
expect( writer.batch.deltas ).to.length( 1 );
} );
} );
@@ -33,7 +31,6 @@ describe( 'DataController utils', () => {
model = new Model();
doc = model.document;
doc.createRoot();
- data = new DataController( model );
const schema = model.schema;
@@ -48,7 +45,7 @@ describe( 'DataController utils', () => {
it( 'returns empty fragment for no selection', () => {
setData( model, 'abc' );
- const frag = getSelectedContent( data, doc.selection );
+ const frag = getSelectedContent( model, doc.selection );
expect( frag ).instanceOf( DocumentFragment );
expect( frag.isEmpty ).to.be.true;
@@ -57,7 +54,7 @@ describe( 'DataController utils', () => {
it( 'returns empty fragment for empty selection', () => {
setData( model, 'a[]bc' );
- const frag = getSelectedContent( data, doc.selection );
+ const frag = getSelectedContent( model, doc.selection );
expect( frag ).instanceOf( DocumentFragment );
expect( frag.isEmpty ).to.be.true;
@@ -66,7 +63,7 @@ describe( 'DataController utils', () => {
it( 'gets one character', () => {
setData( model, 'a[b]c' );
- const frag = getSelectedContent( data, doc.selection );
+ const frag = getSelectedContent( model, doc.selection );
const content = stringify( frag );
expect( frag ).instanceOf( DocumentFragment );
@@ -76,49 +73,49 @@ describe( 'DataController utils', () => {
it( 'gets full text', () => {
setData( model, '[abc]' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'abc' );
} );
it( 'gets text with an attribute', () => {
setData( model, 'xxx<$text bold="true">a[b]c$text>' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( '<$text bold="true">b$text>' );
} );
it( 'gets text with attributes', () => {
setData( model, 'x<$text bold="true">a[b$text><$text italic="true">c]d$text>x' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( '<$text bold="true">b$text><$text italic="true">c$text>' );
} );
it( 'gets text with and without attribute', () => {
setData( model, '<$text bold="true">a[b$text>c]d' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( '<$text bold="true">b$text>c' );
} );
it( 'gets text and element', () => {
setData( model, '[abc]' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'abc' );
} );
it( 'gets one element', () => {
setData( model, 'a[]b' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( '' );
} );
it( 'gets multiple elements', () => {
setData( model, '[]' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( '' );
} );
} );
@@ -128,7 +125,6 @@ describe( 'DataController utils', () => {
model = new Model();
doc = model.document;
doc.createRoot();
- data = new DataController( model );
const schema = model.schema;
@@ -148,63 +144,63 @@ describe( 'DataController utils', () => {
it( 'gets one character', () => {
setData( model, 'a[b]c' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'b' );
} );
it( 'gets entire paragraph content', () => {
setData( model, '[ab]' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'ab' );
} );
it( 'gets two blocks - partial, partial', () => {
setData( model, 'a[bcde]f' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'bcde' );
} );
it( 'gets two blocks - full, partial', () => {
setData( model, '[abcde]f' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'abcde' );
} );
it( 'gets two blocks - full, partial 2', () => {
setData( model, '[abcde]f' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'abcde' );
} );
it( 'gets two blocks - full, partial 3', () => {
setData( model, 'x[abcde]f' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'abcde' );
} );
it( 'gets two blocks - full, partial 4', () => {
setData( model, '[abcde]f' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'abcde' );
} );
it( 'gets two blocks - partial, full', () => {
setData( model, 'a[bcdef]' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'bcdef' );
} );
it( 'gets two blocks - partial, full 2', () => {
setData( model, 'a[bcdef]' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'bcdef' );
} );
@@ -212,7 +208,7 @@ describe( 'DataController utils', () => {
it( 'gets two blocks - empty, full', () => {
setData( model, 'abc[def]' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'def' );
} );
@@ -220,28 +216,28 @@ describe( 'DataController utils', () => {
it( 'gets two blocks - partial, empty', () => {
setData( model, 'a[bc]def' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'bc' );
} );
it( 'gets three blocks', () => {
setData( model, 'a[bcxde]f' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'bcxde' );
} );
it( 'gets block image', () => {
setData( model, 'a[Foo]b' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'Foo' );
} );
it( 'gets two blocks', () => {
setData( model, 'a[]b' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( '' );
} );
@@ -249,7 +245,7 @@ describe( 'DataController utils', () => {
it( 'gets content when multiple text items needs to be removed from the right excess', () => {
setData( model, 'a[bc]d<$text bold="true">e$text>f' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content )
.to.equal( 'bc' );
} );
@@ -258,7 +254,7 @@ describe( 'DataController utils', () => {
it( 'gets content when multiple text items needs to be removed from the left excess', () => {
setData( model, 'a<$text bold="true">b$text>c[de]f' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content )
.to.equal( 'de' );
} );
@@ -269,7 +265,6 @@ describe( 'DataController utils', () => {
model = new Model();
doc = model.document;
doc.createRoot();
- data = new DataController( model );
const schema = model.schema;
@@ -284,28 +279,28 @@ describe( 'DataController utils', () => {
it( 'gets content when ends are equally deeply nested', () => {
setData( model, 'xa[bcde]f
' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'bcde' );
} );
it( 'gets content when left end nested deeper', () => {
setData( model, 'a[bc
de]f' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'bc
de' );
} );
it( 'gets content when left end nested deeper 2', () => {
setData( model, 'a[bcx
de]f' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'bcx
de' );
} );
it( 'gets content when left end nested deeper 3', () => {
setData( model, 'xa[bc
de]f' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'bc
de' );
} );
@@ -313,21 +308,21 @@ describe( 'DataController utils', () => {
it( 'gets content when left end nested deeper 4', () => {
setData( model, 'x[abc
de]f' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'abc
de' );
} );
it( 'gets content when right end nested deeper', () => {
setData( model, 'a[bcde]f
' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content ).to.equal( 'bcde
' );
} );
it( 'gets content when both ends nested deeper than the middle element', () => {
setData( model, 'a[bc
xde]f
' );
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content )
.to.equal( 'bc
xde
' );
} );
@@ -347,7 +342,7 @@ describe( 'DataController utils', () => {
''
);
- const content = stringify( getSelectedContent( data, doc.selection ) );
+ const content = stringify( getSelectedContent( model, doc.selection ) );
expect( content )
.to.equal( 'arbo' );
} );
diff --git a/tests/controller/insertcontent.js b/tests/model/utils/insertcontent.js
similarity index 96%
rename from tests/controller/insertcontent.js
rename to tests/model/utils/insertcontent.js
index 6bc644fca..9b6621da7 100644
--- a/tests/controller/insertcontent.js
+++ b/tests/model/utils/insertcontent.js
@@ -3,31 +3,28 @@
* For licensing, see LICENSE.md.
*/
-import Model from '../../src/model/model';
-import DataController from '../../src/controller/datacontroller';
-import insertContent from '../../src/controller/insertcontent';
+import Model from '../../../src/model/model';
+import insertContent from '../../../src/model/utils/insertcontent';
+import DocumentFragment from '../../../src/model/documentfragment';
+import Text from '../../../src/model/text';
+import Element from '../../../src/model/element';
-import DocumentFragment from '../../src/model/documentfragment';
-import Text from '../../src/model/text';
-import Element from '../../src/model/element';
-
-import { setData, getData, parse } from '../../src/dev-utils/model';
+import { setData, getData, parse } from '../../../src/dev-utils/model';
describe( 'DataController utils', () => {
- let model, doc, data;
+ let model, doc;
describe( 'insertContent', () => {
it( 'should use parent batch', () => {
model = new Model();
doc = model.document;
doc.createRoot();
- data = new DataController( model );
model.schema.allow( { name: '$text', inside: '$root' } );
setData( model, 'x[]x' );
model.change( writer => {
- insertContent( data, new Text( 'a' ), doc.selection );
+ insertContent( model, new Text( 'a' ), doc.selection );
expect( writer.batch.deltas ).to.length( 1 );
} );
} );
@@ -36,13 +33,12 @@ describe( 'DataController utils', () => {
model = new Model();
doc = model.document;
doc.createRoot();
- data = new DataController( model );
model.schema.allow( { name: '$text', inside: '$root' } );
setData( model, 'x[]x' );
- insertContent( data, new DocumentFragment( [ new Text( 'a' ) ] ), doc.selection );
+ insertContent( model, new DocumentFragment( [ new Text( 'a' ) ] ), doc.selection );
expect( getData( model ) ).to.equal( 'xa[]x' );
} );
@@ -51,13 +47,12 @@ describe( 'DataController utils', () => {
model = new Model();
doc = model.document;
doc.createRoot();
- data = new DataController( model );
model.schema.allow( { name: '$text', inside: '$root' } );
setData( model, 'x[]x' );
- insertContent( data, new Text( 'a' ), doc.selection );
+ insertContent( model, new Text( 'a' ), doc.selection );
expect( getData( model ) ).to.equal( 'xa[]x' );
} );
@@ -66,7 +61,6 @@ describe( 'DataController utils', () => {
model = new Model();
doc = model.document;
doc.createRoot();
- data = new DataController( model );
const content = new Element( 'image' );
@@ -76,7 +70,7 @@ describe( 'DataController utils', () => {
setData( model, 'foo[]' );
- insertContent( data, content, doc.selection );
+ insertContent( model, content, doc.selection );
expect( doc.getRoot().getChild( 0 ).getChild( 1 ) ).to.equal( content );
} );
@@ -86,7 +80,6 @@ describe( 'DataController utils', () => {
model = new Model();
doc = model.document;
doc.createRoot();
- data = new DataController( model );
const schema = model.schema;
@@ -217,7 +210,6 @@ describe( 'DataController utils', () => {
model = new Model();
doc = model.document;
doc.createRoot();
- data = new DataController( model );
const schema = model.schema;
@@ -289,7 +281,7 @@ describe( 'DataController utils', () => {
] );
setData( model, '[foo]' );
- insertContent( data, content, doc.selection );
+ insertContent( model, content, doc.selection );
expect( getData( model ) ).to.equal( 'bar[]' );
} );
@@ -590,7 +582,6 @@ describe( 'DataController utils', () => {
model = new Model();
doc = model.document;
doc.createRoot();
- data = new DataController( model );
const schema = model.schema;
@@ -697,7 +688,6 @@ describe( 'DataController utils', () => {
model = new Model();
doc = model.document;
doc.createRoot();
- data = new DataController( model );
const schema = model.schema;
@@ -764,6 +754,6 @@ describe( 'DataController utils', () => {
} );
}
- insertContent( data, content, doc.selection );
+ insertContent( model, content, doc.selection );
}
} );
diff --git a/tests/controller/modifyselection.js b/tests/model/utils/modifyselection.js
similarity index 89%
rename from tests/controller/modifyselection.js
rename to tests/model/utils/modifyselection.js
index f4f75bcfa..e9d1c2ffc 100644
--- a/tests/controller/modifyselection.js
+++ b/tests/model/utils/modifyselection.js
@@ -3,19 +3,17 @@
* For licensing, see LICENSE.md.
*/
-import Model from '../../src/model/model';
-import DataController from '../../src/controller/datacontroller';
-import Selection from '../../src/model/selection';
-import modifySelection from '../../src/controller/modifyselection';
-import { setData, stringify } from '../../src/dev-utils/model';
+import Model from '../../../src/model/model';
+import Selection from '../../../src/model/selection';
+import modifySelection from '../../../src/model/utils/modifyselection';
+import { setData, stringify } from '../../../src/dev-utils/model';
describe( 'DataController utils', () => {
- let model, doc, data;
+ let model, doc;
beforeEach( () => {
model = new Model();
doc = model.document;
- data = new DataController( model );
model.schema.registerItem( 'p', '$block' );
model.schema.registerItem( 'x', '$block' );
@@ -68,7 +66,7 @@ describe( 'DataController utils', () => {
it( 'extends one character backward', () => {
setData( model, 'fo[]o
', { lastRangeBackward: true } );
- modifySelection( data, doc.selection, { direction: 'backward' } );
+ modifySelection( model, doc.selection, { direction: 'backward' } );
expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( 'f[o]o
' );
expect( doc.selection.isBackward ).to.true;
@@ -83,7 +81,7 @@ describe( 'DataController utils', () => {
it( 'extends one character backward (non-collapsed)', () => {
setData( model, 'foob[a]r
', { lastRangeBackward: true } );
- modifySelection( data, doc.selection, { direction: 'backward' } );
+ modifySelection( model, doc.selection, { direction: 'backward' } );
expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( 'foo[ba]r
' );
expect( doc.selection.isBackward ).to.true;
@@ -98,7 +96,7 @@ describe( 'DataController utils', () => {
it( 'extends to element boundary (backward)', () => {
setData( model, 'f[]oo
' );
- modifySelection( data, doc.selection, { direction: 'backward' } );
+ modifySelection( model, doc.selection, { direction: 'backward' } );
expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '[f]oo
' );
expect( doc.selection.isBackward ).to.true;
@@ -114,7 +112,7 @@ describe( 'DataController utils', () => {
it( 'shrinks backward selection (to collapsed)', () => {
setData( model, 'foo[b]ar
', { lastRangeBackward: true } );
- modifySelection( data, doc.selection );
+ modifySelection( model, doc.selection );
expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( 'foob[]ar
' );
expect( doc.selection.isBackward ).to.false;
@@ -129,7 +127,7 @@ describe( 'DataController utils', () => {
it( 'unicode support - combining mark backward', () => {
setData( model, 'foob̂[]ar
' );
- modifySelection( data, doc.selection, { direction: 'backward' } );
+ modifySelection( model, doc.selection, { direction: 'backward' } );
expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( 'foo[b̂]ar
' );
expect( doc.selection.isBackward ).to.true;
@@ -144,7 +142,7 @@ describe( 'DataController utils', () => {
it( 'unicode support - combining mark multiple backward', () => {
setData( model, 'foo̻̐ͩ[]bar
' );
- modifySelection( data, doc.selection, { direction: 'backward' } );
+ modifySelection( model, doc.selection, { direction: 'backward' } );
expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( 'fo[o̻̐ͩ]bar
' );
expect( doc.selection.isBackward ).to.true;
@@ -165,7 +163,7 @@ describe( 'DataController utils', () => {
it( 'unicode support - surrogate pairs backward', () => {
setData( model, '\uD83D\uDCA9[]
' );
- modifySelection( data, doc.selection, { direction: 'backward' } );
+ modifySelection( model, doc.selection, { direction: 'backward' } );
expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '[\uD83D\uDCA9]
' );
expect( doc.selection.isBackward ).to.true;
@@ -182,7 +180,7 @@ describe( 'DataController utils', () => {
it( 'extends over boundary of empty elements (backward)', () => {
setData( model, '[]
' );
- modifySelection( data, doc.selection, { direction: 'backward' } );
+ modifySelection( model, doc.selection, { direction: 'backward' } );
expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '[
]
' );
expect( doc.selection.isBackward ).to.true;
@@ -197,7 +195,7 @@ describe( 'DataController utils', () => {
it( 'extends over boundary of non-empty elements (backward)', () => {
setData( model, 'a
[]bcd
' );
- modifySelection( data, doc.selection, { direction: 'backward' } );
+ modifySelection( model, doc.selection, { direction: 'backward' } );
expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( 'a[
]bcd
' );
expect( doc.selection.isBackward ).to.true;
@@ -212,7 +210,7 @@ describe( 'DataController utils', () => {
it( 'extends over character after boundary (backward)', () => {
setData( model, 'abc[
]d
', { lastRangeBackward: true } );
- modifySelection( data, doc.selection, { direction: 'backward' } );
+ modifySelection( model, doc.selection, { direction: 'backward' } );
expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( 'ab[c
]d
' );
expect( doc.selection.isBackward ).to.true;
@@ -246,7 +244,7 @@ describe( 'DataController utils', () => {
it( 'shrinks over boundary of empty elements', () => {
setData( model, '[
]
', { lastRangeBackward: true } );
- modifySelection( data, doc.selection );
+ modifySelection( model, doc.selection );
expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '[]
' );
expect( doc.selection.isBackward ).to.false;
@@ -255,7 +253,7 @@ describe( 'DataController utils', () => {
it( 'shrinks over boundary of empty elements (backward)', () => {
setData( model, '[
]
' );
- modifySelection( data, doc.selection, { direction: 'backward' } );
+ modifySelection( model, doc.selection, { direction: 'backward' } );
expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '[]
' );
expect( doc.selection.isBackward ).to.false;
@@ -264,7 +262,7 @@ describe( 'DataController utils', () => {
it( 'shrinks over boundary of non-empty elements', () => {
setData( model, 'a[
]b
', { lastRangeBackward: true } );
- modifySelection( data, doc.selection );
+ modifySelection( model, doc.selection );
expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( 'a
[]b
' );
expect( doc.selection.isBackward ).to.false;
@@ -280,7 +278,7 @@ describe( 'DataController utils', () => {
it( 'updates selection attributes', () => {
setData( model, '<$text bold="true">foo$text>[b]
' );
- modifySelection( data, doc.selection, { direction: 'backward' } );
+ modifySelection( model, doc.selection, { direction: 'backward' } );
expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '<$text bold="true">foo[]$text>b
' );
expect( doc.selection.getAttribute( 'bold' ) ).to.equal( true );
@@ -341,7 +339,7 @@ describe( 'DataController utils', () => {
setData( model, '' );
- modifySelection( data, doc.selection, { unit: 'codePoint' } );
+ modifySelection( model, doc.selection, { unit: 'codePoint' } );
expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '[]' );
} );
@@ -363,7 +361,7 @@ describe( 'DataController utils', () => {
it( 'extends one user-perceived character backward - latin letters', () => {
setData( model, 'fo[]o
' );
- modifySelection( data, doc.selection, { unit: 'codePoint', direction: 'backward' } );
+ modifySelection( model, doc.selection, { unit: 'codePoint', direction: 'backward' } );
expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( 'f[o]o
' );
expect( doc.selection.isBackward ).to.true;
@@ -383,7 +381,7 @@ describe( 'DataController utils', () => {
// Document's selection will throw errors in some test cases (which are correct cases, but only for
// non-document selections).
const testSelection = Selection.createFromSelection( doc.selection );
- modifySelection( data, testSelection, { unit: 'codePoint', direction: 'backward' } );
+ modifySelection( model, testSelection, { unit: 'codePoint', direction: 'backward' } );
expect( stringify( doc.getRoot(), testSelection ) ).to.equal( 'foob[̂]ar
' );
expect( testSelection.isBackward ).to.true;
@@ -406,7 +404,7 @@ describe( 'DataController utils', () => {
it( 'unicode support surrogate pairs backward', () => {
setData( model, '\uD83D\uDCA9[]
' );
- modifySelection( data, doc.selection, { unit: 'codePoint', direction: 'backward' } );
+ modifySelection( model, doc.selection, { unit: 'codePoint', direction: 'backward' } );
expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '[\uD83D\uDCA9]
' );
expect( doc.selection.isBackward ).to.true;
@@ -447,7 +445,7 @@ describe( 'DataController utils', () => {
it( 'extends over object elements - backward', () => {
setData( model, '[]', { lastRangeBackward: true } );
- modifySelection( data, doc.selection, { direction: 'backward' } );
+ modifySelection( model, doc.selection, { direction: 'backward' } );
expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '[]' );
expect( doc.selection.isBackward ).to.true;
@@ -549,7 +547,7 @@ describe( 'DataController utils', () => {
// Document's selection will throw errors in some test cases (which are correct cases, but only for
// non-document selections).
const testSelection = Selection.createFromSelection( doc.selection );
- modifySelection( data, testSelection, options );
+ modifySelection( model, testSelection, options );
expect( stringify( doc.getRoot(), testSelection ) ).to.equal( output );
} );