浏览代码

Add core.treeModel.delta.Delta#getReversed.

Szymon Cofalik 9 年之前
父节点
当前提交
26b6e51f22

+ 41 - 0
packages/ckeditor5-engine/src/treemodel/delta/delta.js

@@ -38,6 +38,16 @@ export default class Delta {
 		this.operations = [];
 	}
 
+	/**
+	 * A class that will be used when creating reversed delta.
+	 *
+	 * @private
+	 * @type {Object}
+	 */
+	get _reverseDeltaClass() {
+		return Delta;
+	}
+
 	/**
 	 * Add operation to the delta.
 	 *
@@ -50,6 +60,11 @@ export default class Delta {
 		return operation;
 	}
 
+	/**
+	 * Creates and returns a delta that has the same parameters as this delta.
+	 *
+	 * @returns {core.treeModel.delta.Delta} Clone of this delta.
+	 */
 	clone() {
 		let delta = new this.constructor();
 		delta.batch = this.batch;
@@ -60,4 +75,30 @@ export default class Delta {
 
 		return delta;
 	}
+
+	/**
+	 * Creates and returns a reverse delta. Reverse delta when executed right after the original delta will bring back
+	 * tree model state to the point before the original delta execution. In other words, it reverses changes done
+	 * by the original delta.
+	 *
+	 * Keep in mind that tree model state may change since executing the original delta, so reverse delta may be "outdated".
+	 * In that case you will need to {@link core.treeModel.delta.transform} it by all deltas that were executed after
+	 * the original delta.
+	 *
+	 * @returns {core.treeModel.delta.Delta} Reversed delta.
+	 */
+	getReversed() {
+		let delta = new this._reverseDeltaClass();
+
+		for ( let op of this.operations ) {
+			let reversedOp = op.getReversed();
+			reversedOp.baseVersion += this.operations.length - 1;
+
+			delta.addOperation( reversedOp );
+		}
+
+		delta.operations.reverse();
+
+		return delta;
+	}
 }

+ 47 - 0
packages/ckeditor5-engine/tests/treemodel/delta/delta.js

@@ -9,6 +9,27 @@
 
 import coreTestUtils from '/tests/core/_utils/utils.js';
 import Delta from '/ckeditor5/core/treemodel/delta/delta.js';
+import Operation from '/ckeditor5/core/treemodel/operation/operation.js';
+
+// Some test examples of operations.
+class FooOperation extends Operation {
+	constructor( string, baseVersion ) {
+		super( baseVersion );
+		this.string = string;
+	}
+
+	getReversed() {
+		/* jshint ignore:start */
+		return new BarOperation( this.string, this.baseVersion );
+		/* jshint ignore:end */
+	}
+}
+
+class BarOperation extends FooOperation {
+	getReversed() {
+		return new FooOperation( this.string, this.baseVersion );
+	}
+}
 
 const getIteratorCount = coreTestUtils.getIteratorCount;
 
@@ -56,4 +77,30 @@ describe( 'Delta', () => {
 			expect( count ).to.equal( 3 );
 		} );
 	} );
+
+	describe( 'getReversed', () => {
+		it( 'should return empty Delta if there are no operations in delta', () => {
+			const delta = new Delta();
+			let reversed = delta.getReversed();
+
+			expect( reversed ).to.be.instanceof( Delta );
+			expect( reversed.operations.length ).to.equal( 0 );
+		} );
+
+		it( 'should return Delta with all operations reversed and their order reversed', () => {
+			const delta = new Delta();
+			delta.addOperation( new FooOperation( 'a', 1 ) );
+			delta.addOperation( new BarOperation( 'b', 2 ) );
+
+			let reversed = delta.getReversed();
+
+			expect( reversed ).to.be.instanceof( Delta );
+			expect( reversed.operations.length ).to.equal( 2 );
+
+			expect( reversed.operations[ 0 ] ).to.be.instanceOf( FooOperation );
+			expect( reversed.operations[ 0 ].string ).to.equal( 'b' );
+			expect( reversed.operations[ 1 ] ).to.be.instanceOf( BarOperation );
+			expect( reversed.operations[ 1 ].string ).to.equal( 'a' );
+		} );
+	} );
 } );