﻿/**
  * $Header: /Web36/WebObjects/P2Soap2.js 62    6.07.04 14:58 Mh $
  * ----------------------------------------------------------------
  * 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.
*/

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();

function P2SoapUse(aliasName, serviceUrl)
{
  var s, oCall;
  var op, opNode, opName;
  var msg, params;

  if (_xmlDirectObj == null) {
    _xmlDirectObj = new ActiveXObject(XMLHTTPNAME);
  }

  _xmlDirectObj.Open("GET", absoluteUrl(serviceUrl)+"?WSDL", false);
  _xmlDirectObj.send();

  var xmlSdl = _xmlDirectObj.responseXML;
  xmlSdl.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'");

  if ((xmlSdl == null) || (_xmlDirectObj.status != 200))
    throw new Error(0x990004, "P2Soap: Die Server Referenz wurde nicht gefunden.");

  var port = xmlSdl.selectSingleNode("/wsdl:definitions/wsdl:service/wsdl:port[soap:address]");
  s = port.getAttribute("binding").split(':');
  s = s[s.length-1];

  // get binding
  opNode = xmlSdl.selectSingleNode("/wsdl:definitions/wsdl:binding[@name=\"" + s + "\"]");
  s = opNode.getAttribute("type").split(':');
  s = s[s.length-1];

  var operations = xmlSdl.selectNodes("/wsdl:definitions/wsdl:portType[@name=\"" + s + "\"]/wsdl:operation");

  var oBinder = new Object();
  P2Service[aliasName] = oBinder;
  oBinder.methods = new Array();
  oBinder.uri = port.selectSingleNode("soap:address").getAttribute("location");

  opNode = xmlSdl.selectSingleNode("/wsdl:definitions/wsdl:types/s:schema");
  oBinder.ns = opNode.getAttribute("targetNamespace");

  for (op = 0; op < operations.length; op++) {
    opNode = operations[op];
    opName = opNode.getAttribute("name");
    oCall = oBinder.methods[opName] = new Object();
    oCall.name = opName;
    oCall.args = new Array();

    // find message description
    s = opNode.selectSingleNode("input|wsdl:input").getAttribute("message").split(':');
    s = s[s.length-1];
    msg = xmlSdl.selectSingleNode("/wsdl:definitions/wsdl:message[@name=\"" + s + "\"]/wsdl:part");

    // find parameters
    s = msg.getAttribute("element").split(':');
    s = s[s.length-1];
    params = xmlSdl.selectNodes("/wsdl:definitions/wsdl:types/s:schema/s:element[@name=\"" + s + "\"]//s:element");
    for (var n = 0; n < params.length; n++) {
      oCall.args[n] = new Object();
      oCall.args[n].name = params[n].getAttribute("name");
      s = params[n].getAttribute("type").split(':');
      oCall.args[n].type = s[s.length-1];
    } // for

    // find message result
    params = opNode.selectSingleNode("output|wsdl:output");
    if (params != null) {
      s = params.getAttribute("message").split(':');
      s = s[s.length-1];
      msg = xmlSdl.selectSingleNode("/wsdl:definitions/wsdl:message[@name=\"" + s + "\"]/wsdl:part");
      s = msg.getAttribute("element").split(':');
      oCall.resultName = s[s.length-1];
    } // if

    s = "new Function(";
    for (var n = 0; n < oCall.args.length; n++) s += '"p' + n + '", ';
    s += "\"return P2SoapImpl('" + aliasName + "', '" + opName + "', arguments, 0)\")";
    oBinder[opName] = eval(s);
  } // for
}

/// <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 (P2Service[aliasName] == null)
    P2Service[aliasName] = new Object();
  oBinder = P2Service[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[aliasName];

  oCall = oBinder.methods[mName] = new Object();
  oCall.soapAction = mName;
  oCall.args = new Array();
  oCall.name = mName;
  oCall.resultName = mName + "Response";

  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('" + aliasName + "', '" + 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 (P2Service[CallsPage] == null) {
    // search internal Name by URL
    for (n in P2Service) {
      if ((P2Service[n].uri != null) && (P2Service[n].uri.indexOf(CallsPage) > 0)) {
        CallsPage = n;
        break;
      }
    }
  } // if

  for (n = 0; n < a.length; n++)
    a[n] = arguments[n+2];
  return (P2SoapImpl(CallsPage, 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[alias].uri + "/" + fName;
  var oCall = P2Service[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 arg, argc = arguments.length;
  var n, params;
  var oCall = P2Service[alias].methods[fName];
  var nodeText;
  var resultText;

  _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];

    n = _xmlCallFunc.length;
    _xmlCallPage[n] = alias;
    _xmlFunc[n] = oCall;
    _xmlCallFunc[n] = func;
    _xmlCallParams[n] = _buildSoapRequest(alias, oCall, fName, params);
    _P2AsyncSoapNext();
  } // if
} // _P2AsyncSoapCall


/** */
function _P2AsyncSoapNext()
{
  var url, fName;

  if ((_xmlCallObj == null) && (_xmlCallFunc.length > 0)) {
    if (_xmlObj == null) {
      _xmlObj = new ActiveXObject(XMLHTTPNAME);
    }
    _xmlCallObj = _xmlObj;
	_xmlCallCancel = false;
    url = _xmlCallPage[0];
    fName = _xmlFunc[0].name;
    _xmlObj.Open("post", P2Service[url].uri, true);
    _xmlObj.setRequestHeader("SOAPAction", P2Service[url].ns + fName);
    _xmlObj.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    _xmlObj.onreadystatechange = _P2AsyncSoapResult;
    _xmlObj.send(_xmlCallParams[0]);
  } // 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


/** */
function _P2AsyncSoapResult()
{
  var x, result, href;
  var xObj = _xmlCallObj;
  var oCall = _xmlFunc[0];
  var func = _xmlCallFunc[0];

  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>" );
		  var eText = xObj.responseText.substring ( faultpos + 12 ,  faultposend );
		  if (eText.indexOf ("com.apag.p2plus.p2core.p2exceptions") < 0 
			&& (_P2SoapDysplayAsyncError == true)
			&& (eText.indexOf("System.Web.Services.Protocols.SoapException") < 0)
			&& eText.indexOf("System.Net.WebException") < 0)
			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 CallsPage der Name der CALLS.asp Seite
  * @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(alias, fName, args)
{
  var n, arg, argc = args.length;
  var aText, aName;
  var x, xResult;
  var oCall;

  _cleanUpCancelObj();

  if ((alias == null) || (alias == ""))
    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 = P2Service[alias].methods[fName];

  aText = _buildSoapRequest(alias, oCall, fName, args);

  if (_xmlDirectObj == null) {
    _xmlDirectObj = new ActiveXObject(XMLHTTPNAME);
  }
  
  n = 8;
  while (n > 0) {
    try {
      _xmlDirectObj.Open("post", P2Service[alias].uri, false);
      _xmlDirectObj.setRequestHeader("SOAPAction", P2Service[alias].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


/** construct the text for a soap-call. (P2plus internal Soap protocoll)
* @param fName name of function
* @param params value of parameters
* @return the xml text.
*/
function _buildSoapRequest(alias, oCall, fName, params)
{
  var argc = params.length;
  var nodeText;
  var aName;
  var n, arg;

  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="' + P2Service[alias].ns + '">';

  for (n = 0; n < argc; n++) {
    if (params[n] == null)
      continue; // for

    aName = oCall.args[n].name;
    arg = String(params[n]);
    nodeText +=  '\n<' + aName + ' xsi:type="xsd:' + oCall.args[n].type + '" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">';
    arg = arg.replace(/&/g, "&#x26;");
    arg = arg.replace(/</g, "&#x3c;");
    arg = arg.replace(/>/g, "&#x3e;");
    arg = arg.replace(/\"/g, "&#x22;");
    arg = arg.replace(/\'/g, "&#x27;");
    nodeText +=  arg;
    nodeText +=  '</' + aName + '>';
  } // for
  nodeText += '</' + oCall.name + '></SOAP-ENV:Body></SOAP-ENV:Envelope>';
  return(nodeText);
} // _buildSoapRequest


/** */
function _buildReturn(xmlHTTP, oCall)
{
  var xDom = xmlHTTP.responseXML;
  var xNode;
  var ret = null;

  xNode = xDom.selectSingleNode("//soap: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 = xNode.selectSingleNode("faultcode|soap:faultcode").text;
    errObj.description = xNode.selectSingleNode("faultstring|soap:faultstring").text;
    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 = xNode.text;

    if (xNode.nodeName == "dateTime") {
      if ((typeof(P2Form) != "undefined") && (typeof(P2Form.Format_DateTime) != "undefined")) {
        ret = new Date(Date(ret)).getVarDate();
        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 {
    if (oCall.resultName != null)
      xNode = xDom.selectSingleNode("//" + oCall.resultName + "/*");
    else
      xNode = xDom.selectSingleNode("//soap:Body/*");

    if (xNode != null) {
      if (xNode.firstChild == null) {
        ret = "";
      } else if (xNode.firstChild.nodeType == 3) {
        ret = xNode.firstChild.nodeValue;
      } else if (xNode.firstChild.firstChild == null) {
        ret = innerXml(xNode.xml);
      } else {
        ret = xNode.firstChild.firstChild.nodeValue;
      } // if
      if (ret.toLowerCase() == "true")
        ret = true;
      else if (ret.toLowerCase() == "false")
        ret = false;
    } // if
  } // if
  return(ret);
} // _buildReturn


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 = P2SoapUse;
P2Service.useUrl = P2SoapUseUrl;
P2Service.useKnown = P2SoapUseKnow;

// End

