Source Code

for file /ajaxcore/ajax.js

// ajax.js
// Common Javascript methods and global objects
// Ajax framework for Internet Explorer (6.0, ...) and Firefox (1.0, ...)
// Copyright (c) by Matthias Hertel, http://www.mathertel.de
// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx
// More information on: http://ajaxaspects.blogspot.com/ and http://ajaxaspekte.blogspot.com/
// -----
// 05.06.2005 created by Matthias Hertel.
// 19.06.2005 minor corrections to webservices.
// 25.06.2005 ajax action queue and timing.
// 02.07.2005 queue up actions fixed.
// 10.07.2005 ajax.timeout
// 10.07.2005 a option object that is passed from ajax.Start() to prepare() is also queued.
// 10.07.2005 a option object that is passed from ajax.Start() to prepare(), finish()
//            and onException() is also queued.
// 12.07.2005 correct xml encoding when CallSoap()
// 20.07.2005 more datatypes and XML Documents
// 20.07.2005 more datatypes and XML Documents fixed
// 06.08.2005 caching implemented.
// 07.08.2005 bugs fixed, when queuing without a delay time.
// 04.09.2005 bugs fixed, when entering non-multiple actions.
// 07.09.2005 proxies.IsActive added
// 27.09.2005 fixed error in handling bool as a datatype
// 13.12.2005 WebServices with arrays on strings, ints, floats and booleans - still undocumented
// 27.12.2005 fixed: empty string return values enabled.
// 27.12.2005 enable the late binding of proxy methods.
// 21.01.2006 void return bug fixed.
// 18.02.2006 typo: Finsh -> Finish.
// 25.02.2006 better xmlhttp request object retrieval, see http://blogs.msdn.com/ie/archive/2006/01/23/516393.aspx
// 22.04.2006 progress indicator added.
// 28.01.2006 void return bug fixed again?
// 09.03.2006 enable late binding of prepare and finish methods by using an expression.
// 14.07.2006 Safari Browser Version 2.03/Mac OS X 10.4. compatibility: xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue
// 10.08.2006 date to xml format fixed by Kars Veling
// 16.09.2006 .postUrl -- unfinished...
// 26.11.2006 enable null for xml based objects
// 19.05.2007 enabling ajax engine calls with multiple parameters
// 14.07.2007 xml2json added.
// 14.09.2007 ajax._resolve implemented to avoid the eval function
// 19.09.2007 lots of changes to get a better code as suggested by Breton Slivka (Thanks)
// 01.10.2007 ... more of it.
// 13.10.2007 structured parameter support for calling webservice methods
// 16.10.2007 xml2json bug fixed
// 10.11.2008 better documentation support.
// 10.11.2008 inspectText added.
// 30.11.2008 ajax.StartLocalAction added.
// 03.01.2009 better handling of the result namespace
// 27.04.2009 some issues when using ajax.js standalone, including the xml compatible code
//            in FireFOx for XMLDocument.selectSingleNode
// 18.06.2011 IE9 compatibility issue with XML return values fixed.

// ----- global variable for the proxies to webservices. -----

var proxies = function () {
  /// <summary>The root object for the proxies to webservices.</summary>
};

if (window.OpenAjax && window.OpenAjax.hub) {
  OpenAjax.hub.registerLibrary("proxies", "http://www.mathertel.de/proxies", "1.5", {});
} // if

proxies.current = null; // the current active webservice call.
proxies.xmlhttp = null; // The current active xmlhttp object.


// ----- global variable for the ajax engine. -----

var ajax = {
  /// <summary>The root object for the ajax engine implementation.</summary>
  /// <field name="current" type="ajax.Action">The current active AJAX action.</field>
  current: null,
  /// <field name="option" type="Object">The options for the current active AJAX action.</field>
  option: null,

  /// <field name="queue" type="Array">The pending AJAX actions.</field>
  queue: [],
  /// <field name="options" type="Array">The options for the pending AJAX actions.</field>
  options: [],

  /// <field name="isIE" type="Boolean">Detect InternetExplorer for some specific implementation differences.</field>
  isIE: (window.navigator.userAgent.indexOf("MSIE") > 0)

};

if (window.OpenAjax && window.OpenAjax.hub) {
  OpenAjax.hub.registerLibrary("ajax", "http://www.mathertel.de/ajax", "1.5", {});
} // if

ajax.timer = null; /// The timer for delayed actions.

ajax.progress = false; /// show a progress indicator
ajax.progressTimer = null; /// a timer-object that help displaying the progress indicator not too often.

// ----- AJAX engine and actions implementation -----

ajax.Action = {
  /// <summary>An action object with default values for declaring the details of ajax actions.</summary>
  /// <field name="queueClear" type="Boolean">If set to true, the queue will be cleard before this action is added.</field>
  queueClear: false,
  queueTop: false,
  queueMultiple: true,
  /// <field name="call" type="Function">The call that invokes some action on the server.</field>
  call: null,
  prepare: null,
  finish: null
}; // Action


ajax.Start = function (action, options) {
  ///<summary>Start an AJAX action by entering it into the queue</summary>
  ///<param name="action" type="ajax.Action">An object declaring the ajax action that should be executed.</param>
  ///<param name="options" type="Object">A optional parameter that is passed to the action methods.</param>

  ajax.Add(action, options);
  // check if the action should start
  if ((!ajax.current) && (!ajax.timer)) {
    ajax._next(false);
  } // if
}; // ajax.Start


ajax.StartLocalAction = function (method, options) {
  ///<summary>Start an non-AJAX action that actls only locally in the browser by entering it into the queue</summary>
  ///<param name="method" type="Function">The method that will be called.</param>
  ///<param name="options" type="Object">A optional parameter that is passed to the action methods.</param>
  var a = { queueClear: false, queueTop: false, queueMultiple: true }; // simple action
  a.delay = 1;
  a.prepare = method;
  ajax.Start(a, options);
};  // ajax.StartLocalAction


ajax.Add = function (action, options) {
  ///<summary>Add an AJAX action by entering it into the queue without starting it.</summary>
  ///<param name="action" type="Action">An object declaring the ajax action that should be executed.</param>
  ///<param name="options" type="Object">A optional parameter that is passed to the action methods.</param>
  if (!action) {
    alert("ajax.Start: Argument action must be set.");
    return;
  } // if

  // enable the late binding of the methods by using a string that is evaluated.
  if (typeof (action.call) === "string") action.call = ajax._resolve(action.call);
  if (typeof (action.prepare) === "string") action.prepare = ajax._resolve(action.prepare);
  if (typeof (action.finish) === "string") action.finish = ajax._resolve(action.finish);

  if ((action.queueClear) && (action.queueClear === true)) {
    ajax.queue = [];
    ajax.options = [];

  } else if ((ajax.queue.length > 0) && ((!action.queueMultiple) || (action.queueMultiple === false))) {
    // remove existing action entries from the queue and clear a running timer
    if ((ajax.timer) && (ajax.queue[0] === action)) {
      window.clearTimeout(ajax.timer);
      ajax.timer = null;
    } // if

    var n = 0;
    while (n < ajax.queue.length) {
      if (ajax.queue[n] === action) {
        ajax.queue.splice(n, 1);
        ajax.options.splice(n, 1);
      } else {
        n++;
      } // if
    } // while
  } // if

  if ((!action.queueTop) || (action.queueTop === false)) {
    // to the end.
    ajax.queue.push(action);
    ajax.options.push(options);

  } else {
    // to the top
    ajax.queue.unshift(action);
    ajax.options.unshift(options);
  } // if
}; // ajax.Add


ajax._next = function (forceStart) {
  ///<summary>Check, if the next AJAX action can start.
  ///This is an internal method that should not be called from external.</summary>
  ///<remarks>for private use only.<remarks>
  var ca = null; // current action
  var co = null; // current opptions
  var data = null;

  if (ajax.current)
    return; // a call is active: wait more time

  if (ajax.timer)
    return; // a call is pendig: wait more time

  if (ajax.queue.length === 0)
    return; // nothing to do.

  ca = ajax.queue[0];
  co = ajax.options[0];
  if ((forceStart === true) || (!ca.delay) || (ca.delay === 0)) {
    // start top action
    ajax.current = ca;
    ajax.queue.shift();
    ajax.option = co;
    ajax.options.shift();

    // get the data
    if (ca.prepare) {
      try {
        data = ca.prepare(co);
      } catch (ex) { }
    } // if

    if (ca.call) {
      ajax.StartProgress();

      // start the call
      ca.call.func = ajax.Finish;
      ca.call.onException = ajax.Exception;
      if ((data) && (data.constructor === Array) && (data.multi)) // 19.05.2007
        ca.call.apply(ca, data);
      else
        ca.call(data);
      // start timeout timer
      if (ca.timeout) {
        ajax.timer = window.setTimeout(ajax.Cancel, ca.timeout * 1000);
      } // if

    } else if (ca.postUrl) {
      // post raw data to URL

    } else {
      // no call
      ajax.Finish(data);
    } // if

  } else {
    // start a timer and wait
    ajax.timer = window.setTimeout(ajax.EndWait, ca.delay);
  } // if
}; // ajax._next


ajax.EndWait = function () {
  ///<summary>The delay time of an action is over.</summary>
  ajax.timer = null;
  ajax._next(true);
}; // ajax.EndWait


ajax.Cancel = function () {
  ///<summary>The current action timed out.</summary>
  proxies.cancel(false); // cancel the current webservice call.
  ajax.timer = null;
  ajax.current = null;
  ajax.option = null;
  ajax.EndProgress();
  window.setTimeout(ajax._next, 200); // give some to time to cancel the http connection.
}; // ajax.Cancel


ajax.Finish = function (data) {
  ///<summary>Finish an AJAX Action the normal way</summary>
  // clear timeout timer if set
  if (ajax.timer) {
    window.clearTimeout(ajax.timer);
    ajax.timer = null;
  } // if

  // use the data
  try {
    if ((ajax.current) && (ajax.current.finish))
      ajax.current.finish(data, ajax.option);
  } catch (ex) { }
  // reset the running action
  ajax.current = null;
  ajax.option = null;
  ajax.EndProgress();
  ajax._next(false);
}; // ajax.Finish


ajax.Exception = function (ex) {
  ///<summary>Finish an AJAX Action with an exception</summary>
  // use the data
  if (ajax.current.onException)
    ajax.current.onException(ex, ajax.option);

  // reset the running action
  ajax.current = null;
  ajax.option = null;
  ajax.EndProgress();
}; // ajax.Exception


ajax.CancelAll = function () {
  ///<summary>Clear the current and all pending AJAX actions.</summary>
  ajax.Cancel();
  // clear all pending AJAX actions in the queue.
  ajax.queue = [];
  ajax.options = [];
}; // ajax.CancelAll


// ----- show or hide a progress indicator -----

ajax.StartProgress = function () {
  ///<summary>Show a progress indicator if it takes longer...</summary>
  ajax.progress = true;
  if (ajax.progressTimer)
    window.clearTimeout(ajax.progressTimer);
  ajax.progressTimer = window.setTimeout(ajax.ShowProgress, 220);
}; // ajax.StartProgress


ajax.EndProgress = function () {
  ///<summary>Hide any progress indicator soon.</summary>
  ajax.progress = false;
  if (ajax.progressTimer)
    window.clearTimeout(ajax.progressTimer);
  ajax.progressTimer = window.setTimeout(ajax.ShowProgress, 20);
}; // ajax.EndProgress


ajax.ShowProgress = function () {
  ///<summary>This function is called by a timer to show or hide a progress indicator.</summary>
  ajax.progressTimer = null;
  var a = document.getElementById("AjaxProgressIndicator");
  var s;
  if (ajax.progress && (a)) {
    // just display the existing object
    a.style.top = document.documentElement.scrollTop + 2 + "px";
    a.style.display = "";

  } else if (ajax.progress) {

    // find a relative link to the ajaxcore folder containing ajax.js
    var path = "../ajaxcore/";
    for (var n in document.scripts) {
      s = document.scripts[n].src;
      if ((s) && (s.length >= 7) && (s.substr(s.length - 7).toLowerCase() === "ajax.js"))
        path = s.substr(0, s.length - 7);
    } // for

    // create new standard progress object
    a = document.createElement("div");
    a.id = "AjaxProgressIndicator";
    a.style.position = "absolute";
    a.style.right = "2px";
    a.style.top = document.documentElement.scrollTop + 2 + "px";
    a.style.width = "98px";
    a.style.height = "16px";
    a.style.padding = "2px";
    a.style.verticalAlign = "bottom";
    a.style.backgroundColor = "#51c77d";

    a.innerHTML = "<img style='vertical-align:bottom' src='" + path + "ajax-loader.gif'>&nbsp;please wait...";
    document.body.appendChild(a);

  } else if (a) {
    a.style.display = "none";
  } // if
}; // ajax.ShowProgress


ajax._resolve = function (s) {
  ///<summary>Resolve a method reference given as a string
  /// by following the objects from the window object down to the concrete method.</summary>
  var ref = null;
  if ((s) && (s.length != 0)) {
    ref = window;
    s = s.split('.');
    for (var n = 0; (n < s.length) && (ref); n++)
      ref = ref[s[n]];
  } // if
  return (ref);
}; // _resolve


// ----- simple http-POST server call -----

ajax.postData = function (url, data, func) {
  var x = proxies._getXHR();

  // enable cookieless sessions:
  var cs = document.location.href.match(/\/\(.*\)\//);
  if (cs) {
    url = url.split('/');
    url[3] += cs[0].substr(0, cs[0].length - 1);
    url = url.join('/');
  } // if

  x.open("POST", url, (func));

  if (func) {
    // async call with xmlhttp-object as parameter
    x.onreadystatechange = func;
    x.send(data);

  } else {
    // sync call
    x.send(data);
    return (x.responseText);
  } // if
}; // ajax.postData


proxies.callSoap = function (args) {
  ///<summary>Execute a soap call.
  ///Build the xml for the call of a soap method of a webservice and post it to the server.</summary>
  var p = args.callee;
  var x = null;
  var n;
  // check for existing cache-entry
  if (p._cache) {
    if ((p.params.length === 1) && (args.length === 1) && (p._cache[args[0]])) {
      if (p.func) {
        p.func(p._cache[args[0]]);
        return (null);
      } else {
        return (p._cache[args[0]]);
      } // if
    } else {
      p._cachekey = args[0];
    } // if
  } // if

  proxies.current = p;
  x = proxies._getXHR();
  proxies.xmlhttp = x;

  // envelope start
  var soap = "<?xml version='1.0' encoding='utf-8'?>"
    + "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>"
    + "<soap:Body>"
    + "<" + p.fname + " xmlns='" + p.service.ns + "'>";

  // parameters
  for (n = 0; (n < p.params.length) && (n < args.length); n++) {
    var val = args[n];
    var typ = p.params[n].split(':');

    if ((typ.length === 1) || (typ[1] === "string")) {
      val = String(args[n]).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");

    } else switch (typ[1]) {
      case "int":
        val = parseInt(args[n], 10);
        break;
      case "float":
        val = parseFloat(args[n]);
        break;
      case "x":
        if (typeof (args[n]) === "string") {
          val = args[n];

        } else if (window.XMLSerializer) {
          val = (new XMLSerializer()).serializeToString(args[n].firstChild);

        } else if (args[n]) {
          val = args[n].xml;
        } // if
        break;
      case "ds": // 12.10.2007 complex parameter support
        val = "";
        if (typeof (args[n]) === "string") {
          val = args[n]; // inner xml

        } else if (args[n].constructor === Object) {
          var obj = args[n];
          for (var prop in obj) {
            val += "<" + prop + ">";
            val += String(obj[prop]).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
            val += "</" + prop + ">";
          }

        } else if (args[n]) {
          var xprop = args[n].documentElement.firstChild;
          while (xprop != null) {
            val += "<" + xprop.tagName + ">";
            val += String(xprop.text || xprop.textContent).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
            val += "</" + xprop.tagName + ">";
            xprop = xprop.nextSibling;
          } // for

        } // if
        break;
      case "bool":
        if (typeof (args[n]) === "string") {
          val = args[n].toLowerCase();
        } else {
          val = String(args[n]).toLowerCase();
        } // if
        break;
      case "date":
        // calculate the xml format for datetime objects from a javascript date object
        var s, ret;
        ret = String(val.getFullYear());
        ret += "-";
        s = String(val.getMonth() + 1);
        ret += (s.length === 1 ? "0" + s : s);
        ret += "-";
        s = String(val.getDate());
        ret += (s.length === 1 ? "0" + s : s);
        ret += "T";
        s = String(val.getHours());
        ret += (s.length === 1 ? "0" + s : s);
        ret += ":";
        s = String(val.getMinutes());
        ret += (s.length === 1 ? "0" + s : s);
        ret += ":";
        s = String(val.getSeconds());
        ret += (s.length === 1 ? "0" + s : s);
        val = ret;
        break;
      case "s[]":
        val = proxies._wrapArray2Xml(args[n], "string");
        break;
      case "int[]":
        val = proxies._wrapArray2Xml(args[n], "int");
        break;
      case "float[]":
        val = proxies._wrapArray2Xml(args[n], "float");
        break;
      case "bool[]":
        val = proxies._wrapArray2Xml(args[n], "boolean");
        break;
    } // switch
    soap += "<" + typ[0] + ">" + val + "</" + typ[0] + ">";
  } // for

  // envelope end
  soap += "</" + p.fname + ">"
    + "</soap:Body>"
    + "</soap:Envelope>";

  // enable cookieless sessions:
  var u = p.service.url;
  var cs = document.location.href.match(/\/\(.*\)\//);
  if (cs) {
    u = p.service.url.split('/');
    u[3] += cs[0].substr(0, cs[0].length - 1);
    u = u.join('/');
  } // if

  x.open("POST", u, (p.func != null));
  try { x.responseType = 'msxml-document'; } catch (e) { }
  x.setRequestHeader("SOAPAction", p.action);
  x.setRequestHeader("Content-Type", "text/xml; charset=utf-8");

  if (p.corefunc) {
    // async call with xmlhttp-object as parameter
    x.onreadystatechange = p.corefunc;
    x.send(soap);

  } else if (p.func) {
    // async call
    x.onreadystatechange = proxies._response;
    x.send(soap);

  } else {
    // sync call
    x.send(soap);
    return (proxies._response());
  } // if
}; // proxies.callSoap


proxies.cancel = function (raise) {
  ///<summary>Cancel the running webservice call.</summary>
  //<param name="raise" type="Boolean">If set to true an exception will be thrown through the onException function.</param>
  var cc = proxies.current;
  var cx = proxies.xmlhttp;

  if (!raise) raise === true;

  if (cx) {
    cx.onreadystatechange = function () { };
    cx.abort();
    if (raise && (cc.onException))
      cc.onException("WebService call was canceled.");
    proxies.current = null;
    proxies.xmlhttp = null;
  } // if
}; // proxies.cancel


proxies.EnableCache = function (px) {
  ///<summary>Attach an empty _cache object.</summary>
  ///<param name="px" type="Function">A proxies.service.func object.</param>
  px._cache = {};
}; // proxies.EnableCache


proxies.IsActive = function () {
  ///<summary>Check, if a call is currently waiting for a result.</summary>
  return (proxies.xmlhttp);
}; // proxies.IsActive


proxies._response = function () {
  ///<summary>Callback method for a webservice call that dispatches the response to servive.func or service.onException.</summary>
  ///<remarks>for private use only.<remarks>
  var ret = null;
  var n = null;
  var x = proxies.xmlhttp;
  var cc = proxies.current;
  var rtype = null;

  if ((cc.rtype.length > 0) && (cc.rtype[0]))
    rtype = cc.rtype[0].split(':');

  if ((x) && (x.readyState === 4)) {
    if (x.status === 200) {
      var xNode = null;

      if (rtype) {

        if (window.DOMParser) {
          // IE 9 ...
          var parser = new DOMParser();
          xNode = parser.parseFromString(x.responseText, "text/xml");
          xNode = xNode.getElementsByTagName(rtype[0])[0];
          // doc->(envelope)->(body)->(response)->RESULT (avoiding namespace alias problems

        } else if (ajax.isIE) {
          // old IE versions
          x.responseXML.setProperty("SelectionLanguage", "XPath");
          xNode = x.responseXML.selectSingleNode("//*[local-name()='" + rtype[0] + "']");

        } else {
          //FireFox
          xNode = x.responseXML.getElementsByTagName(rtype[0])[0];
        } // if
      } // if

      if (!xNode) {
        ret = null;

      } else if (!xNode.firstChild) { // 27.12.2005: empty string return values
        ret = ((rtype.length === 1) || (rtype[1] === "string") ? "" : null);

      } else if ((rtype.length === 1) || (rtype[1] === "string")) {
        ret = proxies._getAnyXmlText(xNode);

      } else switch (rtype[1]) {
        case "bool":
          ret = proxies._getAnyXmlText(xNode);
          ret = (ret === "true");
          break;
        case "int":
          ret = proxies._getAnyXmlText(xNode);
          ret = parseInt(ret, 10);
          break;
        case "float":
          ret = proxies._getAnyXmlText(xNode);
          ret = parseFloat(ret);
          break;
        case "x":
          xNode = xNode.firstChild;
          if (xNode.xml) {
            ret = ajax._getXMLDOM(xNode.xml);
          } else if (window.XMLSerializer) {
            var xs = new window.XMLSerializer();
            ret = xs.serializeToString(xNode);
            ret = ajax._getXMLDOM(ret);
          } // if
          break;
        case "ds":
          if (window.XMLSerializer) {
            ret = (new window.XMLSerializer()).serializeToString(xNode);
            ret = ajax._getXMLDOM(ret);
          } else {
            ret = xNode.xml;
            ret = ajax._getXMLDOM(ret);
          } // if
          break;
        case "s[]":
          // Array of strings
          ret = [];
          xNode = xNode.firstChild;
          while (xNode) {
            ret.push(proxies._getAnyXmlText(xNode));
            xNode = xNode.nextSibling;
          } // while
          break;
        case "int[]":
          // Array of int
          ret = [];
          xNode = xNode.firstChild;
          while (xNode) {
            ret.push(parseInt(proxies._getAnyXmlText(xNode)));
            xNode = xNode.nextSibling;
          } // while
          break;
        case "float[]":
          // Array of float
          ret = [];
          xNode = xNode.firstChild;
          while (xNode) {
            ret.push(parseFloat(proxies._getAnyXmlText(xNode)));
            xNode = xNode.nextSibling;
          } // while
          break;
        case "bool[]":
          // Array of bool
          ret = [];
          xNode = xNode.firstChild;
          while (xNode) {
            ret.push((proxies._getAnyXmlText(xNode)).toLowerCase() === "true");
            xNode = xNode.nextSibling;
          } // while
          break;
        default:
          ret = proxies._getAnyXmlText(xNode);
          break;
      } // switch

      // store to _cache
      if ((cc._cache) && (cc._cachekey)) {
        cc._cache[cc._cachekey] = ret;
        cc._cachekey = null;
      } // if

      proxies.xmlhttp = null;
      proxies.current = null;

      if (!cc.func) {
        return (ret); // sync
      } else {
        cc.func(ret); // async
        return (null);
      } // if

    } else if (!proxies.current.onException) {
      // no exception

    } else {
      // raise an exception
      ret = new Error();

      if (x.status === 404) {
        ret.message = "The webservice could not be found.";

      } else if (x.status === 500) {
        ret.name = "SoapException";

        if (ajax.isIE)
          x.responseXML.setProperty("SelectionLanguage", "XPath");

        n = x.responseXML.selectSingleNode("//*[local-name()='Fault']/faultcode");
        if (n) ret.message = n.firstChild.nodeValue;
        n = x.responseXML.selectSingleNode("//*[local-name()='Fault']/faultstring");
        if (n) ret.description = n.firstChild.nodeValue;

      } else if ((x.status === 502) || (x.status === 12031)) {
        ret.message = "The server could not be found.";

      } else {
        // no classified response.
        ret.message = "Result-Status:" + x.status + "\n" + x.responseText;
      } // if
      proxies.current.onException(ret);
    } // if

    proxies.xmlhttp = null;
    proxies.current = null;
  } // if
};      // proxies._response


proxies.alertResult = function () {
  ///<summary>Callback method to show the result of a soap call in an alert box.</summary>
  ///<remarks>To set up a debug output in an alert box use:
  ///proxies.service.method.corefunc = proxies.alertResult;</remarks>
  var x = proxies.xmlhttp;

  if (x.readyState === 4) {
    if (x.status === 200) {
      if (!x.responseXML.documentElement.firstChild.firstChild.firstChild)
        alert("(no result)");
      else
        alert(x.responseXML.documentElement.firstChild.firstChild.firstChild.firstChild.nodeValue);

    } else if (x.status === 404) {
      alert("Error!\n\nThe webservice could not be found.");

    } else if (x.status === 500) {
      // a SoapException
      var ex = new Error();
      ex.name = "SoapException";
      var n = x.responseXML.documentElement.firstChild.firstChild.firstChild;
      while (n) {
        if (n.nodeName === "faultcode") ex.message = n.firstChild.nodeValue;
        if (n.nodeName === "faultstring") ex.description = n.firstChild.nodeValue;
        n = n.nextSibling;
      } // while
      alert("The server threw an exception.\n\n" + ex.message + "\n\n" + ex.description);

    } else if (x.status === 502) {
      alert("Error!\n\nThe server could not be found.");

    } else {
      // no classified response.
      alert("Result-Status:" + x.status + "\n" + x.responseText);
    } // if

    proxies.xmlhttp = null;
    proxies.current = null;
  } // if
}; // proxies.alertResult


proxies.alertResponseText = function () {
  ///<summary>Show all the details of the returned data of a webservice call.
  ///Use this method for debugging transmission problems.</summary>
  ///<remarks>To set up a debug output in an alert box use:
  ///proxies.service.method.corefunc = proxies.alertResponseText;</remarks>
  if (proxies.xmlhttp.readyState === 4)
    alert("Status:" + proxies.xmlhttp.status + "\nRESULT:" + proxies.xmlhttp.responseText);
}; // proxies.alertResponseText


proxies.alertException = function (ex) {
  ///<summary>Show the details about an exception.</summary>
  var s = "Exception:\n\n";

  if (ex.constructor === String) {
    s = ex;
  } else {
    if ((ex.name) && (ex.name != ""))
      s += "Type: " + ex.name + "\n\n";

    if ((ex.message) && (ex.message != ""))
      s += "Message:\n" + ex.message + "\n\n";

    if ((ex.description) && (ex.description != "") && (ex.message != ex.description))
      s += "Description:\n" + ex.description + "\n\n";
  } // if
  alert(s);
}; // proxies.alertException


proxies._getXHR = function () {
  ///<summary>Get a browser specific implementation of the XMLHttpRequest object.</summary>
  // from http://blogs.msdn.com/ie/archive/2006/01/23/516393.aspx
  var x = null;
  if (window.XMLHttpRequest) {
    // if IE7, Mozilla, Safari, etc: Use native object
    x = new XMLHttpRequest();

  } else if (window.ActiveXObject) {
    // ...otherwise, use the ActiveX control for IE5.x and IE6
    try { x = new ActiveXObject("Msxml2.XMLHTTP"); } catch (ex1) { }
    if (!x) {
      try { x = new ActiveXObject("Microsoft.XMLHTTP"); } catch (ex2) { }
    } // if
  } // if
  return (x);
}; // proxies._getXHR


ajax._getXMLDOM = function (xmlText) {
  ///<summary>Get a browser specific implementation of the XMLDOM object, containing a XML document.</summary>
  ///<param name="xmlText">the xml document as string.</param>
  var obj = null;

  if ((!ajax.isIE) && (document.implementation) && (typeof document.implementation.createDocument === "function")) {
    // Gecko / Mozilla / Firefox
    var parser = new DOMParser();
    obj = parser.parseFromString(xmlText, "text/xml");

  } else {
    // IE
    try {
      obj = new ActiveXObject("MSXML2.DOMDocument");
    } catch (ex1) { }

    if (!obj) {
      try {
        obj = new ActiveXObject("Microsoft.XMLDOM");
      } catch (ex2) { }
    } // if

    if (obj) {
      obj.async = false;
      obj.validateOnParse = false;
    } // if
    obj.loadXML(xmlText);
  } // if
  return (obj);
}; // _getXMLDOM


proxies.xml2json = function (xObj) {
  ///<summary>Convert complex XML structures to JavaScript objects (JSON).</summary>
  var ret = {};
  if (xObj.nodeType === 9)
    return (this.xml2json(xObj.documentElement));

  var n = xObj.firstChild;
  if (n === null) {
    if (xObj.getAttribute("xsi:nil") === "true") {
      ret = null;
    } else {
      ret = "";
    } // if

  } else if (n.nodeType === 3) {
    // just a text node.
    ret = n.nodeValue;

  } else {
    // a complex node: analyse all subnodes
    while (n) {
      var nn = n.nodeName;
      // strip namespace aliases from node names: n1:<varname> --> <varname>
      if (nn.indexOf(':') > 0) {
        nn = nn.split(":")[1];
      } // if

      var nv = this.xml2json(n); // recursion !
      if (!ret[nn]) {
        // maybe just a simple nested value
        ret[nn] = nv;
      } else if (ret[nn].constructor === Array) {
        // nn is already an array, now with another value
        ret[nn].push(nv);
      } else {
        // if more than 1 element with the same name is present
        // an array is used to collect them all.
        var tmp = [];
        tmp[0] = ret[nn];
        tmp[1] = nv;
        ret[nn] = tmp;
      } // if
      n = n.nextSibling;
    } // while
  } // if
  return (ret);
}; // xml2json


proxies._wrapArray2Xml = function (arr, tagname) {
  ///<summary>Wrap array in xml tags helper.</summary>
  ///<param name="arr" type="Array">An array containing values.</param>
  ///<param name="tagname" type="String">The tagname used in the array.</param>
  var ts = "<" + tagname + ">";
  var te = "</" + tagname + ">";
  var tm = te + ts;
  return (ts + arr.join(tm) + te);
};


proxies._getAnyXmlText = function (node) {
  ///<summary>Factoring out getting text from a node.</summary>
  return (node.textContent || node.innerText || node.text || node.childNodes[0].nodeValue);
};


function inspectObj(obj) {
  ///<summary>Show the details of a javascript object using an alert box.</summary>
  ///<remarks>This helps a lot while developing and debugging.</remarks>
  alert(inspectText(obj));
} // inspectObj


window.inspectText = function (obj) {
  ///<summary>Create a textual view of the details of a javascript object.</summary>
  var s = "InspectObj:";
  var n, p;

  if (!obj) {
    s = "(null)"; return (s);
  } else if (obj.constructor === String) {
    s = "\"" + obj + "\"";
  } else if (obj.constructor === Array) {
    s += " _ARRAY";
  } else if (typeof (obj) === "function") {
    s += " [function]" + obj;

  } else if ((typeof (XMLSerializer) != "undefined") && (obj.constructor === XMLDocument)) {
    s = "[XMLDocument]:\n" + (new XMLSerializer()).serializeToString(obj.firstChild);
    return (s);

  } else if ((typeof (obj) === "object") && (obj.xml)) {
    s = "[XML]:\n" + obj.xml;
    return (s);
  }

  for (p in obj) {
    try {
      if (!obj[p]) {
        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; }
  } // for
  return (s);
}; // inspectObj


// ----- make FF more IE compatible -----
if (!ajax.isIE) {
  // ----- XML objects -----

  // select the first node that matches the XPath expression
  // xPath: the XPath expression to use
  if (!XMLDocument.selectSingleNode) {
    XMLDocument.prototype.selectSingleNode = function (xPath) {
      var doc = this;
      if (doc.nodeType != 9)
        doc = doc.ownerDocument;
      if (doc.nsResolver === null) doc.nsResolver = function (prefix) { return (null); };
      var node = doc.evaluate(xPath, this, doc.nsResolver, XPathResult.ANY_UNORDERED_NODE_TYPE, null);
      if (node) node = node.singleNodeValue;
      return (node);
    }; // selectSingleNode
  } // if
} // if

// ----- End -----


This page is part of the http://www.mathertel.de/ web site.

For updates and discussions see http://ajaxaspects.blogspot.com/.