/**
 * @author alexander.farkas
 * 
 * @version 2.5.1
 * project site: http://plugins.jquery.com/project/AjaxManager
 */
(function($){
    
    $.manageAjax = (function(){
        var cache             = {},
            queues            = {},
            presets         = {},
            activeRequest     = {},
            allRequests     = {},
            triggerEndCache = {},
            defaults         = {
                        queue: true, //clear
                        maxRequests: 1,
                        abortOld: false,
                        preventDoubbleRequests: true,
                        cacheResponse: false,
                        complete: function(){},
                        error: function(ahr, status){
                            var opts = this;
                            if(status &&  status.indexOf('error') != -1){
                                setTimeout(function(){
                                    var errStr = status +': ';
                                    if(ahr.status){
                                        errStr += 'status: '+ ahr.status +' | ';
                                    }
                                    errStr += 'URL: '+ opts.url;
                                    throw new Error(errStr);
                                }, 1);
                            }
                        },
                        success: function(){},
                        abort: function(){}
                }
        ;
        
        function create(name, settings){
            var publicMethods = {};
            presets[name] = presets[name] ||
                {};
            
            $.extend(true, presets[name], $.ajaxSettings, defaults, settings);
            if(!allRequests[name]){
                allRequests[name]     = {};
                activeRequest[name] = {};
                activeRequest[name].queue = [];
                queues[name]         = [];
                triggerEndCache[name] = [];
            }
            $.each($.manageAjax, function(fnName, fn){
                if($.isFunction(fn) && fnName.indexOf('_') !== 0){
                    publicMethods[fnName] = function(param, param2){
                        if(param2 && typeof param === 'string'){
                            param = param2;
                        }
                        fn(name, param);
                    };
                }
            });
            return publicMethods;
        }
        
        function complete(opts, args){
            
            if(args[1] == 'success' || args[1] == 'notmodified'){
                opts.success.apply(opts, [args[0].successData, args[1]]);
                if (opts.global) {
                    $.event.trigger("ajaxSuccess", args);
                }
            }
            
            if(args[1] === 'abort'){
                opts.abort.apply(opts, args);
                if(opts.global){
                    $.active--;
                    $.event.trigger("ajaxAbort", args);
                }
            }
            
            opts.complete.apply(opts, args);
            
            if (opts.global) {
                $.event.trigger("ajaxComplete", args);
            }
            
            if (opts.global && ! $.active){
                $.event.trigger("ajaxStop");
            }
            //args[0] = null; 
        }
        
        function proxy(oldFn, fn){
            return function(xhr, s, e){
                fn.call(this, xhr, s, e);
                oldFn.call(this, xhr, s, e);
                xhr = null;
                e = null;
            };
        }
        
                    
        function callQueueFn(name){
            var q = queues[name];
            if(q && q.length){
                var fn = q.shift();
                if(fn){
                    fn();
                }
            }
        }

        
        function add(name, opts){
            if(!presets[name]){
                create(name, opts);
            }
            opts = $.extend({}, presets[name], opts);
            //aliases
            var allR     = allRequests[name],
                activeR = activeRequest[name],
                queue    = queues[name];
            
            var id                 = opts.type +'_'+ opts.url.replace(/\./g, '_'),
                triggerStart     = true,
                oldComplete     = opts.complete,
                ajaxFn             = function(){
                                    activeR[id] = {
                                        xhr: $.ajax(opts),
                                        ajaxManagerOpts: opts
                                    };
                                    activeR.queue.push(id);
                                    return id;
                                }
                ;
                
            if(opts.data){
                id += (typeof opts.data == 'string') ? opts.data : $.param(opts.data);
            }
            
            if(opts.preventDoubbleRequests && allRequests[name][id]){
                return false;
            }
            
            allR[id] = true;
            
            opts.complete = function(xhr, s, e){
                var triggerEnd = true;
                if(opts.abortOld){
                    $.each(activeR.queue, function(i, activeID){
                        if(activeID == id){
                            return false;
                        }
                        abort(name, activeID);
                        return activeID;
                    });
                }
                oldComplete.call(this, xhr, s, e);
                //stop memory leak
                if(activeRequest[name][id]){
                    if(activeRequest[name][id] && activeRequest[name][id].xhr){
                        activeRequest[name][id].xhr = null;
                    } 
                    activeRequest[name][id] = null;
                }
                triggerEndCache[name].push({xhr: xhr, status: s});
                xhr = null;
                activeRequest[name].queue = $.grep(activeRequest[name].queue, function(qid){
                    return (qid !== id);
                });
                allR[id] = false;
                
                e = null;
                
                delete activeRequest[name][id];
                
                $.each(activeR, function(id, queueRunning){
                    if(id !== 'queue' || queueRunning.length){
                        triggerEnd = false;
                        return false;
                    }
                });
                
                if(triggerEnd){
                    $.event.trigger(name +'End', [triggerEndCache[name]]);
                    $.each(triggerEndCache[name], function(i, cached){
                        cached.xhr = null; //memory leak
                    });
                    triggerEndCache[name] = [];
                }
            };
            
            if(cache[id]){
                ajaxFn = function(){
                    activeR.queue.push(id);
                    complete(opts, cache[id]);
                    return id;
                };
            } else if(opts.cacheResponse){
                 opts.complete = proxy(opts.complete, function(xhr, s){
                    if( s !== "success" && s !== "notmodified" ){
                        return false;
                    }
                    cache[id][0].responseXML     = xhr.responseXML;
                    cache[id][0].responseText     = xhr.responseText;
                    cache[id][1]                 = s;
                    //stop memory leak
                    xhr = null;
                    return id; //strict
                });
                
                opts.success = proxy(opts.success, function(data, s){
                    cache[id] = [{
                        successData: data,
                        ajaxManagerOpts: opts
                    }, s];
                    data = null;
                });
            }
            
            ajaxFn.ajaxID = id;
            
            $.each(activeR, function(id, queueRunning){
                if(id !== 'queue' || queueRunning.length){
                    triggerStart = false;
                    return false;
                }
            });
            
            if(triggerStart){
                $.event.trigger(name +'Start');
            }
            if(opts.queue){
                opts.complete = proxy(opts.complete, function(){
                    
                    callQueueFn(name);
                });
                 
                if(opts.queue === 'clear'){
                    queue = clear(name);
                }
                
                queue.push(ajaxFn);
                
                if(activeR.queue.length < opts.maxRequests){
                    callQueueFn(name); 
                }
                return id;
            }
            
            
            
            return ajaxFn();
        }
        
        function clear(name, shouldAbort){
            $.each(queues[name], function(i, fn){
                allRequests[name][fn.ajaxID] = false;
            });
            queues[name] = [];
            
            if(shouldAbort){
                abort(name);
            }
            return queues[name];
        }
        
        function getXHR(name, id){
            var ar = activeRequest[name];
            if(!ar || !allRequests[name][id]){
                return false;
            }
            if(ar[id]){
                return ar[id].xhr;
            }
            var queue = queues[name],
                xhrFn;
            $.each(queue, function(i, fn){
                if(fn.ajaxID == id){
                    xhrFn = [fn, i];
                    return false;
                }
                return xhrFn;
            });
            return xhrFn;
        }
        
        function abort(name, id){
            var ar = activeRequest[name];
            if(!ar){
                return false;
            }
            function abortID(qid){
                if(qid !== 'queue' && ar[qid] && ar[qid].xhr && ar[qid].xhr.abort){
                    ar[qid].xhr.abort();
                    complete(ar[qid].ajaxManagerOpts, [ar[qid].xhr, 'abort']);
                }
                return null;
            }
            if(id){
                return abortID(id);
            }
            return $.each(ar, abortID);
        }
        
        function unload(){
            $.each(presets, function(name){
                clear(name, true);
            });
            cache = {};
        }
        
        return {
            defaults:         defaults,
            add:             add,
            create:         create,
            cache:             cache,
            abort:             abort,
            clear:             clear,
            getXHR:         getXHR,
            _activeRequest: activeRequest,
            _complete:         complete,
            _allRequests:     allRequests,
            _unload:         unload
        };
    })();
    //stop memory leaks
    $(window).unload($.manageAjax._unload);
})(jQuery);
