// Build on 10/03/2010 5.02.11
/*****************************************************************************\

 AJWS
 
 @version:      1.1
 @author:       Matteo Casati
 @description:  AJAX JSON Web Service
 
\*****************************************************************************/

var AJWS = {};

AJWS.invoke = function(url, ns, method, parameters, async, callback)
{
    // build SOAP request
	var sr = 
				"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
				"<soap:Envelope " +
				"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
				"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" " +
				"xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
				"<soap:Body>" +
				"<" + method + " xmlns=\"" + ns + "\">" +
				parameters.toXml() +
				"</" + method + "></soap:Body></soap:Envelope>";
	// send request
	var xmlHttp = AJWS._getXmlHttp();
	xmlHttp.open("POST", url, async);
	var soapaction = ((ns.lastIndexOf("/") != ns.length - 1) ? ns + "/" : ns) + method;
	xmlHttp.setRequestHeader("SOAPAction", soapaction);
	xmlHttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
	if(async) 
	{
		xmlHttp.onreadystatechange = function() 
		{
			if(xmlHttp.readyState == 4)
				AJWS._onResponse(method, async, callback, xmlHttp);
		}
	}
	xmlHttp.send(sr);
	if (!async)
		return AJWS._onResponse(method, async, callback, xmlHttp);
}

AJWS._onResponse = function(method, async, callback, req) 
{	
	var nl = new Array();
	try     // trying to get node omitting any namespaces (latest versions of MSXML.XMLDocument)
	{		
		nl = req.responseXML.selectNodes(".//*[local-name()=\""+ method + "Result\"]");
	}
	catch (ex)  // old XML parser support
	{
	    nl = req.responseXML.getElementsByTagName(method + "Result");
	}
	var o = null;
	if (nl.length == 1 && nl[0].childNodes.length > 0)
	{   
	    try 
	    { 
	        // FireFox bug: se la risposta č molto lunga viene interpretata
	        // come pių childNodes; per risolvere concateno i singoli nodeValues
	        // utilizzando un array per ottenere performance pių elevate rispetto
	        // alla concatenazione di stringhe.
	        var json = new Array();
	        for (var i = 0; i < nl[0].childNodes.length; i++)
	            json[i] = nl[0].childNodes[i].nodeValue;
	        o = eval("(" + json.join("") + ")"); 
	    }
	    catch(ex)
	    {
	        o = null;
	    }
	}
	    
	if(callback)
		callback(o);
	if(!async)
		return o;
}


//
// private: xmlhttp factory
//
AJWS._getXmlHttp = function() 
{
	try
	{
		if(window.XMLHttpRequest) 
		{
			var req = new XMLHttpRequest();
			// some versions of Moz do not support the readyState property and the onreadystate event so we patch it!
			if(req.readyState == null) 
			{
				req.readyState = 1;
				req.addEventListener("load", 
									function() 
									{
										req.readyState = 4;
										if(typeof req.onreadystatechange == "function")
											req.onreadystatechange();
									},
									false);
			}
			return req;
		}
		if(window.ActiveXObject) 
			return new ActiveXObject(AJWS._getXmlHttpProgID());
	}
	catch (ex) {}
	throw new Error("Your browser does not support XMLHTTP objects");
}

AJWS._getXmlHttpProgID = function()
{
	if(AJWS._getXmlHttpProgID.progid)
		return AJWS._getXmlHttpProgID.progid;
	var progids = ["Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"];
	var o;
	for(var i = 0; i < progids.length; i++)
	{
		try
		{
			o = new ActiveXObject(progids[i]);
			return AJWS._getXmlHttpProgID.progid = progids[i];
		}
		catch (ex) {};
	}
	throw new Error("Could not find an installed XML parser");
}


/*****************************************************************************\

 AJWSParameters
 
 @version:      1.0
 @author:       Matteo Casati
 @description:  AJAX JSON Web Service Parameters
 
\*****************************************************************************/

function AJWSParameters()
{
	var _pl = new Array();
	this.add = function(name, value) 
	{
		_pl[name] = value; 
		return this; 
	}
	this.toXml = function()
	{
		var xml = "";
		for(var p in _pl)
		{
			switch(typeof(_pl[p])) 
			{
                case "string":
                case "number":
                case "boolean":
                case "object":
                    xml += "<" + p + ">" + AJWSParameters._serialize(_pl[p]) + "</" + p + ">";
                    break;
                default:
                    break;
            }
		}
		return xml;	
	}
}

AJWSParameters._serialize = function(o)
{
    var s = "";
    switch(typeof(o))
    {
        case "string":
            s += o.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"); break;
        case "number":
        case "boolean":
            s += o.toString(); break;
        case "object":
            // Date
            if(o.constructor.toString().indexOf("function Date()") > -1)
            {        
                var year = o.getFullYear().toString();
                var month = (o.getMonth() + 1).toString(); month = (month.length == 1) ? "0" + month : month;
                var date = o.getDate().toString(); date = (date.length == 1) ? "0" + date : date;
                var hours = o.getHours().toString(); hours = (hours.length == 1) ? "0" + hours : hours;
                var minutes = o.getMinutes().toString(); minutes = (minutes.length == 1) ? "0" + minutes : minutes;
                var seconds = o.getSeconds().toString(); seconds = (seconds.length == 1) ? "0" + seconds : seconds;
                var milliseconds = o.getMilliseconds().toString();
                var tzminutes = Math.abs(o.getTimezoneOffset());
                var tzhours = 0;
                while(tzminutes >= 60)
                {
                    tzhours++;
                    tzminutes -= 60;
                }
                tzminutes = (tzminutes.toString().length == 1) ? "0" + tzminutes.toString() : tzminutes.toString();
                tzhours = (tzhours.toString().length == 1) ? "0" + tzhours.toString() : tzhours.toString();
                var timezone = ((o.getTimezoneOffset() < 0) ? "+" : "-") + tzhours + ":" + tzminutes;
                s += year + "-" + month + "-" + date + "T" + hours + ":" + minutes + ":" + seconds + "." + milliseconds + timezone;
            }
            // Array
            else if(o.constructor.toString().indexOf("function Array()") > -1)
            {
                for(var p in o)
                {
                    if(!isNaN(p))   // linear array
                    {
                        var r = (/function\s+(\w*)\s*\(/i).exec(o[p].constructor.toString());
                        var type = (r != null && r.length && r.length > 1) ? r[1] : "";
                        switch(type)
                        {
                            case "":
                                type = typeof(o[p]);
                            case "String":
                                type = "string"; break;
                            case "Number":
                                type = "int"; break;
                            case "Boolean":
                                type = "bool"; break;
                            case "Date":
                                type = "DateTime"; break;
                        }
                        s += "<" + type + ">" + AJWSParameters._serialize(o[p]) + "</" + type + ">"
                    }
                    else    // associative array
                        s += "<" + p + ">" + AJWSParameters._serialize(o[p]) + "</" + p + ">"
                }
            }
            // Object or custom function
            else
                for(var p in o)
                    s += "<" + p + ">" + AJWSParameters._serialize(o[p]) + "</" + p + ">";
            break;
        default:
            break;
    }
    return s;
}