/** * jQuery.Collection * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com * Licensed under GPL license (http://www.opensource.org/licenses/gpl-license.php). * Date: 1/28/2008 * * @projectDescription Extensible and inheritable jQuery-like collections * @author Ariel Flesler * @version 1.0.3 * * @id $.collection * @param { *, Array } items Any amount of items for the collection, this is a generic (and the base) collection. * @return { $.collection } Returns a generic $.collection object. * * @id $.collection.build * @return {subclass of $.collection} Returns a subclass of it's caller ( $.collection in the first case ). */ ;(function( $ ){ var f = function(){}, emptyInstance = function( c ){//get an instance of this constructor, without calling it f.prototype = (c._constructor||c).prototype; return new f(); }, callConstructor = function( obj, args ){//calls the constructor of the object, passing an empty object. return obj.init.apply(emptyInstance(obj), args); }, getConstructor = function(){//generate a constructor for a new collections return(function( list ){ var constructor = arguments.callee, obj = this instanceof constructor ? this : emptyInstance(constructor); if( list && list._constructor === constructor )//special case, cloning return obj.setArray( list.get() ); return obj.init.apply(obj,arguments); }); }; var $collection = $.collection = getConstructor();//$.collection is a valid collection itself $.extend( $collection, { extend: $.extend, fn:$collection.prototype, statics:'extend,build,include,implement', build:function(){//creates a new collection, that include this collections prototype //inheritance is possible, all collection will first inherit from $.collection var constr = getConstructor(); //copy the statics this.include( constr, this, $collection.statics ); //create inheritance. constr.prototype = constr.fn = emptyInstance(this); constr._constructor = constr.fn._constructor = constr;//we could lose it return constr; }, //imports the given methods (names) into target, from source (optional parse function) include:function( target, source, methods, parse ){ if( !methods || !methods.slice ){ [].unshift.call( arguments, this );//insert 'this' first return this.include.apply(this,arguments);//call again with fixed arguments } $.each( methods.split ? methods.split(/\s?,\s?/) : methods, function( i, func ){ target[func] = parse ? parse(source[func], func, source) : source[func]; }); return target; }, //same as include, but when calling an implemented function, it will map EACH matched element. implement:function( source, methods ){ this.fn.include( source, methods, function( method ){ return function(){ var args = arguments; return this.map(function(){ return method.apply(this,args); }); }; }); } }); $collection.extend( $collection.fn, { extend:$collection.extend, include:$collection.include, init:function( els ){//IMPORTANT: this is the main function to rewrite for each collection. //init should always call setArray with the array of parsed items, to keep jQuery's array structure. var items = typeof els == 'object' && 'length' in els ? els : arguments; return this.setArray( items );//this is just a generic init. }, filter:function( filter ){//TODO: add more filtering options if( typeof filter != 'function' ){ var out = filter.constructor == Array ? filter : [filter]; filter = function(){ return $.inArray( this, out ) != -1; }; } return this.pushStack($.grep( this, function( e, i ){ return filter.call( e, i ); })); }, not:function( right ){ right = this.filter(right); return this.filter(function(){ return $.inArray( this, right ) == -1; }); }, is:function( s ){ return !!(s && this.filter( s ).length); }, add:function(){ return this.pushStack( $.merge(this.get(), callConstructor(this,arguments) ) ); }, pushStack:function(items){ var ret = emptyInstance(this).setArray( items.get ? items.get() : items ); ret.prevObject = this; return ret; }, end:function(){ return this.prevObject || callConstructor(this); }, attr:function( key, value ){ return value === undefined ? this[0] != null && this[0][key] : this.each(function(){ this[key] = value; }); } }); //all these methods can be used in the collections, and are exactly (and literally) like in jQuery. $collection.fn.include( $.fn, 'each,extend,index,setArray,get,size,eq,slice,map,andSelf' ); })( jQuery );