浏览代码

Introduced "Transaction.setAttr".

pjasiun 10 年之前
父节点
当前提交
2bef48ae2b

+ 45 - 7
packages/ckeditor5-engine/src/document/deltas/changedelta.js

@@ -8,22 +8,60 @@
 CKEDITOR.define( [
 	'document/deltas/delta',
 	'document/deltas/register',
-	'document/operations/changeoperation'
-], ( Delta, register, ChangeOperation ) => {
+	'document/operations/changeoperation',
+	'document/range',
+	'document/attribute'
+], ( Delta, register, ChangeOperation, Range, Attribute ) => {
 	/**
 	 * @class document.delta.ChangeDelta
 	 */
 	class ChangeDelta extends Delta {}
 
-	register( 'setAttr', ( doc, transaction, attr, range ) => {
+	register( 'setAttr', ( doc, transaction, key, value, range ) => {
 		var ops = [];
-		var startPosition = range.get
+		var lastSplitPosition = range.start;
 
-		for ( value of range ) {
-			value
+		var position;
+		var valueBefore;
+		var valueAfter;
+
+		var iterator = range[ Symbol.iterator ]();
+		var next = iterator.next();
+
+		while ( !next.done ) {
+			valueAfter = next.value.node.getAttr( key );
+
+			if ( position && valueBefore != valueAfter ) {
+				if ( valueBefore != value ) {
+					ops.push( new ChangeOperation(
+						new Range( lastSplitPosition, position ),
+						valueBefore ? new Attribute( key, valueBefore ) : null,
+						new Attribute( key, value ),
+						doc.version + ops.length
+					) );
+				}
+
+				lastSplitPosition = position;
+			}
+
+			position = iterator.position;
+			valueBefore = valueAfter;
+
+			next = iterator.next();
 		}
 
-		return new ChangeDelta( transaction, {} );
+		if ( position != lastSplitPosition && valueBefore != value ) {
+			ops.push( new ChangeOperation(
+				new Range( lastSplitPosition, position ),
+				valueBefore ? new Attribute( key, valueBefore ) : null,
+				new Attribute( key, value ),
+				doc.version + ops.length
+			) );
+		}
+
+
+
+		return new ChangeDelta( transaction, ops );
 	} );
 
 	register( 'removeAttr', ( doc, transaction, key, range ) => {

+ 4 - 0
packages/ckeditor5-engine/src/document/deltas/delta.js

@@ -18,6 +18,10 @@ CKEDITOR.define( [], function() {
 			this.operations = Array.from( operations );
 		}
 
+		[ Symbol.iterator ]() {
+			return this.operations[ Symbol.iterator ]();
+		}
+
 		_execute() {
 			for ( var operation of this.operations ) {
 				this.transaction.doc.applyOperation( operation );

+ 7 - 3
packages/ckeditor5-engine/src/document/deltas/transaction-base.js

@@ -5,7 +5,7 @@
 
 'use strict';
 
-CKEDITOR.define( [ 'utils' ], ( utils ) => {
+CKEDITOR.define( [ 'utils', 'document/deltas/delta' ], ( utils, Delta ) => {
 	/**
 	 * @class document.Transaction
 	 */
@@ -18,15 +18,19 @@ CKEDITOR.define( [ 'utils' ], ( utils ) => {
 			this.deltas = [];
 		}
 
+		[ Symbol.iterator ]() {
+			return this.deltas[ Symbol.iterator ]();
+		}
+
 		static register( name, creator ) {
 			if ( Transaction.prototype[ name ] ) {
 				throw 'error';
 			}
 
 			Transaction.prototype[ name ] = function() {
-				var deltas = creator( [ this.doc, this ].concat( arguments ) );
+				var deltas = creator.apply( this, [ this.doc, this ].concat( Array.from( arguments ) ) );
 
-				if ( !utils.isIterable( deltas ) ) {
+				if ( deltas instanceof Delta ) {
 					deltas = [ deltas ];
 				}
 

+ 64 - 27
packages/ckeditor5-engine/tests/document/deltas/changedelta.js

@@ -64,49 +64,73 @@ describe( 'Transaction', () => {
 		return count;
 	}
 
-	function assertAttrs( expected ) {
-		var range = Range.createFromElement( root );
-		var actual = Array.from( range ).map( value => value.node.getAttr( 'a' ) || '-' ).join( '' );
+	function getChangesAttrsCount() {
+		var count = 0;
 
-		// actual: 111---111222---111
-		expect( actual ).to.be.deep.equal( expected );
+		for ( var delta of transaction ) {
+			for ( var operation of delta ) {
+				for ( var value of operation.range ) {
+					count++;
+				}
+			}
+		}
+
+		return count;
+	}
+
+	function getCompressedAttrs() {
+		// default: 111---111222---111
+		var range = Range.createFromElement( root );
+		return Array.from( range ).map( value => value.node.getAttr( 'a' ) || '-' ).join( '' );
 	}
 
 	describe( 'setAttr', () => {
 		it( 'should set the attribute on the range', () => {
-			transaction.setAttr( new Attribute( 'a', 3 ), getRange( 3, 6 ) );
-			expect( getOperationsCount() ).
-			assertAttrs( '111333111222---111' );
+			transaction.setAttr( 'a', 3, getRange( 3, 6 ) );
+			expect( getOperationsCount() ).to.equal( 1 );
+			expect( getChangesAttrsCount() ).to.equal( 3 );
+			expect( getCompressedAttrs() ).to.equal( '111333111222---111' );
 		} );
 
 		it( 'should split the operations if parts of the range have different attributes', () => {
-			transaction.setAttr( new Attribute( 'a', 3 ), getRange( 4, 14 ) );
-			assertAttrs( '111-3333333333-111' );
+			transaction.setAttr( 'a', 3, getRange( 4, 14 ) );
+			expect( getOperationsCount() ).to.equal( 4 );
+			expect( getChangesAttrsCount() ).to.equal( 10 );
+			expect( getCompressedAttrs() ).to.equal( '111-3333333333-111' );
 		} );
 
 		it( 'should split the operations if parts of the part of the range have the attribute', () => {
-			transaction.setAttr( new Attribute( 'a', 2 ), getRange( 4, 14 ) );
-			assertAttrs( '111-2222222222-111' );
+			transaction.setAttr( 'a', 2, getRange( 4, 14 ) );
+			expect( getOperationsCount() ).to.equal( 3 );
+			expect( getChangesAttrsCount() ).to.equal( 7 );
+			expect( getCompressedAttrs() ).to.equal( '111-2222222222-111' );
 		} );
 
 		it( 'should strip the range if the beginning have the attribute', () => {
-			transaction.setAttr( new Attribute( 'a', 1 ), getRange( 1, 5 ) );
-			assertAttrs( '11111-111222---111' );
+			transaction.setAttr( 'a', 1, getRange( 1, 5 ) );
+			expect( getOperationsCount() ).to.equal( 1 );
+			expect( getChangesAttrsCount() ).to.equal( 2 );
+			expect( getCompressedAttrs() ).to.equal( '11111-111222---111' );
 		} );
 
 		it( 'should strip the range if the ending have the attribute', () => {
-			transaction.setAttr( new Attribute( 'a', 3 ), getRange( 13, 17 ) );
-			assertAttrs( '111---111222-11111' );
+			transaction.setAttr( 'a', 1, getRange( 13, 17 ) );
+			expect( getOperationsCount() ).to.equal( 1 );
+			expect( getChangesAttrsCount() ).to.equal( 2 );
+			expect( getCompressedAttrs() ).to.equal( '111---111222-11111' );
 		} );
 
 		it( 'should do nothing if the range has attribute', () => {
-			transaction.setAttr( new Attribute( 'a', 3 ), getRange( 0, 3 ) );
-			assertAttrs( '111---111222---111' );
+			transaction.setAttr( 'a', 1, getRange( 0, 3 ) );
+			expect( getOperationsCount() ).to.equal( 0 );
+			expect( getCompressedAttrs() ).to.equal( '111---111222---111' );
 		} );
 
 		it( 'should create a proper operations for the mixed range', () => {
-			transaction.setAttr( new Attribute( 'a', 1 ), getRange( 0, 18 ) );
-			assertAttrs( '111111111111111111' );
+			transaction.setAttr( 'a', 1, getRange( 0, 18 ) );
+			expect( getOperationsCount() ).to.equal( 3 );
+			expect( getChangesAttrsCount() ).to.equal( 9 );
+			expect( getCompressedAttrs() ).to.equal( '111111111111111111' );
 		} );
 	} );
 
@@ -114,37 +138,50 @@ describe( 'Transaction', () => {
 	describe( 'removeAttr', () => {
 		it( 'should remove the attribute on the range', () => {
 			transaction.removeAttr( 'a', getRange( 0, 2 ) );
-			assertAttrs( '--1---111222---111' );
+			expect( getOperationsCount() ).to.equal( 1 );
+			expect( getChangesAttrsCount() ).to.equal( 2 );
+			expect( getCompressedAttrs() ).to.equal( '--1---111222---111' );
 		} );
 
 		it( 'should split the operations if parts of the range have different attributes', () => {
 			transaction.removeAttr( 'a', getRange( 7, 11 ) );
-			assertAttrs( '111---1----2---111' );
+			expect( getOperationsCount() ).to.equal( 2 );
+			expect( getChangesAttrsCount() ).to.equal( 4 );
+			expect( getCompressedAttrs() ).to.equal( '111---1----2---111' );
 		} );
 
 		it( 'should split the operations if parts of the part of the range have no attribute', () => {
 			transaction.removeAttr( 'a', getRange( 1, 7 ) );
-			assertAttrs( '1------11222---111' );
+			expect( getOperationsCount() ).to.equal( 2 );
+			expect( getChangesAttrsCount() ).to.equal( 2 );
+			expect( getCompressedAttrs() ).to.equal( '1------11222---111' );
 		} );
 
 		it( 'should strip the range if the beginning have no attribute', () => {
 			transaction.removeAttr( 'a', getRange( 4, 12 ) );
-			assertAttrs( '111------------111' );
+			expect( getOperationsCount() ).to.equal( 2 );
+			expect( getChangesAttrsCount() ).to.equal( 6 );
+			expect( getCompressedAttrs() ).to.equal( '111------------111' );
 		} );
 
 		it( 'should strip the range if the ending have no attribute', () => {
 			transaction.removeAttr( 'a', getRange( 7, 15 ) );
-			assertAttrs( '111---1--------111' );
+			expect( getOperationsCount() ).to.equal( 2 );
+			expect( getChangesAttrsCount() ).to.equal( 5 );
+			expect( getCompressedAttrs() ).to.equal( '111---1--------111' );
 		} );
 
 		it( 'should do nothing if the range has no attribute', () => {
 			transaction.removeAttr( 'a', getRange( 4, 5 ) );
-			assertAttrs( '111---111222---111' );
+			expect( getOperationsCount() ).to.equal( 0 );
+			expect( getCompressedAttrs() ).to.equal( '111---111222---111' );
 		} );
 
 		it( 'should create a proper operations for the mixed range', () => {
 			transaction.removeAttr( 'a', getRange( 3, 15 ) );
-			assertAttrs( '111------------111' );
+			expect( getOperationsCount() ).to.equal( 2 );
+			expect( getChangesAttrsCount() ).to.equal( 6 );
+			expect( getCompressedAttrs() ).to.equal( '111------------111' );
 		} );
 	} );
 } );

+ 17 - 2
packages/ckeditor5-engine/tests/document/transaction.js

@@ -63,8 +63,8 @@ describe( 'Transaction', () => {
 		} );
 
 		it( 'should register function which return an multiple deltas', () => {
-			Transaction.register( 'foo', ( doc, transaction ) => {
-				return [ new TestDelta( transaction ), new TestDelta( transaction ) ];
+			Transaction.register( 'foo', ( doc, t ) => {
+				return [ new TestDelta( t ), new TestDelta( t ) ];
 			} );
 
 			var transaction = new Transaction();
@@ -76,5 +76,20 @@ describe( 'Transaction', () => {
 			expect( transaction.deltas[ 1 ] ).to.be.instanceof( TestDelta );
 			expect( executeCount ).to.equal( 2 );
 		} );
+
+		it( 'should pass arguments properly', () => {
+			var doc = 'doc';
+			var arg = 'arg';
+
+			var transaction = new Transaction( doc );
+
+			var stub = sinon.stub().returns( new TestDelta( transaction ) );
+
+			Transaction.register( 'foo', stub );
+
+			transaction.foo( arg );
+
+			sinon.assert.calledWith( stub, doc, transaction, arg );
+		} );
 	} );
 } );