// complemento con xAjax
/*
lib Mensaje enhacedXMLRPC
Parecido con el XMLRPC, diferencia, agrega en el tag <param> el nombre del parametro.
y tiene un  tipo de dato mas, DOMElement, que es un elemento xml, con sus hijos y todos
*/


/*
objeto servidor
*/

function xRPCServer(url){
	this.url=url;
	this.faultError='';
	this.responseText='';
	this.requests=[];

	var me=this;

	this.allComplete=function(){
		for (var i=0;i<this.requests.length;i++){
			if (this.requests[i].completed==false){
				return false;
			}
		}
		return true;
	}

	this.send=function(msg,asyncFunction){
		this.requests[this.requests.length]=new xHttpRequest();
		var x=this.requests[this.requests.length-1];
		x.applyTo=me;

		if (xDef(asyncFunction)){
			x.async=true;
			x.oncomplete=asyncFunction;
		}
		else{
			x.async=false;
			x.oncomplete=new Function();
		}
		try{
			x.call(this.url,'POST',msg.toXML());
		}
		catch(e){
			alert(e.description);
		}
		return this.requests[this.requests.length-1];
	}

	this.receive=function(request){
		if (xDef(request))
		var x=request;
		else
		var x=this.requests[this.requests.length-1]

		this.faultError=null;
		this.responseText=x.responseText;
		//alert(x.responseText)
		var resp=false;
		try{
			resp=receive(x.responseXML);
		}
		catch(e){
			if (e.description)
			this.faultError=e.description;
			else
			this.faultError=e.message;

			resp=false;
		}
		return resp;
	}
}

/*
funciones de soporte
*/
function newXMLDOM(message){
	var obj=null;
	if(window.ActiveXObject){
		obj = new ActiveXObject("microsoft.XMLDOM");
		obj.loadXML(message)
	}else if(window.DOMParser){
		obj = new DOMParser();
		obj = obj.parseFromString(message, "text/xml");
	}
	return obj;
}

// convierto xmldom a una string
function XMLString(xmldom){
	if (typeof xmldom.xml!='undefined')
	return xmldom.xml;
	else
	return (new XMLSerializer()).serializeToString(xmldom);
}

/*
intenta interpretar el xml pasado
si es un mensage devuelve un objeto xRPCMessage
de otro modo, retorna un objeto xmldom;
o false
*/

function receive(responseXML){
	//getIncoming message

	var dom = responseXML;

	if(dom){

		if (dom.childNodes[0].nodeName=='xml')
		var firstChild=dom.childNodes[1];
		else
		var firstChild=dom.childNodes[0];

		//alert(XMLString(dom))

		if(firstChild.nodeName!='methodResponse')
		return false;

		var rpcErr, main;
		//Check for XMLRPC Errors
		rpcErr = dom.getElementsByTagName("fault");
		if(rpcErr.length > 0){
			//alert(XMLString(rpcErr[0].firstChild))
			rpcErr = toObject(rpcErr[0].firstChild);
			throw new Error(rpcErr.faultString);
		}

		main=false;

		//handle method result
		main = dom.getElementsByTagName("params");

		if(main.length == 0){
			throw new Error("Malformed RPC message");
			return false;
		}

		main=main[0];

		var msg= new xRPCMessage();

		for (var i=0;i<main.childNodes.length;i++){
			if (main.childNodes[i].nodeType==1){

				msg.add(main.childNodes[i].getAttribute('name'), toObject(getNode(main.childNodes[i],[0])));
			}
		}

		return msg;
	}
	else{
		return false;
	}
}
function toObject(data){
	var ret, i;
	switch(data.tagName){
		case "DOMElement":
		return normalizeWhitespace(data.firstChild);
		break;
		case "string":
		if (data.firstChild) {
			var s= new String(data.firstChild.nodeValue);
			return unescape(s);
		}
		else
		return "";
		break;
		case "int":
		case "i4":
		case "double":
		return (data.firstChild) ? new Number(data.firstChild.nodeValue) : 0;
		break;
		case "dateTime.iso8601":
		/*
		Have to read the spec to be able to completely
		parse all the possibilities in iso8601
		07-17-1998 14:08:55
		19980717T14:08:55
		*/

		var sn = (isIE) ? "-" : "/";

		if(/^(\d{4})(\d{2})(\d{2})T(\d{2}):(\d{2}):(\d{2})/.test(data.firstChild.nodeValue)){
		return new Date(RegExp.$2 + sn + RegExp.$3 + sn +
		RegExp.$1 + " " + RegExp.$4 + ":" +
		RegExp.$5 + ":" + RegExp.$6);
	}
	else{
		return new Date();
	}

	break;
	case "array":
	data = getNode(data, [0]);

	if(data && data.tagName == "data"){
		ret = new Array();

		var i = 0;
		while(child = getNode(data, [i++])){
			ret.push(toObject(child));
		}

		return ret;
	}
	else{
		throw new Error("Malformed XMLRPC Message1");
		return false;
	}
	break;
	case "struct":
	ret = {};

	var i = 0;
	while(child = getNode(data, [i++])){
		if(child.tagName == "member"){
			ret[getNode(child, [0]).firstChild.nodeValue] = toObject(getNode(child, [1]));
		}
		else{
			e = new Error("Malformed XMLRPC Message2");
		}
	}

	return ret;
	break;
	case "boolean":
	return Boolean(isNaN(parseInt(data.firstChild.nodeValue)) ? (data.firstChild.nodeValue == "true") : parseInt(data.firstChild.nodeValue))

	break;
	case "base64":
	return decodeBase64(data.firstChild.nodeValue);
	break;
	case "value":
	child = getNode(data, [0]);
	return (!child) ? ((data.firstChild) ? new String(data.firstChild.nodeValue) : "") : toObject(child);

	break;
	default:
	throw new Error("Malformed XMLRPC Message: " + data.tagName);
	break;
}
}
function getNode (data, tree){
	var nc = 0;//nodeCount
	//node = 1
	if(data != null){
		for(i=0;i<data.childNodes.length;i++){
			if(data.childNodes[i].nodeType == 1){
				if(nc == tree[0]){
					data = data.childNodes[i];
					if(tree.length > 1){
						tree.shift();
						data = getNode(data, tree);
					}
					return data;
				}
				nc++
			}
		}
	}
}
/*** Decode Base64 ******
* Original Idea & Code by thomas@saltstorm.net
* from Soya.Encode.Base64 [http://soya.saltstorm.net]
**/
function decodeBase64 (sEncoded){
	// Input must be dividable with 4.
	if(!sEncoded || (sEncoded.length % 4) > 0)
	return sEncoded;

	/* Use NN's built-in base64 decoder if available.
	This procedure is horribly slow running under NN4,
	so the NN built-in equivalent comes in very handy. :) */

	else if(typeof(atob) != 'undefined')
	return atob(sEncoded);

	var nBits, i, sDecoded = '';
	var base64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
	sEncoded = sEncoded.replace(/\W|=/g, '');

	for(i=0; i < sEncoded.length; i += 4){
		nBits =
		(base64.indexOf(sEncoded.charAt(i))   & 0xff) << 18 |
		(base64.indexOf(sEncoded.charAt(i+1)) & 0xff) << 12 |
		(base64.indexOf(sEncoded.charAt(i+2)) & 0xff) <<  6 |
		base64.indexOf(sEncoded.charAt(i+3)) & 0xff;
		sDecoded += String.fromCharCode(
		(nBits & 0xff0000) >> 16, (nBits & 0xff00) >> 8, nBits & 0xff);
	}

	// not sure if the following statement behaves as supposed under
	// all circumstances, but tests up til now says it does.

	return sDecoded.substring(0, sDecoded.length -
	((sEncoded.charCodeAt(i - 2) == 61) ? 2 :
	(sEncoded.charCodeAt(i - 1) == 61 ? 1 : 0)));
}

/*
clase del mensage
copadaso
*/

function xRPCMessage(){
	this.method=null;

	// params
	var names=[];
	this.params=[];
	// setea el nombre del metodo a enviar
	this.setMethod=function(methodName){
		this.method=methodName;
	}
	// vacia el mensage
	this.clear=function(){
		for(var i=0; i< this.params.length;i++){
			delete this.params[i];
		}
		this.params=[];
	}
	// agrega un parametro
	this.add=function(name,value){
		if (!xDef(name))
		name=null;
		names.push(name);
		this.params.push(value);
	}

	this.param=function(paramName,setValue){

		var indice=null;
		if (typeof paramName=='integer'){
			indice=paramName;
		}
		else{
			for (var i=0;i<this.params.length;i++){
				if (names[i]==paramName){
					indice=i;
					break;
				}
			}
		}
		if (indice==null){
			return false;
		}
		if (typeof setValue!='undefined'){
			this.params[indice]=setValue;
		}
		return this.params[indice];
	}
	// retorna un xml con el contenido del mensage
	this.toXML=function(){
		var method = this.method;
		var xml = "";

		xml += '<?xml version="1.0" encoding="iso-8859-1"?>' + "\n";
		xml += "<methodCall>\n";
		if (method!=null)
		xml += "<methodName>" + method+ "</methodName>\n";
		xml += "<params>\n";

		for(var i=0; i< this.params.length;i++){
			xml+= "<param name=\""+names[i]+"\">"+"\n";
			xml += "<value>\n";
			xml+=toXML(this.params[i])+"\n";
			xml += "</value>\n";
			xml+= "</param>"+"\n";
		}

		xml += "</params>\n";
		xml += "</methodCall>";

		return xml;
	}
	// agrega un formulario con sus valores
	this.appendForm=function(form){
		var item=null;
		var value=null;
		var i=0;
		var e=0;
		for (e=0; e<form.elements.length;e++){
			item=form.elements[e];
			switch (item.tagName.toUpperCase()){
				case 'INPUT':
				switch(item.type.toLowerCase()){
					case 'radio':
					case 'checkbox':
					if (item.checked!=true)
					break;
					case 'button':
					case 'text':
					case 'password':
					if (item.name!='')
					this.add(item.name,item.value);
					break;
				}
				break;
				case 'TEXTAREA':
				case 'BUTTON':
				if (item.name!='')
				this.add(item.name,item.value);
				break;
				case 'SELECT':
				if (item.type=='select-multiple'){
					value=new Array()
					for (i=0;i<item.options.length;i++){
						if (item.options[i].selected==true)
						value[value.length]=item.options[i].value;
					}
					if (item.name!='')
					this.add(item.name,value);
				}
				else{
					if (item.selectedIndex!=-1){
						if (item.name!='')
						this.add(item.name,item.options[item.selectedIndex].value);
					}
				}
				break;
			}
		}
	}
}



Object.prototype.toXMLRPC = function(){
	var wo = this.valueOf();

	if(wo.toXMLRPC == this.toXMLRPC){
		retstr = "<struct>";
		for(prop in this){
			if(typeof wo[prop] != "function"){
				retstr += "<member><name>" + prop + "</name><value>" + toXML(wo[prop]) + "</value></member>";
			}
		}
		retstr += "</struct>";

		return retstr;
	}
	else{
		return wo.toXMLRPC();
	}
}

String.prototype.toXMLRPC = function(){
	//<![CDATA[***your text here***]]>
	return "<string><![CDATA[" + escape(this) + "]]></string>";
}

Number.prototype.toXMLRPC = function(){
	if(this == parseInt(this)){
		return "<int>" + this + "</int>";
	}
	else if(this == parseFloat(this)){
		return "<double>" + this + "</double>";
	}
	else{
		return false.toXMLRPC();
	}
}

Boolean.prototype.toXMLRPC = function(){
	if(this==true) return "<boolean>1</boolean>";
	else return "<boolean>0</boolean>";
}

Date.prototype.toXMLRPC = function(){
	var xml = "<dateTime.iso8601>";
	xml += dateToISO8601(this);
	xml += "</dateTime.iso8601>";
	return xml;
}

function dateToISO8601(date){
	// wow I hate working with the Date object

	var year = new String(date.getYear());
	var month = leadingZero(new String(date.getMonth()));
	var day = leadingZero(new String(date.getDate()));
	var time = leadingZero(new String(date.getHours())) + ":" + leadingZero(new String(date.getMinutes())) + ":" + leadingZero(new String(date.getSeconds()));

	var converted = year+month+day+"T"+time;
	return converted;
}

function leadingZero(n){
	// pads a single number with a leading zero. Heh.
	if (n.length==1) n = "0" + n;
	return n;
}

Array.prototype.toXMLRPC = function(){
	var retstr = "<array><data>";
	for(var i=0;i<this.length;i++){
		retstr += "<value>" + toXML(this[i]) + "</value>";
	}
	return retstr + "</data></array>";
}

function toXML(obj){
	if (typeof obj.nodeName != "undefined" & typeof obj.nodeType != "undefined" ){
		if (obj.nodeType== 1){
			return "<DOMElement>"+XMLString(obj)+"</DOMElement>";
		}
		else
		return false.toXMLRPC();
	}
	if(typeof obj == "function"){
		new Error("Cannot Parse functions");
		throw e;
	}else if(obj == null || obj == undefined || (typeof obj == "number" && !isFinite(obj)))
	return false.toXMLRPC();
	else
	return obj.toXMLRPC();
}

