function SOAPClient(URLaddress)
{
    this.WSDLLocation = URLaddress;

    var SOAPServer = null;
    var SOAPName = null;
    var WSDLDoc = null;
    var XHR = null;
    var thiso = this;  //Allow member functions to refer to other members of the
                       //object

    this.ParseWSDL = function()
    /*
      This priviledged function loads the WSDL of the SOAP service and then
      parses it to determine the location of the actual server and other details
      needed to successfully use the service.
    */
    {
        var MethodNodes = null;

        try //to load the WSDL file and parse it in Firefox (possibly Safari and Opera...not sure)
        {
            WSDLDoc = document.implementation.createDocument("","",null);
        }
        catch(Error)  //Internet Explorer is causing problems
        {
            try
            {
                WSDLDoc = new ActiveXObject("Microsoft.XMLDOM");
            }
            catch(Error)  //Something went wrong
            {
                alert("Error parsing " + thiso.WSDLLocation + "\n" + Error.message);
                WSDLDoc = false;
            }
        }
        WSDLDoc.async = false;  //Do not continue until WSDL is loaded
        WSDLDoc.load(thiso.WSDLLocation);

        if(WSDLDoc)
        {
            //Get the URL of the actual SOAP server
            //First we find the tag containing the information
            SOAPServer = WSDLDoc.getElementsByTagName("soap:address")[0];
            //Now we extract the URL
            SOAPServer = SOAPServer.getAttribute("location");
            /*Now we get the name of the service which is need when building a
              SOAP request (known as an envelope which is an XML document)*/
            //As before, we find the tag containing the info
            SOAPName = WSDLDoc.getElementsByTagName("definitions")[0];
            //And now we get the actual string
            SOAPName = SOAPName.getAttribute("name");

            /*Now we need to figure our what methods are provided by the SOAP
              service.  So we'll find the nodes in the WSDL file with that info
              and iterate through them*/
            MethodNodes = WSDLDoc.getElementsByTagName("portType")[0]
            MethodNodes = MethodNodes.getElementsByTagName("operation");

            /*Now that we have all the methods exposed by the service, we will
              add methods to this object to "mirror" the functionality*/
            for(var i = 0; i < MethodNodes.length; i++)
            {
                BuildSOAPMethod(MethodNodes[i]);
            }
        }

        return;
    }

    var BuildSOAPMethod = function(MethodElem)
    /*
      Given a child element of <portType>, which should be an <operation>, build
      a method for this object that takes the same number of arguments and
      invokes the remote method.  The new "local" method will have the same name
      as the "remote" method.
    */
    {
        var MethodName = MethodElem.getAttribute("name");
        var InputMessageName = null;
        var ArgNodes = null;
        var ArgNames = new Array();
        var ArgTypes = new Array();
        var OutputMessageName = null;
        var ReturnName;  //The message returned by the SOAP server names the
                         //return value.  This name is needed to successfully
                         //parse the response.
        var WorkingNodeList = null;  //A variable used to temporarily store
                                     //nodes which are currently being
                                     //manipulated

        /*Find the <input> element for the <operation> which represents the
          message sent to the server when an method is invoked*/
        WorkingNodeList = MethodElem.getElementsByTagName("input")[0];
        //Determine what the input message is named
        InputMessageName = WorkingNodeList.getAttribute("message");
        /*The name of the input message is prepended with a namespace which is
          not needed, so we'll remove it here*/
        InputMessageName = InputMessageName.split(":")[1];

        /*Now, using the name of the message, find all the <part>s so we know
          how many parameters there are and what they're named.  All <part> tags
          are children of the <message> tag, so that's where we'll look*/
        WorkingNodeList = WSDLDoc.getElementsByTagName("message");
        /*Now sift through the <message> elements look for the one that
          corresponds to the input message.*/
        for(var i = 0; i < WorkingNodeList.length && ArgNodes == null; i++)
        {
            if(WorkingNodeList[i].getAttribute("name") == InputMessageName)
            /*If we've found it, then we want all the children which should all
              be <part> tags.  The count of <part>s indicates how many
              parameters this particular method requires*/
            {
                ArgNodes = WorkingNodeList[i].getElementsByTagName("part");
            }
        }
        /*Take the <part> elements and extract the names and types of the
          parameters*/
        for(var i = 0; i < ArgNodes.length; i++)
        {
            ArgNames.push(ArgNodes[i].getAttribute("name"));
            ArgTypes.push(ArgNodes[i].getAttribute("type"));
        }

        /*Now it's time to find the name of the return value (which is needed to
          parse the response of the SOAP service*/
        WorkingNodeList = MethodElem.getElementsByTagName("output")[0];
        //Determine the name of the output message which contains the value
        OutputMessageName = WorkingNodeList.getAttribute("message");
        //The response message is also prepended with a namespace that is unneed
        OutputMessageName = OutputMessageName.split(":")[1];

        //Now we use this to find the corresponding <message> element
        WorkingNodeList = WSDLDoc.getElementsByTagName("message");
        /*Sift throught the <message> tags again, this time looking for the
          response message.*/
        for(var i = 0; i < WorkingNodeList.length; i++)
        {
            if(WorkingNodeList[i].getAttribute("name") == OutputMessageName)
            {
                //There should only be one return value, so we can just get the
                //name right here
                ReturnName = WorkingNodeList[i].getElementsByTagName("part")[0];
                ReturnName = ReturnName.getAttribute("name");
            }
        }

        /*Now the fun part.  Building the actual "local" method which will
          invoke the remote method*/
        var NewMethod = "SOAPClient.prototype." + MethodName + " = function(" +
                                                   ArgNames.join(",") + ") {";
        for(var i = 0; i < ArgNames.length; i++)
        {
            NewMethod += "var SOAPParam" + i + " = BuildSOAPParam(\"" +
                         ArgNames[i] + "\",\"" + ArgTypes[i] + "\"," +
                         ArgNames[i] + ");";
        }
        NewMethod += "var Query = BuildSOAPRequest(SOAPName,\"" + MethodName +
                     "\"";
        for(var i = 0; i < ArgNames.length; i++)
        {
            NewMethod += ",SOAPParam" + i;
        }
        NewMethod += ");" +
                     "var Answer = null;" +
                     "XHR.open(\"POST\",SOAPServer,false);" +
                     "XHR.send(Query);" +
                     "Answer = ParseResponse(XHR.responseText,\"" + ReturnName +
                                                                        "\");" +
                     "return Answer;}";
        eval(NewMethod);  /*Interpret the string that results from all that
                            ugliness and hard to decifer code*/

        return;
    }

    var BuildSOAPParam = function(ParamName,SoapType,ParamVal)
    {
        var Output = "<" + ParamName + " xsi:type=\"" + SoapType + "\">" +
                      encodeURI(ParamVal) +
                      "</" + ParamName + ">";

        return Output;
    }

    var BuildSOAPRequest = function(NameSpace,MethodName,Params)
    {
        var OpenTags = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
                       "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
                                          "xmlns:ns1=\"urn:" + NameSpace + "\" " + 
                                          "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" " +
                                          "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
                                          "xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\" " +
                                          "SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
                         "<SOAP-ENV:Body>";
        var MethodTagOpen = "<ns1:" + MethodName + ">";
        var MethodTagClose = "</ns1:" + MethodName + ">";
        var CloseTags = "</SOAP-ENV:Body></SOAP-ENV:Envelope>";

        Request = OpenTags + MethodTagOpen;
        for(var i = 2; i < arguments.length; i++)
        {
            Request += arguments[i];
        }
        Request += MethodTagClose + CloseTags;

        return Request;
    }

    var ParseResponse = function(SOAPResponse,ReturnName)
    {
        var XMLParser = null;
        var XMLDoc = null;
        var WorkingNodeList = null;
        var Answer = "";

        try
        {
            XMLParser = new DOMParser();
            XMLDoc = XMLParser.parseFromString(SOAPResponse,"text/xml");
            delete XMLParser;
        }
        catch(Error)  //Yep...IE..._AGAIN_!
        {
            try
            {
                XMLDoc = new ActiveXObject("Microsoft.XMLDOM");
                XMLDoc.loadXML(SOAPResponse);
            }
            catch(Error)  //Something went wrong.
            {
                alert("Error parsing response from SOAP server\n" + Error.message);
                XMLDoc = false;
            }
        }

        WorkingNodeList = XMLDoc.getElementsByTagName(ReturnName)[0].childNodes;
        for(var i = 0; i < WorkingNodeList.length; i++)
        {
            Answer += WorkingNodeList[i].nodeValue;
        }

        return Answer;
    }

    try  //Firefox, Safari, IE 7.0+, and Opera 8.0+...who uses Opera?
    {
        XHR = new XMLHttpRequest();
    }
    catch(Error)  //Somebody's using and old version o IE...shame on them.
    {
        try
        {
            XHR = new ActiveXObject("Microsoft.XMLHTTP");
        }
        catch(Error)  //Something went wrong!
        {
            XHR = false;
            alert("Failed to create an XMLHttpRequest object.\n" + Error.message);
        }
    }
    thiso.ParseWSDL();
}

// function RunningInOldIE()
// {
//     var IsOldIE = true;
// 
//     if(typeof XMLHttpRequest == "undefined")
//     {
//         IsOldIE = false;
//     }
// 
//     return IsOldIE;
// }
