﻿(function($) {
	//	---------------------------------------------------------
	//	             [jQuery.ajaxDotNet, ver 3.1]
	//	Copyright (c) 2008 Richard Kimber, http://www.dogma.co.uk
	//	Licensed under the terms of the MIT Licence (LICENSE.txt)
	//	---------------------------------------------------------
	//	On a personal note, I would love to hear about any
	//	implementations of this plugin. richard [at] dogma.co.uk
	//	---------------------------------------------------------

	//	Defaults
	var _defaults = {
		verb: "POST",
		useGet: false,
		data: new Object(),
		async: true,
		username: null,
		password: null,
		beforeSend: null,
		processData: null,
		success: null,
		error: null,
		complete: null,
		context: new Object(),
		usePrefix: true
	};

	//	Globals
	var _globals = {
		beforeSend: null,
		processData: null,
		success: null,
		error: null,
		complete: null,
		queue: 0,
		urlPrefix: null
	};

	//	Return new XMLHttpRequest Object
	function getXhr() {
		if (typeof XMLHttpRequest != "undefined") {
			return new XMLHttpRequest();
		}
		else if (typeof ActiveXObject != "undefined") {
			try {
				return new ActiveXObject("Msxml2.XMLHTTP");
			}
			catch (err) {
				try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { }
			}
		}
	};

	//	Test parameters of type String for dates and return either
	//	the orignal String or a newly created Date object.
	function cTD(v) {
		var rx = /\/Date\((-?[0-9]+)([+-][0-9]+)?\)\//g;
		if (rx.test(v)) {
			var milli = Number(v.replace(rx, "$1"));
			if (!isNaN(milli))
				v = new Date(Number(v.replace(rx, "$1")));
			else
				throw new Error("Date format not recognised.");
		}
		return v;
	};

	//	Recursively parse the JSON returned from the server.
	function tO(v) {
		if (v != null) {
			if (v.constructor == String) {
				v = cTD(v);
			}
			else if (v.constructor == Object || $.isArray(v)) {
				$.each(v, function(i, x) {
					v[i] = i != "__type" ? tO(x) : x;
				});
			}
		}
		return v;
	};

	//	Main Function
	$.ajaxDotNet = function(url, options) {
		options = $.extend({}, _defaults, options);

		if (options.usePrefix && _globals.urlPrefix && _globals.urlPrefix != "") {
			url = _globals.urlPrefix + url;
		}

		var xhr = getXhr();

		//	Convert data option into a format readable by the server.
		var data = "";
		if (options.verb == "GET" || options.useGet) {
			options.verb = "GET";
			
			for (var i in options.data) {
				if (data != "") {
					data += "&";
				}
				data += i + "=" + JSON.stringify(options.data[i]).replace(/#/, "%23");
			}
			url += "?" + data;
		}
		else {
			data = JSON.stringify(options.data);
		}

		//	Before Send Event Handler, remeber to return the XMLHttpRequest object.
		if ($.isFunction(_globals.beforeSend)) {
			xhr = _globals.beforeSend(xhr, url, options) || false;
		}

		if (xhr && $.isFunction(options.beforeSend)) {
			xhr = options.beforeSend(xhr, url, options) || false;
		}

		//	Make call to server
		if (xhr) {
			xhr.open(options.verb, url, options.async, options.username, options.password);
			xhr.onreadystatechange = function() {
				if (xhr.readyState == 4) {
					var r = xhr.responseText;
					var e = null;

					//	Parse response from server.
					if (r.constructor == String) {
						if (r != "") {
							try {
								r = JSON.parse(xhr.responseText);
								if (typeof r == "object"
									&& r.Message !== undefined
									&& r.StackTrace !== undefined
									&& r.ExceptionType !== undefined) {
									e = r;
								}
								else {
									r = tO(r);
								}
							}
							catch (err) {
								e = err;
							}
						}
						else {
							r = {};
						}
					}

					//	Process Data Event Handler
					if ($.isFunction(_globals.processData)) {
						r = _globals.processData(r, url, options) || false;
					}

					if (r && $.isFunction(options.processData)) {
						r = options.processData(r, url, options) || false;
					}

					if (e == null && xhr.status == 200) {
						if ($.isFunction(_globals.success)) {
							_globals.success(r, xhr.status, xhr.statusText, options.context);
						}

						if ($.isFunction(options.success)) {
							options.success(r || e, xhr.status, xhr.statusText, options.context);
						}
					}
					else {
						if ($.isFunction(_globals.error)) {
							_globals.error(xhr, xhr.statusText, e, options.context);
						}

						if ($.isFunction(options.error)) {
							options.error(xhr, xhr.statusText, e, options.context);
						}
					}

					if ($.isFunction(_globals.complete)) {
						_globals.complete(r, e, xhr, options.context);
					}

					if ($.isFunction(options.complete)) {
						options.complete(r, e, xhr, options.context);
					}
				}
			};
			xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
			xhr.send(options.verb == "POST" ? data : null);
		}
		return xhr;
	};

	$.ajaxDotNet.defaults = function(options) {
		_defaults = $.extend(_defaults, options);
	};

	$.ajaxDotNet.globals = function(options) {
		_globals = $.extend(_globals, options);
	};
})(jQuery);