ソースを参照

Change Selection to use AttributeList.

Szymon Cofalik 10 年 前
コミット
23a127df77

+ 57 - 1
packages/ckeditor5-engine/src/treemodel/selection.js

@@ -7,10 +7,11 @@
 
 CKEDITOR.define( [
 	'treemodel/liverange',
+	'treemodel/attributelist',
 	'emittermixin',
 	'utils',
 	'ckeditorerror'
-], ( LiveRange, EmitterMixin, utils, CKEditorError ) => {
+], ( LiveRange, AttributeList, EmitterMixin, utils, CKEditorError ) => {
 	/**
 	 * Represents a selection that is made on nodes in {@link treeModel.Document}. Selection instance is
 	 * created by {@link treeModel.Document}. In most scenarios you should not need to create an instance of Selection.
@@ -18,7 +19,20 @@ CKEDITOR.define( [
 	 * @class treeModel.Selection
 	 */
 	class Selection {
+		/**
+		 * Creates an empty selection.
+		 *
+		 * @constructor
+		 */
 		constructor() {
+			/**
+			 * List of attributes set on current selection.
+			 *
+			 * @private
+			 * @property {treeModel.AttributeList} _attrs
+			 */
+			this._attrs = new AttributeList();
+
 			/**
 			 * Stores all ranges that are selected.
 			 *
@@ -112,6 +126,20 @@ CKEDITOR.define( [
 			detachRanges.call( this );
 		}
 
+		/**
+		 * @see {@link treeModel.AttributeList#getAttr}
+		 */
+		getAttr( key ) {
+			return this._attrs.getAttr( key );
+		}
+
+		/**
+		 * @see {@link treeModel.AttributeList#getAttrs}
+		 */
+		getAttrs() {
+			return this._attrs.getAttrs();
+		}
+
 		/**
 		 * Returns an array of ranges added to the selection. The method returns a copy of internal array, so
 		 * it will not change when ranges get added or removed from selection.
@@ -122,6 +150,20 @@ CKEDITOR.define( [
 			return this._ranges.slice();
 		}
 
+		/**
+		 * @see {@link treeModel.AttributeList#hasAttr}
+		 */
+		hasAttr( key ) {
+			return this._attrs.hasAttr( key );
+		}
+
+		/**
+		 * @see {@link treeModel.AttributeList#removeAttr}
+		 */
+		removeAttr( key ) {
+			this._attrs.removeAttr( key );
+		}
+
 		/**
 		 * Removes all ranges that were added to the selection. Fires update event.
 		 */
@@ -132,6 +174,20 @@ CKEDITOR.define( [
 			this.fire( 'update' );
 		}
 
+		/**
+		 * @see {@link treeModel.AttributeList#setAttr}
+		 */
+		setAttr( attr ) {
+			this._attrs.setAttr( attr );
+		}
+
+		/**
+		 * @see {@link treeModel.AttributeList#setAttrsTo}
+		 */
+		setAttrsTo( attrs ) {
+			this._attrs.setAttrsTo( attrs );
+		}
+
 		/**
 		 * Replaces all ranges that were added to the selection with given array of ranges. Last range of the array
 		 * is treated like the last added range and is used to set {@link #anchor} and {@link #focus}. Accepts a flag

+ 109 - 1
packages/ckeditor5-engine/tests/treemodel/selection.js

@@ -5,10 +5,15 @@
 
 /* bender-tags: treemodel */
 
+/* bender-include: ../_tools/tools.js */
+
 'use strict';
 
+const getIteratorCount = bender.tools.core.getIteratorCount;
+
 const modules = bender.amd.require(
 	'treemodel/document',
+	'treemodel/attribute',
 	'treemodel/element',
 	'treemodel/range',
 	'treemodel/position',
@@ -20,10 +25,12 @@ const modules = bender.amd.require(
 );
 
 describe( 'Selection', () => {
-	let Document, Element, Range, Position, LiveRange, Selection, InsertOperation, MoveOperation, CKEditorError;
+	let Document, Attribute, Element, Range, Position, LiveRange, Selection, InsertOperation, MoveOperation, CKEditorError;
+	let attrFooBar;
 
 	before( () => {
 		Document = modules[ 'treemodel/document' ];
+		Attribute = modules[ 'treemodel/attribute' ];
 		Element = modules[ 'treemodel/element' ];
 		Range = modules[ 'treemodel/range' ];
 		Position = modules[ 'treemodel/position' ];
@@ -32,6 +39,8 @@ describe( 'Selection', () => {
 		InsertOperation = modules[ 'treemodel/operation/insertoperation' ];
 		MoveOperation = modules[ 'treemodel/operation/moveoperation' ];
 		CKEditorError = modules.ckeditorerror;
+
+		attrFooBar = new Attribute( 'foo', 'bar' );
 	} );
 
 	let doc, root, selection, liveRange, range;
@@ -417,4 +426,103 @@ describe( 'Selection', () => {
 			} );
 		} );
 	} );
+
+	// Testing integration with attributes list.
+	// Tests copied from AttributeList tests.
+	// Some cases were omitted.
+
+	describe( 'setAttr', () => {
+		it( 'should insert an attribute', () => {
+			selection.setAttr( attrFooBar );
+
+			expect( getIteratorCount( selection.getAttrs() ) ).to.equal( 1 );
+			expect( selection.getAttr( attrFooBar.key ) ).to.equal( attrFooBar.value );
+		} );
+	} );
+
+	describe( 'setAttrsTo', () => {
+		it( 'should remove all attributes and set passed ones', () => {
+			selection.setAttr( attrFooBar );
+
+			let attrs = [ new Attribute( 'abc', true ), new Attribute( 'xyz', false ) ];
+
+			selection.setAttrsTo( attrs );
+
+			expect( getIteratorCount( selection.getAttrs() ) ).to.equal( 2 );
+			expect( selection.getAttr( 'foo' ) ).to.be.null;
+			expect( selection.getAttr( 'abc' ) ).to.be.true;
+			expect( selection.getAttr( 'xyz' ) ).to.be.false;
+		} );
+	} );
+
+	describe( 'getAttr', () => {
+		beforeEach( () => {
+			selection.setAttr( attrFooBar );
+		} );
+
+		it( 'should return attribute value if key of previously set attribute has been passed', () => {
+			expect( selection.getAttr( 'foo' ) ).to.equal( attrFooBar.value );
+		} );
+
+		it( 'should return null if attribute with given key has not been found', () => {
+			expect( selection.getAttr( 'bar' ) ).to.be.null;
+		} );
+	} );
+
+	describe( 'removeAttr', () => {
+		it( 'should remove an attribute', () => {
+			let attrA = new Attribute( 'a', 'A' );
+			let attrB = new Attribute( 'b', 'B' );
+			let attrC = new Attribute( 'c', 'C' );
+
+			selection.setAttr( attrA );
+			selection.setAttr( attrB );
+			selection.setAttr( attrC );
+
+			selection.removeAttr( attrB.key );
+
+			expect( getIteratorCount( selection.getAttrs() ) ).to.equal( 2 );
+			expect( selection.getAttr( attrA.key ) ).to.equal( attrA.value );
+			expect( selection.getAttr( attrC.key ) ).to.equal( attrC.value );
+			expect( selection.getAttr( attrB.key ) ).to.be.null;
+		} );
+	} );
+
+	describe( 'hasAttr', () => {
+		it( 'should check attribute by key', () => {
+			selection.setAttr( attrFooBar );
+			expect( selection.hasAttr( 'foo' ) ).to.be.true;
+		} );
+
+		it( 'should return false if attribute was not found by key', () => {
+			expect( selection.hasAttr( 'bar' ) ).to.be.false;
+		} );
+
+		it( 'should check attribute by object', () => {
+			selection.setAttr( attrFooBar );
+			expect( selection.hasAttr( attrFooBar ) ).to.be.true;
+		} );
+
+		it( 'should return false if attribute was not found by object', () => {
+			expect( selection.hasAttr( attrFooBar ) ).to.be.false;
+		} );
+	} );
+
+	describe( 'getAttrs', () => {
+		it( 'should return all set attributes', () => {
+			let attrA = new Attribute( 'a', 'A' );
+			let attrB = new Attribute( 'b', 'B' );
+			let attrC = new Attribute( 'c', 'C' );
+
+			selection.setAttrsTo( [
+				attrA,
+				attrB,
+				attrC
+			] );
+
+			selection.removeAttr( attrB.key );
+
+			expect( [ attrA, attrC ] ).to.deep.equal( Array.from( selection.getAttrs() ) );
+		} );
+	} );
 } );