/**
 * response class to save ajax reponse
 */
function Response() {
	var responseStatusCode;
	var responseStatusText;
	var responseText;
	var responseXML;
}

/**
 * class to save a form object
 */
function FormObject(sname, svalue, bencoded){
	this.name = sname;
	this.value = svalue;
	this.encoded = bencoded;
}

/**
 * @constructor construct an Ajax request object
 * @param {String} formAction, action name of the name, opitional
 */
function Ajax(formAction) {
	this.ARGUMENTSEPARATOR = "&";
	this.QUERYSTRINGSEPARATOR = "?";

	this.isIE = true;
	this.xmlhttp = null;
	this.action = formAction;
	
	this.method = "POST";
	this.URLString = "";
	this.encodeURIString = true;
	this.formParams = new Array();
	this.response = new Response();
	this.failed = false;
	this.debug = false;
	this.async = true;
	this.uniqueId = null;
	this.uniqueIdentifier = null;

	this.retryString = null;
	this.retry = 0;
	this.MAXRETRY = 3;

	this.reset();
	this.setup();
}

/**
 * reset the Ajax request data to initial state
 * @private
 */
Ajax.prototype.resetData = function() {
		this.method = "POST";
		this.URLString = "";
		this.encodeURIString = true;
		this.formParams = new Array();
		this.response = new Response();
		this.failed = false;
};

/**
 * reset the Ajax request function to initial state
 * @private
 */
Ajax.prototype.resetFunctions = function() {
  		this.onLoading = function() { };
  		this.onLoaded = function() { };
  		this.onInteractive = function() { };
  		this.onCompletion = function() { };
  		this.onError = function() { };
		this.onFail = function() { };
};

/**
 * reset Ajax request to initial state
 */
Ajax.prototype.reset = function() {
	this.resetFunctions();
	this.resetData();
};

/**
 * processURLString, process the query string, save into the form object
 * encode the string if neccessary
 * @private
 */
Ajax.prototype.processURLString = function(string, encode) {
	encoded = encodeURIComponent(this.ARGUMENTSEPARATOR);
	regexp = new RegExp(this.ARGUMENTSEPARATOR + "|" + encoded);
	varArray = string.split(regexp);
	for (i = 0; i < varArray.length; i++){
		urlVars = varArray[i].split("=");
		if (encode){
			this.setVar(urlVars[0], this.encodeURI(urlVars[1]));
		} else {
			this.setVar(urlVars[0], urlVars[1]);
		}
	}
}

/**
 * separate the query string into form variable
 * @param {String} query String of url
 */
Ajax.prototype.generateURIArgu = function(urlstring) {
	if (this.encodeURIString && this.URLString.length) {
		this.processURLString(this.URLString, true);
	}

	var argumentString = '';
	for (var i=0;i<this.formParams.length; i++) {
		var fo = this.formParams[i];
		if (!fo.encoded && this.encodeURIString) {
			encoded = this.encodeURI(fo.value);
		}
		if (this.formParams.length - 1 != i )
			argumentString += fo.name + "=" + encoded + this.ARGUMENTSEPARATOR;
		else
			argumentString += fo.name + "=" + encoded;
	}
	if (urlstring){
		if (this.URLString.length) {
			this.URLString += this.ARGUMENTSEPARATOR + urlstring;
		} else {
			this.URLString = urlstring;
		}
		this.URLString += this.ARGUMENTSEPARATOR + argumentString;
	} else {
		this.URLString += argumentString;
	}
}

/*

*/

/**
 * fire ajax request
 * @param {String} urlstring, opinional parameter, request uri
 */
Ajax.prototype.runAJAX = function(urlstring) {
	if (this.failed) {
		this.onFail();
	} else {
		this.retryString = urlstring;
		this.generateURIArgu(urlstring);
		if (this.xmlhttp) {
			var me = this;
			this.xmlhttp.onreadystatechange = function() {
				me.processOnReadyStatechange();
			};
if (this.debug)
	alert ("Form method = " + this.method);
			if (this.method.toUpperCase()  == "GET") {
				totalurlstring = this.action + this.QUERYSTRINGSEPARATOR + this.URLString;
if (this.debug)
	alert ("urlstring = " + totalurlstring);
				this.xmlhttp.open(this.method, totalurlstring, this.async);
				if (this.isIE)
					this.xmlhttp.send();
				else
					this.xmlhttp.send(null);
			} else {
				this.xmlhttp.open(this.method, this.action, this.async);
				try {
					this.xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
				} catch (e) { }
if (this.debug)
	alert ("this.URLString = " + this.URLString);
				this.xmlhttp.send(this.URLString);
			}
			
		}
	}
};

/**
 * call back function of ajax request
 * @private
 */
Ajax.prototype.processOnReadyStatechange = function(){
	switch (this.xmlhttp.readyState) {
	case 1:
		this.onLoading();
		break;
	case 2:

		this.onLoaded();
		break;
	case 3:
		this.onInteractive();
		break;
	case 4:
		this.response.responseText = this.xmlhttp.responseText;
		this.response.responseXML = this.xmlhttp.responseXML;
		this.response.responseStatusCode = this.xmlhttp.status;
		this.response.responseStatusText = this.xmlhttp.statusText;

		if (this.response.responseStatusCode == "200") {
			try{
			   this.onCompletion();
			}catch (e){
				this.retry++;
				if (this.retry < this.MAXRETRY){
					this.runAJAX(this.retryString);
				}else{
					this.onError();
					//alert ("Um... error in processOnReadyStatechange");
					//alert ("Error Message: " + e.message);
				}
			}finally{
		      if (this.debug){
		   	     alert ("Response Text :\n" + this.response.responseText);
		      }
			}
		} else {
			this.onError();
		}

		this.URLString = "";
		break;
	}
}

/**
 * create xmlhttp request
 * @private
 */
Ajax.prototype.setup = function(){

	try {
		this.xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
	} catch (e1) {
		try {
			this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
		} catch (e2) {
			this.xmlhttp = null;
		}
	}

	if (! this.xmlhttp) {
		if (typeof XMLHttpRequest != "undefined") {
			this.xmlhttp = new XMLHttpRequest();
			this.isIE = false;
		} else {
			this.failed = true;
		}
	}		
};

/**
 * save the variable into form object
 * @param
 */
Ajax.prototype.setVar = function(name, value){
	var fo = new FormObject(name, value, false);
	this.formParams.push(fo);
};

/**
 * encodeURI if necessary
 * @param
 */
Ajax.prototype.encodeURI = function(value){
	return encodeURIComponent(value);
};

/**
 * recursively eval the form and save into the corrospond object
 * @param {DOMFormObject} dom form object
 */
Ajax.prototype.evalForm = function(form) {
if (form.action != "")
  this.action = form.action;
if (form.method != "")  
  this.method = form.method;
if (this.debug){
	alert ("this.action = " + form.action);
	alert ("this.method = " + form.method);
}
  if (form.length > 0)
    this.recEval(form)
//  	this.recEval(form[0], form[form.length - 1]);
};

/**
 * recursively process the form data.
 * @private
 */
Ajax.prototype.recEval = function(form){
  for (var i =0; i< form.length; i++){
    this.processLeaf(form[i]);
  }
}
/*
Ajax.prototype.recEval = function(firstNode, lastNode){
  do{
   if (this.checkNodeisNull(firstNode)){
      firstNode = lastNode;
   }
   if (!this.checkNodeisNull(firstNode)){
      if (firstNode.childNodes.length > 0){
		this.recEval(firstNode.childNodes[0],
			firstNode.childNodes[firstNode.childNodes.length - 1]);
      }else{
      	this.processLeaf(firstNode);
      }
    }
    if (firstNode == lastNode) break;
  }while(firstNode= firstNode.nextSibling);
} 
*/
 
/**
 * check whether the node is null or not
 * @return true if node is null 
 * @private
 */
Ajax.prototype.checkNodeisNull = function(node){
 return (node == null || typeof node == "undefined" || 
  node.tagName == "undefined");
}

/**
 * Save the leaf data into the array
 * @private
 */
Ajax.prototype.processLeaf = function (childNode){
	var tagName = childNode.tagName;
	if (typeof tagName == "string"){
	 	 tagName = tagName.toUpperCase();
	     if (tagName == "INPUT") {
	     	var inputType = childNode.type;
	     	if (typeof tagName == "string"){
	     		inputType = inputType.toUpperCase();
		        if (inputType == "TEXT" || inputType == "HIDDEN") {
		        	this.setVar(childNode.name, childNode.value);
		        }
		        else if (inputType == "CHECKBOX" || inputType == "RADIO") {
		           if (childNode.checked) {
		              this.setVar(childNode.name, childNode.value);
		           } 
		        }
				else if (inputType == "SUMBIT") {
		           	this.setVar(childNode.name, childNode.value);
		        }
	     	}
	     }   
	     else if (tagName == "SELECT") {
	        var sel = childNode.options;
	
	        for (var j=0; j<sel.length; j++){
	        	if (sel[j].selected){
		        	this.setVar(childNode.name, sel[j].value);
	        	}
	        }
	     }
	     else if ( tagName == "TEXTAREA"){
	     	this.setVar(childNode.name, childNode.value);
	     }
	 }
}

