ソースを参照

Introduced split delta.

Piotr Jasiun 10 年 前
コミット
31f220c3c9

+ 53 - 0
packages/ckeditor5-utils/src/document/delta/splitdelta.js

@@ -0,0 +1,53 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+CKEDITOR.define( [
+	'document/delta/delta',
+	'document/delta/register',
+	'document/position',
+	'document/element',
+	'document/operation/insertoperation',
+	'document/operation/moveoperation',
+	'ckeditorerror'
+], ( Delta, register, Position, Element, InsertOperation, MoveOperation, CKEditorError ) => {
+	/**
+	 * @class document.delta.SplitDelta
+	 */
+	class SplitDelta extends Delta {}
+
+	register( 'split', ( doc, transaction, position ) => {
+		const delta = new SplitDelta();
+		const splitElement = position.parent;
+
+		if ( !splitElement.parent ) {
+			/**
+			 * Root element can not be splitted.
+			 *
+			 * @error transaction-split-root
+			 */
+			throw new CKEditorError( 'transaction-split-root: Root element can not be splitted.' );
+		}
+
+		const copy = new Element( splitElement.name, splitElement.getAttrIterator() );
+
+		const insert = new InsertOperation( Position.createAfter( splitElement ), copy, doc.version );
+		doc.applyOperation( insert );
+		delta.addOperation( insert );
+
+		const move = new MoveOperation(
+			position,
+			Position.createFromParentAndOffset( copy, 0 ),
+			splitElement.getChildCount() - position.offset,
+			doc.version );
+		doc.applyOperation( move );
+		delta.addOperation( move );
+
+		transaction.addDelta( delta );
+	} );
+
+	return SplitDelta;
+} );

+ 11 - 0
packages/ckeditor5-utils/src/document/node.js

@@ -231,6 +231,17 @@ CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], ( Attribute
 			return json;
 		}
 
+		/**
+		 * Returns attribute iterator. It can be use to create a new element with the same attributes:
+		 *
+		 *	  const copy = new Element( element.name, element.getAttrIterator() );
+		 *
+		 * @returns {Iterable.<document.Attribute>} Attribute iterator.
+		 */
+		getAttrIterator() {
+			return this._attrs[ Symbol.iterator ]();
+		}
+
 		/**
 		 * Gets the number of attributes.
 		 *

+ 2 - 1
packages/ckeditor5-utils/src/document/transaction.js

@@ -9,7 +9,8 @@ CKEDITOR.define( [
 	'document/delta/transaction-base',
 	'document/delta/insertdelta',
 	'document/delta/removedelta',
-	'document/delta/changedelta'
+	'document/delta/changedelta',
+	'document/delta/splitdelta'
 ], ( Transaction ) => {
 	return Transaction;
 } );

+ 92 - 0
packages/ckeditor5-utils/tests/document/deltas/splitdelta.js

@@ -0,0 +1,92 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* bender-tags: document, delta */
+
+/* bender-include: ../../_tools/tools.js */
+
+'use strict';
+
+const modules = bender.amd.require(
+	'document/document',
+	'document/position',
+	'document/element',
+	'document/attribute',
+	'ckeditorerror' );
+
+describe( 'Transaction', () => {
+	let Document, Position, Element, Attribute, CKEditorError;
+
+	let doc, root, p;
+
+	before( () => {
+		Document = modules[ 'document/document' ];
+		Position = modules[ 'document/position' ];
+		Element = modules[ 'document/element' ];
+		Attribute = modules[ 'document/attribute' ];
+		CKEditorError = modules.ckeditorerror;
+	} );
+
+	beforeEach( () => {
+		doc = new Document();
+		root = doc.createRoot( 'root' );
+
+		p = new Element( 'p', [ new Attribute( 'key', 'value' ) ], 'foobar' );
+
+		root.insertChildren( 0, p );
+	} );
+
+	describe( 'split', () => {
+		it( 'should split foobar to foo and bar', () => {
+			doc.makeTransaction().split( new Position( [ 0, 3 ], root ) );
+
+			expect( root.getChildCount() ).to.equal( 2 );
+
+			expect( root.getChild( 0 ).name ).to.equal( 'p' );
+			expect( root.getChild( 0 ).getChildCount() ).to.equal( 3 );
+			expect( root.getChild( 0 )._getAttrCount() ).to.equal( 1 );
+			expect( root.getChild( 0 ).getAttr( 'key' ) ).to.equal( 'value' );
+			expect( root.getChild( 0 ).getChild( 0 ).character ).to.equal( 'f' );
+			expect( root.getChild( 0 ).getChild( 1 ).character ).to.equal( 'o' );
+			expect( root.getChild( 0 ).getChild( 2 ).character ).to.equal( 'o' );
+
+			expect( root.getChild( 1 ).name ).to.equal( 'p' );
+			expect( root.getChild( 1 ).getChildCount() ).to.equal( 3 );
+			expect( root.getChild( 1 )._getAttrCount() ).to.equal( 1 );
+			expect( root.getChild( 1 ).getAttr( 'key' ) ).to.equal( 'value' );
+			expect( root.getChild( 1 ).getChild( 0 ).character ).to.equal( 'b' );
+			expect( root.getChild( 1 ).getChild( 1 ).character ).to.equal( 'a' );
+			expect( root.getChild( 1 ).getChild( 2 ).character ).to.equal( 'r' );
+		} );
+
+		it( 'should create an empty paragraph if we split at the end', () => {
+			doc.makeTransaction().split( new Position( [ 0, 6 ], root ) );
+
+			expect( root.getChildCount() ).to.equal( 2 );
+
+			expect( root.getChild( 0 ).name ).to.equal( 'p' );
+			expect( root.getChild( 0 ).getChildCount() ).to.equal( 6 );
+			expect( root.getChild( 0 )._getAttrCount() ).to.equal( 1 );
+			expect( root.getChild( 0 ).getAttr( 'key' ) ).to.equal( 'value' );
+			expect( root.getChild( 0 ).getChild( 0 ).character ).to.equal( 'f' );
+			expect( root.getChild( 0 ).getChild( 1 ).character ).to.equal( 'o' );
+			expect( root.getChild( 0 ).getChild( 2 ).character ).to.equal( 'o' );
+			expect( root.getChild( 0 ).getChild( 3 ).character ).to.equal( 'b' );
+			expect( root.getChild( 0 ).getChild( 4 ).character ).to.equal( 'a' );
+			expect( root.getChild( 0 ).getChild( 5 ).character ).to.equal( 'r' );
+
+			expect( root.getChild( 1 ).name ).to.equal( 'p' );
+			expect( root.getChild( 1 ).getChildCount() ).to.equal( 0 );
+			expect( root.getChild( 1 )._getAttrCount() ).to.equal( 1 );
+			expect( root.getChild( 1 ).getAttr( 'key' ) ).to.equal( 'value' );
+		} );
+
+		it( 'should throw if we try to split a root', () => {
+			expect( () => {
+				doc.makeTransaction().split( new Position( [ 0 ], root ) );
+			} ).to.throw( CKEditorError, /^transaction-split-root/ );
+		} );
+	} );
+} );