/**
*     RPC 1.0 
*     Written by Gary W. Yerby Jr. Dec 2005
*     Used to call defined remote procedures using AJAX (Assyncronous Javascript and XML)
*      
*     Current Supported Procedures: 
*	updateDocument
*	getDocument
*	getDocumentByKey
*	deleteDocument
*   dblookup
*
**/
//var req;
RPC = function(frm,procedure,url,unid , args,doGet) {
	this.args=args;
	this.form=frm;
	this.unid=unid;
	this.url = url;
	this.stateHandler = null; //document.createEventObject();
	this.procedure=procedure;
	if (typeof doGet =='undefined' || doGet == true) {
		this.requesttype="Get"
		this.XML="";
	} else {
		this.XML = this.buildXML(procedure,this.form);
		this.requesttype="Post"
	}
		 RPC.prototype.req=getReq();
		 
}

function getReq( ){
		var req;
		// branch for native XMLHttpRequest object
		if(window.XMLHttpRequest) {
			try {
				req = new XMLHttpRequest();
				
			} catch(e) {
				req = false;
			}
		// branch for IE/Windows ActiveX version
		} else if(window.ActiveXObject) {
			try {
				req = new ActiveXObject("Msxml2.XMLHTTP");
			} catch(e) {
				try {
					req = new ActiveXObject("Microsoft.XMLHTTP");
				} catch(e) {
				req = false;
				}
			}
		}
		return req;

} // end RPC function

// used to set the function that will be called when a successful transmission has taken place
RPC.prototype.setStateHandler = function(f) {
	RPC.prototype.stateHandler=f;
}

// used to build the RPC xml that will be passed to the server
RPC.prototype.buildXML = function(procedure,form) {
		var xmlstr='<?xml version="1.0" ?>\n'
		xmlstr+='<RPC>\n'
		xmlstr+='<procedure name="' + procedure + '">'
		xmlstr += this.procedureList[procedure](form, this.unid,this.args)
		xmlstr+='</procedure>\n'
		xmlstr+='</RPC>\n'
		return xmlstr
}	//end buildXML





/** createStateHandler creates a method object 

a method object fired by processReqChange.
syntax: 
stateHandler = RPC.createStateHandler(custommethod,args) 
Where: 
	custommethod - the method that will be called when the stateHandler is fired.
	args - th	e arguments passed to the method that is called when the state handler is fired.

properties:
	method - a custom function written by the programmer to handle 
	the needs of the application. this is obtained in the instanciation of the object
	args - the arguments that the custom function will need to use to fulfill its needs.


**/
RPC.prototype.createStateHandler = function(method,args){
	this.method = method;
	this.args = args;
	return this;
} // end createStateHandler


/** this method of RPC is fired off each time there is a change of state in the XMLHttpRequest. 
Each time there's a change in status, it calls a function called statusChange if that function exists.
If the ready state is 4 (Complete) it checkeds the response statuscode. if the code is 200 (okay) 
the stateHandler is given a property called req set to the XMLHttpRequest object. 

The State Handler's is an object used to call a custom function written to handle the needs of the application. 
the function is binded to the stateHandler property by doing the following:
first a stateHandler object is created by calling RPC.createStateHandler(method, args)
then the returned stageHandler object is assigned to the RPC by calling RPC setStateHandler(stateHandlerObj).

The custom function is used to do the workflow of the application. It can use the req property of itself (this.req) to obtain response information from the request object. and it can use the args property of itself to obtain any arguments passed to it when the state handler was fired.
**/
function processReqChange() {
	state= new Array("uninitialized","loading","loaded","interactive","Complete")
	status = state[RPC.prototype.req.readyState]
	// this function is called each time the status changes after sending request
	if (typeof statusChange !='undefined') {
		statusChange(status)
	} // end if
		if (RPC.prototype.req.readyState == 4) {
			// only if "OK"
			if (RPC.prototype.req.status == 200) {
			
				if (typeof RPC.prototype.stateHandler =='undefined') {
					alert("stateHandler method must be created the RPC.prototype.processReqCHange Method must be overridden: Contact a developer")
				} else {
					RPC.prototype.stateHandler.req = RPC.prototype.req
					RPC.prototype.stateHandler.method(RPC.prototype.stateHandler.args);
				}
				
			} else  {
				if (typeof statusChange != 'undefined') {
					statusChange("There was a problem retrieving the XML data:\n" +
					RPC.prototype.req.status +": " +RPC.prototype.req.statusText)
				}
			}
		}

	
	
} // end processReqChange

// Function that is called when a successful transmission is completed
// this can be overridden with RPC.prototype.setStateHandler.
RPC.prototype.stateHandler = null; //  document.createEventObject();

//Submits the form to the server
RPC.prototype.submitForm = function(async) {

	if (typeof async =='undefined' || async==null) {
	var async = true;
	} 
	if(this.req) {
		this.req.onreadystatechange = processReqChange
  		this.req.open("Post", this.url, async);
  		this.req.send(this.XML);
	} else {
	alert("no request")
	}
} // END submitform()


/** getItemValues  - utility function designed to take a collection of form items and return an array of their values.
the collection of form items are usually obtained by getting all items with the same element id or name. 
but this function is not particular about whether they have the same id or not. It just assumes that if there is more than one item in the collection that all values should be part of the returned array of values

Syntax:
array = getItemValues(itmcoll)
Where: 
itmcoll - a single element or a collection of elements obtained from a form by using "namedItem" function of the form element
Example:
This example grabs all of the items with the element id  of "chk_favorites" and gets their values returned in an array. there are 2 checkboxes with the element id of chk_favorites that the user has checked, so the result would be an array of length 2 that has the values of each one of the items checked.

var itmcoll = form.namedItem("chk_favorites")
var valueArray = getItemValues(itmcoll)
var message = "You selected the following:\n"
for(i =0;i< valueArray.length;i++) {
message += valueArray[i] + "\n"
}

Output:
You selected the following:
favorite value 1
faviorite value 2 
**/
function getItemValues(itmcoll) {
	var rtnArray = new Array();
	var val = null;
	var cnt;
	switch (itmcoll.type) {
		case 'text':
			rtnArray[0] = itmcoll.value;
			break;
		case 'textarea':
			rtnArray[0] = itmcoll.value;
			break;
		case 'select-one':
			rtnArray[0] =  itmcoll.options[itmcoll.options.selectedIndex].value
			break;
		case 'checkbox':
			if (itmcoll.checked) 	{
				 rtnArray[0] =  itmcoll.value
					cnt++;
			} // end if 
			break;
		case "select-multiple":
			cnt=0;
			for (i=0;i<itmcoll.options.length; i++) {
				if (itmcoll.options[i].selected) {
					rtnArray[cnt] = itmcoll.options[i].value;
					cnt++;
				} // end if 
			} // end for i
			break;
		case "hidden":
			rtnArray[0] = itmcoll.value
		default:
			 cnt=0;
			if (itmcoll.length !='undefined') {
				for (var i=0;i<itmcoll.length;i++) {
					if (itmcoll[i].type=="text") {
						 rtnArray[cnt]=itmcoll[i].value;
						 cnt++
					} else if (itmcoll[i].type =='textarea') {
						rtnArray[cnt]=itmcoll[i].value;
						cnt++
					} else if (itmcoll[i].type == 'select-one') {
						rtnArray[cnt] = Itmcoll[i].options[itmcoll[i].options.selectedIndex].value;
						cnt++;
					} else if (itmcoll[i].type == 'checkbox') {
						if (itmcoll[i].checked) {
							rtnArray[cnt]=itmcoll[i].value
							cnt++;
						} 
					} else if(itmcoll[i].type == 'hidden') {
						rtnArray[cnt]=itmcoll[i].value;
						cnt++;
					}
				} // end for i
			} // end if (itmcoll.length !='undefined')
			break;
	} // end switch
	return rtnArray;
} // end getItemValues function

RPC.prototype.procedureList = new Array();




/*******************************************************************************************
			 EXPANSION SECTION *********************************************************************************************/
/** The code below define the procedures that can be called and the code that builds the xml used by the procedures. **/

// Specific XML build for getDocument Procedure call
RPC.prototype.procedureList["getDocument"] = function(form,unid,args) {
	return "<item name='unid'><value>" + unid + "</value></item>"
} 


//getDocumentByKey 
// args[0] = viewname
// args[1 to n...] lookup keys.
RPC.prototype.procedureList["getDocumentByKey"] = function(form, unid, args) {
	var xmlstr = "<view>" + args[0] + "</view>";
	for (var i=1;i<args.length;i++) {
		xmlstr+="<keyword><![CDATA[" + args[i] + "]]></keyword>";
	}
	return xmlstr;
}


//deleteDocument
// Specific XML build for getDocument Procedure call
RPC.prototype.procedureList["deleteDocument"] = function(form,unid,args) {
	return "<item name='unid'><value>" + unid + "</value></item>"
} 


// function to build the xml for the createDocument procedure 
// if a unid is specified the document with the Unid will be retreived and updated. otherwise, a new document will be created.
RPC.prototype.procedureList["updateDocument"] = function(form,unid, args) {
	var itemArray = new Array();
	var id =""
	var cnt=0;
	var valArray;
	
	if (unid !=null) var returnval = '<item name="unid"><value>' + unid + '</value></item>'
	returnval += '<item name="Form"><value>' + form.name + '</value></item>'
	for (i = 0 ; i<form.elements.length;i++) {
		if (form.elements[i].type !="button") {
			if (form.elements[i].id !="") {
				id = form.elements[i].id;
			} else if (form.elements[i].name !="") {
				id = form.elements[i].name;
			} else {
				id = ""
			}
			if (id !="" ) {	
				if (typeof itemArray[form.elements[i].name] == 'undefined') {
					itemArray[id] = 0;
				}	else {
					itemArray[id] ++;
				}
			} // end if id=""
		} // end if button
	} //end for i
	var item;
	for (itm in itemArray ) {
		item=form.namedItem(itm)
		returnval+='<item name="' + itm + '">'
		valArray=getItemValues(item)
		for (v in valArray) {
			returnval+='<value><![CDATA[' + valArray[v]  + ']]></value>\n'
		} // end for v
		returnval+='</item>\n'
	} //end for (itm in itemArray)
	return returnval;
} 

/** dbLookup
* args[0] = dbfilepath
* args[1] = viewnname
* args[2] = fieldname  - db lookup returns the values from this field
* args[3 to n...] lookup keys.
* example return
*
*
**/
RPC.prototype.procedureList["dbLookup"] = function(form, unid, args) {
	var xmlstr = "";
	if (args[0] != null) {
		xmlstr += "<dbfilepath>" + args[0] + "</dbfilepath>";
	}
	xmlstr += "<view>" + args[1] + "</view>";
	xmlstr += "<fieldname>" + args[2] + "</fieldname>"
	for (var i=3;i<args.length;i++) {
		xmlstr+="<keyword><![CDATA[" + args[i] + "]]></keyword>";
	}
	
	return xmlstr;
}

RPC.prototype.procedureList["demoRPC"] = function(form, unid, args) {
	var xmlstr="";
	xmlstr+="<value><![CDATA[" + args + "]]></value>"
	return xmlstr;
}