8
0
Pārlūkot izejas kodu

#396 Serialization/deserialization of Position.

Maciej Gołaszewski 9 gadi atpakaļ
vecāks
revīzija
5805545a66

+ 11 - 1
packages/ckeditor5-engine/src/treemodel/document.js

@@ -202,7 +202,7 @@ export default class Document {
 			);
 		}
 
-		const root = new RootElement( this, elementName );
+		const root = new RootElement( this, elementName, rootName );
 		this._roots.set( rootName, root );
 
 		return root;
@@ -263,6 +263,16 @@ export default class Document {
 		return this._roots.get( name );
 	}
 
+	/**
+	 * Checks if root with given name is defined.
+	 *
+	 * @param {String} name Name of root to check.
+	 * @returns {Boolean}
+	 */
+	hasRoot( name ) {
+		return this._roots.has( name );
+	}
+
 	/**
 	 * Custom toJSON method to solve child-parent circular dependencies.
 	 *

+ 30 - 0
packages/ckeditor5-engine/src/treemodel/position.js

@@ -10,6 +10,7 @@ import Element from './element.js';
 import last from '../../utils/lib/lodash/last.js';
 import compareArrays from '../../utils/comparearrays';
 import CKEditorError from '../../utils/ckeditorerror.js';
+import clone from '../../utils/lib/lodash/clone.js';
 
 /**
  * Position in the tree. Position is always located before or after a node.
@@ -443,6 +444,18 @@ export default class Position {
 		return this.offset == this.parent.getChildCount();
 	}
 
+	/**
+	 * Custom toJSON method.
+	 *
+	 * @returns {Object} Object representation of this position with the root property replaced with its rootName.
+	 */
+	toJSON() {
+		return {
+			root: this.root.rootName,
+			path: clone( this.path )
+		};
+	}
+
 	/**
 	 * Creates position at the given location. The location can be specified as:
 	 *
@@ -563,6 +576,23 @@ export default class Position {
 		return new this( position.root, position.path.slice() );
 	}
 
+	/**
+	 *
+	 * @param {Object} json
+	 * @param {engine.treeModel.Document} doc
+	 * @returns {engine.treeModel.Position}
+	 */
+	static fromJSON( json, doc ) {
+		if ( !doc.hasRoot( json.root ) ) {
+			throw new CKEditorError(
+				'position-fromjson-no-root: Cannot create position for document. Root with specified name does not exist.',
+				{ rootName: json.root }
+			);
+		}
+
+		return new Position( doc.getRoot( json.root ), json.path );
+	}
+
 	/**
 	 * Returns a new position that is a combination of this position and given positions. The combined
 	 * position is this position transformed by moving a range starting at `from` to `to` position.

+ 11 - 2
packages/ckeditor5-engine/src/treemodel/rootelement.js

@@ -19,16 +19,25 @@ export default class RootElement extends Element {
 	 *
 	 * @param {engine.treeModel.Document} doc {@link engine.treeModel.Document} that is an owner of the root.
 	 * @param {String} name Node name.
+	 * @param {String} rootName Root name inside parent {@link engine.treeModel.Document}.
 	 */
-	constructor( doc, name ) {
+	constructor( doc, name, rootName ) {
 		super( name );
 
 		/**
 		 * {@link engine.treeModel.Document} that is an owner of this root.
 		 *
 		 * @readonly
-		 * @member {engine.treeModel.Document} engine.treeModel.RootElement#doc
+		 * @member {engine.treeModel.Document} engine.treeModel.RootElement#document
 		 */
 		this.document = doc;
+
+		/**
+		 * Name of this root inside {@link engine.treeModel.Document} that is an owner of this root.
+		 *
+		 * @readonly
+		 * @member {String} engine.treeModel.RootElement#rootName
+		 */
+		this.rootName = rootName;
 	}
 }

+ 14 - 0
packages/ckeditor5-engine/tests/treemodel/document/document.js

@@ -56,12 +56,14 @@ describe( 'Document', () => {
 			expect( root ).to.be.instanceof( RootElement );
 			expect( root.getChildCount() ).to.equal( 0 );
 			expect( root ).to.have.property( 'name', '$root' );
+			expect( root ).to.have.property( 'rootName', 'root' );
 		} );
 
 		it( 'should create a new RootElement with the specified name', () => {
 			let root = doc.createRoot( 'root', 'foo' );
 
 			expect( root ).to.have.property( 'name', 'foo' );
+			expect( root ).to.have.property( 'rootName', 'root' );
 		} );
 
 		it( 'should throw an error when trying to create a second root with the same name', () => {
@@ -92,6 +94,18 @@ describe( 'Document', () => {
 		} );
 	} );
 
+	describe( 'hasRoot', () => {
+		it( 'should return true when Document has RootElement with given name', () => {
+			doc.createRoot( 'root' );
+
+			expect( doc.hasRoot( 'root' ) ).to.be.true;
+		} );
+
+		it( 'should return false when Document does not have RootElement with given name', () => {
+			expect( doc.hasRoot( 'noroot' ) ).to.be.false;
+		} );
+	} );
+
 	describe( 'applyOperation', () => {
 		it( 'should increase document version, execute operation and fire event with proper data', () => {
 			const changeCallback = sinon.spy();

+ 28 - 0
packages/ckeditor5-engine/tests/treemodel/position.js

@@ -14,6 +14,7 @@ import Text from '/ckeditor5/engine/treemodel/text.js';
 import Position from '/ckeditor5/engine/treemodel/position.js';
 import CKEditorError from '/ckeditor5/utils/ckeditorerror.js';
 import testUtils from '/tests/ckeditor5/_utils/utils.js';
+import treeModelTestUtils from '/tests/engine/treemodel/_utils/utils.js';
 
 testUtils.createSinonSandbox();
 
@@ -681,4 +682,31 @@ describe( 'position', () => {
 			expect( shifted.path ).to.deep.equal( [ 1, 2, 0 ] );
 		} );
 	} );
+
+	describe( 'toJSON', () => {
+		it( 'should serialize position', () => {
+			let position = new Position( root, [ 0 ] );
+
+			let serialized = treeModelTestUtils.jsonParseStringify( position );
+
+			expect( serialized ).to.deep.equal( { root: 'root', path: [ 0 ] } );
+		} );
+	} );
+
+	describe( 'fromJSON', () => {
+		it( 'should create object with given document', () => {
+			let deserialized = Position.fromJSON( { root: 'root', path: [ 0, 1, 2 ] }, doc );
+
+			expect( deserialized.root ).to.equal( root );
+			expect( deserialized.path ).to.deep.equal( [ 0, 1, 2 ] );
+		} );
+
+		it( 'should throw error when creating object in document that does not have provided root', () => {
+			expect(
+				() => {
+					Position.fromJSON( { root: 'noroot', path: [ 0 ] }, doc );
+				}
+			).to.throw( CKEditorError, /position-fromjson-no-root/ );
+		} );
+	} );
 } );