/** 
 * @fileoverview http.js
 *  
 * Copyright (c) 2009 Qubes Inc. All rights reserved.
 *
 * @version 0.1 
 */
 
/*
Note that it has used XMLRPCMessage.js. The license is below.

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.


/** 
 * @fileoverview http.js
 *  
 * Copyright (c) 2008 Anthropedia International Co. Ltd. All rights reserved.
 *
 * @version 0.1 
 */
 
/*
Note that it has used XMLRPCMessage.js. The license is below.

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 getHost() {
        var host = window.location.href.split('//')[0];
	    host = host + "//" + window.location.href.split('//')[1].split('/')[0] + '/' + window.location.href.split('//')[1].split('/')[1];
	    return host;
}

function each(obj, iterator) {
	    var i;
	    if (obj) {
            if (obj instanceof Array) {
    	    	for (i = 0; i < obj.length; i++) if (iterator(obj[i], i) == false) break;
    		} else if (typeof obj.length != "undefined" && typeof obj.item != "undefined") {
    	    	for (i = 0; i < obj.length; i++) if (iterator(obj.item(i), i) == false) break;
    		} else {
    	    	for (i in obj) if (iterator(obj[i], i) == false) break;
    		}
        }
}
	
  /**
   * Submit a generic ajax request.
   * @param {string} url destination url
   * @param {array} options connection options  
   */
function ajax(url, options){
		p = options.postBody || '',				// Post Body
		m = options.method || 'GET',			// Method
		o = options.onComplete || null,			// OnComplete handler
		u = options.onUpdate || null,		    // OnUpdate handler
		c = options.close || null,              // Close
		a = options.async || 1;                 // Async
	
	// branch for native XMLHttpRequest object
	if (window.XMLHttpRequest)
	{
		t = new XMLHttpRequest();
		t.onreadystatechange = function()
				{
					// only if req shows "loaded"
					if (t && t.readyState == 4) {
						// only if "OK"
						if (t.status == 200)
						{			
							if (o) {
								o(t); 
							}					
							delete t.onreadystatechange;
				      t = p = m = o = u = a = options = url = null;
						}
					}
				};
		t.open("POST", url, true); //
		t.send(p);
	// branch for IE/Windows ActiveX version
	}
	else if (window.ActiveXObject)
	{
		t = new ActiveXObject("Microsoft.XMLHTTP");
		if (t)
		{
			t.onreadystatechange = function()
				{				
					// only if req shows "loaded"
					if (t.readyState == 4) {
						// only if "OK"
						if (t.status == 200)
						{
							if (o) o(t); 
							delete t.onreadystatechange;
				      t = p = m = o = u = a = options = url = null;
						}
					}
				};
			t.open(m, url, true);
			t.send(p);
		}
	} 
}

  /**
  * Parse the xmlrpc response contained in the string data and return an xmlrpcresp object.
  * @param method method name
  * @param params array of XML rpc parameters
  * @param async the request is sync/async        
  * @param callback callback method for async
  * @return xmlrpcresp (sync) or XMLHttpRequest(async)
  */
 function xmlrpc(method, params, async, callback) {

      var XMLRPCMessage = function (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 = "";
        var charset_encoding = "UTF-8";
        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
        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 (typeof(o.sort) == "function") 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 leadingZero = function (n){
          if (n.length==1) n = "0" + n;
          return n;
        }
        var year = data.getYear();
        var month = leadingZero(data.getMonth());
        var day = leadingZero(data.getDate());
        var time = leadingZero(data.getHours()) + ":" + leadingZero(data.getMinutes()) + ":" + leadingZero(data.getSeconds());
        var xml = "<dateTime.iso8601>";
        xml += year+month+day+"T"+time;
        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;
      };
      
      var xmlrpcresp = function (val, fcode, fstr, valtyp) {
          	this.val = 0;
          	this.valtyp = '';
          	this.faultCode = 0;
          	this.faultString = '';
          	this.payload = '';
          	this.raw_data = '';
          	if (fcode != undefined && fcode != 0) {
          		// error response
          		this.faultCode = fcode;
          		this.faultString = fstr;
          	} else {
          		// successful response
          		this.val = val;
          		if (valtyp == undefined) {
          			// user did not declare type of response value: try to guess it
          			/// @todo: verify class...
          			if (typeof(val) == 'object') this.valtyp = 'xmlrpcvals';
          			else if (typeof(val) == 'string') this.valtyp = 'xml';
          			else this.valtyp = 'jsvals';
          		} else {
          			// user declares type of resp value: believe him
          			this.valtyp = valtyp;
          		}
          	}
          },
          // Used to parse xml nodes: retrieve the single child if it is of correct type.
          getSingleChild = function(node, expectedType) {
          	var k, ret = null, child;
          	for(var i = 0, j = 0; i < node.childNodes.length; i++) {
          		child = node.childNodes.item(i);
          		if (child.nodeType == 1) { // ignore comments (8), character data (3), ...
          			for (k = 0; k < expectedType.length; k++) {
          				if (child.tagName == expectedType[k]) {
          					ret = child;
          					break;
          				}
          			}
          			if (ret === null) throw 'Found incorrect element inside ' + node.tagName + ': ' + child.tagName;
          			if (++j > 1) throw 'Found too many elements inside ' + node.tagName;
          		}
          	}
          	if (j == 0) throw 'Found no element inside ' + node.tagName;
          	return ret;
          },
          // Get node content as string
          getChildText = function (node) {
          	var ret = '';
          	for (var i = 0; i < node.childNodes.length; i++) {
          		child = node.childNodes.item(i);
          		if (child.nodeType == 3) { // ignore comments (8), character data (3), ...
          			ret += String(child.nodeValue)
          		} else if (child.nodeType == 1) { // ignore comments (8), character data (3), ...
          			throw 'elements found inside a ' + node.tagName;
          		}
          	}
          	return ret;
          },
          // Parse XML rpc value
          parseXmlrpcValue = function (node) {
          	var s = '', ret = null, child;
          	for(var i = 0, j = 0; i < node.childNodes.length; i++) {
          		child = node.childNodes.item(i);
                  if (child.nodeType == 3) {
                      s += new String(child.nodeValue);
                  } else if (child.nodeType == 1) { // ignore comments (8), character data (3), ...
          			switch (child.tagName) { 
          				case 'string':
          					ret = getChildText(child);
          					break;
          				case 'int':
          				case 'i4':
          					ret = getChildText(child);
          					ret = (ret.search(/^[+-]?[0123456789 \t]+$/) == -1) ? 'ERROR_NON_NUMERIC_FOUND' : parseInt(ret);
          					break;
          				case 'double':
          					ret = getChildText(child);
          					ret = (ret.search(/^[+-]?[eE0123456789 \t.]+$/) == -1) ? 'ERROR_NON_NUMERIC_FOUND': parseFloat(ret);
          					break;
          				case 'boolean':
          					ret = getChildText(child);
          					ret = (ret == '1' || ret.search(/^true$/i) != -1) ? true : false;
          					break;
          				case 'datetime.iso8601':
          					ret = getChildText(child);
          					break;
          				case 'array':
          					child = getSingleChild(child, ['data']);
          					ret = [];
          					for (var k =0; k < child.childNodes.length; k++) {
          						if (child.childNodes[k].nodeType == 1) {
          							if (child.childNodes[k].tagName != 'value') throw 'invalid element found inside array: '+ child.childNodes[k].tagName;
          							ret[ret.length] = parseXmlrpcValue(child.childNodes[k]);
          						}
          					}
          					break;
          				case 'struct':
          					ret = {};
          					var membername, member, memberval;
          					for (var k =0; k < child.childNodes.length; k++) {
          						if (child.childNodes[k].nodeType == 1) {
          							if (child.childNodes[k].tagName != 'member') throw 'invalid element found inside struct: '+ child.childNodes[k].tagName;
          							member = child.childNodes[k];
          							membername = ''; // struct members without a name will get named '' instead of rejected...
          							memberval = undefined;
          							for (var l =0; l < member.childNodes.length; ++l) {
          								if (member.childNodes[l].nodeType == 1) {
          									switch(member.childNodes[l].tagName) {
          										case 'name':
          											membername = getChildText(member.childNodes[l]);
          											break;
          										case 'value':
          											memberval = parseXmlrpcValue(member.childNodes[l]);
          											break;
          										default:
          											throw 'invalid element found inside struct: '+ member.childNodes[l].tagName;
          									}
          								}
          							}
          							if (memberval === undefined) throw 'invalid member found inside struct: missing value';
          							ret[membername] = memberval;
          						}
          					}
          					break;
          				default:
          					throw 'Found incorrect element inside \'value\' :'+ child.tagName;
          			}
          			if (++j > 1) throw 'Found too many elements inside ' + node.tagName;
          		} 
          	}
          	return (j == 0) ? s : ret;
          },
          // Parse XML rpc value
          parseXMLRPCValue = function (obj) {
          	try
          	{
          		var node = obj.documentElement;
          		if (node == null) throw 'No documentElement found.';
          		if (node.tagName != 'methodResponse') throw 'missing top level xmlrpc element';
      			node = getSingleChild(node, ['params', 'fault']);
      			if (node.tagName == 'fault') {
      				node = getSingleChild(node, ['value']);
      				var value = parseXmlrpcValue(node);
      				var r = new xmlrpcresp(0, value['faultCode'], value['faultString']);
      			}
      			else
      			{
      				node = getSingleChild(node, ['param']);
      				node = getSingleChild(node, ['value']);
      				var value = parseXmlrpcValue(node);
      				var r = new xmlrpcresp(value, 0, '');
      			}
      			return r;
          	}
          	catch (e)
          	{
          		var r = new xmlrpcresp(0, 2, 'Invalid return payload: (' + e.toString() + ')');
          		return r;
          	}
          };
          
      var msg = new XMLRPCMessage(method);
      each(params, function(param) { msg.addParameter(param); });
      if (!async) async = 2;
      if (async == 1 && !callback) callback = function(resp) { 
          if (resp.faultCode) alert("Error: " + resp.faultString); };
      var options = { 
          async : async, 
          postBody : msg.xml(),
          onComplete : function(t) {

              var s = t.status;
              // The following cases are wininet.dll error codes that may be encountered.
              if (s == 12002 || // Server timeout
				s == 12029 || // 12029 to 12031 correspond to dropped connections.
				s == 12030 ||
				s == 12031 ||
				s == 12152 || // Connection closed by server.
				s == 404   || // Network Disconnected
				s == 13030) s = 0; // See above comments for variable status.

			if (!t.responseXML.documentElement) {
			    alert(t.responseText);
			}
              if (callback) {
              	callback(s ? parseXMLRPCValue(t.responseXML) : 
                { faultCode : t.status, faultString: "login.notconnected" });}
          }
      };
      var t = ajax(getHost() + "/xmlrpc.php", options);
      return (async == 1) ? t : parseXMLRPCValue(t.responseXML);
  }
  

 



