function hbXmlReader (filename, synchron, callback, callbackObject)
{
  this.filename = filename;
  this.getRetries = function () {return 5};
  this.getRetryTimeout = function () {return 5000};
  this.callback = callback;
  this.callbackObject = callbackObject;
  this.isInitialized = function () {return false};
  this.reading = false;
  this.content = null;
  this.rawString = null; // nur zu debug-Zwecken
  this.parsedDoc = null; // nur zu debug-Zwecken

  if (synchron)
  {
    this.synchron = synchron;
  }
  else
  {
    this.synchron = false;
  }
}

hbXmlReader.prototype.getContent = function ()
{
  return this.content;
}

hbXmlReader.prototype.getFilename = function ()
{
  return this.filename;
}

// obtained from: "JavaScript The Definitive Guide" (5th edition) from David Flanagan published by O'Reilly; Example 21-1
hbXmlReader.prototype.newDocument = function (rootTagName, namespaceURL)
{
  if (!rootTagName)
  {
    rootTagName = "";
  }

  if (!namespaceURL)
  {
    namespaceURL = "";
  }

  if (document.implementation && document.implementation.createDocument)
  {
    return document.implementation.createDocument (namespaceURL, rootTagName, null);
  }
  else
  {
    var doc = new ActiveXObject ("MSXML2.DOMDocument");

    if (rootTagName)
    {
      var prefix = "";
      var tagname = rootTagName;
      var p = rootTagName.indexof (':');

      if (p != -1)
      {
        prefix = rootTagName.substring (0, p);
        tagname = rootTagName.substring (p + 1);
      }

      if (namespaceURL)
      {
        if (!prefix)
        {
          prefix = "a0";
        }
      }
      else
      {
        prefix = "";
      }

      var text = "<" + (prefix ? (prefix + ":") : "") + tagname
               + (namespaceURL ? (" xmlns:" + prefix + '="' + namespaceURL + '"') : "") + "/>";
      doc.loadXML (text);
    }

    return doc;
  }
}

// obtained from: "JavaScript The Definitive Guide" (5th edition) from David Flanagan published by O'Reilly; Example 21-2
hbXmlReader.prototype.loadSync = function (url)
{
  var xmldoc = this.newDocument ();
  xmldoc.async = false;
  xmldoc.load (url);
  return xmldoc;
}

// obtained from: "JavaScript The Definitive Guide" (5th edition) from David Flanagan published by O'Reilly; Example 21-3
hbXmlReader.prototype.loadAsync = function (url, callback)
{
  var xmldoc = this.newDocument ();

  if (document.implementation && document.implementation.createDocument)
  {
    xmldoc.onload = function ()
    {
      callback (xmldoc);
    };
  }
  else
  {
    xmldoc.onreadystatechange = function ()
    {
      if (4 == xmldoc.readyState)
      {
        callback (xmldoc);
      }
    }
  }

  xmldoc.load (url);
}

// obtained from: "JavaScript The Definitive Guide" (5th edition) from David Flanagan published by O'Reilly; Example 21-4
hbXmlReader.prototype.parse = function (text)
{
  if (typeof DOMParser != "undefined")
  {
    return (new DOMParser ()).parseFromString (text, "application/xml");
  }
  else if (typeof ActiveXObject != "undefined")
  {
    var doc = this.newDocument ();
    doc.loadXML (text);
    return doc;
  }
  else
  {
    var url = "data:text/xml;charset=utf-8," + encodeURIComponent (text);
    var request = new XMLHttpRequest ();
    request.open ("GET", url, false);
    request.send (null);
    return request.responseXML;
  }
}

hbXmlReader.prototype.getHandleResponseFct = function (retries)
{
  var thisReader = this;
  var callback = this.callback;
  var callbackObject = this.callbackObject;
  var remainingRetries = retries;

  return function (data, responseCode)
  {
    if (200 == responseCode)
    {
      thisReader.rawString = data;
      thisReader.parsedDoc = GXml.parse (data);
      var root = thisReader.parsedDoc.documentElement;

      if ("parsererror" != root.nodeName.toLowerCase ())
      {
        thisReader.content = thisReader.handleNode (root);

        if ((undefined != callback) && (null !=  callback))
        {
          if ((undefined != callbackObject) && (null != callbackObject))
          {
            callback.call (callbackObject);
          }
          else
          {
            callback ();
          }
        }

        thisReader.isInitialized = function () {return true;};
      }

      thisReader.reading = false;
    }
    else if (0 < remainingRetries)
    {
      --remainingRetries;

      setTimeout (function ()
      {
        GDownloadUrl (thisReader.getFilename (),
                      thisReader.getHandleResponseFct (remainingRetries))
      },
      thisReader.getRetryTimeout ());
    }
    else
    {
      thisReader.reading = false;
    }
  }
}

hbXmlReader.prototype.handleNode = function (node)
{
  if ((undefined == node) || (null == node))
  {
    return null;
  }

  var result = new Object ();
  result.text = null;
  result.getType = function () {return node.nodeName;};
  result.getText = function () {return this.text;};

  if (null != node.attributes)
  {
    for (var attIndex = 0; attIndex < node.attributes.length; ++attIndex)
    {
      result [node.attributes [attIndex].name] = node.attributes [attIndex].value;
    }
  }

  if (null != node.childNodes)
  {
    for (var childIndex = 0; childIndex < node.childNodes.length; ++childIndex)
    {
      if (1 == node.childNodes [childIndex].nodeType)
      {
        if (false == node.childNodes [childIndex].nodeName in result)
        {
          result [node.childNodes [childIndex].nodeName] = new Array ();
        }

        result [node.childNodes [childIndex].nodeName].push (this.handleNode (node.childNodes [childIndex]));
      }
      else if (3 == node.childNodes [childIndex].nodeType)
      {
        if (null == result.text)
        {
          result.text = "";
        }

        result.text += node.childNodes [childIndex].nodeValue;
      }
    }
  }

  return result;
}

hbXmlReader.prototype.read = function ()
{
  if (false == this.reading)
  {
    this.reading = true;
    this.isInitialized = function () {return false};
    this.content = null;

    if (false == this.synchron)
    {
      GDownloadUrl (this.getFilename (), this.getHandleResponseFct (this.getRetries ()));
    }
    else
    {
      this.parsedDoc = this.loadSync (this.getFilename ());
      this.content = this.handleNode (this.parsedDoc);
      this.isInitialized = function () {return true;};
      this.reading = false;
    }
  }
}
