Click here to Skip to main content
15,888,984 members
Articles / Web Development / HTML
Article

JavaScript SOAP Client

Rate me:
Please Sign up or sign in to vote.
4.77/5 (44 votes)
24 Jan 20064 min read 1.1M   17.4K   140   174
Using AJAX to call a Web Service.

Introduction

A lot of talking about AJAX is taking place here and there; AJAX is the acronym of "Asynchronous JavaScript and XML", a technology based on XMLHttpRequest, which is now supported by all main browsers. The basic idea is quite simple - and not actually a breakthrough - but it allows updating a page following a server request, without reloading the entire set of data. Some examples can be found on GMail or Google Suggest. For additional information about AJAX, you can see Wikipedia.

In this article, we propose a solution based on AJAX that has a great advantage with respect to those commonly found in Internet: calls are made to the Web Services.

This permits:

  1. On the server side, we only have to expose a Web Service with the required methods (instead of generating dynamic pages incorporating data that are based on a custom syntax or on a generic XML).
  2. On the client side, we use the WSDL (Web Service Description Language) to automatically generate a JavaScript proxy class so as to allow using the Web Service return types - that is similar to what Visual Studio does when a Web Reference is added to the solution.

The following diagram shows the SOAP Client workflow for asynchronous calls:

Image 1

The Client invokes the "SOAPClient.invoke" method using a JavaScript function and specifies the following:

  • Web Service URL (please note that many browsers do not allow cross-domain calls for security reasons).
  • Web method name.
  • Web method parameter values.
  • Call mode (async = true, sync = false).
  • Callback method invoked upon response reception (optional for sync calls).

The "SOAPClient.invoke" method executes the following operations (numbers refer to the previous diagram):

  1. It gets the WSDL and caches the description for future requests.
  2. It prepares and sends a SOAP (v. 1.1) request to the server (invoking method and parameter values).
  3. It processes the server reply using the WSDL so as to build the corresponding JavaScript objects to be returned.
  4. If the call mode is async, the callback method is invoked, otherwise it returns the corresponding object.

Using the code

After having exposed our idea about consuming a Web Service via JavaScript, we only have to analyze the code.

Let's start with the class for the definition of the parameters to be passed to the Web method: "SOAPClientParameters":

JavaScript
function SOAPClientParameters()
{
    var _pl = new Array();
    this.add = function(name, value) 
    {
        _pl[name] = value; 
        return this; 
    }
    this.toXml = function()
    {
        var xml = "";
        for(var p in _pl)
        {
            if(typeof(_pl[p]) != "function")
                xml += "<" + p + ">" + 
                       _pl[p].toString().replace(/&/g, 
                         "&").replace(/</g, 
                         "<").replace(/>/g, 
                         ">

The code simply consists of an internal dictionary (associative array) with the parameter name (key) and the related value; the "add" method allows appending new parameters, while the "toXml" method provides XML serialization for SOAP requests (see "SOAPClient._sendSoapRequest").

Let's define the "SOAPClient" class, which can only contain static methods in order to allow async calls, and the only "public" method within this class: "SOAPClient.invoke".

Note: since JavaScript does not foresee access modifiers - such as "public", "private", "protected", etc. - we'll use the "_" prefix to indicate private methods.

JavaScript
function SOAPClient() {}
SOAPClient.invoke = function(url, method, 
                      parameters, async, callback)
{
    if(async)
        SOAPClient._loadWsdl(url, method, 
                    parameters, async, callback);
    else
        return SOAPClient._loadWsdl(url, method, 
                   parameters, async, callback);
}

The "SOAPClient.invoke" method interface is described above; our implementation checks whether the call is async (call result will be passed to the callback method) or sync (call result will be directly returned). The call to the Web Service begins by invoking the "SOAPClient._loadWsdl" method:

JavaScript
SOAPClient._loadWsdl = function(url, method, parameters, async, callback)
{
    // load from cache?
    var wsdl = SOAPClient_cacheWsdl[url];
    if(wsdl + "" != "" && wsdl + "" != "undefined")
        return SOAPClient._sendSoapRequest(url, method, 
                    parameters, async, callback, wsdl);
    // get wsdl
    var xmlHttp = SOAPClient._getXmlHttp();
    xmlHttp.open("GET", url + "?wsdl", async);
    if(async) 
    {
        xmlHttp.onreadystatechange = function() 
        {
            if(xmlHttp.readyState == 4)
                SOAPClient._onLoadWsdl(url, method, 
                     parameters, async, callback, xmlHttp);
        }
    }
    xmlHttp.send(null);
    if (!async)
        return SOAPClient._onLoadWsdl(url, method, parameters, 
                                    async, callback, xmlHttp);
}

The method searches the cache for the same WSDL in order to avoid repetitive calls:

JavaScript
SOAPClient_cacheWsdl = new Array();

If the WSDL is not found in the cache (it's the first call in the current context), it is requested from the server using an XMLHttpRequest, according to the required mode (sync or not). Once an answer is obtained from the server, the "SOAPClient._onLoadWsdl" method is invoked:

JavaScript
SOAPClient._onLoadWsdl = function(url, method, 
             parameters, async, callback, req)
{
    var wsdl = req.responseXML;
    SOAPClient_cacheWsdl[url] = wsdl;
    return SOAPClient._sendSoapRequest(url, method, 
                       parameters, async, callback, wsdl);
}

A WSDL copy is stored into the cache and then the "SOAPClient._sendSoapRequest" method is executed:

JavaScript
SOAPClient._sendSoapRequest = function(url, method, 
                 parameters, async, callback, wsdl)
{
    var ns = (wsdl.documentElement.attributes["targetNamespace"] + 
              "" == "undefined") ? 
              wsdl.documentElement.attributes.getNamedItem(
              "targetNamespace").nodeValue : 
              wsdl.documentElement.attributes["targetNamespace"].value;
    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>";
    var xmlHttp = SOAPClient._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)
                SOAPClient._onSendSoapRequest(method, 
                     async, callback, wsdl, xmlHttp);
        }
    }
    xmlHttp.send(sr);
    if (!async)
        return SOAPClient._onSendSoapRequest(method, 
                    async, callback, wsdl, xmlHttp);
}

The service namespace is taken out of the WSDL (using different XPath queries for Internet Explorer and Mozilla / FireFox), then a SOAP v. 1.1 request is created and submitted. The "SOAPClient._onSendSoapRequest" method will be invoked upon receiving the server response:

JavaScript
SOAPClient._onSendSoapRequest = function(method, 
                     async, callback, wsdl, req)
{
    var o = null;
    var nd = SOAPClient._getElementsByTagName(
             req.responseXML, method + "Result");
    if(nd.length == 0)
    {
        if(req.responseXML.getElementsByTagName(
                     "faultcode").length > 0)
            throw new Error(500, 
              req.responseXML.getElementsByTagName(
              "faultstring")[0].childNodes[0].nodeValue);
    }
    else
        o = SOAPClient._soapresult2object(nd[0], wsdl);
    if(callback)
        callback(o, req.responseXML);
    if(!async)
        return o;        
}

The server response is processed looking for faults: if found, an error is raised. Instead, if a correct result is obtained, a recursive function will generate the return type by using the service description:

JavaScript
SOAPClient._soapresult2object = function(node, wsdl)
{
    return SOAPClient._node2object(node, wsdl);
}

SOAPClient._node2object = function(node, wsdl)
{
    // null node
    if(node == null)
        return null;
    // text node
    if(node.nodeType == 3 || node.nodeType == 4)
        return SOAPClient._extractValue(node, wsdl);
    // leaf node
    if (node.childNodes.length == 1 && 
       (node.childNodes[0].nodeType == 3 || 
        node.childNodes[0].nodeType == 4))
          return SOAPClient._node2object(node.childNodes[0], wsdl);
    var isarray = SOAPClient._getTypeFromWsdl(node.nodeName, 
                  wsdl).toLowerCase().indexOf("arrayof") != -1;
    // object node
    if(!isarray)
    {
        var obj = null;
        if(node.hasChildNodes())
            obj = new Object();
        for(var i = 0; i < node.childNodes.length; i++)
        {
            var p = SOAPClient._node2object(node.childNodes[i], wsdl);
            obj[node.childNodes[i].nodeName] = p;
        }
        return obj;
    }
    // list node
    else
    {
        // create node ref
        var l = new Array();
        for(var i = 0; i < node.childNodes.length; i++)
            l[l.length] = 
              SOAPClient._node2object(node.childNodes[i], wsdl);
        return l;
    }
    return null;
}

SOAPClient._extractValue = function(node, wsdl)
{
    var value = node.nodeValue;
    switch(SOAPClient._getTypeFromWsdl(
           node.parentNode.nodeName, wsdl).toLowerCase())
    {
        default:
        case "s:string":            
            return (value != null) ? value + "" : "";
        case "s:boolean":
            return value+"" == "true";
        case "s:int":
        case "s:long":
            return (value != null) ? parseInt(value + "", 10) : 0;
        case "s:double":
            return (value != null) ? parseFloat(value + "") : 0;
        case "s:datetime":
            if(value == null)
                return null;
            else
            {
                value = value + "";
                value = value.substring(0, value.lastIndexOf("."));
                value = value.replace(/T/gi," ");
                value = value.replace(/-/gi,"/");
                var d = new Date();
                d.setTime(Date.parse(value));                                        
                return d;                
            }
    }
}
SOAPClient._getTypeFromWsdl = function(elementname, wsdl)
{
    var ell = wsdl.getElementsByTagName("s:element");    // IE
    if(ell.length == 0)
        ell = wsdl.getElementsByTagName("element");    // MOZ
    for(var i = 0; i < ell.length; i++)
    {
        if(ell[i].attributes["name"] + "" == "undefined")    // IE
        {
            if(ell[i].attributes.getNamedItem("name") != null && 
               ell[i].attributes.getNamedItem("name").nodeValue == 
               elementname && ell[i].attributes.getNamedItem("type") != null) 
                return ell[i].attributes.getNamedItem("type").nodeValue;
        }    
        else // MOZ
        {
            if(ell[i].attributes["name"] != null && 
               ell[i].attributes["name"].value == 
               elementname && ell[i].attributes["type"] != null)
                return ell[i].attributes["type"].value;
        }
    }
    return "";
}

The "SOAPClient._getElementsByTagName" method optimizes XPath queries according to the available XML parser:

JavaScript
SOAPClient._getElementsByTagName = function(document, tagName)
{
    try
    {
        return document.selectNodes(".//*[local-name()=\""+ 
                                           tagName +"\"]");
    }
    catch (ex) {}
    return document.getElementsByTagName(tagName);
}

A factory function returns the XMLHttpRequest according to the browser type:

JavaScript
SOAPClient._getXmlHttp = function() 
{
    try
    {
        if(window.XMLHttpRequest) 
        {
            var req = new XMLHttpRequest();
            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(SOAPClient._getXmlHttpProgID());
    }
    catch (ex) {}
    throw new Error("Your browser does not support XmlHttp objects");
}

SOAPClient._getXmlHttpProgID = function()
{
    if(SOAPClient._getXmlHttpProgID.progid)
        return SOAPClient._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 SOAPClient._getXmlHttpProgID.progid = progids[i];
        }
        catch (ex) {};
    }
    throw new Error("Could not find an installed XML parser");
}

Points of Interest

By using a single little (less than 10 KB) JavaScript library and, on the server side, simply exposing a Web Service with remote methods, you can use AJAX to create dynamic Web applications with no need for reloading the entire page.

See the on-line demo for an example of usage.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralRe: Demo files: what is .asmx file? Pin
mike00421-Aug-06 2:11
mike00421-Aug-06 2:11 
GeneralRe: Demo files: what is .asmx file? Pin
Matteo Casati21-Aug-06 2:16
Matteo Casati21-Aug-06 2:16 
QuestionIt is beyond my.. Pin
Sendilkumar.M26-Jul-06 18:44
Sendilkumar.M26-Jul-06 18:44 
AnswerRe: It is beyond my.. [modified] Pin
Matteo Casati26-Jul-06 21:19
Matteo Casati26-Jul-06 21:19 
AnswerRe: It is beyond my.. Pin
MarcoPolloPollo8-Aug-06 7:27
MarcoPolloPollo8-Aug-06 7:27 
GeneralNamespace issues with SOAP XML [modified] Pin
JoeInBoston7-Jul-06 7:39
JoeInBoston7-Jul-06 7:39 
GeneralRe: Namespace issues with SOAP XML Pin
Daniel Tingstrom5-Mar-07 10:32
Daniel Tingstrom5-Mar-07 10:32 
GeneralStub [modified] Pin
MarcoPolloPollo6-Jul-06 2:21
MarcoPolloPollo6-Jul-06 2:21 
I´ve changed some methods to make this more like a stub: it add´s the WSDL functions to the SOAPClient class. This means that we don´t have to get the correct url, methodname and type. I´ve used your code as a bases and made some new methods. It´s tested with your WSDL, Axis 1 WSDL and a gSOAP WSDL and they parsed.

Sorry VB.Net it took a little while to response, as you can see i´ve been busy.

The functions are added in a the following fashion: ws<WebserviceName><WebserviceMethod>(async, callback or null, params)

The constructor has been given to params: to parse or not to parse and the WSDL url.

Note: the stub only works on the global level, this means: NOT in a function. But i will look into this.

NEW code, I should have made clear that ie wasn´t working,

This is a test sample use Matteo's webservice:

<script>
var soapi = new SOAPClient("http://www.guru4.net/articoli/javascript-soap-client/demo/webservicedemo.asmx?wsdl", true);

function test() {
SOAPClient.wsWebServiceDemoSoapHelloTo(true, called, "Marco");
}
function called(back) {
alert(back);
}

</script>
<BUTTON onclick="test()" >TEST</BUTTON>

Have fun,

Marco.

/*****************************************************************************\

Javascript "SOAP Client" library

@version: 2.0 BETA 1 - 2006.03.12
@author: Matteo Casati - http://www.guru4.net/
@description: added support for complex object as webservice input paramaters

@version: 1.5 - 2006.02.20
@author: Matteo Casati, Martin Reich - http://www.guru4.net/
@description: the SOAPClient._onSendSoapRequest method has been updated
to handling SOAP exceptions in async calls

@version: 1.4 - 2005.12.10
@author: Matteo Casati, Ihar Voitka - http://www.guru4.net/
@description: (1) SOAPClientParameters.add() method returns 'this' pointer.
(2) "_getElementsByTagName" method added for xpath queries.
(3) "_getXmlHttpPrefix" refactored to "_getXmlHttpProgID" (full
ActiveX ProgID).

@version: 1.3 - 2005.12.06
@author: Matteo Casati - http://www.guru4.net/
@description: callback function now receives (as second - optional - parameter)
the SOAP response too. Thanks to Ihar Voitka.

@version: 1.2 - 2005.12.02
@author: Matteo Casati - http://www.guru4.net/
@description: (1) fixed update in v. 1.1 for no string params.
(2) the "_loadWsdl" method has been updated to fix a bug when
the wsdl is cached and the call is sync. Thanks to Linh Hoang.

@version: 1.1 - 2005.11.11
@author: Matteo Casati - http://www.guru4.net/
@description: the SOAPClientParameters.toXML method has been updated to allow
special characters ("<", ">" and "&"). Thanks to Linh Hoang.

@version: 1.0 - 2005.09.08
@author: Matteo Casati - http://www.guru4.net/
@notes: first release.

\*****************************************************************************/
//private
var _wsdl_url = null;
function SOAPClientParameters()
{
var _pl = new Array();
this.add = function(name, value)
{
_pl[name] = value;
return this;
}
this.toXml = function()
{
var xml = "";
for(var p in _pl)
xml += "<" + p + ">" + SOAPClientParameters._serialize(_pl[p]) + "</" + p + ">";
return xml;
}
}

SOAPClientParameters._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
{
(/function\s+(\w*)\s*\(/ig).exec(o[p].constructor.toString());
var type = RegExp.$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 + ">" + SOAPClientParameters._serialize(o[p]) + "</" + type + ">"
}
else // associative array
s += "<" + p + ">" + SOAPClientParameters._serialize(o[p]) + "</" + p + ">"
}
}
// Object or custom function
else
for(var p in o)
s += "<" + p + ">" + SOAPClientParameters._serialize(o[p]) + "</" + p + ">";
break;
default:
throw new Error(500, "SOAPClientParameters: type '" + typeof(o) + "' is not supported");
}
return s;
}

/*
class to store method information
*/
function SOAPClientMethod(aName, aUrl, ns) {
this.name = aName;
this.url = aUrl;
this.namespace = ns;
this.paramsIn = {};
this.paramsOut = {};
this.method = "";
}

var global = this;

var soapClientMethods = {};
function SOAPClient(wsdl_url, createMethods)
{
_wsdl_url = wsdl_url;

this.methods = {};
if (_wsdl_url)
{
var xmlHttp = SOAPClient._getXmlHttp();
xmlHttp.open("GET", _wsdl_url, true);
xmlHttp.onreadystatechange = function()
{
if(xmlHttp.readyState == 4)
{
var uddi = xmlHttp.responseXML;
if (uddi)
{
var name = null;

var definition = SOAPClient._getElementsByTagName(uddi, "definitions")[0];

var namespace = definition.getAttribute("targetNamespace");

var services = SOAPClient._getElementsByTagName(uddi, "service");
for (var service = 0; service < services.length; service++)
{
var ports = SOAPClient._getElementsByTagName(services[service], "port");
for (var port = 0; port < ports.length; port++)
{
name = ports[port].getAttribute("name");
var addresses = SOAPClient._getElementsByTagName(ports[port], "address");

var bName = ports[port].getAttribute("binding").split(':')[1];

var bindings = SOAPClient._getElementsByTagName(uddi, "binding");

for (var binding = 0; binding < bindings.length; binding++)
{
if (bName == bindings[binding].getAttribute("name"))
{
var ptName = bindings[binding].getAttribute("type").split(':')[1];
var url = null;
for(var address = 0; address < addresses.length; address++)
{
url = addresses[address].getAttribute("location");
if (url != null) SOAPClient_cacheWsdl[url] = uddi;
}
var portTypes = SOAPClient._getElementsByTagName(uddi, "portType");
for(var portType = 0; portType < portTypes.length; portType++)
{
var prName = portTypes[portType].getAttribute("name");

if(prName == ptName)
{
var operations = SOAPClient._getElementsByTagName(portTypes[portType], "operation");
var messages = SOAPClient._getElementsByTagName(uddi, "message");
var elements = SOAPClient._getElementsByTagName(uddi, "element");
var complexTypes = SOAPClient._getElementsByTagName(uddi, "complexType");
var types = SOAPClient._getElementsByTagName(uddi, "types")[0];

for (var operation = 0;operation < operations.length; operation++)
{
var methodname = operations[operation].getAttribute("name");

soapClientMethods["ws" + name + methodname] = new SOAPClientMethod("ws" + name + methodname, url, namespace);
soapClientMethods["ws" + name + methodname].method = methodname;

var inputs = SOAPClient._getElementsByTagName( operations[operation], "input");
SOAPClient._processInputs(inputs, messages, types, complexTypes, soapClientMethods["ws" + name + methodname].paramsIn);
var outputs = SOAPClient._getElementsByTagName(operations[operation], "output");
SOAPClient._processInputs(outputs, messages, types, complexTypes, soapClientMethods["ws" + name + methodname].paramsOut);

}
}
}
}
}
}
}
}
var tmp;
for (s in soapClientMethods)
{

var pList = "(async, callback, ";
var params = "var soapP = new SOAPClientParameters();";
for (p in soapClientMethods[s].paramsIn)
{
pList += p + ", ";
params += "soapP.add(\"" + p + "\", " + p + ");";
}
pList = pList.substring(0, pList.lastIndexOf(","));
pList += ")";
//url, method, parameters, async, callback, soapClientMethod
var scriptText = "global._" + s + " = function " + pList + " {" + params
+ " return SOAPClient._sendSoapRequest2(\"" + soapClientMethods[s].url + "\", \""
+ soapClientMethods[s].method+"\", soapP, async, callback, soapClientMethods[\"" + s +
"\"]);}";

(global.eval) ? global.eval(scriptText) : eval(scriptText);

(global.eval) ? global.eval(("SOAPClient." + s + " = _" + s)) : eval(("SOAPClient." + s + " = _" + s));

if (eval("typeof(\"SOAPClient.\" + s)")=="string")
{

scriptText = "function _"+ s + pList + " {" + params
+ " return SOAPClient._sendSoapRequest2(\"" + soapClientMethods[s].url + "\", \""
+ soapClientMethods[s].method+"\", soapP, async, callback, soapClientMethods[\"" + s +
"\"]);};";

(global.eval) ? global.eval(scriptText) : eval(scriptText);

(global.eval) ? global.eval(("SOAPClient." + s + " = _" + s)) : eval(("SOAPClient." + s + " = _" + s));
}

}
}
}
xmlHttp.send(null);
}
}

SOAPClient._processInputs = function (inputs, messages, types, complexTypes, params)
{
for (var input = 0; input < inputs.length; input++)
{
var mName = inputs[input].getAttribute("message").split(':')[1];

var parts = null;
for (var message = 0; message < messages.length && parts==null; message++)
{
if (mName == messages[message].getAttribute("name"))
{
parts = SOAPClient._getElementsByTagName(messages[message], "part");
for (var part = 0; part < parts.length; part++)
{
var pName = parts[part].getAttribute("name");
if (varType = parts[part].getAttribute("type"))
{
var tmp = null;
var varType = varType.split(':')[1];
tmp = SOAPClient._makeVariable(pName, varType, tmp, null, params);
params[pName] = tmp;
}
else
{
var inElmName = parts[part].getAttribute("element").split(':')[1];

var schema = SOAPClient._getElementsByTagName( types, "schema")[0];
var typeElements = SOAPClient._getElementsByTagName(schema, "element");
var stop = false;
for (var element = 0; element < typeElements.length && !stop; element++)
{
if (typeElements[element].getAttribute("name") == inElmName)
{

var scTypes = typeElements[element].childNodes; //simple or complextype, (n)ever seen any simple examples?

for (var scType=0; scType < scTypes.length && !stop; scType++)
{
if ((scTypes[scType].localName
&& (scTypes[scType].localName == "complexType" || scTypes[scType].localName == "simpleType"))
|| (scTypes[scType].nodeName &&
( scTypes[scType].nodeName.split(':')[1] == "complexType" || scTypes[scType].nodeName.split(':')[1] == "simpleType")))
{
SOAPClient._parseType(scTypes[scType], complexTypes, null, params);
stop = true; //break
}
}
}
}
}
}
}
}
}
}

SOAPClient._parseType = function(scType, complexTypes, o, params )
{
var tmp = null;
if (scType.hasChildNodes())
{
//TODO localName
var egcsa = null; //element, group, choice, sequence, any
var elements = scType.childNodes;
for (var element=0; element < elements.length && egcsa == null; element++)
{
var elName = (elements[element].localName) ? elements[element].localName : elements[element].nodeName.split(":")[1];
if (elName == "element"
|| elName == "sequence"
|| elName == "group"
|| elName == "choice"
|| elName == "any"
)
{
egcsa = elements[element].childNodes;
}
}

for (var elmEgcsa=0; elmEgcsa < egcsa.length; elmEgcsa++)
{
var egcsaName = (egcsa[elmEgcsa].localName) ? egcsa[elmEgcsa].localName : egcsa[elmEgcsa].nodeName.split(':')[1];
if (egcsaName == "element" )
{
var varName = egcsa[elmEgcsa].getAttribute("name");
var varType = egcsa[elmEgcsa].getAttribute("type").split(':')[1];
var varMinOcc = egcsa[elmEgcsa].getAttribute("minOccurs");
var varMaxOcc = egcsa[elmEgcsa].getAttribute("maxOccurs");

tmp = SOAPClient._makeVariable(varName, varType, tmp, o, complexTypes, params);
}
}
}
//tmp == null void soap call
if (o == null && tmp != null) params[varName] = tmp;
}

SOAPClient._makeVariable = function(varName, varType, tmp, o, complexTypes, params)
{
if (varType.toLowerCase().indexOf("arrayof") == 0) // starts width
{

var aType = varType.substring(7, varType.length);
if(aType.indexOf('_') >-1)
{
//Axis ArrayOf_xsd_bla bla and ArrayOf_soapenc_bla bla
//for native array?s
aType = aType.substring(aType.lastIndexOf('_') + 1, varType.length);
}
tmp = SOAPClient._getNativeForStringType(aType);
tmp.isAArray = true;
tmp.typeName = aType;
if (o == null && typeof(tmp) == "object")
{
//We need to determin the nature of the complextype
SOAPClient._findAndParseComplexType(complexTypes, tmp, aType, params);
}
else
{
if (o != null)
{
(global.eval) ? global.eval("o." + varName + " = tmp") : eval("o." + varName + " = tmp");
}
}
}
else
{
tmp = SOAPClient._getNativeForStringType(varType);
tmp.typeName = varType;
if (o == null && typeof(tmp) == "object")
{
tmp.isAArray = false;
//mind: it's recursive
SOAPClient._findAndParseComplexType(complexTypes, tmp, varType, params);
}
else
{
if (o != null)
{
(global.eval) ? global.eval("o." + varName + " = tmp") : eval("o." + varName + " = tmp");
}
}
}
return tmp;
}

SOAPClient._findAndParseComplexType = function (complexTypes, o, objectName, params)
{
for (var complexType = 0; complexType < complexTypes.length; complexType++)
{
if (objectName == complexTypes[complexType].getAttribute("name"))
{
//mind: it's recursive, to complex types could blow up the browser!
SOAPClient._parseType(complexTypes[complexType], complexTypes, o, params);
}
}
}

SOAPClient._getNativeForStringType = function (type)
{
var o = null;
switch (type)
{
case "string": o = new String("");break;
case "gYearMonth": o = new String("");break;
case "gYear": o = new String("");break;
case "gMonthDay": o = new String("");break;
case "gDay": o = new String("");break;
case "gMonth": o = new String("");break;
case "hexBinary": o = new String("");break;
case "anyURI": o = new String("");break;
case "QName": o = new String("");break;
case "NOTATION": o = new String("");break;
case "boolean": o = new Boolean(true);break;
case "decimal": o = new Number(0);break;
case "float": o = new Number(0.0);break;
case "double": o = new Number(0E00);break;
case "duration": o = new Number(0);break;
case "dateTime": o = new Date();break;
case "time": o = new Time();break;
case "date": o = new Date();break;
case "base64Binary": o = new Number(0);break;
case "int": o = new Number(0);break;
case "long": o = new Number(0);break;
case "integer": o = new Number(0);break;
case "nonPositiveInteger": o = new Number(-1);break;
case "negativeInteger": o = new Number(-1);break;
case "short": o = new Number(0);break;
case "byte": o = new Number(0);break;
case "nonNegativeInteger": o = new Number(0);break;
case "unsignedLong": o = new Number(0);break;
case "unsignedInt": o = new Number(0);break;
case "unsignedShort": o = new Number(0);break;
case "unsignedByte": o = new Number(0);break;
case "positiveInteger": o = new Number(0);break;
default: o = new Object();break;
}
return o;
}

SOAPClient.invoke = function(url, method, parameters, async, callback)
{
if(async)
SOAPClient._loadWsdl(url, method, parameters, async, callback);
else
return SOAPClient._loadWsdl(url, method, parameters, async, callback);
}

// private: wsdl cache
SOAPClient_cacheWsdl = new Array();

// private: invoke async
SOAPClient._loadWsdl = function(url, method, parameters, async, callback)
{
// load from cache?
var wsdl = SOAPClient_cacheWsdl[url];
if(wsdl + "" != "" && wsdl + "" != "undefined")
return SOAPClient._sendSoapRequest(url, method, parameters, async, callback, wsdl);
// get wsdl
var xmlHttp = SOAPClient._getXmlHttp();
xmlHttp.open("GET", ((_wsdl_url + "" != "undefined") ? _wsdl_url : url+ "?wsdl"), async);
if(async)
{
xmlHttp.onreadystatechange = function()
{
if(xmlHttp.readyState == 4)
SOAPClient._onLoadWsdl(url, method, parameters, async, callback, xmlHttp);
}
}
xmlHttp.send(null);
if (!async)
return SOAPClient._onLoadWsdl(url, method, parameters, async, callback, xmlHttp);
}


SOAPClient._onLoadWsdl = function(url, method, parameters, async, callback, req)
{
var wsdl = req.responseXML;
SOAPClient_cacheWsdl[url] = wsdl; // save a copy in cache
return SOAPClient._sendSoapRequest(url, method, parameters, async, callback, wsdl);
}
SOAPClient._sendSoapRequest = function(url, method, parameters, async, callback, wsdl)
{
// get namespace
var ns = (wsdl.documentElement.attributes["targetNamespace"] + "" == "undefined") ? wsdl.documentElement.attributes.getNamedItem("targetNamespace").nodeValue : wsdl.documentElement.attributes["targetNamespace"].value;
// 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 = SOAPClient._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)
SOAPClient._onSendSoapRequest(method, async, callback, wsdl, xmlHttp);
}
}
xmlHttp.send(sr);
if (!async)
return SOAPClient._onSendSoapRequest(method, async, callback, wsdl, xmlHttp);
}

SOAPClient._sendSoapRequest2 = function(url, method, parameters, async, callback, soapClientMethod)
{
// build SOAP request
var ns = soapClientMethod.namespace;
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 = SOAPClient._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)
SOAPClient._processResponse(callback, xmlHttp, soapClientMethod);
}
}
xmlHttp.send(sr);
if (!async)
return SOAPClient._processResponse(null, xmlHttp, soapClientMethod);
}

SOAPClient._processResponse = function(callback, xmlHttp, soapClientMethod)
{
var soapR = xmlHttp.responseXML;
var result = new Array();
var o = {};
var i = 0;
for (p in soapClientMethod.paramsOut)
{
var results = SOAPClient._getElementsByTagName(soapR, p);
var value = null;
if (results.length>0)
{
if (soapClientMethod.paramsOut[p].isAArray)
{
value = new Array();
for (var a = 0; a < results.length; a++)
{
value[a] = SOAPClient._extractValue(results[a].childNodes[0], null, soapClientMethod.paramsOut[p].typeName) ;
}
}
else
{
value = SOAPClient._extractValue(results[0].childNodes[0], null, soapClientMethod.paramsOut[p].typeName) ;
}
} else {
if(req.responseXML.getElementsByTagName("faultcode").length > 0)
throw new Error(500, req.responseXML.getElementsByTagName("faultstring")[0].childNodes[0].nodeValue);
}
var scriptText = "o." + p + "= soapClientMethod.paramsOut[p]; result[" + i
+ "] = soapClientMethod.paramsOut[p]; result[" + i + "] = value; o." + p + " = value;";
(global.eval) ? global.eval(scriptText) : eval(scriptText);
i++;
}
if (callback != null) {
var scriptText = "callback(";
for (var rs = 0; rs < result.length; rs++ )
{
scriptText += "result[ " + rs + " ], ";
}
scriptText = scriptText.substring(0, scriptText.lastIndexOf(','));
scriptText += ");";
if (result.length>0)
(global.eval) ? global.eval(scriptText) : eval(scriptText);
}
if (result.length == 1) return result[0];
return o;
}

SOAPClient._onSendSoapRequest = function(method, async, callback, wsdl, req)
{
var o = null;
var nd = SOAPClient._getElementsByTagName(req.responseXML, method + "Result");
//if nothing is found, lets try method + Response...
if(nd.length == 0) nd = SOAPClient._getElementsByTagName(req.responseXML, method + "Response");
if(nd.length == 0) {
//still nothing, we've got a wsdl let's try to find it.
//caching should be added later
var nodes = req.responseXML.getElementsByTagName("ouput");
for (var h=0; h<nodes.length() && nd.length == 0;h++) {
var output = nodes[h];
if (output.nodeName="output" && (output.getAttribute("message") + "") != "undefined" ) {
var responseNS = output.getAttribute("message");
var response = responseNS.split(':')[responseNS.split(':').length - 1];
nd = SOAPClient._getElementsByTagName(req.responseXML, response);
}
}
}
if(nd.length == 0)
{
if(req.responseXML.getElementsByTagName("faultcode").length > 0)
{
if(async || callback)
o = new Error(500, req.responseXML.getElementsByTagName("faultstring")[0].childNodes[0].nodeValue);
else
throw new Error(500, req.responseXML.getElementsByTagName("faultstring")[0].childNodes[0].nodeValue);
}
}
else
o = SOAPClient._soapresult2object(nd[0], wsdl);
if(callback)
callback(o, req.responseXML);
if(!async)
return o;
}

// private: utils
SOAPClient._getElementsByTagName = function(document, tagName)
{
try
{
// trying to get node omitting any namespaces (latest versions of MSXML.XMLDocument)
var tmp = document.selectNodes(".//*[local-name()=\""+ tagName +"\"]");
if (tmp != null && tmp.length >0) return tmp;
}
catch (ex) {}
// old XML parser support
var aResult = document.getElementsByTagName(tagName);
// namespace and old DOM and ie

if (aResult.length >0) return aResult;
var i=0, oTemp;
for(i; i < document.childNodes.length; i++)
{
oTemp=document.childNodes[i];
if(oTemp.localName==tagName) aResults.push(oTemp);

}
return aResult;
}

SOAPClient._soapresult2object = function(node, wsdl)
{
return SOAPClient._node2object(node, wsdl);
}
SOAPClient._node2object = function(node, wsdl)
{
// null node
if(node == null)
return null;
// text node
if(node.nodeType == 3 || node.nodeType == 4)
return SOAPClient._extractValue(node, wsdl);
// leaf node
if (node.childNodes.length == 1 && (node.childNodes[0].nodeType == 3 || node.childNodes[0].nodeType == 4))
return SOAPClient._node2object(node.childNodes[0], wsdl);
var isarray = SOAPClient._getTypeFromWsdl(node.nodeName, wsdl).toLowerCase().indexOf("arrayof") != -1;
// object node
if(!isarray)
{
var obj = null;
if(node.hasChildNodes())
obj = new Object();
for(var i = 0; i < node.childNodes.length; i++)
{
var p = SOAPClient._node2object(node.childNodes[i], wsdl);
obj[node.childNodes[i].nodeName] = p;
}
return obj;
}
// list node
else
{
// create node ref
var l = new Array();
for(var i = 0; i < node.childNodes.length; i++)
l[l.length] = SOAPClient._node2object(node.childNodes[i], wsdl);
return l;
}
return null;
}
SOAPClient._extractValue = function(node, wsdl, type)
{
var value = node.nodeValue;
if (type == null)
type = SOAPClient._getTypeFromWsdl(node.parentNode.nodeName, wsdl).toLowerCase();
else type = "s:" + type;
switch(type)
{
default:
case "s:string":
return (value != null) ? value + "" : "";
case "s:boolean":
return value+"" == "true";
case "s:int":
case "s:long":
return (value != null) ? parseInt(value + "", 10) : 0;
case "s:double":
return (value != null) ? parseFloat(value + "") : 0;
case "s:datetime":
if(value == null)
return null;
else
{
value = value + "";
value = value.substring(0, value.lastIndexOf("."));
value = value.replace(/T/gi," ");
value = value.replace(/-/gi,"/");
var d = new Date();
d.setTime(Date.parse(value));
return d;
}
}
}
SOAPClient._getTypeFromWsdl = function(elementname, wsdl)
{
var ell = wsdl.getElementsByTagName("s:element"); // IE
if(ell.length == 0)
ell = wsdl.getElementsByTagName("element"); // MOZ
for(var i = 0; i < ell.length; i++)
{
if(ell[i].attributes["name"] + "" == "undefined") // IE
{
if(ell[i].attributes.getNamedItem("name") != null && ell[i].attributes.getNamedItem("name").nodeValue == elementname && ell[i].attributes.getNamedItem("type") != null)
return ell[i].attributes.getNamedItem("type").nodeValue;
}
else // MOZ
{
if(ell[i].attributes["name"] != null && ell[i].attributes["name"].value == elementname && ell[i].attributes["type"] != null)
return ell[i].attributes["type"].value;
}
}
return "";
}
// private: xmlhttp factory
SOAPClient._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(SOAPClient._getXmlHttpProgID());
}
catch (ex) {}
throw new Error("Your browser does not support XmlHttp objects");
}
SOAPClient._getXmlHttpProgID = function()
{
if(SOAPClient._getXmlHttpProgID.progid)
return SOAPClient._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 SOAPClient._getXmlHttpProgID.progid = progids[i];
}
catch (ex) {};
}
throw new Error("Could not find an installed XML parser");
}


-- modified at 8:24 Friday 11th August, 2006
GeneralRe: Stub Pin
dadgad26-Jul-06 4:47
dadgad26-Jul-06 4:47 
GeneralRe: Stub [modified] Pin
manzanotti8-Aug-06 6:49
manzanotti8-Aug-06 6:49 
GeneralRe: Stub Pin
MarcoPolloPollo10-Aug-06 0:34
MarcoPolloPollo10-Aug-06 0:34 
GeneralRe: Stub Pin
pabz_sarquilla28-Oct-09 10:34
pabz_sarquilla28-Oct-09 10:34 
GeneralWebService return a DataSet Pin
mashry6828-Jun-06 4:41
mashry6828-Jun-06 4:41 
GeneralRe: WebService return a DataSet Pin
MarcoPolloPollo11-Aug-06 2:33
MarcoPolloPollo11-Aug-06 2:33 
QuestionRe: WebService return a DataSet Pin
jack_zhao13-Mar-07 22:53
jack_zhao13-Mar-07 22:53 
Generalsoapclient.js incompatible with prototype.js [modified] Pin
maarten meijer22-Jun-06 9:47
maarten meijer22-Jun-06 9:47 
GeneralRe: soapclient.js incompatible with prototype.js [modified] Pin
maarten meijer22-Jun-06 10:52
maarten meijer22-Jun-06 10:52 
GeneralRe: soapclient.js incompatible with prototype.js Pin
tobimarq5-Oct-06 3:16
tobimarq5-Oct-06 3:16 
QuestionWS-I compliace? Pin
maarten meijer14-Jun-06 12:53
maarten meijer14-Jun-06 12:53 
GeneralJavaScript SOAP client with nusoap Pin
NabeelAzar23-May-06 15:33
NabeelAzar23-May-06 15:33 
GeneralRe: JavaScript SOAP client with nusoap Pin
maarten meijer1-Jun-06 12:12
maarten meijer1-Jun-06 12:12 
GeneralRe: JavaScript SOAP client with nusoap Pin
NabeelAzar1-Jun-06 12:29
NabeelAzar1-Jun-06 12:29 
Generalcross-domain calls Pin
alexsantos22-May-06 13:09
alexsantos22-May-06 13:09 
GeneralRe: cross-domain calls Pin
m.casati22-May-06 21:34
m.casati22-May-06 21:34 
GeneralBUG Pin
-EB-20-May-06 5:08
-EB-20-May-06 5:08 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.