/**
jsRequest v.2.0
Author: Carlos Andres Caballero
v.2.0 2007
NEW:
* Now jsRequest is an Object not a series of functions
* jsRequestCommand has functions to set values to the call through setData to keep things cleaner
* new jsRequestForm call to use in onsubmit event on forms

XML HTTP Request Object
This objects work as an event based object , ie: you register handler for server events
that means if you you have something line "list_users" you register a handler for that
event like jsRequest.addHandler("list_users",handler), and later when you want to trigger it you call
jsRequest.call("list_users") and is executed.
*/

var jsRequest={
	__handle:null,
	__currentCommand:null,
	__commands:new Array(),
	__call_stack:new Array(),
	__debug:null,
	clone:function(){
		var tmp={};
		for(var p in this){
			tmp[p]=this[p];
		}
		return tmp;
	},
	init:function(){
		try{
			if (window.XMLHttpRequest){
				this.__handle=new XMLHttpRequest();
			}else if (window.ActiveXObject){
				this.__handle=new ActiveXObject("Microsoft.XMLHTTP");
			}
			this.__commands=new Array();
			this.__call_stack=new Array();
			this._anonimous=new jsRequestCommand('xml');
			this.addHandler('anonimous_call',this._anonimous);			
		}catch(e){
			alert('Cannot Create Request Object');
		}
	},
	debugEnable:function(enable){
		if(enable){
			this.__debug=_('_jsreq_debug_');
			if(!this.__debug){
				this.__debug=_(document.createElement('div'));
				this.__debug.setStyle({
				'width':'200px',
				'height':'300px',
				'overflow':'auto',
				'backgroundColor':'#000000',
				'border':'1px solid #00ff00',
				'color':'#00dd00',
				'padding':'3px',
				'position':'absolute',
				'top':'10px',
				'left':'10px',
				'align':'left'});
								
				document.body.appendChild(this.__debug);
			}
			this.__debug.innerHTML='jsRequest Debug Window<br />';
		}else{
			this.__debug=null;
		}
	},
	debugMsg:function(str){
		if(this.__debug){
			this.__debug.innerHTML+=str+"<br>";
		}
	},
	getHandle:function(){
		return this.__handle;
	},
	_handler:function(){
		var _handle=this.getHandle();		
		if (_handle.readyState==4 || _handle.readyState=="complete"){
			if(this.__currentCommand){
				this.debugMsg('Current Command:'+this.__currentCommand.handle);
				if(_handle.status==200){
					this.__currentCommand.onEnd(this.__currentCommand.handle=='xml'?_handle.responseXML:_handle.responseText);
				}else{
					this.__currentCommand.onFailed(_handle.statusText);
				}
				window.clearTimeout(this.__currentCommand.idTime);
				this.__currentCommand=null;
			}
			var next=this.__call_stack.pop();
			if(next){
				this.debugMsg('Next command:'+next);
				this.call(next);
			}
		}
	},
	commandExists:function(ev){
		return this.__commands[ev]?true:false;
	},
	addHandler:function(ev,command){
		if(jsLib.isObject(command) && command.onBegin!=null){
			jsRequest.debugMsg('Add Handler:'+ev);
			command._name=ev;
			this.__commands[ev]=command;
		}else{
			alert('Invalid handler for "'+ev+'" event');
		}
	},
	abortRequest:function(){
		var _handle=this.getHandle();
		if((_handle.readyState == 1)||(_handle.readyState == 2)||(_handle.readyState == 3)){			
			_handle.abort();
			jsRequest.debugMsg('Request Aborted');
			if(this.__currentCommand)	jsRequest.__currentCommand.onFailed('Timeout reached');
		}
		if(this.__currentCommand) window.clearTimeout(jsRequest.__currentCommand.idTime);
		this.__currentCommand=null;
	},
	call:function(ev){
		jsRequest.debugMsg('Calling:'+ev);
		if(!this.__handle) this.init();
		if(this.__commands[ev]){
			this.__commands[ev].onBegin();
			if (this.__handle.readyState==4 || this.__handle.readyState==0){
				jsRequest.debugMsg('Command found');
				this.__currentCommand=this.__commands[ev];
				this.__currentCommand.idTime=window.setTimeout(function(){jsRequest.abortRequest();},this.__currentCommand.timeout);
				this.__handle.open(this.__currentCommand.method,this.__currentCommand.popState(),true);
				this.__currentCommand._onCall(this.__handle);
				var ref=this;
				this.__handle.onreadystatechange=function(){
					ref._handler();
				}
				this.__handle.send(this.__currentCommand._getRequestData());
			}else{
				if(this.__currentCommand){
					if(this.__currentCommand._name==ev){
						this.__call_stack.push(ev);
					}else{
						this.__commands[ev].onFailed('bussy');
					}
				}
			}
		}else{
			alert('Command "'+ev+'" not found');
		}
	},
	quickCall:function(type,url,data,method,ready_action,onfail){
		this._anonimous.handle=type;
		for(var k in data){
			this._anonimous.setData(k,data[k]);
		}		
		if(method) this._anonimous.method=method;
		this._anonimous.onEnd=ready_action;

		if(onfail){
			this._anonimous.onFailed=onfail;
		}
		this._anonimous.setUrl(url);
		this.call('anonimous_call');
	},
	Command:jsRequestCommand	
}
jsRequest.init();

function jsRequestCommand(handles){
	this.handle=handles;
	this.url='';
	this.idTime=0;
	this.timeout=5000;
	this.method='GET';
	this.data=new Array();
	this.state_stack=new Array();
	this.onBegin=function(){};
	this.onEnd=function(data){}
	this.onFailed=function(msg){	alert('Error:'+msg);	}
	this.pushState=function(){	this.state_stack.push(this.url);	}
	this.setUrl=function(url){
		if(this.url){
			this.pushState();
			this.url=url;
		}else{
			this.url=url;
		}
	}
	this.popState=function(){
		this.method=this.method.toUpperCase();
		var res=this.url;
		if(this.state_stack.length>0){			
			res=this.state_stack.shift();
		}
		res=this.method=='GET'?res+'?'+this._getDataUrl():res;
		res=res.replace("??",'?');	
//		this.url=res;
		jsRequest.debugMsg('Current Command State:'+res);
		return res;
	}
	this.clearData=function(){
		delete this.data;
		this.data=new Array();
	}
	this.setData=function(key,value){
		this.data[key]=value;
	}
	this.getData=function(){
		this.method=this.method.toUpperCase();
		return this.method=='GET'?null:this.data;
	}
	this._getDataUrl=function(){		
		var ret='';		
		for(k in this.data){
			if(!jsLib.isFunction(this.data[k])){
				ret+=k+"="+escape(this.data[k])+"&";
			}
		}		
		return ret;
	}
	this._getRequestData=function(){
		this.method=this.method.toUpperCase();
		return this.method=='GET'?null:this._getDataUrl();
	}
	this._onCall=function(_xobj){
		this.method=this.method.toUpperCase();		
		jsRequest.debugMsg('Command Method:'+this.method);
		if(this.method=='POST'){
			 var dlen=this._getDataUrl();
			_xobj.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
			_xobj.setRequestHeader("Content-length", dlen.length);
			_xobj.setRequestHeader("Connection", "close");
		}
	}
}

function jsRequestForm(frm,postCall,preCall){
	var next=true;	
	if(preCall){
		next=preCall(frm);
	}
	if(!next)	return false;
	if(!frm._jsrequest_handler){
		frm._jsrequest_handler=new jsRequestCommand();
		frm.postCall=postCall;
		frm._jsrequest_handler.form=frm;
		frm._jsrequest_handler.url=frm.action?frm.action:document.URL;
		frm._jsrequest_handler.method=frm.method;
		frm._jsrequest_handler.onEnd=function(data){
			if(this.form.postCall){
				this.form.postCall(data,this.form);
			}
		}
		frm._jsrequest_handler.onBegin=function(){
			this.clearData();			
			for(var i in this.form.elements){
				var elem=this.form.elements[i];
				if (jsLib.isObject(elem)){
					if (elem.name) {
						this.setData(elem.name, elem.value);
					}
				}
			}
		}
		jsRequest.addHandler(frm.name,frm._jsrequest_handler);
	}
	jsRequest.call(frm.name);
	return false;
}