//*** ajax_request.js
//		Copyright 2008 by Mark Pemburn
//******************************************
// Required: Function prototype to overload methods:
/*
Function.prototype.overload = function(name, constructors, method) {
	// get overloaded name based on arguments
	// becomes something like _originalNameStringArray
	function _getName(args, name, instance) {
		var call = '_' + name, reg = /function ([^(]+)/;
		for(var obj,r,i=0; i<args.length; i++) {
			obj = instance ? args[i].constructor : args[i];
			call += ((r = reg.exec(obj.toString())) ? r[1] : 'Object');
		}
		return call;
	}

	// create the wrapper
	if(!this.prototype[name].overloaded) {
		var base = this.prototype[name];
		this.prototype[name] = function() {
			var call = this[_getName(arguments, name, true)] || base;
			call.apply(this, arguments);
		}
		this.prototype[name].overloaded = true;
	}
	
	// store prototype 
	this.prototype[_getName(constructors, name)] = method;
}

// Peter Nederlof (http://blogger.xs4all.nl/peterned/)
*/

//*** AjaxRequest creates AJAX request and handles returned content
function AjaxRequest() {
	//*** Uber important alias for the class object.  Used to reference methods and properties from within methods and event handlers
	var me = this;
	
	//*** AjaxRequest public properties
	this.xmlhttp = null;
	this.async = true;
	this.URL;
	this.URLParams = null;
	this.literal
	this.formAction = "POST";
	this.statusID = "status";
	this.statusField;
	this.silent = false;
	this.overwrite = true;
	this.currentStatus = "";
	this.responseAction = "fields";
	this.responseText;
	this.errorMsg = "";
	this.pending = true;
	this.awake = false;
	this.ignoreError = false;
	this.callBack = new Array();
	this.done = false;
	this.uid = new Date().getTime();

	//*** Instantiate AJAX object
	if (window.XMLHttpRequest) {
		this.xmlhttp = new XMLHttpRequest();
	} else {
		this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
	}
	
	//*** AjaxRequest private methods
	
	function post_listener () {
		var success = false;
		var response;
		this.errorMsg = "";
		me.currentStatus = "Finished";
		
		switch (me.xmlhttp.readyState) {
			case 1 :
				me.show_status("Processing . . .");
				break;
			case 3 :
				me.pending = true;
				break;
		
			case 4 :
				me.response_ready();
				break;
		}
		return success;
	}
	
	function get_listener () {
		if (me.xmlhttp.readyState == 1) {
			if (statusField != "")
				me.show_status("Processing . . .");
		}
		if (me.xmlhttp.readyState == 4) {
			me.show_status(xmlhttp.responseText);
		}
	}
		
	function clear_options(optionList) {
	   //*** Always clear an option list from the last entry to the first
	   for (x = optionList.length; x >= 0; x--) {
	      optionList[x] = null;
	   }
	   optionList.length = 0;
	}
	
	function add_to_optionList(optionList, optionValue, optionText) {
	   //*** Add option to the bottom of the list
		if (optionText == "null" || optionText == null) {
			optionText = "";
			optionValue = "";
		}
	   optionList[optionList.length] = new Option(optionText, optionValue);
	}

	//*** AjaxRequest public methods
		
	AjaxRequest.prototype.ignore_error = function () {
		me.ignoreError = true;
	}
	
	AjaxRequest.prototype.is_pending = function () {
		return me.pending;
	}

	//*** request serves as base method for several overloaded methods	
	AjaxRequest.prototype.request = function() {
	}
	
	AjaxRequest.overload ('request', [String], function (url) {
		if (this.xmlhttp != null) {
			this.send_request(url);
		}
	});
	
	AjaxRequest.overload ('request', [String,String], function (url, action) {
		if (this.xmlhttp != null) {
			this.responseAction = action;
			this.send_request(url);
		}
	});
	
	AjaxRequest.overload ('request', [String,Array], function (url, fieldArray) {
		if (this.xmlhttp != null) {
			this.url_params(fieldArray);
			this.send_request(url);
		}
	});
	
	AjaxRequest.overload ('request', [String,String,Array], function (url, action, fieldArray) {
		if (this.xmlhttp != null) {
			this.responseAction = action;
			this.url_params(fieldArray);
			this.send_request(url);
		}
	});
	
	AjaxRequest.overload ('request', [String,String,String], function (url, action, urlString) {
		if (this.xmlhttp != null) {
			this.responseAction = action;
			this.URLParams = urlString;
			this.send_request(url);
		}
	});
	
	AjaxRequest.prototype.show_status = function (statusText) {
		if (this.statusID != "")
		this.statusField = document.getElementById(this.statusID);
		if (me.statusField != null && typeof(me.statusField) != "undefined" && !this.silent) {
			me.statusField.innerHTML = statusText;
		}
	}
	
	AjaxRequest.prototype.sleep = function(millis) {
		var date = new Date();
		var curDate = null;
		
		do { curDate = new Date(); }
		while(curDate-date < millis);
	}
	
	AjaxRequest.prototype.url_params = function (args) {
		var argParts;
		var argc;
		var param;
		var elemID;
		var formControl;
		var controlValue;
		var amp = "";
		this.URLParams = "";
		
		//*** Test whether the "args" argument is an array.  If so, use it
		if (args instanceof Array)	
			argc = args;
		else
			argc = arguments; //*** Use built-in argument array
		
		if (argc.length > 0) {
			for (var i=0; i<argc.length; i++) {
				argParts = argc[i].split("=");
				param = argParts[0];
				elemID = argParts[1];
				if (this.literal) {
					controlValue = elemID;
				} else {
					formControl = document.getElementById(elemID);
					switch (formControl.type) {
						case "checkbox" :
							controlValue = (formControl.checked) ? "TRUE" : "FALSE";
							break;
						default :
							controlValue = escape(formControl.value);
							break;
					}
				}
				try {
					this.URLParams += amp + param + "=" + controlValue;
					amp = "&";
				} catch (e) {
				}
			}
		}
	} //*** url_params

	AjaxRequest.prototype.test_uid = function () {
		var matchUID = this.responseText.match("UID\=[0-9]+ ");
		var testUID = "";
		var remainder = null;
		if (matchUID != null) {
			//*** Remove everything before the UID.
			this.responseText = this.responseText.substring(matchUID.index, this.responseText.length - matchUID.index);
			//*** Replace the UID in the responseText
			remainder = this.responseText.replace(matchUID[0],"");
			//*** Isolate the numeric UID value for test
			testUID = matchUID[0].replace("UID=","");
			//*** Set the responseText equal to the remainder.  Remove 'carriage return' character if present
			this.responseText = remainder.replace("\r","");
		}
		return (testUID == this.uid); 
	}
	
	AjaxRequest.prototype.send_request = function (url) {
		if (url != "") {
			this.done = false;
			this.xmlhttp.open(this.formAction,url,this.async);
			this.xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

			if (this.async) {
				if (this.formAction == "POST")
					this.xmlhttp.onreadystatechange = post_listener;
				else
					this.xmlhttp.onreadystatechange = get_listener;
			}
			
			this.xmlhttp.send(this.URLParams + "&UID=" + this.uid);
		}
		if (!this.async) {
			this.responseText = this.xmlhttp.responseText;
			this.response_ready();
		}
	}
			
	AjaxRequest.prototype.response_ready = function () {
		//*** me.xmlhttp.responseText is the content of document requested
		me.responseText = me.xmlhttp.responseText;
		if (me.responseText.indexOf("UID=") != -1) {
			if (!me.test_uid())
				return false;
		}
		switch (me.responseAction) {
			case "fields" :
				success = me.process_response(me.responseText);
				break;
			case "fields-no-overwrite" :
				me.overwrite = false;
				success = me.process_response(me.responseText);
				break;
			case "display-response" :
				if (me.responseText != null) {
					me.currentStatus = me.responseText;
					success = true;
				}
				break;
			case "action-only" :
				success = true;
				break;
		}
		if (success) {
			if (me.ignoreError)
				me.errorMsg = "";
			if (me.errorMsg == "") {
				me.show_status(me.currentStatus);
			} else  {
				success = false;
				me.show_status("Finished with error(s): " + me.errorMsg);
			}
		
			if (success) {
				//*** After delay, clear the status field
				setTimeout(function() {	me.show_status(""); }, 6000);
				
				//*** If the callback array contains at least one function call, call it
				if (me.callBack.length > 0) {
					for (var i=0; i<me.callBack.length; i++) {
						eval(me.callBack[i]);
					}
					//*** Shift the call off the front of the array
					//me.callBack.shift();
				}
			}
			
		} else {
			me.show_status("No values returned.");
		}
		me.done = true;
		me.pending = false;
	}

	//*** populate_select fills a select box with value returned in JSON text
	AjaxRequest.prototype.process_response = function (response) {
		//alert(response);
		var json;
		var cType;
		var thisSelect;
		if (response == "" || response == null || response.length == 0)
			return false;
			
		//*** Eval JSON formatted text into a Javascript array
		try {
			json = eval('(' + response + ')');
			if (json instanceof Array) {
				for (var i=0; i<json.length; i++) {
					cType = json[i]['type'];
					switch (cType) {
						case "text" :
						case "hidden" :
							this.process_field(json[i]);
							break;
						case "select" :
							this.process_select(json[i]);
							break;
					}
				}
			}
			return true;
		} catch (e) {
			//*** Error in JSON translation
			//alert(e);
		}
	}
		
	AjaxRequest.prototype.process_field = function (ctrlData) {
		var fieldName = ctrlData['name'];
		var fieldValue = (ctrlData['value'] != null) ? ctrlData['value'].toString() : "";
		var thisCtrl = document.getElementById(fieldName);
		
		if (thisCtrl != null && typeof(thisCtrl) != "undefined") {
			me.show_status("Writing . . . " + fieldName);
			//*** If the 'don't overwrite' flag is set
			if (!this.overwrite && thisCtrl.value !== "") {
				fieldValue = thisCtrl.value;
			}
			thisCtrl.value = fieldValue.replace(/\\/g, "");
		} else {
			this.errorMsg += "Field " + fieldName + " not found.";
		}
	}
	
	AjaxRequest.prototype.process_select = function (ctrlData) {
		var fieldName = ctrlData['name'];
		var textArray = ctrlData['text'];
		var valueArray = ctrlData['values'];
		var selectedItem = ctrlData['selected'];
		var thisSelect = document.getElementById(fieldName);
		if (thisSelect != null && typeof(thisSelect) != "undefined") {
			//*** Remove existing options
			if (this.overwrite)
				clear_options(thisSelect);
			//*** Populate list with returned values
			for (i=0; i<valueArray.length; i++) {
				add_to_optionList(thisSelect,valueArray[i],textArray[i]);
			}
			//*** Pre-select an item if specified
			if (selectedItem != "")
				thisSelect.value = selectedItem;
		} else {
			this.errorMsg += "Field " + fieldName + " not found.";
		}
	}
	
	//*** on_complete defines user function to be called on completion 
	AjaxRequest.prototype.on_complete = function (callBack) {
		this.callBack[this.callBack.length] = callBack;
	}
	
} //*** AjaxRequest

