// AjaxQ jQuery Plugin
// Copyright (c) 2012 Foliotek Inc.
// MIT License
// https://github.com/Foliotek/ajaxq

(function($) {

	var queues     = {};
	var activeReqs = {};

	// Register an $.ajaxq function, which follows the $.ajax interface, but allows a queue name which will force only one request per queue to fire.
	$.ajaxq = function(qname, opts) {

		if (typeof opts === "undefined") {
			throw ("AjaxQ: queue name is not provided");
		}

		// Will return a Deferred promise object extended with success/error/callback, so that this function matches the interface of $.ajax
		var deferred = $.Deferred(),
			promise  = deferred.promise();

		promise.success  = promise.done;
		promise.error    = promise.fail;
		promise.complete = promise.always;

		// Create a deep copy of the arguments, and enqueue this request.
		var clonedOptions = $.extend( true, {}, opts );
		enqueue(
			function() {
				// Send off the ajax request now that the item has been removed from the queue
				var jqXHR = $.ajax.apply( window, [clonedOptions] );

				// Notify the returned deferred object with the correct context when the jqXHR is done or fails
				// Note that 'always' will automatically be fired once one of these are called: http://api.jquery.com/category/deferred-object/.
				jqXHR.done(
					function() {
						deferred.resolve.apply( this, arguments );
					}
				);
				jqXHR.fail(
					function() {
						deferred.reject.apply( this, arguments );
					}
				);

				jqXHR.always( dequeue ); // make sure to dequeue the next request AFTER the done and fail callbacks are fired
				return jqXHR;
			}
		);

		return promise;

		// If there is no queue, create an empty one and instantly process this item.
		// Otherwise, just add this item onto it for later processing.
		function enqueue(cb) {
			if ( ! queues[qname]) {
				queues[qname]     = [];
				var xhr           = cb();
				activeReqs[qname] = xhr;
			} else {
				queues[qname].push( cb );
			}
		}

		// Remove the next callback from the queue and fire it off.
		// If the queue was empty (this was the last item), delete it from memory so the next one can be instantly processed.
		function dequeue() {
			if ( ! queues[qname]) {
				return;
			}
			var nextCallback = queues[qname].shift();
			if (nextCallback) {
				var xhr           = nextCallback();
				activeReqs[qname] = xhr;
			} else {
				delete queues[qname];
				delete activeReqs[qname];
			}
		}
	};

	// Register a $.postq and $.getq method to provide shortcuts for $.get and $.post
	// Copied from jQuery source to make sure the functions share the same defaults as $.get and $.post.
	$.each(
		[ "getq", "postq" ],
		function( i, method ) {
			$[ method ] = function( qname, url, data, callback, type ) {

				if ( $.isFunction( data ) ) {
					type     = type || callback;
					callback = data;
					data     = undefined;
				}

				return $.ajaxq(
					qname,
					{
						type: method === "postq" ? "post" : "get",
						url: url,
						data: data,
						success: callback,
						dataType: type
					}
				);
			};
		}
	);

	var isQueueRunning = function(qname) {
		return queues.hasOwnProperty( qname );
	};

	var isAnyQueueRunning = function() {
		for (var i in queues) {
			if (isQueueRunning( i )) {
				return true;
			}
		}
		return false;
	};

	$.ajaxq.isRunning = function(qname) {
		if (qname) {
			return isQueueRunning( qname );
		} else {
			return isAnyQueueRunning();
		}
	};

	$.ajaxq.getActiveRequest = function(qname) {
		if ( ! qname) {
			throw ("AjaxQ: queue name is required");
		}

		return activeReqs[qname];
	};

	$.ajaxq.abort = function(qname) {
		if ( ! qname) {
			throw ("AjaxQ: queue name is required");
		}

		var current = $.ajaxq.getActiveRequest( qname );
		delete queues[qname];
		delete activeReqs[qname];
		if (current) {
			current.abort();
		}
	};

	$.ajaxq.clear = function(qname) {
		if ( ! qname) {
			for (var i in queues) {
				if (queues.hasOwnProperty( i )) {
					queues[i] = [];
				}
			}
		} else {
			if (queues[qname]) {
				queues[qname] = [];
			}
		}
	};

})( jQuery );
