﻿(function($) {
	//	---------------------------------------------------------
	//	             [jQuery.ajaxDotNet, ver 3]
	//	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',
		data: new Object(),
		async: true,
		username: null,
		password: null,
		beforeSend: null,
		processData: null,
		success: null,
		error: null,
		complete: null,
		context: new Object()
	};

	//	Globals
	var _globals = {
		beforeSend: null,
		processData: null,
		success: null,
		error: null,
		complete: 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 || v.constructor == Array) {
				$.each(v, function(i, x) {
					v[i] = i != "__type" ? tO(x) : x;
				});
			}
		}
		return v;
	};

	//	Main Function
	$.ajaxDotNet = function(url, options) {
		options = $.extend({}, _defaults, options);

		var xhr = getXhr();

		//	Convert data option into a format readable by the server.
		var data = '';
		if (options.verb == 'GET') {
			for (var i in options.data) {
				if (data != '') {
					data += '&';
				}
				data += i + '=' + JSON.stringify(options.data[i]);
			}
			url += '?' + data;
			data = null;
		}
		else if (options.verb == 'POST') {
			data = JSON.stringify(options.data);
		}

		//	Before Send Event Handler, remeber to return the XMLHttpRequest object.
		if (typeof _globals.beforeSend == "function") {
			xhr = _globals.beforeSend(xhr) || false;
		}

		if (xhr && typeof options.beforeSend == "function") {
			xhr = options.beforeSend(xhr) || 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 (typeof _globals.processData == "function") {
						r = _globals.processData(r) || false;
					}

					if (r && typeof options.processData == "function") {
						r = options.processData(r) || false;
					}

					if (e == null && xhr.status == 200) {
						if (typeof _globals.success == "function") {
							_globals.success(r, xhr.status, xhr.statusText, options.context);
						}

						if (typeof options.success == "function") {
							options.success(r || e, xhr.status, xhr.statusText, options.context);
						}
					}
					else {
						if (typeof _globals.error == "function") {
							_globals.error(xhr, xhr.statusText, e, options.context);
						}

						if (typeof options.error == "function") {
							options.error(xhr, xhr.statusText, e, options.context);
						}
					}

					if (typeof _globals.complete == "function") {
						_globals.complete(r, e, xhr, options.context);
					}

					if (typeof options.complete == "function") {
						options.complete(r, e, xhr, options.context);
					}
				}
			};
			xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
			xhr.send(data);
		}
	};

	$.ajaxDotNet.defaults = function(options) {
		_defaults = $.extend(_defaults, options);
	};

	$.ajaxDotNet.globals = function(options) {
		_globals = $.extend(_globals, options);
	};
})(jQuery);
