8
0
Pārlūkot izejas kodu

Change operation accepts multiple ranges.

Piotr Jasiun 10 gadi atpakaļ
vecāks
revīzija
5266b4950d

+ 75 - 69
packages/ckeditor5-utils/src/document/changeoperation.js

@@ -5,7 +5,7 @@
 
 'use strict';
 
-CKEDITOR.define( [ 'document/operation', 'ckeditorerror' ], function( Operation, CKEditorError ) {
+CKEDITOR.define( [ 'document/operation', 'ckeditorerror', 'utils' ], function( Operation, CKEditorError, utils ) {
 	/**
 	 *
 	 *
@@ -15,97 +15,103 @@ CKEDITOR.define( [ 'document/operation', 'ckeditorerror' ], function( Operation,
 		/**
 		 *
 		 */
-		constructor( range, oldAttr, newAttr, baseVersion ) {
+		constructor( ranges, oldAttr, newAttr, baseVersion ) {
 			super( baseVersion );
 
-			this.range = range;
+			this.ranges = utils.isArray( ranges ) ? ranges : [ ranges ];
 			this.oldAttr = oldAttr;
 			this.newAttr = newAttr;
 		}
 
 		_execute() {
-			var range = this.range;
+			var ranges = this.ranges;
 			var oldAttr = this.oldAttr;
 			var newAttr = this.newAttr;
 
-			var value;
+			var value, range;
 
 			// Remove.
 			if ( newAttr === null ) {
-				for ( value of range ) {
-					if ( !value.node.hasAttr( oldAttr ) ) {
-						/**
-						 * The attribute which should be removed does not exists.
-						 *
-						 * @error operation-change-no-attr-to-remove
-						 * @param {document.ChangeOperation} changeOperation
-						 * @param {document.Node} node
-						 * @param {document.Attribute} attr
-						 */
-						throw new CKEditorError(
-							'operation-change-no-attr-to-remove: The attribute which should be removed does not exists.',
-							{ changeOperation: this, node: value.node, attr: oldAttr } );
+				for ( range of ranges ) {
+					for ( value of range ) {
+						if ( !value.node.hasAttr( oldAttr ) ) {
+							/**
+							 * The attribute which should be removed does not exists.
+							 *
+							 * @error operation-change-no-attr-to-remove
+							 * @param {document.ChangeOperation} changeOperation
+							 * @param {document.Node} node
+							 * @param {document.Attribute} attr
+							 */
+							throw new CKEditorError(
+								'operation-change-no-attr-to-remove: The attribute which should be removed does not exists.',
+								{ changeOperation: this, node: value.node, attr: oldAttr } );
+						}
+
+						doRemove( value.node.attrs, oldAttr );
 					}
-
-					doRemove( value.node.attrs, oldAttr );
 				}
 			}
 			// Insert.
 			else if ( oldAttr === null ) {
-				for ( value of range ) {
-					if ( value.node.hasAttr( newAttr.key ) ) {
-						/**
-						 * The attribute with given key already exists.
-						 *
-						 * @error operation-change-attr-exists
-						 * @param {document.ChangeOperation} changeOperation
-						 * @param {document.Node} node
-						 * @param {document.Attribute} attr
-						 */
-						throw new CKEditorError(
-							'operation-change-attr-exists: The attribute with given key already exists.',
-							{ changeOperation: this, node: value.node, attr: newAttr } );
+				for ( range of ranges ) {
+					for ( value of range ) {
+						if ( value.node.hasAttr( newAttr.key ) ) {
+							/**
+							 * The attribute with given key already exists.
+							 *
+							 * @error operation-change-attr-exists
+							 * @param {document.ChangeOperation} changeOperation
+							 * @param {document.Node} node
+							 * @param {document.Attribute} attr
+							 */
+							throw new CKEditorError(
+								'operation-change-attr-exists: The attribute with given key already exists.',
+								{ changeOperation: this, node: value.node, attr: newAttr } );
+						}
+
+						doInsert( value.node.attrs, newAttr );
 					}
-
-					doInsert( value.node.attrs, newAttr );
 				}
 			}
 			// Change.
 			else {
-				for ( value of range ) {
-					if ( oldAttr.key != newAttr.key ) {
-						/**
-						 * Old and new attributes should have the same keys.
-						 *
-						 * @error operation-change-different-keys
-						 * @param {document.ChangeOperation} changeOperation
-						 * @param {document.Node} node
-						 * @param {document.Attribute} oldAttr
-						 * @param {document.Attribute} newAttr
-						 */
-						throw new CKEditorError(
-							'operation-change-different-keys: Old and new attributes should have the same keys.',
-							{ changeOperation: this, node: value.node, oldAttr: oldAttr, newAttr: newAttr } );
-					}
-
-					if ( !value.node.hasAttr( oldAttr ) ) {
-						/**
-						 * The attribute which should be changed does not exists.
-						 *
-						 * @error operation-change-no-attr-to-change
-						 * @param {document.ChangeOperation} changeOperation
-						 * @param {document.Node} node
-						 * @param {document.Attribute} oldAttr
-						 * @param {document.Attribute} newAttr
-						 */
-						throw new CKEditorError(
-							'operation-change-no-attr-to-change: The attribute which should be changed does not exists.',
-							{ changeOperation: this, node: value.node, oldAttr: oldAttr, newAttr: newAttr } );
+				for ( range of ranges ) {
+					for ( value of range ) {
+						if ( oldAttr.key != newAttr.key ) {
+							/**
+							 * Old and new attributes should have the same keys.
+							 *
+							 * @error operation-change-different-keys
+							 * @param {document.ChangeOperation} changeOperation
+							 * @param {document.Node} node
+							 * @param {document.Attribute} oldAttr
+							 * @param {document.Attribute} newAttr
+							 */
+							throw new CKEditorError(
+								'operation-change-different-keys: Old and new attributes should have the same keys.',
+								{ changeOperation: this, node: value.node, oldAttr: oldAttr, newAttr: newAttr } );
+						}
+
+						if ( !value.node.hasAttr( oldAttr ) ) {
+							/**
+							 * The attribute which should be changed does not exists.
+							 *
+							 * @error operation-change-no-attr-to-change
+							 * @param {document.ChangeOperation} changeOperation
+							 * @param {document.Node} node
+							 * @param {document.Attribute} oldAttr
+							 * @param {document.Attribute} newAttr
+							 */
+							throw new CKEditorError(
+								'operation-change-no-attr-to-change: The attribute which should be changed does not exists.',
+								{ changeOperation: this, node: value.node, oldAttr: oldAttr, newAttr: newAttr } );
+						}
+
+						doRemove( value.node.attrs, oldAttr );
+
+						doInsert( value.node.attrs, newAttr );
 					}
-
-					doRemove( value.node.attrs, oldAttr );
-
-					doInsert( value.node.attrs, newAttr );
 				}
 			}
 
@@ -127,7 +133,7 @@ CKEDITOR.define( [ 'document/operation', 'ckeditorerror' ], function( Operation,
 		}
 
 		reverseOperation() {
-			return new ChangeOperation( this.range, this.newAttr, this.oldAttr, this.baseVersion + 1 );
+			return new ChangeOperation( this.ranges, this.newAttr, this.oldAttr, this.baseVersion + 1 );
 		}
 	}
 

+ 103 - 3
packages/ckeditor5-utils/tests/document/changeoperation.js

@@ -46,6 +46,38 @@ describe( 'ChangeOperation', function() {
 		expect( doc.root.children[ 2 ].attrs.length ).to.be.equal( 0 );
 	} );
 
+	it( 'should insert attribute to multiple ranges', function() {
+		var Document = modules[ 'document/document' ];
+		var ChangeOperation = modules[ 'document/changeoperation' ];
+		var Position = modules[ 'document/position' ];
+		var Range = modules[ 'document/range' ];
+		var Character = modules[ 'document/character' ];
+		var Attribute = modules[ 'document/attribute' ];
+
+		var doc = new Document();
+
+		var newAttr = new Attribute( 'isNew', true );
+
+		doc.root.children.push( new Character( doc.root, 'b' ) );
+		doc.root.children.push( new Character( doc.root, 'a' ) );
+		doc.root.children.push( new Character( doc.root, 'r' ) );
+
+		doc.applyOperation( new ChangeOperation(
+			[
+				new Range( new Position( [ 0 ], doc ), new Position( [ 1 ], doc ) ),
+				new Range( new Position( [ 2 ], doc ), new Position( [ 3 ], doc ) )
+			],
+			null,
+			newAttr,
+			doc.version ) );
+
+		expect( doc.version ).to.be.equal( 1 );
+		expect( doc.root.children.length ).to.be.equal( 3 );
+		expect( doc.root.children[ 0 ].hasAttr( newAttr ) ).to.be.true;
+		expect( doc.root.children[ 1 ].attrs.length ).to.be.equal( 0 );
+		expect( doc.root.children[ 2 ].hasAttr( newAttr ) ).to.be.true;
+	} );
+
 	it( 'should add attribute to the existing attributes', function() {
 		var Document = modules[ 'document/document' ];
 		var ChangeOperation = modules[ 'document/changeoperation' ];
@@ -76,6 +108,42 @@ describe( 'ChangeOperation', function() {
 		expect( doc.root.children[ 0 ].hasAttr( barAttr ) ).to.be.true;
 	} );
 
+	it( 'should change attributes on multiple ranges', function() {
+		var Document = modules[ 'document/document' ];
+		var ChangeOperation = modules[ 'document/changeoperation' ];
+		var Position = modules[ 'document/position' ];
+		var Range = modules[ 'document/range' ];
+		var Character = modules[ 'document/character' ];
+		var Attribute = modules[ 'document/attribute' ];
+
+		var doc = new Document();
+
+		var oldAttr = new Attribute( 'isNew', false );
+		var newAttr = new Attribute( 'isNew', true );
+
+		doc.root.children.push( new Character( doc.root, 'b', [ oldAttr ] ) );
+		doc.root.children.push( new Character( doc.root, 'a', [ oldAttr ] ) );
+		doc.root.children.push( new Character( doc.root, 'r', [ oldAttr ] ) );
+
+		doc.applyOperation( new ChangeOperation(
+			[
+				new Range( new Position( [ 0 ], doc ), new Position( [ 1 ], doc ) ),
+				new Range( new Position( [ 2 ], doc ), new Position( [ 3 ], doc ) )
+			],
+			oldAttr,
+			newAttr,
+			doc.version ) );
+
+		expect( doc.version ).to.be.equal( 1 );
+		expect( doc.root.children.length ).to.be.equal( 3 );
+		expect( doc.root.children[ 0 ].attrs.length ).to.be.equal( 1 );
+		expect( doc.root.children[ 0 ].hasAttr( newAttr ) ).to.be.true;
+		expect( doc.root.children[ 1 ].attrs.length ).to.be.equal( 1 );
+		expect( doc.root.children[ 1 ].hasAttr( oldAttr ) ).to.be.true;
+		expect( doc.root.children[ 2 ].attrs.length ).to.be.equal( 1 );
+		expect( doc.root.children[ 2 ].hasAttr( newAttr ) ).to.be.true;
+	} );
+
 	it( 'should change attribute to the set of nodes', function() {
 		var Document = modules[ 'document/document' ];
 		var ChangeOperation = modules[ 'document/changeoperation' ];
@@ -169,6 +237,38 @@ describe( 'ChangeOperation', function() {
 		expect( doc.root.children[ 0 ].hasAttr( barAttr ) ).to.be.true;
 	} );
 
+	it( 'should remove attributes on multiple ranges', function() {
+		var Document = modules[ 'document/document' ];
+		var ChangeOperation = modules[ 'document/changeoperation' ];
+		var Position = modules[ 'document/position' ];
+		var Range = modules[ 'document/range' ];
+		var Character = modules[ 'document/character' ];
+		var Attribute = modules[ 'document/attribute' ];
+
+		var doc = new Document();
+
+		var fooAttr = new Attribute( 'foo', true );
+
+		doc.root.children.push( new Character( doc.root, 'b', [ fooAttr ] ) );
+		doc.root.children.push( new Character( doc.root, 'a', [ fooAttr ] ) );
+		doc.root.children.push( new Character( doc.root, 'r', [ fooAttr ] ) );
+
+		doc.applyOperation( new ChangeOperation(
+			[
+				new Range( new Position( [ 0 ], doc ), new Position( [ 1 ], doc ) ),
+				new Range( new Position( [ 2 ], doc ), new Position( [ 3 ], doc ) )
+			],
+			fooAttr,
+			null,
+			doc.version ) );
+
+		expect( doc.version ).to.be.equal( 1 );
+		expect( doc.root.children.length ).to.be.equal( 3 );
+		expect( doc.root.children[ 0 ].attrs.length ).to.be.equal( 0 );
+		expect( doc.root.children[ 1 ].hasAttr( fooAttr ) ).to.be.true;
+		expect( doc.root.children[ 2 ].attrs.length ).to.be.equal( 0 );
+	} );
+
 	it( 'should create a change operation as a reverse', function() {
 		var Document = modules[ 'document/document' ];
 		var ChangeOperation = modules[ 'document/changeoperation' ];
@@ -181,15 +281,15 @@ describe( 'ChangeOperation', function() {
 		var oldAttr = new Attribute( 'x', 'old' );
 		var newAttr = new Attribute( 'x', 'new' );
 
-		var range = new Range( new Position( [ 0 ], doc ), new Position( [ 3 ], doc ) );
+		var ranges = [ new Range( new Position( [ 0 ], doc ), new Position( [ 3 ], doc ) ) ];
 
-		var oppertaion = new ChangeOperation( range, oldAttr, newAttr, doc.version );
+		var oppertaion = new ChangeOperation( ranges, oldAttr, newAttr, doc.version );
 
 		var reverse = oppertaion.reverseOperation();
 
 		expect( reverse ).to.be.an.instanceof( ChangeOperation );
 		expect( reverse.baseVersion ).to.be.equals( 1 );
-		expect( reverse.range ).to.be.equals( range );
+		expect( reverse.ranges ).to.be.equals( ranges );
 		expect( reverse.oldAttr ).to.be.equals( newAttr );
 		expect( reverse.newAttr ).to.be.equals( oldAttr );
 	} );