﻿/**
  * $Header: /P2plus40/WebObjects/P2Soap2.js 65    18.08.04 12:51 AP $
  * ----------------------------------------------------------------
  * ReportUtils.js
  * Copyright by AP AG 2002
  * ----------------------------------------------------------------
  * The Client Scripts are always sent to the client
  * The Server is checking for the rights.
  * 04.12.2002 MHE OneWay Soap Calls enabled.
  * 07.01.2003 MHE Zeitüberschreitung bei langlaufenden Jobs auch bei deutschen Servern akzeptieren.
  * 26.03.2003 MHE minor changes.
  * 11.09.2003 MHE FIX: F370045 _cleanUpCancelObj added.
 * 23.04.2004 MHE FIX: F370111 Wiederverwendung des XMLHttp Objektes.
 * 18.08.2004 MHE FIX40: F400011 datetime Ergebniswerte mit Zeitanteil hatten immer die aktuelle Uhrzeit
 * 04.02.2005 MHE FIX40: F400071 #32998 vorläufiger Fix wegen Encoding Problem (.NET 1.1 SP1) in Response-Exceptions. 
 * 20.06.2005 MHE Firefox kompatible Implementierung von SPOTS.
 * 08.07.2005 MHE Namespace-alias Definition bei den soap-result Werten korrigiert.
 * 18.05.2008 CMU EP00850 WebService-Proxies auf dem Client werden jetzt serverseitig generiert.
 * 21.05.2008 CMU EP00850 WebServices werden jetzt mit Lazy-Loading erst bei Bedarf geladen.
 * 23.09.2008 AVO #181783 _P2AsyncSoapResult(): AppServer-Fehlermeldungen werden ausgegeben. 
 * 10.09.2009 CMU #186733 Arrayunterstützung bei Webservices für Parameter und Rückgabewerte. Zudem ist jetzt auch der Datentyp Date möglich.
 ****************************************************************
   ADN-ANPASSUNGEN:
   01.09.2007 ADN:AP: Nur bestimmte asynchrone Funktionen stoppen
*/

var _xmlCallObj = null; // the async xmlhttp Object
var _xmlCallFunc = new Array(); // async function array
var _xmlCallPage = new Array(); // async WebService
var _xmlFunc = new Array(); // async function descriptors
var _xmlCallParams = new Array(); // async params array
var _xmlCallCancel = false; // cancel the httpAsyncSoapObject

_P2SoapDysplayAsyncError = true;

var XMLDOMNAME = "MSXML2.DOMDocument"; // "Microsoft.XMLDOM";
var XMLHTTPNAME = "Msxml2.XMLHttp"; // "Microsoft.XMLHTTP";

var _xmlObj = null; // cache
var _xmlDirectObj = null; // cache
var _xmlCancelObj = null; // cancel this Object as soon as possible

var P2Service = new Object();
var wsRegistry = new Object();
var wsProxies = new Object();
var wsLoaded = new Object();

var isIE = (window.navigator.userAgent.indexOf("MSIE ") > 0);

/// Namespace Resolver für FF XPath Ausdrücke
function NSResolver(prefix) {
  if (prefix == 'wsdl') {
    return 'http://schemas.xmlsoap.org/wsdl/';

  } else if (prefix == 'soap') {
    return 'http://schemas.xmlsoap.org/wsdl/soap/';
    
  } else if (prefix == 'soapenv') {
    return 'http://schemas.xmlsoap.org/soap/envelope/';

  } else if (prefix == 's') {
    return 'http://www.w3.org/2001/XMLSchema';

  } else  {
  //this shouldn't ever happen
    return null;
  }
} // NSResolver


/// Erstellen eines Proxy Objektes durch Laden vom Server.
function createWSProxy(aliasName, serviceUrl)
{
  if ((_xmlDirectObj == null) && isIE) {
    _xmlDirectObj = new ActiveXObject(XMLHTTPNAME);
  } else if ((_xmlDirectObj == null) && (window.navigator.userAgent.indexOf("Gecko") > 0)) {
    _xmlDirectObj = new XMLHttpRequest();
  }
  
  var appPath = absoluteUrl("~/");
  /*
  This is a bit hacky. serviceUrl can be ../wp/someservice.asmx or /./webobjects/navbar.asmx.
  The problem is, that rel2absPath can't work with the second case. absoluteUrl can convert the
  second case to a absolute url, and if it is passed to rel2absPath, it is simply returned, cause it
  is already absolute. But absoluteUrl can't work with the first case, and returns it just like it
  was passed to it. But rel2absPath can convert it to an absolute path. So the outcome is always an
  absolute path.
  In the next step, we convert the absolute path to a relative path starting from the APplus virtual 
  dir. This is done, to clear the path of any relative patterns like ../.
  */
  var wsPath = rel2absPath(document.location.href, absoluteUrl(serviceUrl));
  wsPath = abs2relPath(appPath, wsPath);
  
  var jsWsProxyGetter = absoluteUrl("~/WebObjects/getJSWSProxy.aspx");
  
  _xmlDirectObj.open("GET", jsWsProxyGetter + "?wsPath=" + codeQS(wsPath), false);
  _xmlDirectObj.send(null);
  
  var jsWsProxyText = _xmlDirectObj.responseText;
  
  if ((jsWsProxyText == null) || (_xmlDirectObj.status != 200))
    throw new Error(0x990004, "P2Soap: Die Server Referenz wurde nicht gefunden.");
  
  return eval(jsWsProxyText);
}


/* Registers a web service. This needs to be done, before using it.
 * 
 * @param wsAlias The web service alias.
 * @param url The url of the web service.
 */
function registerWS(wsAlias, url) {
  wsRegistry[wsAlias] = url;
  
  if(isCreated(wsAlias)) {
    delete wsProxies[wsAlias];
  }
  if(isLoaded(wsAlias)) {
    delete wsLoaded[wsAlias];
  }
}


/* returns a web service proxy
 * 
 * @param wsAlias The web service alias.
 * @return Returns the web service proxy if previously registered.
 *         Returns null, if is was not registered before.
 */
function getService(wsAlias) {
  if(isRegistered(wsAlias) && !isLoaded(wsAlias)) {
    var url = wsRegistry[wsAlias];
    
    wsProxies[wsAlias] = createWSProxy(wsAlias, url);
    wsLoaded[wsAlias] = true;
  }
  
  // wsProxies[wsAlias] returns null, if the alias is not found. This is OK, cause 
  // some pages do the lazy loading itself and check on null return value of this method.
  return wsProxies[wsAlias];
}


/* Returns a webservice proxy if everything is OK, but throws an exception, if no webservice can be created.
 * @param alias The webservice alias.
 * @return The webservice proxy.
 */
function getServiceOrFail(alias) {
  var wsProxy = P2Service.get(alias);
  
  if(wsProxy == null) {
    throw new Error("P2Soap: No registered WebService found with alias '" + alias + "'!");
  }
  
  return wsProxy;
}


/* Returns, whether a web service was registered.
 * 
 * @param wsAlias The web service alias.
 * @return true or false.
 */
function isRegistered(wsAlias) {
  return wsRegistry[wsAlias] != null;
}


/* Returns, whether a web service was created. This can mean loaded, 
 * like isLoaded() would return, but also that the web service proxy 
 * was created manually by useUrl() and useKnown().
 * 
 * @param wsAlias The web service alias.
 * @return true or false.
 */
function isCreated(wsAlias) {
  return wsProxies[wsAlias] != null;
}


/* Returns, whether a web service proxy was really loaded from server 
 * and not created manually.
 * 
 * @param wsAlias The web service alias.
 * @return true or false.
 */
function isLoaded(wsAlias) {
  return wsLoaded[wsAlias] == true;
}


/// <summary>
/// registering a alias to a url without fetching the WSDL file.
/// </summary>
/// <param name="aliasName">new registered alias name.</param>
/// <param name="serviceUrl">URL where the http service can be found.</param>
function P2SoapUseUrl(aliasName, serviceUrl)
{
  var oBinder;

  if (!isCreated(aliasName))
    wsProxies[aliasName] = new Object();
  oBinder = P2Service.get(aliasName);
  oBinder.uri = absoluteUrl(serviceUrl);
  oBinder.ns = "http://tempuri.org/"; // "http://p2plus.com/webservices/";
  if (oBinder.methods == null)
    oBinder.methods = new Array();
} // P2SoapUseUrl


/// <summary>
/// registering a new method for a well known Web service.
/// </summary>
function P2SoapUseKnow(aliasName, mName)
{
  var p, s, oCall;
  var oBinder = P2Service.get(aliasName);

  oCall = oBinder.methods[mName] = new Object();
  oCall.soapAction = mName;
  oCall.args = new Array();
  oCall.name = mName;
  oCall.resultName = mName + "Response";
  //#186733
  oCall.returnType = null;

  p = 0;
  for (var n = 2; n < arguments.length; n+=2) {
    oCall.args[p] = new Object();
    oCall.args[p].type = arguments[n];
    oCall.args[p].name = arguments[n+1];
    p++;
  } // for

  s = "new Function(";
  for (var n = 0; n < oCall.args.length; n++) s += '"p' + n + '", ';
  s += "\"return P2SoapImpl(this, '" + mName + "', arguments, 0)\")";
  oBinder[mName] = eval(s);
}

/** Dies ist die Standard SOAP Funktion bei der Verwendung
  * von Funktionsaufrufen ueber window.server.funcname(...).
  * @param fName der Name der Funktion
  * @param weitere Parameter werden dynamisch ausgewertet.
  * @return der Rueckgabewert der aufgerufenen Funktion
  * @exception ... werden weitergereicht.
*/
function P2SoapCall(fName)
{
  throw new Error(0x990004, "P2Soap: Umstellung auf Standard - SOAP notwendig !");
} // P2SoapCall


/** Dies ist eine SOAP Funktion, mit der direkt Funktionen auf anderen
  * Calls- Seiten aufgerufen werden können.
  * Die Verwendung dieser Funktion sollte auf ein Minimum reduziert sein, da die
  * Funktionen zu REC Seiten immer in der gleichnamigen CALLS Seite existieren sollen.
  * @param CallsPage der Name der CALLS.asp Seite
  * @param fName der Name der Funktion
  * @param weitere Parameter werden dynamisch ausgewertet.
  * @return der Rueckgabewert der aufgerufenen Funktion
  * @exception ... werden weitergereicht.
*/
function P2SoapDirectCall(CallsPage, fName)
{
  var argc = arguments.length;
  var n;
  var a = new Array(argc - 2);

  if (!isCreated(CallsPage)) {
    // search internal Name by URL
    for (n in wsProxies) {
      if ((wsProxies[n].uri != null) && (wsProxies[n].uri.indexOf(CallsPage) > 0)) {
        CallsPage = n;
        break;
      }
    }
  } // if
  
  var wsProxy = getServiceOrFail(CallsPage);

  for (n = 0; n < a.length; n++)
    a[n] = arguments[n+2];
  return (P2SoapImpl(wsProxy, fName, a));
} // P2SoapDirectCall


/** Dies ist eine SOAP Funktion, mit der direkt Funktionen über einen http-get aufgerufen werden können.
  * Die Verwendung dieser Funktion sollte auf ein Minimum reduziert sein, da die
  * Funktionen zu REC Seiten immer in der gleichnamigen CALLS Seite existieren sollen.
  * @param url Die URL zum Web-Service incl. Parameter
  * @return der Rueckgabewert der aufgerufenen Funktion
  * @exception ... werden weitergereicht.
*/
function P2SoapUrlCall(url)
{
  var n;
  var result = null;
  if (_xmlDirectObj == null) {
    _xmlDirectObj = new ActiveXObject(XMLHTTPNAME);
  }
  n = 8;
  while (n > 0) {
    try {
      _xmlDirectObj.open("GET", url, false);
      _xmlDirectObj.setRequestHeader("SOAPAction", url);
      _xmlDirectObj.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
      _xmlDirectObj.send();
      n = -1;
    } catch (e) {
      n--;
    } // try
  } // while

  if (n == -1)
    result = _buildReturn(_xmlDirectObj, null);
  return(result);
} // P2SoapUrlCall


/// build a get-url for a WebMethod
/// <param name="alias">new registered alias name.</param>
/// <param name="fName">Name der Funktion.</param>
/// <return>Url zum Aufruf der WebMethod mit Platzhaltern für Parameter</return>
function GetWebMethodUrl(alias, fName) {
  var url = P2Service.get(alias).uri + "/" + fName;
  var oCall = P2Service.get(alias).methods[fName];
  var n;
  for (n = 0; n < oCall.args.length; n++) {
    url += "&" + oCall.args[n].name + "=\\" + n;
  } // for
  return(url.replace(/&/, "?"));
}


// The Client Scripts are always sent to the client
// The Server is checking for the rights.
function _P2AsyncSoapCall(alias, fName, func)
{
  var n, params;

  _cleanUpCancelObj();

  if (fName == null) {
    throw("P2Soap: Funktionsname nicht vorhanden.");

  } else if ((func == null) || (func == null)) {
    throw("P2Soap: async-Funktion nicht definiert.");

  } else {
    params = new Array(arguments.length - 3);
    for (n = 3; n < arguments.length; n++)
      params[n-3] = arguments[n];

    var wsProxy = getServiceOrFail(alias);
    
    n = _xmlCallFunc.length;
    _xmlCallPage[n] = alias;
    _xmlFunc[n] = wsProxy.methods[fName];
    _xmlCallFunc[n] = func;
    _xmlCallParams[n] = _buildSoapRequest(wsProxy, _xmlFunc[n], fName, params);
    _P2AsyncSoapNext();
  } // if
} // _P2AsyncSoapCall


// The Client Scripts are always sent to the client
// The Server is checking for the rights.
function _P2AsyncSoapUrlCall(url, func)
{
  var n;

  _cleanUpCancelObj();

  if (url == null) {
    throw("P2Soap: url Paramneter nicht vorhanden.");

  } else if ((func == null) || (func == null)) {
    throw("P2Soap: func Paramneter nicht definiert.");

  } else {
    n = _xmlCallFunc.length;
    _xmlCallPage[n] = url;
    _xmlCallFunc[n] = func;
    _xmlCallParams[n] = null; // parameters are in the url !
    _xmlFunc[n] = null;
    _P2AsyncSoapNext();
  } // if
} // _P2AsyncSoapUrlCall


/** */
function _P2AsyncSoapNext()
{
  var url;

  if ((_xmlCallObj == null) && (_xmlCallFunc.length > 0)) {

    if ((_xmlObj == null) && isIE) {
      _xmlObj = new ActiveXObject(XMLHTTPNAME);
    } else if ((_xmlObj == null) && (window.navigator.userAgent.indexOf("Gecko") > 0)) {
      _xmlObj = new XMLHttpRequest();
    } // if
    _xmlCallObj = _xmlObj;
	_xmlCallCancel = false;

    url = _xmlCallPage[0];
    if (_xmlCallParams[0] == null) {
      // url-Call
      _xmlObj.open("GET", url, true);
      _xmlObj.onreadystatechange = _P2AsyncSoapResult;
      _xmlObj.send(null);

    } else {
      // soap-Call
      _xmlObj.open("post", P2Service.get(url).uri, true);
      _xmlObj.setRequestHeader("SOAPAction", P2Service.get(url).ns + _xmlFunc[0].name);
      _xmlObj.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
      _xmlObj.onreadystatechange = _P2AsyncSoapResult;
      _xmlObj.send(_xmlCallParams[0]);
    } // if
  } // if
} // _P2AsyncSoapNext


/** cancel the actual waiting async Result. */
function _P2AsyncSoapCancelResult()
{
  if (_xmlCallObj != null) {
    _xmlCallCancel = true;
    
    if ((_xmlCallFunc.length > 0) && (_xmlCallObj.readyState >= 2) && (_xmlCallObj.readyState < 4)) {
      // clean up the stack
      TraceWrite("abort() called.");
	  _xmlCallObj.abort();
	  _xmlCallFunc = _xmlCallFunc.slice(1);
	  _xmlFunc = _xmlFunc.slice(1);
	  _xmlCallPage = _xmlCallPage.slice(1);
	  _xmlCallParams = _xmlCallParams.slice(1);
	  _xmlCallObj = null;
	  _xmlObj = null;
    } // if
  } // if
  _P2AsyncSoapNext();
} // _P2AsyncSoapCancelResult


// destroy canceled http request
function _cleanUpCancelObj() {
  if (_xmlCancelObj != null) {
    _xmlCancelObj.abort();
    _xmlCancelObj = null;
  } // if
} // _cleanUpCancelObj


// #181783
/** */
function _P2AsyncSoapResult()
{
  var x, result, href;
  var xObj = _xmlCallObj;
  var oCall = _xmlFunc[0];
  var func = _xmlCallFunc[0];
  // #181783
  var eText;
  
  if ((xObj != null) && (_xmlCallFunc.length > 0)) {
  
    if ((_xmlCallCancel == true) && (xObj.readyState >= 2)) {
      // abort & clean up the stack
	  xObj.abort();
      TraceWrite("abort() called.");
	  _xmlCallFunc = _xmlCallFunc.slice(1);
	  _xmlFunc = _xmlFunc.slice(1);
	  _xmlCallPage = _xmlCallPage.slice(1);
	  _xmlCallParams = _xmlCallParams.slice(1);
	  _xmlCallObj = null;
	  _xmlObj = null;
	  
    } else if (xObj.readyState == 4) {
	  _xmlCallFunc = _xmlCallFunc.slice(1);
	  _xmlFunc = _xmlFunc.slice(1);
	  _xmlCallPage = _xmlCallPage.slice(1);
	  _xmlCallParams = _xmlCallParams.slice(1);
	  _xmlCallObj = null;

	  if ((xObj != null) && (func != null) && (xObj.status > 0)) {
		if (xObj.status < 200 || xObj.status >= 400) {
		  var faultpos = xObj.responseText.toLowerCase().indexOf ( "<soap:fault>" );
		  var faultposend = xObj.responseText.toLowerCase().indexOf ( "</soap:fault>" );
		
          // #181783  
		  if(faultpos != -1 && faultposend != -1) {
		    eText = xObj.responseText.substring ( faultpos + 12 ,  faultposend );
		  } else {
		    eText = xObj.responseText;
		  }
		  
		  askException ( "P2Soap.js: Es ist ein Fehler im Hintergrundprozess aufgetreten:\n\n" + eText );
		} else {
		  result = _buildReturn(xObj, oCall);
		  func(result);
		} // if
      } // if
      _P2AsyncSoapNext();
    } // if
  }
} // _P2AsyncSoapResult


/** Dies ist die Implementierung der SOAP Funktion.
  * @param wsProxy Die Instanz des WebService proxies.
  * @param fName der Name der Funktion
  * @param args Die Liste der weiteren dynamisch ausgewerteten Argumente.
  * @return der Rueckgabewert der aufgerufenen Funktion
  * @exception ... werden weitergereicht.
*/
function P2SoapImpl(wsProxy, fName, args)
{
  var n;
  var aText, aName;
  var x, xResult;
  var oCall;

  _cleanUpCancelObj();

  
  // for the old usages, that pass a alias name:
  if(typeof(wsProxy) == "string") {
    var aliasName = wsProxy;
    wsProxy = getServiceOrFail(aliasName);
  }
  
  if (wsProxy == null)
    throw new Error(0x990002, "P2Soap: Die Server Referenz wurde nicht gesetzt.");

  if ((fName == null) || (fName == ""))
    throw new Error(0x990003, "P2Soap: Der Funktionsname ist nicht vorhanden.");

  oCall = wsProxy.methods[fName];

  aText = _buildSoapRequest(wsProxy, oCall, fName, args);

  if (_xmlDirectObj == null) {
    _xmlDirectObj = new ActiveXObject(XMLHTTPNAME);
  }
  
  n = 8;
  while (n > 0) {
    try {
      _xmlDirectObj.open("post", wsProxy.uri, false);
      _xmlDirectObj.setRequestHeader("SOAPAction", wsProxy.ns + fName);
      _xmlDirectObj.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
      _xmlDirectObj.send(aText);
      n = 0;
    } catch (e) {
      n--;
    } // try
  } // while

  xResult = _buildReturn(_xmlDirectObj, oCall);
  return(xResult);
} // P2SoapImpl


//#186733
/** construct the text for a soap-call. (P2plus internal Soap protocoll)
* @param wsProxy Die Instanz des WebService proxies.
* @param oCall Der Pointer auf eine WebService-Methode.
* @param fName name of function
* @param params value of parameters
* @return the xml text.
*/
function _buildSoapRequest(wsProxy, oCall, fName, params)
{
  var argc = params.length;
  var nodeText;
  var aName, type;
  var n, arg;
  var v;

  if(wsProxy == null || typeof(wsProxy) != "object") {
    throw new Error("P2Soap:_buildSoapRequest: No wsProxy parameter passed or it is no proxy object!");
  }
  
  if ( oCall == null || oCall == "undefined" )
    throw new Error(0x990003, "P2Soap: Die Funktion \"" + fName + "\" ist im WebService nicht vorhanden.");

  nodeText = "<?xml version='1.0' encoding='utf-8' ?>"
    + '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"'
    + ' xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"'
    + ' xmlns:xsd="http://www.w3.org/1999/XMLSchema">'
    + '<SOAP-ENV:Body>'
    + '<' + oCall.name + ' xmlns="' + wsProxy.ns + '">';

  for (n = 0; n < argc; n++) {
    if (params[n] == null)
      continue; // for

    v = params[n];

    aName = oCall.args[n].name;
    type = oCall.args[n].type;
    
    nodeText +=  '\n<' + aName + ' xsi:type="xsd:' + type + '" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">';
    if (type.indexOf("ArrayOf") == 0) { // is array
      var arrayType = type.substring(7); // read the array type, cut off the "ArrayOf"
      arrayType = capitalizeString(arrayType);
      for (i = 0; i < params[n].length; i++) {
        nodeText += "<" + arrayType + ">" + 
                    convertValueForSoap(params[n][i], arrayType, aName) + 
                    "</" + arrayType + ">";
      } // for
    } else {
      nodeText += convertValueForSoap(params[n], type, aName);
    }
    
    nodeText +=  '</' + aName + '>';
  } // for
  nodeText += '</' + oCall.name + '></SOAP-ENV:Body></SOAP-ENV:Envelope>';
  return(nodeText);
} // _buildSoapRequest


/** */
function _buildReturn(xmlHTTP, oCall)
{
  var xDom = xmlHTTP.responseXML;
  var ret = null;

  if (isIE)
    xDom.setProperty("SelectionNamespaces", "xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/' xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/' xmlns:s='http://www.w3.org/2001/XMLSchema' xmlns:soapenv ='http://schemas.xmlsoap.org/soap/envelope/'");

  var xNode = selectSingleNode(xDom, "//soapenv:Fault");

  if (xNode != null) {
    // Server created a exception, that was catched and is re-thrown here on client-side.
    var errObj = new Error();
    errObj.number = selectSingleNode(xNode, "faultcode|soapenv:faultcode").text;
    ret = selectSingleNode(xNode, "faultstring|soapenv:faultstring").text;
    // Encoding Problem in Response-Exceptions. #32998
    ret = ret.replace(/&#228;/g, "ä").replace(/&#246;/g, "ö").replace(/&#252;/g, "ü");    
    ret = ret.replace(/&#196;/g, "Ä").replace(/&#214;/g, "Ö").replace(/&#220;/g, "Ü");    
    ret = ret.replace(/&#223;/g, "ß");    
    errObj.description = ret;
    throw errObj;

  } else if (xmlHTTP.status == 202) {
      ret = null; // Accepted of a OneWay Message
      
  } else if (xmlHTTP.status != 200) {  // statusText != "OK"
    // 25.07.2000 MP
    // um bei einem HTTPError nicht null, sondern eine Fehlermeldung zu bekommen:
    var errObj = new Error();
    errObj.number = ((xmlHTTP.status != null) ? xmlHTTP.status : 0x990001);
    errObj.description = ((xmlHTTP.statusText != null) ? xmlHTTP.statusText : "unknown fault");
    throw errObj;

  } else if (oCall == null) {
    xNode = xDom.documentElement;
    ret = (isIE ? xNode.text : xNode.textContent);
    debugger;
    if (xNode.nodeName == "dateTime") {
      if ((typeof(P2Form) != "undefined") && (typeof(P2Form.Format_DateTime) != "undefined")) {
        ret = ret.replace("T", " ").substr(0, 19);
        ret = P2Form.Format_DateTime(ret);
      } else {
        ret = ret.substr(8, 2) + "." + ret.substr(5, 2) + "." + ret.substr(0, 4);
      }
    } else if ((xNode.nodeName == "number") || (xNode.nodeName == "decimal") || (xNode.nodeName == "double")) {
      if ((typeof(P2Form) != "undefined") && (typeof(P2Form.Format_Number) != "undefined")) {
        ret = P2Form.Format_Number(parseFloat(ret));
      } // if

    } else if (xNode.nodeName == "boolean") {
      ret = (ret == "true");
    } // if

  } else {
    //#186733
    if (oCall.resultName != null) {
      xNode = selectSingleNode(xDom, "/soapenv:Envelope/soapenv:Body/*/*");
    } else {
      xNode = selectSingleNode(xDom, "/soapenv:Envelope/soapenv:Body/*");
    }
    
    if (xNode != null) {
      if (oCall.returnType && oCall.returnType.indexOf("ArrayOf") == 0) { // an array
        ret = new Array();
        var i;
        
        for(i=0; i<xNode.childNodes.length; i++) {
          ret[ret.length] = parseSingleReturnValue(xNode.childNodes[i], xNode.childNodes[i].nodeName);
        }

      } else {
        
        if (xNode.firstChild == null) {
          ret = "";
        } else if (xNode.firstChild.nodeType == 3) {
          ret = parseSingleReturnValue(xNode.firstChild, oCall.returnType);
        } else if (xNode.firstChild.firstChild == null) {
          ret = innerXml(xNode.xml);
        } else {
          ret = parseSingleReturnValue(xNode.firstChild.firstChild, oCall.returnType);
        }
      }
    } // if
  } // if
  return(ret);
} // _buildReturn


//#186733
/** Parses the single return values of a webservice or is 
  * also called for every array value if an array is returned.
  * @param xNode the XmlNode from the SOAP message, that contains the value.
  * @param type The soap data type of the value.
  */
function parseSingleReturnValue(xNode, type) {
  var ret = xNode.text;
  
  
  // CUSTOM:BEGIN:ADN:AP:11.01.2009: FIX fuer Firefox
  if((typeof(ret) == "undefined" || ret == null) && (typeof(xNode.wholeText) != "undefined" && xNode.wholeText != null))
  {
    ret = xNode.wholeText;
  }
  // CUSTOM:END:ADN:AP:11.01.2009: FIX fuer Firefox
  
  
  // the type can be used to have different conversions into 
  // native JS types from the returned text values:
  if(type != null) {
    type = type.toLowerCase();
  }
  
  // this is old code, and today, we would use the fact, that the element name is <boolean> to convert it to a bool:
  if(typeof(ret) == "string") {
    if (ret.toLowerCase() == "true")
      ret = true;
    else if (ret.toLowerCase() == "false")
      ret = false;
  }
  
  return ret;
}


//#186733
/** Converts from java script objects to soap text representation.
  * @param value The value object.
  * @param type The soap type of the value.
  * @param paramName The name of the param. Currently only used for the error message.
  */
function convertValueForSoap(value, type, paramName) {
  var retValue;
  
  // the type can be used to have different conversions 
  // from native JS types into soap text values:
  type = type.toLowerCase();
  
  retValue = String(value);
  retValue = encodeXml(retValue);
  
  return retValue;
}


function innerXml(s)
{
  if ((s != null) && (s.length > 4)) {
    n = s.indexOf(">");
    if (n > 0) s = s.substr(n+1);
    n = s.lastIndexOf("<");
    if (n > 0) s = s.substr(0, n);
  }
  return(s);
} // innerXml


/// no Operation CallBack.
function _NOP(){}

// some inits
P2Service.use = registerWS;
P2Service.get = getService;
P2Service.useUrl = P2SoapUseUrl;
P2Service.useKnown = P2SoapUseKnow;

function inspectObj(obj) {
  var s = "InspectObj:";
  
  if (obj == null)
    s += "[null]";
  else if (obj.constructor == Array)
    s += " _ARRAY";
  else if (typeof(obj) == "function")
    s += " [function]" + obj;
      
  for (p in obj)
    try {
      if (obj[p] == null) {
        s += "\n" + String(p) + " (...)";

      } else if (typeof(obj[p]) == "function") {
        s += "\n" + String(p) + " [function]";

      } else if (obj[p].constructor == Array) {
        s += "\n" + String(p) + " [ARRAY]: " + obj[p];
        for (n = 0; n < obj[p].length; n++)
          s += "\n  " + n + ": " + obj[p][n];

      } else {
        s += "\n" + String(p) + " [" + typeof(obj[p]) + "]: " + obj[p];
      } // if
    } catch (e) { s+= e;}
  alert(s);
}

// End


/**
  * sucht ein bestimmtes Unterelement in einem XML Document.
  * @param node Startknoten für die Suche
  * @param XPath Anweisung
  * @return das gefundene Element
  */
function selectSingleNode(node, path) 
{
  var retnode;
  if (isIE) {
    retnode = node.selectSingleNode(path);
    
  } else {
    var doc = node;
    if (node.nodeType != 9) {
      doc = node.ownerDocument;
    } 
    node = doc.evaluate(path, node, NSResolver, XPathResult.ANY_UNORDERED_NODE_TYPE, null);
    if (node != null) {
      node = node.singleNodeValue;
    }
    retnode = node;
  } // if
  
  return(retnode);
} // selectSingleNode


/**
  * sucht ein bestimmtes Unterelement in einem XML Document.
  * @param node Startknoten für die Suche
  * @param XPath Anweisung
  * @return Array mit den gefundenen XML Elementen
  */
function selectNodes(node, path) 
{
  if (isIE) {
    return(node.selectNodes(path));
  } else {
    try {
      var doc = node;
      if (node.nodeType != 9)
        doc = node.ownerDocument;
  
      var xResult = doc.evaluate(path, node, NSResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
      // copy to an array
      var ret = new Array();
      for (var n = 0; n < xResult.snapshotLength; n++)
        ret[n] = xResult.snapshotItem(n);
      return(ret);
    } catch (ex) {
      alert(ex);
    }
  } // if
} // selectNodes


// CUSTOM:BEGIN:ADN:AP:01.09.2007: Nur bestimmte asynchrone Funktionen stoppen
function _P2AsyncSoapCancelSpecial(calledPage, calledFunction)
{
  if (_xmlCallObj != null && _xmlCallPage[0] == calledPage && _xmlCallParams[0].indexOf("<" + calledFunction + " ") > 0)
  {
    _P2AsyncSoapCancelResult();
  } 
}
// CUSTOM:BEGIN:ADN:AP:01.09.2007: Nur bestimmte asynchrone Funktionen stoppen






function absoluteUrl(aUrl)
{
  if ((aUrl != null) && (aUrl.length > 0)) {
    if (aUrl.substr(0, 3) == "/./")
      aUrl = location.href.split("/").slice(0,4).join("/") + aUrl.substr(2);
    else if (aUrl.substr(0, 2) == "~/")
      aUrl = location.href.split("/").slice(0,4).join("/") + aUrl.substr(1);
    else if (aUrl.substr(0, 18) == "http://server/web/")
      aUrl = location.href.split("/").slice(0,4).join("/") + aUrl.substr(17);
    else if (aUrl.charAt(0) == "/")
      aUrl = location.href.split("/").slice(0,3).join("/") + aUrl;
  }
  return(aUrl);
} // absoluteUrl


/** Konvertierung eines relativen Pfades in einen absoluten Pfad.
* @param BasePath absoluter Pfad des aktuellen Dokumentes (Base).
* @param DocPath relativer Pfad zu einer anderen Datei (Resource).
* @return absoluter Pfad zur Resource. */
function rel2absPath(BasePath, DocPath)
{
  var result;
  var l, n = 0;
  var isURLpath;
  
  if (DocPath.match(/^(http|https|ftp|mailto|about):/i) != null) {
    result = DocPath; // absolute path already
  } else if (DocPath.charAt(0) == '/') {
    BasePath = BasePath.split('/');
    result = BasePath.slice(0, 3).join('/') + DocPath; // server relative Path
  } else {
    isURLpath = (BasePath.toLowerCase().indexOf("url(") == 0);
    BasePath = BasePath.split("?")[0];
    BasePath = BasePath.split("#")[0];
    BasePath = BasePath.split("/");
    DocPath = DocPath.split("/");

    l = BasePath.length - 1; // all folders, not the document
    while ((n < DocPath.length) && (DocPath[n] == "..")) {
      l--;
      n++;
    } // while

    result = BasePath.slice(0, l).join("/");
    result += "/";
    result += DocPath.slice(n).join("/");
    if (isURLpath) result += ")";
  }
  return(result);
} // rel2absPath

/** Konvertierung eines absoluten Pfades in einen relativen Pfad.
* @param BasePath absoluter Pfad des aktuellen Dokumentes (Base).
* @param DocPath absoluter Pfad zu einer anderen Datei (Resource).
* @return relativer Pfad vom Dokument zur Resource. */
function abs2relPath(BasePath, DocPath)
{
  BasePath = BasePath;
  DocPath = DocPath;
  var result = DocPath;
  var l, n = 0;
  var isURLpath = (result.indexOf("url(") == 0);
  
  BasePath = BasePath.split("/");
  DocPath = DocPath.split("/");
  var minParts = Math.min(BasePath.length, DocPath.length);
  
  if ((minParts > 3) && (result.indexOf("..") < 0)) {
    // strip protokoll & identical folders
    while ((n < minParts) && (BasePath[n].toLowerCase() == DocPath[n].toLowerCase()))
      n++;

    if (n < minParts) {    
      result = (isURLpath) ? "url(" : "";
      // calc level of upDots
      l = BasePath.length - n;
      while (l-- > 1)
        result += "../";
    
      result += DocPath.slice(n).join("/");
    } // if
  } // if
  return(result);
} // abs2relPath

/** substitute special characters for use in QueryString Values
  * @param aText plain string.
  * @return encoded string.*/
function codeQS(aText)
{
  aText = String(aText);
  
  aText = encodeURIComponent(aText);
  
  // decode some special APplus placeholder chars again:
  aText = aText.replace(/%5B/g, "[");
  aText = aText.replace(/%5D/g, "]");
  aText = aText.replace(/%3B/g, ";");
  
  return(aText);
} // codeQS

/** substitute special characters for use in xml text.
  * @param aText plain string.
  * @return encoded string.*/
function encodeXml(aText)
{
  aText = String(aText);
  if (aText != null) {
    aText = aText.replace(/&/g, "&amp;");
    aText = aText.replace(/</g, "&lt;");
    aText = aText.replace(/>/g, "&gt;");
    //#186733
    aText = aText.replace(/\"/g, "&#x22;")
    aText = aText.replace(/\'/g, "&#x27;")
  } // if
  return(aText);
} // encodeXml