Ver código fonte

Removed treeModel.Character. Added treeModel.TextNode. Enchanced treeModel.Text.

Szymon Cofalik 10 anos atrás
pai
commit
f001391ab9

+ 0 - 34
packages/ckeditor5-engine/src/treemodel/character.js

@@ -1,34 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-import Node from './node.js';
-
-/**
- * Data structure for character stored in the tree data model.
- *
- * @class treeModel.Character
- */
-export default class Character extends Node {
-	/**
-	 * Creates character linear item.
-	 *
-	 * @param {String} character Described character.
-	 * @param {Iterable} attrs Iterable collection of {@link treeModel.Attribute attributes}.
-	 * @constructor
-	 */
-	constructor( character, attrs ) {
-		super( attrs );
-
-		/**
-		 * Described character.
-		 *
-		 * @readonly
-		 * @property {String} character
-		 */
-		this.character = character;
-	}
-}

+ 37 - 3
packages/ckeditor5-engine/src/treemodel/text.js

@@ -5,9 +5,17 @@
 
 'use strict';
 
+import AttributeList from './attributelist.js';
+import TextNode from './textnode.js';
+import langUtils from '../lib/lodash/lang.js';
+
 /**
- * Data structure for text with attributes. Note that the `Text` is not a {@link treeModel.Node},
- * because it will never be part of the document tree. {@link treeModel.Character is a node}.
+ * Data structure for text with attributes. Note that the `Text` is not a {@link treeModel.Node}. This class is used
+ * as an aggregator for multiple characters that have same attributes. Example usage:
+ *
+ *		let attrFoo = new Attribute( 'foo', true );
+ *		let attrBar = new Attribute( 'bar', true );
+ *		let myElem = new Element( 'li', [], new Text( 'text with attributes', [ attrFoo, attrBar ] ) );
  *
  * @class treeModel.Text
  */
@@ -26,7 +34,7 @@ export default class Text {
 		 * @readonly
 		 * @property {String}
 		 */
-		this.text = text;
+		this.text = text || '';
 
 		/**
 		 * Iterable collection of {@link treeModel.Attribute attributes}.
@@ -35,4 +43,30 @@ export default class Text {
 		 */
 		this.attrs = new AttributeList( attrs );
 	}
+
+	/**
+	 * Creates and returns a text node that represents whole text contained in this text object.
+	 *
+	 * @returns {TextNode}
+	 */
+	getTextNode( start, length ) {
+		start = start && start >= 0 ? start : 0;
+		length = length && length >= 0 ? length : this.text.length;
+
+		return new TextNode( this, start, length );
+	}
+
+	/**
+	 * Custom toJSON method to solve child-parent circular dependencies.
+	 *
+	 * @returns {Object} Clone of this object with the parent property replaced with its name.
+	 */
+	toJSON() {
+		const json = langUtils.clone( this );
+
+		// Due to circular references we need to remove parent reference.
+		json.parent = this.parent ? this.parent.name : null;
+
+		return json;
+	}
 }

+ 88 - 0
packages/ckeditor5-engine/src/treemodel/textnode.js

@@ -0,0 +1,88 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+import Node from './node.js';
+
+/**
+ * A proxy object representing one or more characters stored in the tree data model. It looks like and behaves like
+ * normal node, but is a readonly structure. This is a representation of the data. Manipulating it won't affect
+ * the actual nodes in tree model.
+ *
+ * TextNode instance is static, meaning that it is up-to-date only for actual tree model state. No mutations
+ * of a tree model containing this character are reflected in its properties. Because of this, it is not advised to store
+ * instances of TextNode class. Comparing TextNode instances is pointless too. If you want to keep live reference
+ * to a point in a text, you should use {@link treeModel.LivePosition}.
+ *
+ * In most scenarios you should not create an instance of this class by your own. When passing parameters to
+ * constructors, use string literals or {@link treeModel.Text} instead.
+ *
+ * @class treeModel.TextNode
+ */
+export default class TextNode extends Node {
+	/**
+	 * Creates text node.
+	 *
+	 * @param {String} text Characters contained in this text node.
+	 * @param {Iterable} attrs Iterable collection of {@link treeModel.Attribute attributes}.
+	 * @constructor
+	 */
+	constructor( textItem, start, length ) {
+		super( textItem.attrs );
+
+		/**
+		 * {@link treeModel.Text} instance that contains text represented by this text node.
+		 *
+		 * @protected
+		 * @readonly
+		 * @property {treeModel.Text} textItem
+		 */
+		this._textItem = textItem;
+
+		/**
+		 * Defines where text represented by this text node starts in original {@link treeModel.Text text} object.
+		 *
+		 * @protected
+		 * @readonly
+		 * @property {Number} start
+		 */
+		this._start = start;
+
+		/**
+		 * Characters contained in this text node.
+		 *
+		 * @protected
+		 * @readonly
+		 * @property {String} text
+		 */
+		this.text = textItem.text.substr( start, length );
+	}
+
+	/**
+	 * @readonly
+	 * @property {treeModel.Element} parent
+	 * @see {@link treeModel.Node#parent}
+	 */
+	get parent() {
+		return this._textItem.parent;
+	}
+
+	/**
+	 * Does nothing as parent is readonly property in {@link treeModel.TextNode}.
+	 */
+	set parent( parent ) {
+		/* jshint unused:false */
+	}
+
+	/**
+	 * @see {@link treeModel.Node#nextSibling}
+	 */
+	get nextSibling() {
+		const index = this.getIndex();
+
+		return ( index !== null && this.parent.getChild( index + this.text.length ) ) || null;
+	}
+}

+ 0 - 39
packages/ckeditor5-engine/tests/treemodel/character.js

@@ -1,39 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* bender-tags: treemodel */
-
-'use strict';
-
-import Character from '/ckeditor5/core/treemodel/character.js';
-import Node from '/ckeditor5/core/treemodel/node.js';
-import Element from '/ckeditor5/core/treemodel/element.js';
-import Attribute from '/ckeditor5/core/treemodel/attribute.js';
-
-describe( 'Character', () => {
-	describe( 'constructor', () => {
-		it( 'should create character without attributes', () => {
-			let character = new Character( 'f' );
-			let parent = new Element( 'parent', [], character );
-
-			expect( character ).to.be.an.instanceof( Node );
-			expect( character ).to.have.property( 'character' ).that.equals( 'f' );
-			expect( character ).to.have.property( 'parent' ).that.equals( parent );
-			expect( character.attrs.size ).to.equal( 0 );
-		} );
-
-		it( 'should create character with attributes', () => {
-			let attr = new Attribute( 'foo', 'bar' );
-			let character = new Character( 'f', [ attr ] );
-			let parent = new Element( 'parent', [], character );
-
-			expect( character ).to.be.an.instanceof( Node );
-			expect( character ).to.have.property( 'character' ).that.equals( 'f' );
-			expect( character ).to.have.property( 'parent' ).that.equals( parent );
-			expect( character.attrs.size ).to.equal( 1 );
-			expect( character.attrs.get( attr.key ) ).to.equal( attr );
-		} );
-	} );
-} );

+ 53 - 0
packages/ckeditor5-engine/tests/treemodel/text.js

@@ -8,6 +8,7 @@
 'use strict';
 
 import Text from '/ckeditor5/core/treemodel/text.js';
+import TextNode from '/ckeditor5/core/treemodel/textnode.js';
 import Attribute from '/ckeditor5/core/treemodel/attribute.js';
 import AttributeList from '/ckeditor5/core/treemodel/attributelist.js';
 
@@ -21,5 +22,57 @@ describe( 'Text', () => {
 			expect( text ).to.have.property( 'attrs' ).that.is.instanceof( AttributeList );
 			expect( Array.from( text.attrs ) ).to.deep.equal( attrs );
 		} );
+
+		it( 'should create empty text object', () => {
+			let empty1 = new Text();
+			let empty2 = new Text( '' );
+
+			expect( empty1.text ).to.equal( '' );
+			expect( empty2.text ).to.equal( '' );
+		} );
+	} );
+
+	describe( 'getTextNode', () => {
+		let attrs, text;
+
+		beforeEach( () => {
+			attrs = [ new Attribute( 'bold', true ) ];
+			text = new Text( 'bar', attrs );
+		} );
+
+		it( 'should return text node containing whole text object if no parameters are passed', () => {
+			let textNode = text.getTextNode();
+
+			expect( textNode ).to.be.instanceof( TextNode );
+			expect( textNode.text ).to.equal( 'bar' );
+			expect( textNode._start ).to.equal( 0 );
+			expect( textNode._textItem ).to.equal( text );
+		} );
+
+		it( 'should return text node containing characters from start index to the end of text object if one parameter is passed', () => {
+			let textNode = text.getTextNode( 1 );
+
+			expect( textNode ).to.be.instanceof( TextNode );
+			expect( textNode.text ).to.equal( 'ar' );
+			expect( textNode._start ).to.equal( 1 );
+			expect( textNode._textItem ).to.equal( text );
+		} );
+
+		it( 'should return text node containing given number of characters, starting from given index if two parameters are passed', () => {
+			let textNode = text.getTextNode( 1, 1 );
+
+			expect( textNode ).to.be.instanceof( TextNode );
+			expect( textNode.text ).to.equal( 'a' );
+			expect( textNode._start ).to.equal( 1 );
+			expect( textNode._textItem ).to.equal( text );
+		} );
+	} );
+
+	it( 'should create proper JSON string using toJSON method', () => {
+		let text = new Text( 'bar' );
+		let parsed = JSON.parse( JSON.stringify( text ) );
+
+		expect( parsed.text ).to.equal( 'bar' );
+		expect( parsed.parent ).to.equal( null );
 	} );
 } );

+ 78 - 0
packages/ckeditor5-engine/tests/treemodel/textnode.js

@@ -0,0 +1,78 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* bender-tags: treemodel */
+
+'use strict';
+
+const modules = bender.amd.require(
+	'core/treemodel/node',
+	'core/treemodel/element',
+	'core/treemodel/text',
+	'core/treemodel/textnode',
+	'core/treemodel/attribute',
+	'core/treemodel/attributelist'
+);
+
+describe( 'TextNode', () => {
+	let Node, Element, Text, TextNode, Attribute, AttributeList;
+	let text, element, textNode;
+
+	before( () => {
+		Node = modules[ 'core/treemodel/node' ];
+		Element = modules[ 'core/treemodel/element' ];
+		Text = modules[ 'core/treemodel/text' ];
+		TextNode = modules[ 'core/treemodel/textnode' ];
+		Attribute = modules[ 'core/treemodel/attribute' ];
+		AttributeList = modules[ 'core/treemodel/attribute' ];
+
+		text = new Text( 'foobar', [ new Attribute( 'foo', true ) ] );
+		element = new Element( 'div', [], [ new Element( 'p' ), 'abc', text, new Element( 'p' ) ] );
+	} );
+
+	beforeEach( () => {
+		textNode = new TextNode( text, 2, 3 );
+	} );
+
+	it( 'should extend Node class', () => {
+		expect( textNode ).to.be.instanceof( Node );
+	} );
+
+	it( 'should have text property basing on passed Text instance', () => {
+		expect( textNode ).to.have.property( 'text' ).that.equals( 'oba' );
+	} );
+
+	it( 'should have parent property basing on passed Text instance', () => {
+		expect( textNode ).to.have.property( 'parent' ).that.equals( element );
+	} );
+
+	it( 'should have attributes list equal to passed Text instance', () => {
+		expect( textNode.attrs.isEqual( text.attrs ) ).to.be.true;
+	} );
+
+	it( 'should have correct start property', () => {
+		expect( textNode._start ).to.equal( 2 );
+	} );
+
+	it( 'should not change original Text instance when changed', () => {
+		textNode.text = 'ab';
+		textNode._start = 0;
+		textNode.parent = new Element( 'p' );
+		textNode.attrs = new AttributeList();
+
+		expect( text.text ).to.equal( 'foobar' );
+		expect( text.parent ).to.equal( element );
+		expect( text.attrs.size ).to.equal( 1 );
+	} );
+
+	it( 'getIndex should return value with text node offset in original Text instance', () => {
+		expect( textNode.getIndex() ).to.equal( 6 );
+	} );
+
+	it( 'should have nextSibling property which is a node/character placed after the last character from text node', () => {
+		expect( textNode ).to.have.property( 'nextSibling' );
+		expect( textNode.nextSibling.text ).to.equal( 'r' );
+	} );
+} );