/*

_chain.js v0.1
created by Starcode team
licensed under GPL v.3

mailto : info@starcode.ru
www    : starcode.ru
rtfm   : blog.starcode.ru/chain-js-part1/

*/

window.$$ = {

    _next : function( args ){
        var self = this;
        if ( self.array.length ) {
            var chainElem = self.array.shift();
            self.running                        = true;
            self.applyObj.next                  = self.next;
            if ( args ) self.applyObj.arguments = $$._extend( self.applyObj.arguments, args );
            self.applyObj.arguments.chainArray  = self.array;
            chainElem._function.apply( self.applyObj, chainElem.varsArray );            
            
        } else {
            self.arguments = self.applyObj.arguments;
            self.running = false;            
        }
        return self;
    },
    
    _chain : function( _function, varsArray, itemIndex ){
    
        var self = (this.array )? $$._extend( { array : $$._cloneArray( this.array) }, this ) : $$._extend( { array : [] }, this ); 
        if ( _function && _function.array ){
            self.array = $$._insertArrayItem( _function.array, self.array, self.array.length );
            return self;
        }
        
        if( (! _function ) || ( typeof( _function ) != 'function') ) return self; 
        if( ! ( varsArray instanceof Array ) ) varsArray = [ varsArray ];     
        var chainObj = { _function : _function, varsArray : varsArray };
        
        chainObj._remove = function(){
            self.array = $$._deleteArrayItem(chainObj, self.array);
        }
        
        chainObj._after = function( _function, varsArray ){
            var insertIndex = $.inArray(chainObj, self.array) + 1;
            self._chain( _function, varsArray, insertIndex );
        }    
        
        chainObj._before = function( _function, varsArray ){
            var insertIndex = $.inArray(chainObj, self.array);
            self._chain( _function, varsArray, insertIndex );
        }                
        
        if (! itemIndex){
            self.array.push( chainObj );    
        } else {
            self.array = $$._insertArrayItem( chainObj, self.array, itemIndex );
        }
        
        return self;
    },  
    
    _arguments : function( argsObj ){
        return this._chain( function(){ this.next( argsObj ) } );
    },
    
    _chainStack : function( applyObj ){
        var chainStack =  {
            array     : $$._cloneArray( applyObj.array ),
            running   : false,
            applyObj  : applyObj,
            next      : function(){ return $$._next.apply( chainStack, arguments ) },
            _chain    : function(){ return $$._chain.apply( chainStack, arguments ) },
            _run      : function(){ return $$._run.apply( chainStack, arguments ) }
        };    
        return chainStack;
    },

    _run : function( ){
        if (! this.arguments ) this.arguments = {};
        var self = new $$._chainStack( this );
        return self.next();
    },  
    
    _each : function( object, callback ){
        var name, i = 0, length = object.length;
			if ( length === undefined ) 
				for ( name in object ) 
					if ( callback.call( object[ name ], name, object[ name ] ) === false ) break;
			else 
                for ( var value = object[0]; i < length && callback.call( value, i, value ) !== false; value = object[++i] ){};
        return object;
    },
    
    _extend : function( target, source){
        $$._each( source, function( propertyName, property ){
            target[ propertyName ] = property;
        });
        return target;
    },
    
    _trimArray : function( arr ){
        var len  = arr.length,
            a    = [];
        for (var i = 0; i < len; i++ )
            if ( (arr[i] != '') && (arr[i] != null) ) a[ a.length ] = arr[ i ];
        return a;
    },       

    _cloneArray : function( _array ){
        return $$._insertArrayItem( _array, [], 0);    
    },
    
    _insertArrayItem : function(item, array, position){
        var len  = array.length,
            a    = [],
            j    = 0;
        if (! ( item instanceof Array )) item = [ item ];
        for (var i = 0; i <= len; i++ ){
            if ( i < position ){
                a[ j ] = array[ i ];
            } else {
                if ( (i != 0) && ( i != len ) )i--;
                position = -1;
                var len2 = item.length;
                for( var k = j; ( k - j )< len2; k++ ){
                    a[ k ] = item[ k - j ];
                }
                j += len2-1;
            }
            j++;
        }
        return a;        
    },

    _deleteArrayItem : function(item, array){
        var self           = this,
            itemIndex      = $.inArray( item, array );
        array[ itemIndex ] = null;
        return self._trimArray( array );
    }

};