//ver 6

/*
maybe this is out of date!

usage:

var my_ajax = new ajax;

---------------------------------------------------------------------------------------------------------
my_ajax.addCallBack( function:Function , *arguments:Array or Boolean , *stat:Boolean , *response_position:Boolean );
my_ajax.addCallBack( function:Function , *arguments:Array or Boolean , *stat:Boolean , *response_position:Boolean );
my_ajax.addCallBack( function:Function , *arguments:Array or Boolean , *stat:Boolean , *response_position:Boolean );

- as many addCallBack as u want

@stat true if u want the resposne send to this function
	  false is the default!
@response_position true if u want to add the response at the end of the arguments
				   false or ignore it if u want response in front of the arguments! false is the default

*optional!

--if u need to send the response to a function!
u can use:

my_ajax.addCallBack( function:Function , *arguments:Array );
my_ajax.addCallBack( function:Function , *arguments:Array, *stat:Boolean );
my_ajax.addCallBack( function:Function , *arguments:Array );

and u will have the response in the second function

u also can use:

my_ajax.addCallBack( function:Function , *arguments:Array );
my_ajax.addCallBack( function:Function , *stat:Boolean ); // if the u don't have any other arguments to send!
my_ajax.addCallBack( function:Function , *arguments:Array );

if u don't have any arguments for the function nr 2

u cannot define stat in a addCallBack if u have already defined response.index
u cannot have more then one function with stat defined

OR

my_ajax.response.index = nr:Number;

	- nr is the location of the addCallBack
	if u have 3 addCallBack functions and u want to place the response on the second function
	then u have the index 1.
	the response is always the first argument of the function!!
	so u might have:
		
		my_ajax.addCallBack(my_function, [arg1, arg2]);
		-ur function will look like:
		function my_function (farg1, farg2, farg3) {
			farg1 is the response from the server
			farg2 is arg1
			farg3 is arg2
		}
---------------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------------
--if u want to set the response as individual arguments!
--this is applicable only is u send an array from server side [e1, e2, ...]
my_ajax.response.as_arguments = bool:Boolean;

	- on ur server side have the response as a array like [a, b]
	my_ajax.addCallBack(my_function, [arg1, arg2]);
	
	ur function in this case would look like:
	function (ar1, ar2, ar3, ar4) {
		ar1 is a
		ar2 is b
		ar3 is arg1
		ar4 is arg2
	}
---------------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------------
--if u want to set the response as global
my_ajax.response.global = bool:Boolean;

	- ur server side must be a valid javascript code!
---------------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------------
--if u want a request to be executed after a period of time continuously use:
my_ajax.timer(nr:Number, var:String or Number or Array, fct:Function, *arg:Array);
*optional
	
	-nr is the time betwen the request in miliseconds,
	-var is an indentifier for the request to stop else this is going to be an infinite requests,
		-if this indentifier will match the response the requests will stop
		else the requests will start again!
	-fct a function where the response is send
	-arg an array with the arguments for the function, this is optional.
	
	so far u can't use timer with response.global or request.index!!
	NOTE: the call back functions (if u have any) will be executed at the end of the
			timer, when the indentifier will match the response.
---------------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------------
--if ur response is an javascript code then u can use:
my_ajax.evalCode.wait(arg1, arg2, arg3 ... :String);
OR
my_ajax.evalCode.end(arg1, arg2, arg3 ... :String);
OR 
use both

	-wait is while the communication with server is made,
	-end is when the communication with server is done.
	-arg1, arg2 .... are code strings, u can add as many as u want!
---------------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------------
--if u want to execute an code in case of a communication failure then use
my_ajax.onError(fct:Function, args:Array);
	
	this will be executed only if u get a non redystate 200.
---------------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------------
--if u want to redirect the user to some other page in case of communication fail use:
my_ajax.error_redirect = "url_here";
---------------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------------
--this part execute the ajax, so don't add any commands after this line!!
my_ajax.post(server_side_page:String, *post_variables:String or Object);
*optional

or

my_ajax.get(server_side_page:String, *get_variables:String or Object);
*optional
---------------------------------------------------------------------------------------------------------
*/

function ajax () {
	
	var _me = this;
	var _d = new Date;
	
	//
	var _add_call = new Array;
	var _error_args = new Array;
	
	var _wait = new Array;
	var _end = new Array;
	
	var _timer = new Array;
	
	var _post_done = new Array;
	var _get_done = new Array;
	var _current_errors = false;
	
	var _http_request = false;
	
	// create the _http_request object
	if (window.XMLHttpRequest) { // Mozilla, Safari, ...
		_http_request = new XMLHttpRequest();
		if (_http_request.overrideMimeType) {
			//FF bug?
			//_http_request.overrideMimeType('text/xml');
			_http_request.overrideMimeType('text/html');
		}
	} else if (window.ActiveXObject) { // IE
		try {
			_http_request = new ActiveXObject("Msxml2.XMLHTTP");
		} catch (e) {
			try {
				_http_request = new ActiveXObject("Microsoft.XMLHTTP");
			} catch (e) {}
		}
	}
	if (!_http_request) {
		alert('Cannot send the request!\n\nPlease try to update your browser and set the security to default!');
		return false;
	}
	
	_http_request.onreadystatechange = function() {
		
		if (_http_request.readyState != 4)
			if (_wait.length > 0) make_wait();
		
		if (_http_request.readyState == 4) {
			
			if (_http_request.status == 200) {
				
				if (_me.response.global) {
					try {
						eval(String(_http_request.responseText));
					} catch (e) {
						alert("Could not eval response as global, error:\n" + e);
					}
				}
				
				if (_add_call.length > 0 && _timer.length == 0) make_callBack();
				if (_end.length > 0 && _timer.length == 0 && _add_call.length == 0) make_end();
				if (_timer.length >= 3) make_timer();
				
			} else get_custom_errors(_http_request.status);
			
		}
		
	};
	
	this.error_redirect = "none";
	
	this.response = {
		
		//this is the global as_arguments
		as_arguments : {
			timer : false,
			call_back : false
		},
		
		global : false,
		
		//u can access this in the timer and/or addCallBack
		index : null,
		call_back_at_end : false,
		timer_at_end : false
		
	};
	
	// --- setters
	this.set_timer = function (arg) {
		_timer = arg;
	};
	this.set_add_call = function (arg) {
		_add_call = arg;
	};
	this.set_error_args = function (arg) {
		_error_args = arg;
	};
	this.set_end = function (arg) {
		_end = arg;
	};
	// ---
	
	this.timer = function (t, i, f, a, rp) {
		
		if (typeof(t) != "number") {
			alert("First timer argument must be a number!");
			_current_errors = true;
			return;
		}
		
		if (i == "" || i == undefined) {
			alert("Second timer argument is undefined! Infinite request!");
			_current_errors = true;
			return;
		}
		
		if (!(f instanceof Function)) {
			alert("Third timer argument must be a Function!");
			_current_errors = true;
			return;
		}
		
		if (a != undefined)
			if (!(a instanceof Array)) {
				alert("Forth timer argument must be a Array!");
				_current_errors = true;
				return;
			}
		
		if (rp != undefined) {
			if (typeof(rp) != "boolean") {
				alert("Fifth timer argument must be an Boolean value!");
				_current_errors = true;
				return;
			}
			if (rp) _me.response.timer_at_end = true;
		}
		
		_timer = [t, i, f, a];
		
	};
	
	this.onErrorRun = function (fct, args) {
		
		if (!(fct instanceof Function)) {
			alert("onError wrong data type argument, need Function!");
			_current_errors = true;
			return;
		}
		
		if (args != undefined)
			if (!(fct instanceof Array)) {
				alert("onError wrong data type argument, need Array!");
				_current_errors = true;
				return;
			}
		
		_error_args = [fct, args];
		
	};
	
	this.evalCode = {
		
		wait : function () {
			_wait = [].slice.call(arguments);
		},
		end : function () {
			_end = [].slice.call(arguments);
		}
		
	};
	
	this.addCallBack = function (fn, args, resp, resp_pos) {
		
		var seca;
		
		if (!(fn instanceof Function)) {
			alert("First addCallBack argument must be a function!"+
				  "\naddCallBack number " + (_add_call.length+1) + " fails!");
			_current_errors = true;
			return;
		}
		
		if (args != undefined) {
			
			if (args instanceof Array) {
				
				seca = args;
				
			} else if (typeof(args) == "boolean") {
				
				if (_me.response.index != null && args) {
					alert("response.index was already defined!"+
						  "\naddCallBack number " + (_add_call.length+1) + " fails!");
					_current_errors = true;
					return;
				}
				
				if (resp != undefined) {
					alert("No arguments found for your call back function!"+
						  "\naddCallBack number " + (_add_call.length+1) + " fails!");
					_current_errors = true;
					return;
				}
				
				if (args) _me.response.index = _add_call.length;
				
			} else {
				
				alert("Second addCallBack argument is optional and must be an Array or Boolean value!"+
					  "\naddCallBack number " + (_add_call.length+1) + " fails!");
				_current_errors = true;
				return;
				
			}
			
		}
		
		if (resp != undefined) {
			if (typeof(resp) != "boolean") {
				alert("Third addCallBack argument must be an Boolean value!"+
					  "\naddCallBack number " + (_add_call.length+1) + " fails!");
				_current_errors = true;
				return;
			} else {
				if (_me.response.index != null && resp) {
					alert("response.index was already defined!"+
						  "\naddCallBack number " + (_add_call.length+1) + " fails!");
					_current_errors = true;
					return;
				}
				if (resp) _me.response.index = _add_call.length;
			}
		}
		
		if (resp_pos != undefined) {
			if (typeof(resp_pos) != "boolean") {
				alert("Forth addCallBack argument must be an Boolean value!"+
					  "\naddCallBack number " + (_add_call.length+1) + " fails!");
				_current_errors = true;
				return;
			}
			if (!(args instanceof Array)) {
				alert("You don't need to define the position of response!\n"+
					  "You don't have arguments to send!\n"+
					  "addCallBack number " + (_add_call.length+1) + " fails!");
				_current_errors = true;
				return;
			}
			if (resp == false) {
				alert("No response set for this function!\n"+
					  "Third argument is false!\n"+
					  "addCallBack number " + (_add_call.length+1) + " fails!");
				_current_errors = true;
				return;
			}
			if (resp_pos) _me.response.call_back_at_end = true;
		}
		
		_add_call.push({fct:fn, arg:seca});
		
	};
	
	//dummy is to prevent ie cashing
	this.post = function (url, data) {
		
		if (_current_errors) return;
		if (!check_ajax()) return;
		
		if (!url || url == "") {
			alert("No server side page found!");
			_me = null;
			return;
		}
		
		_post_done = [url, data];
		
		switch (typeof(data)) {
			
			case "string":
			data += "&dummy=" + _d.getTime();
			break;
			
			case "object":
			if (!(data instanceof Object)) {
				alert("Post arguments must be an Object or an String");
				return;
			}
			data = com_args(data);
			break;
			
			case "undefined":
			data = "dummy=" + _d.getTime();
			break;
			
			default:
			alert("Post data type not found: " + typeof(data));
			return;
			break;
			
		}
		
		_http_request.open('POST', url, true);
		_http_request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
		_http_request.setRequestHeader("Content-length", data.length);
		
		// IE6 on Windows 2000 doesn't like this
		// it hangs between _http_request.readyState 3 and 4
		//_http_request.setRequestHeader("Connection", "close");
		
		_http_request.send(data);
		
	};
	
	this.get = function (url, data) {
		
		if (_current_errors) return;
		if (!check_ajax()) return;
		
		if (!url || url == "") {
			alert("No server side page found");
			_me = null;
			return;
		}
		
		var r = url.indexOf('?');
		
		if (r != -1 && data != undefined) {
			alert("Wrong get arguments!");
			return;
		}
		
		_get_done = [url, data];
		
		switch (typeof(data)) {
			
			case "string":
			if (data.indexOf('?') != -1) url += data + "&dummy=" + _d.getTime();
			else url += "?dummy=" + _d.getTime() + "&" + data;
			break;
			
			case "object":
			if (!(data instanceof Object)) {
				alert("Post arguments must be an Object or an String");
				return;
			}
			url += "?" + com_args(data);
			break;
			
			case "undefined":
			if (r != -1) {
				url += "&dummy=" + _d.getTime();
			} else {
				url += "?dummy=" + _d.getTime();
			}
			break;
			
			default:
			alert("Get data type not found: " + typeof(data));
			return;
			break;
			
		}
		
		_http_request.open('GET', url, true);
		_http_request.send(null);
		
	};
	
	this.clear = function () {
		
		_http_request = null;
		_me = null;
		
	};
	
	var check_ajax = function () {
		
		if (_add_call.length > 0 && _timer[2]) {
			for (var i = 0; i < _add_call.length; i++) {
				if (_add_call[i].fct == _timer[2]) {
					alert("Timer call back function was found in addCallBack!\nTimer fails!");
					return false;
				}
			}
		}
		
		if (_me.response.index != null)
			if (typeof(_me.response.index) != "number") {
				alert("response.index must be a number!");
				return false;
			}
		
		if (_me.response.global)
			if (typeof(_me.response.global) != "boolean") {
				alert("response.global must be a boolean value!");
				return false;
			}
		
		if (typeof(_me.response.as_arguments) == "boolean") {
			if (_me.response.as_arguments && _me.response.index == null) {
				alert("No response.index specified!");
				return false;
			}
			if (_me.response.global && _me.response.as_arguments) {
				alert("You can't have global asociated with index or as_arguments response!\n"+
					  "Use response.as_arguments.timer to use it just for timer!");
				return false;
			}
		} else if (typeof(_me.response.as_arguments) == "object") {
			if (typeof(_me.response.as_arguments.timer) != "boolean") {
				alert("response.as_arguments.timer must be an Boolean value!");
				return false;
			}
			if (typeof(_me.response.as_arguments.call_back) != "boolean") {
				alert("response.as_arguments.call_back must be an Boolean value!");
				return false;
			} else {
				if (_me.response.as_arguments.call_back && _me.response.index == null) {
					alert("No response.index specified!");
					return false;
				}
				if (_me.response.global && _me.response.as_arguments.call_back) {
					alert("You can't have global asociated with index or as_arguments response!"+
						  "Error found in response.as_arguments.call_back!");
					return false;
				}
			}
		} else {
			alert("response.as_arguments type missmatch!"+
				  "response.as_arguments must be an Boolean value!\n"+
				  "Use response.as_arguments.timer or response.as_arguments.call_back!");
			return false;
		}
		
		if ((_me.response.index != null && _me.response.global)) {
			alert("You can't have global asociated with index or as_arguments response!");
			return false;
		}
		
		return true;
		
	}
	
	var com_args = function (obj) {
		
		if (!(obj instanceof Object))
			return "";
		
		var str = "";
		
		for (var i in obj) {
			str += i + "=" + escape(obj[i]) + "&";
		}
		
		str += "dummy=" + _d.getTime();
		
		return str;
	}
	
	var timer_body = function () {
		
		if (_post_done.length == 0 && _get_done.length == 0) {
			alert("Timer could not be executed!");
			return;
		}
		
		var t = new ajax();
		
		if (_timer) t.set_timer(_timer);
		
		if (_me.response.global) t.response.global = true;
		
		if (typeof(_me.response.as_arguments) == "boolean") t.response.as_arguments = true;
		else {
			if (_me.response.as_arguments.timer) t.response.as_arguments.timer = true;
			if (_me.response.as_arguments.call_back) t.response.as_arguments.call_back = true;
		}
		
		if (_me.response.index != null) t.response.index = _me.response.index;
		if (_me.response.call_back_at_end) t.response.call_back_at_end = true;
		if (_me.response.timer_at_end) t.response.timer_at_end = true;
		
		if (_add_call.length > 0) t.set_add_call(_add_call);
		if (_error_args.length > 0) t.set_error_args(_error_args);
		
		if (_end.length > 0) t.set_end(_end);
		
		if (_post_done.length > 0) t.post(_post_done[0], _post_done[1]);
		if (_get_done.length > 0) t.get(_get_done[0], _get_done[1]);
		
	}
	
	var make_callBack = function () {
		
		if (_end.length > 0)
			make_end();
		
		if (_add_call.length != 0)
			for (var i = 0; i < _add_call.length; i++) {
				
				if (_me.response.index == i) {
					
					var m_aa = (typeof(_me.response.as_arguments) == "boolean") ?
						_me.response.as_arguments : _me.response.as_arguments.call_back;
					
					if (!make_response(_add_call[i].fct, _add_call[i].arg, _me.response.call_back_at_end, m_aa)) {
						_me.clear();
						return;
					}
					
					continue;
					
				}
				
				if (_add_call[i].arg instanceof Array)
					_add_call[i].fct.apply(null, _add_call[i].arg);
				else
					_add_call[i].fct.apply(null);
				
			}
		
	}
	
	var make_response = function (f, a, r, aa) {
		
		var response_eval;
		
		try {
			// try to see if is an object
			response_eval = eval("(" + _http_request.responseText + ")");
			
		} catch (e) {
			
			try {
				// else make it a string!
				response_eval = String(_http_request.responseText);
				
			} catch (e) {
				
				alert("Error:\n" + e);
				return false;
				
			}
			
		}
		//alert(typeof(response_eval));
		switch (typeof(response_eval)) {
			
			case "number":
			var num = response_eval;
			response_eval = new Array();
			response_eval.push(num);
			break;
			
			case "function":
			case "boolean":
			response_eval = new Array(response_eval);
			break;
			
			case "object":
			if (aa) {
				if (!(response_eval instanceof Array))
					response_eval = new Array(response_eval);
			} else {
				response_eval = new Array(response_eval);
			}
			break;
			
			case "string":
			response_eval = (response_eval != _http_request.responseText) ? 
				new Array(String(_http_request.responseText)) : new Array(response_eval);
			break;
			
			case "undefined":
			response_eval = new Array(String(_http_request.responseText));
			break;
			
			default:
			alert("Data type not supported: " + typeof(response_eval));
			return false;
			break;
			
		}
		
		if (a != undefined)
			response_eval = (r) ? a.concat(response_eval) : response_eval.concat(a);
		
		f.apply(null, response_eval);
		
		return true;
		
	}
	// --------------
	
	var make_timer = function () {
		
		var m_aa = (typeof(_me.response.as_arguments) == "boolean") ?
			_me.response.as_arguments : _me.response.as_arguments.timer;
		
		if (!make_response(_timer[2], _timer[3], _me.response.timer_at_end, m_aa)) {
			alert("Could not make response!");
			return;
		}
		
		var in_timer = (_timer[1] instanceof Array) ? _timer[1] : new Array(_timer[1]);
		
		for (var i = 0; i < in_timer.length; i++) {
			
			if (in_timer[i] == _http_request.responseText) {
				
				if (_end.length > 0)
					make_end();
				
				if (_add_call.length > 0)
					make_callBack();
				
				return;
				
			}
		}
		
		setTimeout(timer_body, _timer[0]);
		
	}
	
	var make_wait = function () {
		
		for (var j = 0; j < _wait.length; j++) {
			
			try {
				eval(_wait[j]);
			} catch (e) {
				alert("Wait evalCode error:\n " + e);
			}
		}
		
		_wait = new Array;
		
	}
	
	var make_end = function () {
		
		for (var j = 0; j < _end.length; j++) {
			
			try {
				eval(_end[j]);
			} catch (e) {
				alert("End evalCode error:\n" + e);
			}
			
		}
		
		_end = new Array;
		
	}
	
	var get_custom_errors = function (val) {
		
		switch (val) {
			case 500:
			alert('There was a error on server side script! Error:\n\n' + String(_http_request.responseText));
			break;
			
			case 404:
			alert('Server side script not found!');
			break;
			
			default:
			alert('The request could not be completed!');
			break;
			
			// add more if u want
		}
		
		if (_end.length > 0)
			make_end();
		
		if (_me.error_redirect != "none")
			window.location = _me.error_redirect;
		
		if (_error_args.length > 0) {
			if (_error_args[1]) _error_args[0].apply(null, _error_args[1]);
			else _error_args[0].apply(null);
		}
		
	}
	
}


