
function XMLRPC(uri, method, params, callback, sender)
{
    var message = new XMLRPCMessage(method);
    message.addParameter(params);
    this.sender = sender;
	this.callback = callback;
    this.request(uri, message.xml());
}

XMLRPC.prototype.error = function (msg)
{
   this.loading(false, 'Error: ' + msg);
}

XMLRPC.prototype.abort = function()
{
   this.xmlhttp.abort();
   this.loading(false, "Cancelled");
}

XMLRPC.prototype.loading = function(y, msg)
{  
	if ($('rpcprogress') && $('rpcmessage')) {
	    $('rpcprogress').style.visibility = y ? 'visible' : 'hidden';
	    $('rpcmessage').innerHTML = msg; 
	    if (y) {
			var c = document.createElement('input');
			c.setAttribute('type', 'button');
			c.setAttribute('value', 'Cancel');
			c.owner = this;
			c.onclick = function () { this.cancel() }
			$('rpcmessage').appendChild(c);
		}
	}
}

XMLRPC.prototype.parse = function () {
	var dom = this.xmlhttp.responseXML;
	// alert('resp: ' . this.xmlhttp.responseText);
    var err = dom.getElementsByTagName('fault');
    if (err.length > 0)
    {
        var msg = 'Unknown';
        for (var i = 0; i < err[0].childNodes.length; i++)
        {
            if (typeof(err[0].childNodes[i].tagName) && err[0].childNodes[i].tagName == 'value')
            {
                msg = XMLRPC.parseNode(err[0].childNodes[i]).faultString;
            }
        }
        this.error(msg);
        return;
    }
        
    

    var params = dom.getElementsByTagName('param');
    var resp = new Array();
    for (var i = 0; i < params.length; i++)
    {
        resp[i] = XMLRPC.parseNode(params[i]);
    }
    this.callback(resp, this.sender);
}

XMLRPC.getNodeText = function(node)
{
    var txt = '';
    for (var i=0; i < node.childNodes.length; i++)
    {
    	txt += node.childNodes[i].nodeValue;
    }
    return txt;
}

XMLRPC.parseNode = function (node)
{
    if (node.tagName == 'param')
    {
        for (var i = 0; i < node.childNodes.length; i++)
        {
            if (typeof(node.childNodes[i].tagName) && node.childNodes[i].tagName == 'value')
                return this.parseNode(node.childNodes[i]);
        }
	//alert("Fell out of param loop");
    }
    else if (node.tagName == 'value')
    {
        var child;
        for (var i = 0; i < node.childNodes.length; i++)
        {
            child = node.childNodes[i];
            if (typeof(child.tagName))
            {
                switch (child.tagName)
                {
                    //scalars
					case 'i4':
                    case 'int':
                    case 'double':
 	                case 'boolean':
                   	return Number(this.getNodeText(child));

                    case 'string':
                    case 'datetime.iso8601':
                    case 'base64':
                        return this.getNodeText(child);
                        
                    case 'struct':
                        var str = new Object();
                        var name;
                        var member;
                        var mem;
			
                        for (var j = 0; j < child.childNodes.length; j++)
                        {
                            member = child.childNodes[j];
                            if ((!typeof(member.tagName)))
                                continue;
			    for (var k = 0; k < member.childNodes.length; k++)
			    {
                            	mem = member.childNodes[k];
				if (!typeof(mem.tagName)) 
					continue;
			    	if (mem.tagName == 'name')
                                {
					name = this.getNodeText(mem);
                            	}
				if (mem.tagName == 'value')
                                {
					str[name] = this.parseNode(mem);
				}
                           }
			}
                        return str;
                        
                    case 'array':
                        var arr = new Array();
                        var member;
			var $mem;
                        
                        for (var j = 0; j < child.childNodes.length; j++)
                        {
                            member = child.childNodes[j];
			    if (!typeof(member.tagName))
                                continue;
			    var l = 0;
			    for (var k = 0; k < member.childNodes.length; k++)
			    {
			    	mem = member.childNodes[k];
				if ((typeof(mem.tagName) == 'undefined') || mem.tagName != 'value')
				    continue;
                            	if (mem.tagName == 'value')
                             	   arr[l++] = this.parseNode(mem);
			    }
                        }
                        return arr;
                        
                    default:
                        //alert("Error:Invalid tag: " + child.tagName); 
                } //switch
            }//if
        }//for
    } 
}//function
    
                

XMLRPC.prototype.request = function(uri,postdata,whencomplete) {
  // Initialise an xmlhttp object
  this.xmlhttp=false;
  /*@cc_on @*/
  /*@if (@_jscript_version >= 5)
  // JScript gives us Conditional compilation, we can cope with old IE version
  // and security blocked creation of the objects.
  try {
  this.xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
  } catch (e) {
  try {
  this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
  } catch (E) {
  this.xmlhttp = false;
  }
  }
  @end @*/
  if (!this.xmlhttp)
      if(typeof XMLHttpRequest!='undefined') 
         this.xmlhttp = new XMLHttpRequest();
      else
         XMLRPC.error("Your browser does not support XMLHttpRequest, which is required for this site. Please upgrade to the latest version of <a href='http://www.mozilla.org/'>FireFox</a> or Internet Explorer.");
  this.loading(true, "Loading data...");
  this.xmlhttp.open("POST", uri, true);
  var x = this; //safari gets confused by 'this'
  this.xmlhttp.onreadystatechange = function() {
	    if (x.xmlhttp.readyState==4) {
	      if (x.xmlhttp.status == '200')
	      {
	         x.loading(false, "");
			x.parse();
	      }
	      else {
		x.error(x.xmlhttp.status + ' ' + x.xmlhttp.statusText);
	}
	    }
	}

  this.xmlhttp.setRequestHeader("Content-type", "text/xml");
  this.xmlhttp.send(postdata);
}


/*

xmlrpc.js beta version 1
Tool for creating XML-RPC formatted requests in JavaScript

Copyright 2001 Scott Andrew LePera
scott@scottandrew.com
http://www.scottandrew.com/xml-rpc

License: 
You are granted the right to use and/or redistribute this 
code only if this license and the copyright notice are included 
and you accept that no warranty of any kind is made or implied 
by the author.

*/

function XMLRPCMessage(methodname){
  this.method = methodname||"system.listMethods";
  this.params = [];
  return this;
}

XMLRPCMessage.prototype.setMethod = function(methodName){
  if (!methodName) return;
  this.method = methodName;
}

XMLRPCMessage.prototype.addParameter = function(data){
  if (arguments.length==0) return;
  this.params[this.params.length] = data;
}

XMLRPCMessage.prototype.xml = function(){

  var method = this.method;
  
  // assemble the XML message header
  var xml = "";
  
  xml += "<?xml version=\"1.0\"?>\n";
  xml += "<methodCall>\n";
  xml += "<methodName>" + method+ "</methodName>\n";
  xml += "<params>\n";
  
  // do individual parameters
  for (var i = 0; i < this.params.length; i++){
    var data = this.params[i];
    xml += "<param>\n";

    xml += "<value>" + XMLRPCMessage.getParamXML(XMLRPCMessage.dataTypeOf(data),data) + "</value>\n";
    
    xml += "</param>\n";
  }
  
  xml += "</params>\n";
  xml += "</methodCall>";
  
  return xml; // for now
}

XMLRPCMessage.dataTypeOf = function (o){
  // identifies the data type
  if (o == null)
     return false;
  var type = typeof(o);
  type = type.toLowerCase();
  switch(type){
    case "number":
      if (Math.round(o) == o) type = "i4";
      else type = "double";
      break;
    case "object":
      var con = o.constructor;
      if (con == Date) type = "date";
      else if (con == Array) type = "array";
      else type = "struct";
      break;
  }
  return type;
}

XMLRPCMessage.doValueXML = function(type,data){
  var xml = "<" + type + ">" + data + "</" + type + ">";
  return xml;
}

XMLRPCMessage.doBooleanXML = function(data){
  var value = (data==true)?1:0;
  var xml = "<boolean>" + value + "</boolean>";
  return xml;
}

XMLRPCMessage.doDateXML = function(data){
  var xml = "<dateTime.iso8601>";
  xml += dateToISO8601(data);
  xml += "</dateTime.iso8601>";
  return xml;
}

XMLRPCMessage.doArrayXML = function(data){
  var xml = "<array><data>\n";
  for (var i = 0; i < data.length; i++){
    xml += "<value>" + XMLRPCMessage.getParamXML(XMLRPCMessage.dataTypeOf(data[i]),data[i]) + "</value>\n";
  }
  xml += "</data></array>\n";
  return xml;
}

XMLRPCMessage.doStructXML = function(data){
  var xml = "<struct>\n";
  for (var i in data){
    xml += "<member>\n";
    xml += "<name>" + i + "</name>\n";
    xml += "<value>" + XMLRPCMessage.getParamXML(XMLRPCMessage.dataTypeOf(data[i]),data[i]) + "</value>\n";
    xml += "</member>\n";
  }
  xml += "</struct>\n";
  return xml;
}

XMLRPCMessage.getParamXML = function(type,data){
  var xml;
  switch (type){
    case "date":
      xml = XMLRPCMessage.doDateXML(data);
      break;
    case "array":
      xml = XMLRPCMessage.doArrayXML(data);
      break;
    case "struct":
      xml = XMLRPCMessage.doStructXML(data);
      break;
	  case "boolean":
      xml = XMLRPCMessage.doBooleanXML(data);
      break;
    default:
      xml = XMLRPCMessage.doValueXML(type,data);
      break;
  }
  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;
}

function getcookie(cookiename) {
    var cookiestring=""+document.cookie;
    var index1=cookiestring.indexOf(cookiename);
    if (index1==-1 || cookiename=="") return ""; 
    var index2=cookiestring.indexOf(';',index1);
    if (index2==-1) index2=cookiestring.length; 
    return unescape(cookiestring.substring(index1+cookiename.length+1,index2));
}
