/**
* For working with AJAX and urlencoded form data/form elements.
*
* @param action The action URL to submit form data to.
*/
function FormAjax(action) {
	this.action = action;
	this.req = false;
	/**
	* Set this to GET or POST depending on how you want to send 
	* the request. 
	*/
	this.method = 'POST';
	this.send = null;	
	this.elements = new Array();	
	this.sendHeaders = function(){
		if(this.method == 'POST'){
			this.req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
			this.req.setRequestHeader("Content-Length",this.send.length);
		}
	}
}
/**
* Class for disecting the common responses. It is important
* not to hang on to these between Ajax requests. This throws
* exceptions: FormAjax.Response.Error
*
* @param r The request (from ajax's handleResponse(r) )
* @param expected (Optional) the expected response type. 
*/
FormAjax.Response = function(r,expected){
	this.request = r;
	/**
	* The underlying Document element for the response.
	*/
	this.document = r.responseXML;
	if(! this.document){
		throw new FormAjax.Response.Error("Invalid response");	
	}
	var top = this.document.documentElement;
	if(top.tagName != 'response'){
		throw new FormAjax.Response.Error("Unknown Element");	
	}
	this.type = top.getAttribute('type');
	if(this.type == 'error'){
		msg = this.collectMessage(top);
		throw new FormAjax.Response.Error(msg.value);	
	}
}
/**
* Thrown on an error, or, when a <result type="error"> element is in 
* the result.
*/
FormAjax.Response.Error = function(txt){
	this.text = txt;
	this.toString = function(){ return(this.text); }
}


// Encode form, return URL.
FormAjax.prototype.encodeForm = function(){
	this.send = null;
	var buf = '';
	if(this.elements.length > 0){
		for(var t = 0; t < this.elements.length; ++t){
			buf += encodeURIComponent(this.elements[t].name);
			buf += '=';
			buf += encodeURIComponent(this.elements[t].value);
			buf += '&';
		}
		// Strip last '&'
		buf = buf.substr(0,(buf.length - 1));
	}
	if(this.method == 'POST'){
		this.send = buf;
		return(this.action);
	}else{
		if(this.action.indexOf("?") == -1){
			return(this.action + "?" + buf);
		}else{
			return(this.action + "&" + buf);
		}
	}	
}

FormAjax.prototype.toString = function(){
	return("FormAjax[" + this.method + " " + this.action + "]");	
}

/** 
* Submit AJAX request.
*/
FormAjax.prototype.submit = function(){
	// Normal browsers.
	var req = false;
	if (window.XMLHttpRequest) {
		this.req = new XMLHttpRequest();
	}else if(window.ActiveXObject){
		this.req = new ActiveXObject("Microsoft.XMLHTTP");
	}
	if(! this.req){
		var er = new Object();
		er.toString = function(){ return("Can't create XMLHttpRequest"); }
		throw er;
	}
	// We don't have access to 'this' within the function.
	var ajax = this;
	this.req.onreadystatechange = function(){
		if(ajax.req.readyState == 4){
			if(ajax.req.status == 200){
				ajax.handleResponse(ajax.req);
			}else{
				ajax.handleProblem(ajax.req);
			}
			ajax.req = null;
		}
	}
	var url = this.encodeForm();
	this.req.open(this.method,url,true);
	this.sendHeaders();
	this.req.send(this.send);
}
/**
* Add a FORM element. NOTE that this has some issues with
* dynamic forms..
* 
* @param el Object supporting a value and name property.
*/
FormAjax.prototype.addElement = function(el){
	l = this.elements.length;
	this.elements[l] = el;
}

/**
* Add a variable. 
*/
FormAjax.prototype.addVariable = function(name,value){
	var obj = new Object();
	obj.name = name;
	obj.value = value;
	this.addElement(obj);
}
/**
* Assign this to function for processing.	
*
* @param r the XMLHttpRequest object.
*/
FormAjax.prototype.handleResponse = function(r){
	alert(r.responseText);
}

/**
* Assign this for handling a problem.
*
* @param r XMLHttpRequest object.
*/
FormAjax.prototype.handleProblem = function(r){
	alert("Problem: " + r.status + " " + r.statusText);
}


/**
* Collect text from an element.
* @param el Element.
*/
FormAjax.Response.prototype.elementText = function(el){
	var nl = el.childNodes;
	var buf = '';
	for(var i = 0; i < nl.length; ++i){
		if(nl[i].nodeType == 3){
			buf += nl[i].nodeValue
		}else{
			buf += this.elementText(nl[i]); // recursion.
		}
	}
	return(buf);
}

/**
* Collect from <map><entry key=""></entry></map> values.
*/
FormAjax.Response.prototype.collectMap = function(el){
	if(! el){
		el = this.document.documentElement;
	}
	var rv = new Array();
	var elist = el.getElementsByTagName('entry');
	for(var ix = 0; ix < elist.length; ++ix){
		var key = elist[ix].getAttribute('key');
		var txt = this.elementText(elist[ix]).replace(/^\s+/g,'');
		rv[key] = txt.replace(/\s+$/g,'');
	}
	return(rv);
}
FormAjax.Response.prototype.collectListMap = function(el){
	var list = new Object();
	list.attr = new Array();
	list.entries = new Array();

	if(! el){
		el = this.document.documentElement;
	}
	// Collect the attributes of the list tag, this is typically
	// pagination info.
	var ll = el.getElementsByTagName('list');
	var elist = ll[0];
	if(! elist){
		throw new FormAjax.Response.Error("Missing list tag");
	}
	var la = elist.attributes;
	for(var i = 0; i < la.length; ++i){
		node = la.item(i);
		var n = node.name;
		list.attr[n] = node.value;
	}

	var mlist = el.getElementsByTagName('map');
	for(var i = 0; i < mlist.length; ++i){
		list.entries.push(this.collectMap(mlist[i]));
	}
	return(list);
}

/**
* Returns an object with a 'value' and an
* attr property. value is the text of the message,
* attr is an array of attributes.
*/
FormAjax.Response.prototype.collectMessage = function(el){
	if(! el){
		el = this.document.documentElement;
	}
	var msg = new Object();
	msg.attr = new Array();
	var ml = el.getElementsByTagName('message');
	for(var ix = 0; ix < ml.length; ++ix){
		msg.value = this.elementText(ml[ix]);
		nm = ml[ix].attributes;
		for(var i = 0; i < nm.length; ++i){
			node = nm.item(i);
			var n = node.name;
			msg.attr[n] = node.value;
		}
	}
	return(msg);
}

FormAjax.Response.prototype.toString = function (){
	return("FormAjax.Response[\n" + this.request.responseText +"\n]");
}
/**
* The TYPE of response. (map/error/message/Others??/)
*/
FormAjax.Response.prototype.type = 'unknown';

/**
* The underlying Document object. (or null)
*/
FormAjax.Response.prototype.document = null;

