var SOAP = {
   sens: 'http://schemas.xmlsoap.org/soap/encoding/', /* SOAP-ENC */
  xsins: 'http://www.w3.org/2001/XMLSchema-instance', /* xsi */
   svns: 'http://schemas.xmlsoap.org/soap/envelope/', /* SOAP-ENV */

  q_async: function(
    api_url, /* the endpoint address indicated by the WSDL */
    wsdl_namespace, /* the name found in targetNamespace in the WSDL */
    function_name, /* the function to call */
    params, /* the parameters to pass */
    ok_callback, /* callback function for receiving the response */
    error_callback /* callback function for error situation (not supported on IE) */
  )
  {
    var req = create_xmlhttp();
    req.open('POST', api_url, true);
    /* change true to false here^ if you want to use synchronous calling instead */
    req.onreadystatechange =
      function() { if (req.readyState==4) { ok_callback(req); } }
    if(req.onerror) req.onerror = function() { error_callback(req); }
    req.send(
      '<?xml version="1.0" encoding="utf-8"?>'+
      '<s:Envelope xmlns:s="'+SOAP.svns     +'"'+
                 ' xmlns:n="'+wsdl_namespace+'"'+
                 '>'+
      '<s:Body><n:'+function_name+'>'
            + SOAP.esc_params(params)
            + '</n:'+function_name+'></s:Body>'+
      '</s:Envelope>');
  },
  
  esc_params: function(a)
  {
    if(typeof a != 'object')
      return a.toString().
             replace(/&/g, '&amp;').
             replace(/</g, '&lt;'). 
             replace(/>/g, '&gt;');
    var p,res='';
    for(p in a) res += '<' + p + '>' + this.esc_params(a[p]) + '</' + p + '>';
//    alert(res);
    return res
  },

  q_async_trans: function(url,ns,q,params,func_ok,func_nok)
  {
    SOAP.q_async(url,ns,q,params,
      function(req) { return func_ok(SOAP.trans(req,q)) },
      func_nok)
  },

  // Translates a XML response object into a function return value.
  trans: function(req,q)
  {
    if(!req) throw "no xmlhttp?";
    var x=req.responseXML,rr=new RegExp('^'+q+'Response$');
    if(!x) throw req.responseText;
    return SOAP.respt.find(x,rr)
  },
  
  respt:{ // response translator
    itp:/^xsd:(int|long|short|byte|((non)?(nega|posi)tive)?integer)$/,
    ftp:/^xsd:(float|double)$/,
    find:function(xml,rr)
    {
      var x=xml.childNodes,a,b=x.length,n;
      for(a=0;a<b;++a)
      {
        n = x[a];
        var nn = n.localName;
        if(nn != 'undefined' && rr.test(nn))
          return this.trans(n.firstChild)
        // ^Note: multipart return values not supported
        var subq=this.find(n,rr)
        if(typeof subq != 'null') return subq
      }
      return null
    },
    trans:function(n)
    {
      if(n.nodeType != 1) throw 'unexpected nodetype: '+n.nodeType;
      if(n.hasAttributeNS(SOAP.sens, 'arrayType'))
      {
        // Array of something
        var res = [], x=n.childNodes, a,b=x.length;
        for(a=0;a<b;++a) res.push(this.trans(x[a]))
        return res
      }
      if(n.hasAttributeNS(SOAP.xsins, 'nil')) return ''; //null;
      var text = dom_rtext(n), type = n.getAttributeNS(SOAP.xsins, 'type');
      if(type=='xsd:boolean') return text=='true'||text=='1';
      if(this.itp.test(type)) return parseInt(text,10);
      if(this.ftp.test(type)) return parseFloat(text);
      if(!type || /^xsd:/.test(type)) return text;

      // It was not an XSD type, so handle it as a struct/object then.
      var res = {}, x=n.childNodes, a,b=x.length;
      for(a=0;a<b;++a)
      {
        var nn = x[a].localName;
        res[nn] = this.trans(x[a])
      }
      return res
  } }
}
