Procházet zdrojové kódy

Added forEach, filter and find methods to Collection and NamedCollection.

Piotrek Koszuliński před 10 roky
rodič
revize
a2d82a81c8

+ 38 - 0
packages/ckeditor5-ui/src/collection.js

@@ -100,6 +100,44 @@ CKEDITOR.define( [ 'emittermixin', 'ckeditorerror', 'utils' ], function( Emitter
 
 			return removedModel;
 		}
+
+		/**
+		 * Executes the callback for each model in the collection.
+		 *
+		 * @param {Function} callback
+		 * @param {Model} callback.item
+		 * @param {String} callback.index
+		 * @params {Object} ctx Context in which the `callback` will be called.
+		 */
+		forEach( callback, ctx ) {
+			this._models.forEach( callback, ctx );
+		}
+
+		/**
+		 * Finds the first item in the collection for which the `callback` returns a true value.
+		 *
+		 * @param {Function} callback
+		 * @param {Model} callback.item
+		 * @param {String} callback.name
+		 * @returns {Model} The item for which `callback` returned a true value.
+		 * @params {Object} ctx Context in which the `callback` will be called.
+		 */
+		find( callback, ctx ) {
+			return this._models.find( callback, ctx );
+		}
+
+		/**
+		 * Returns an array with items for which the `callback` returned a true value.
+		 *
+		 * @param {Function} callback
+		 * @param {Model} callback.item
+		 * @param {String} callback.name
+		 * @params {Object} ctx Context in which the `callback` will be called.
+		 * @returns {Model[]} The array with matching items.
+		 */
+		filter( callback, ctx ) {
+			return this._models.filter( callback, ctx );
+		}
 	}
 
 	utils.extend( Collection.prototype, EmitterMixin );

+ 38 - 0
packages/ckeditor5-ui/src/namedcollection.js

@@ -132,6 +132,44 @@ CKEDITOR.define( [ 'emittermixin', 'ckeditorerror', 'utils' ], function( Emitter
 		forEach( callback ) {
 			this._models.forEach( callback );
 		}
+
+		/**
+		 * Finds the first item in the collection for which the `callback` returns a true value.
+		 *
+		 * @param {Function} callback
+		 * @param {Model} callback.item
+		 * @param {String} callback.name
+		 * @params {Object} ctx Context in which the `callback` will be called.
+		 * @returns {Model} The item for which `callback` returned a true value.
+		 */
+		find( callback, ctx ) {
+			for ( var name in this._models ) {
+				if ( callback.call( ctx, this._models[ name ], name ) ) {
+					return this._models[ name ];
+				}
+			}
+		}
+
+		/**
+		 * Returns an object (`name => item`) with items for which the `callback` returned a true value.
+		 *
+		 * @param {Function} callback
+		 * @param {Model} callback.item
+		 * @param {String} callback.name
+		 * @params {Object} ctx Context in which the `callback` will be called.
+		 * @returns {Object} The object with matching items.
+		 */
+		filter( callback, ctx ) {
+			var ret = {};
+
+			for ( var name in this._models ) {
+				if ( callback.call( ctx, this._models[ name ], name ) ) {
+					ret[ name ] = this._models[ name ];
+				}
+			}
+
+			return ret;
+		}
 	}
 
 	utils.extend( NamedCollection.prototype, EmitterMixin );

+ 56 - 0
packages/ckeditor5-ui/tests/mvc/collection/collection.js

@@ -7,6 +7,8 @@
 
 var modules = bender.amd.require( 'collection', 'model' );
 
+bender.tools.createSinonSandbox();
+
 describe( 'add', function() {
 	it( 'should change the length and enable get', function() {
 		var Model = modules.model;
@@ -114,6 +116,60 @@ describe( 'remove', function() {
 	} );
 } );
 
+describe( 'forEach', function() {
+	it( 'uses native forEach', function() {
+		var collection = getCollection();
+		collection.add( getItem() );
+
+		var spy = bender.sinon.spy( Array.prototype, 'forEach' );
+		var ctx = {};
+
+		collection.forEach( callback, ctx );
+
+		sinon.assert.calledWithExactly( spy, callback, ctx );
+
+		function callback() {}
+	} );
+} );
+
+describe( 'find', function() {
+	it( 'uses native find', function() {
+		var collection = getCollection();
+		var needl = getItem();
+
+		var spy = bender.sinon.stub( Array.prototype, 'find', function() {
+			return needl;
+		} );
+		var ctx = {};
+
+		var ret = collection.find( callback, ctx );
+
+		sinon.assert.calledWithExactly( spy, callback, ctx );
+		expect( ret ).to.equal( needl, 'ret value was forwarded' );
+
+		function callback() {}
+	} );
+} );
+
+describe( 'filter', function() {
+	it( 'uses native filter', function() {
+		var collection = getCollection();
+		var needl = getItem();
+
+		var spy = bender.sinon.stub( Array.prototype, 'filter', function() {
+			return needl;
+		} );
+		var ctx = {};
+
+		var ret = collection.filter( callback, ctx );
+
+		sinon.assert.calledWithExactly( spy, callback, ctx );
+		expect( ret ).to.equal( needl, 'ret value was forwarded' );
+
+		function callback() {}
+	} );
+} );
+
 function getCollection() {
 	var Collection = modules.collection;
 

+ 63 - 10
packages/ckeditor5-ui/tests/mvc/collection/namedcollection.js

@@ -168,20 +168,73 @@ describe( 'remove', function() {
 } );
 
 describe( 'forEach', function() {
-	it( 'should iterate over the models', function() {
-		var box = getCollection();
-		var item1 = getItem( 'foo' );
-		var item2 = getItem( 'bar' );
+	it( 'executes callback for each item', function() {
+		var collection = getCollection();
+		collection.add( getItem( 'foo' ) );
+		collection.add( getItem( 'bar' ) );
+
+		var ctx = {};
+		var models = [];
+		var names = [];
+
+		collection.forEach( callback, ctx );
+		expect( models.sort() ).deep.equals( [ 'bar', 'foo' ] );
+		expect( names.sort() ).deep.equals( [ 'bar', 'foo' ] );
+
+		function callback( model, name ) {
+			expect( this ).to.equal( ctx ); /* jshint ignore:line */
+			models.push( model.name );
+			names.push( name );
+		}
+	} );
+} );
 
-		box.add( item1 );
-		box.add( item2 );
+describe( 'find', function() {
+	it( 'finds the right item', function() {
+		var Model = modules.model;
 
-		expect( box ).to.have.length( 2 );
+		var collection = getCollection();
+		var needl = getItem( 'foo' );
+		collection.add( getItem( 'bar' ) );
+		collection.add( needl );
+		collection.add( getItem( 'bom' ) );
 
-		var spy = sinon.spy();
-		box.forEach( spy );
+		var ctx = {};
+
+		var ret = collection.find( callback, ctx );
+		expect( ret ).to.equal( needl );
+
+		function callback( model, name ) {
+			expect( this ).to.equal( ctx ); /* jshint ignore:line */
+			expect( model ).is.an.instanceof( Model );
+			expect( name ).to.be.a( 'string' );
+
+			return model.name == 'foo';
+		}
+	} );
+} );
+
+describe( 'filter', function() {
+	it( 'finds the right items', function() {
+		var Model = modules.model;
+
+		var collection = getCollection();
+		collection.add( getItem( 'bar' ) );
+		collection.add( getItem( 'foo' ) );
+		collection.add( getItem( 'bom' ) );
+
+		var ctx = {};
+
+		var ret = collection.filter( callback, ctx );
+		expect( ret ).to.have.keys( [ 'bar', 'bom' ] );
+
+		function callback( model, name ) {
+			expect( this ).to.equal( ctx ); /* jshint ignore:line */
+			expect( model ).is.an.instanceof( Model );
+			expect( name ).to.be.a( 'string' );
 
-		sinon.assert.callOrder( spy.withArgs( item1, 'foo' ), spy.withArgs( item2, 'bar' ) );
+			return model.name[ 0 ] == 'b';
+		}
 	} );
 } );