8
0
Просмотр исходного кода

Merge pull request #95 from cksource/t/92

t/92: Introduce UI Controller class.
Piotrek Koszuliński 10 лет назад
Родитель
Сommit
518d2996f6

+ 18 - 3
packages/ckeditor5-utils/src/collection.js

@@ -79,8 +79,10 @@ CKEDITOR.define( [ 'emittermixin', 'ckeditorerror', 'utils' ], ( EmitterMixin, C
 		 *
 		 * @chainable
 		 * @param {Object} item
+		 * @param {Number} [index] The position of the item in the collection. The item
+		 * is pushed to the collection when `index` not specified.
 		 */
-		add( item ) {
+		add( item, index ) {
 			let itemId;
 			const idProperty = this._idProperty;
 
@@ -109,10 +111,23 @@ CKEDITOR.define( [ 'emittermixin', 'ckeditorerror', 'utils' ], ( EmitterMixin, C
 				item[ idProperty ] = itemId;
 			}
 
-			this._items.push( item );
+			// TODO: Use ES6 default function argument.
+			if ( index === undefined ) {
+				index = this._items.length;
+			} else if ( index > this._items.length || index < 0 ) {
+				/**
+				 * The index number has invalid value.
+				 *
+				 * @error collection-add-item-bad-index
+				 */
+				throw new CKEditorError( 'collection-add-item-invalid-index' );
+			}
+
+			this._items.splice( index, 0, item );
+
 			this._itemMap.set( itemId, item );
 
-			this.fire( 'add', item );
+			this.fire( 'add', item, index );
 
 			return this;
 		}

+ 54 - 54
packages/ckeditor5-utils/src/model.js

@@ -194,6 +194,60 @@ CKEDITOR.define( [ 'emittermixin', 'ckeditorerror', 'utils' ], ( EmitterMixin, C
 			};
 		}
 
+		/**
+		 * Removes the binding created with {@link #bind}.
+		 *
+		 *		A.unbind( 'a' );
+		 *		A.unbind();
+		 *
+		 * @param {String...} [bindAttrs] Model attributes to unbound. All the bindings will
+		 * be released if not attributes provided.
+		 */
+		unbind( ...unbindAttrs ) {
+			if ( unbindAttrs.length ) {
+				if ( !isStringArray( unbindAttrs ) ) {
+					/**
+					 * Attributes must be strings.
+					 *
+					 * @error model-unbind-wrong-attrs
+					 */
+					throw new CKEditorError( 'model-unbind-wrong-attrs: Attributes must be strings.' );
+				}
+
+				unbindAttrs.forEach( attrName => {
+					for ( let to of this._boundTo ) {
+						// TODO, ES6 destructuring.
+						const boundModel = to[ 0 ];
+						const bindings = to[ 1 ];
+
+						for ( let boundAttrName in bindings ) {
+							if ( bindings[ boundAttrName ].has( attrName ) ) {
+								bindings[ boundAttrName ].delete( attrName );
+							}
+
+							if ( !bindings[ boundAttrName ].size ) {
+								delete bindings[ boundAttrName ];
+							}
+
+							if ( !Object.keys( bindings ).length ) {
+								this._boundTo.delete( boundModel );
+								this.stopListening( boundModel, 'change' );
+							}
+						}
+					}
+
+					delete this._bound[ attrName ];
+				} );
+			} else {
+				this._boundTo.forEach( ( bindings, boundModel ) => {
+					this.stopListening( boundModel, 'change' );
+					this._boundTo.delete( boundModel );
+				} );
+
+				this._bound = {};
+			}
+		}
+
 		/**
 		 * A chaining for {@link #bind} providing `.to()` interface.
 		 *
@@ -291,60 +345,6 @@ CKEDITOR.define( [ 'emittermixin', 'ckeditorerror', 'utils' ], ( EmitterMixin, C
 
 			updateModelAttrs( this, this._bindAttrs[ 0 ] );
 		}
-
-		/**
-		 * Removes the binding created with {@link #bind}.
-		 *
-		 *		A.unbind( 'a' );
-		 *		A.unbind();
-		 *
-		 * @param {String...} [bindAttrs] Model attributes to unbound. All the bindings will
-		 * be released if not attributes provided.
-		 */
-		unbind( ...unbindAttrs ) {
-			if ( unbindAttrs.length ) {
-				if ( !isStringArray( unbindAttrs ) ) {
-					/**
-					 * Attributes must be strings.
-					 *
-					 * @error model-unbind-wrong-attrs
-					 */
-					throw new CKEditorError( 'model-unbind-wrong-attrs: Attributes must be strings.' );
-				}
-
-				unbindAttrs.forEach( attrName => {
-					for ( let to of this._boundTo ) {
-						// TODO, ES6 destructuring.
-						const boundModel = to[ 0 ];
-						const bindings = to[ 1 ];
-
-						for ( let boundAttrName in bindings ) {
-							if ( bindings[ boundAttrName ].has( attrName ) ) {
-								bindings[ boundAttrName ].delete( attrName );
-							}
-
-							if ( !bindings[ boundAttrName ].size ) {
-								delete bindings[ boundAttrName ];
-							}
-
-							if ( !Object.keys( bindings ).length ) {
-								this._boundTo.delete( boundModel );
-								this.stopListening( boundModel, 'change' );
-							}
-						}
-					}
-
-					delete this._bound[ attrName ];
-				} );
-			} else {
-				this._boundTo.forEach( ( bindings, boundModel ) => {
-					this.stopListening( boundModel, 'change' );
-					this._boundTo.delete( boundModel );
-				} );
-
-				this._bound = {};
-			}
-		}
 	}
 
 	/**

+ 59 - 3
packages/ckeditor5-utils/tests/collection/collection.js

@@ -10,8 +10,10 @@ const modules = bender.amd.require( 'collection', 'ckeditorerror' );
 bender.tools.createSinonSandbox();
 
 function getItem( id, idProperty ) {
+	idProperty = idProperty || 'id';
+
 	return {
-		[ idProperty || 'id' ]: id
+		[ idProperty ]: id
 	};
 }
 
@@ -165,7 +167,61 @@ describe( 'Collection', () => {
 
 			collection.add( item );
 
-			sinon.assert.calledWithExactly( spy, sinon.match.has( 'source', collection ), item );
+			sinon.assert.calledWithExactly( spy, sinon.match.has( 'source', collection ), item, 0 );
+		} );
+
+		it( 'should support an optional index argument', () => {
+			let collection = new Collection();
+			let item1 = getItem( 'foo' );
+			let item2 = getItem( 'bar' );
+			let item3 = getItem( 'baz' );
+			let item4 = getItem( 'abc' );
+
+			collection.add( item1 );
+			collection.add( item2, 0 );
+			collection.add( item3, 1 );
+			collection.add( item4, 3 );
+
+			expect( collection.get( 0 ) ).to.equal( item2 );
+			expect( collection.get( 1 ) ).to.equal( item3 );
+			expect( collection.get( 2 ) ).to.equal( item1 );
+			expect( collection.get( 3 ) ).to.equal( item4 );
+		} );
+
+		it( 'should throw when index argument is invalid', () => {
+			let collection = new Collection();
+			let item1 = getItem( 'foo' );
+			let item2 = getItem( 'bar' );
+			let item3 = getItem( 'baz' );
+
+			collection.add( item1 );
+
+			expect( () => {
+				collection.add( item2, -1 );
+			} ).to.throw( /^collection-add-item-invalid-index/ );
+
+			expect( () => {
+				collection.add( item2, 2 );
+			} ).to.throw( /^collection-add-item-invalid-index/ );
+
+			collection.add( item2, 1 );
+			collection.add( item3, 0 );
+
+			expect( collection.length ).to.be.equal( 3 );
+		} );
+
+		it( 'should fire the "add" event with the index argument', () => {
+			let spy = sinon.spy();
+
+			collection.add( {} );
+			collection.add( {} );
+
+			collection.on( 'add', spy );
+
+			const item = {};
+			collection.add( item, 1 );
+
+			sinon.assert.calledWithExactly( spy, sinon.match.has( 'source', collection ), item, 1 );
 		} );
 	} );
 
@@ -387,4 +443,4 @@ describe( 'Collection', () => {
 			expect( items ).to.deep.equal( [ 'foo', 'bar', 'bom' ] );
 		} );
 	} );
-} );
+} );