/*
 * These functions retrieve XML data asynchronously from somewhere on
 * the web. This 'should' (but need not necessarily) be from the same
 * site the main web page came from.  In this version, only one fetch
 * can be active at a time.
 */

/*
Sample elements from the XML response:
  xmlDoc=xmlhttp.responseXML;
  x = xmlDoc.getElementsByTagName("firstname")[0].childNodes[0].nodeValue;
  x = xmlDoc.getElementsByTagName("lastname")[0].childNodes[0].nodeValue;
  x = xmlDoc.getElementsByTagName("job")[0].childNodes[0].nodeValue;
  x = xmlDoc.getElementsByTagName("age")[0].childNodes[0].nodeValue;
  x = xmlDoc.getElementsByTagName("hometown")[0].childNodes[0].nodeValue;
 */


var xmlobj;
var what_element = false; // fetches initiate only when this = false.
var fetch_this;

/*
 * Main function to load XML data.  Receive:
 *  - URL of document to fetch
 *  - datum we are to fetch from the returned result
 *  - the document element that should receive the result
 */
function loadXMLDoc(url, we_want, element_id)
{
  if (what_element)
  {
    // Something else is already fetching. Wait half a second, try again.
    var r = 'loadXMLDoc("' + url + '","' + element_id + '","' + we_want + '")';
    setTimeout(r, 500);
    return;
  }
  xmlobj = GetXmlHttpObject();
  if (xmlobj == null)
  {
    alert("Your browser does not support XMLHTTP.");
    return;
  }
  what_element = element_id;
  fetch_this = we_want;
  xmlobj.onreadystatechange = stateChanged;
  xmlobj.open("GET",url,true);
  xmlobj.send(null);
}

/*
 * Browser-specific function to return a request object.
 */
function GetXmlHttpObject()
{
  if (window.XMLHttpRequest)
  {
    // code for IE7+, Firefox, Chrome, Opera, Safari
    return new XMLHttpRequest();
  }
  if (window.ActiveXObject)
  {
    // code for IE6, IE5
    return new ActiveXObject("Microsoft.XMLHTTP");
  }
  return null;
}

/*
 * Fetch element 'nm' from XML object 'xobj'.
 */
function fetchElement(nm, xo)
{
  if (!xo) xo = xmlobj;
  var ret = '';
  var r = xo.responseXML.documentElement;
  var elem = r.getElementsByTagName(nm);
  for(var i = 0; i < elem.length; i++)
  {
    r = elem[i].firstChild.nodeValue;
    if (r != false) {
      if (i > 0) ret = ret + ' ';
      ret = ret + r;
    }
  }
  return ret;
}

/*
 * Async function to handle document state changes.  When state
 * becomes 4, the document is fully loaded.  This function uses
 * global variables 'what_element' and 'fetch_this', clearing
 * 'what_element' when the document is fully loaded.
 */
function stateChanged()
{
  if (xmlobj.readyState == 4)
  {
    // We are done fetching. Let others run.
    var w = what_element;
    var e = fetch_this;
    what_element = false;

    if (xmlobj.status == 200)
    {
	  var o = document.getElementById(w);
	  if (o) {
        var a = fetchElement(xmlobj, e);
        if (a) o.innerHTML = a;
      }
      else {
        alert("Document element '" + w + "' does not exist.");
      }
    }
    else
    {
      alert('Error ' + xmlobj.status + ' retrieving XML data: '
            + xmlobj.statusText);
    }
  }
}

/*
 * This function checks the URL query string for an argument matching
 * the input key.  If found, it returns it; else it returns the supplied
 * default, which may be null.
 */
function getQueryString(key, dflt)
{
  key = key.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
  var regex = new RegExp("[\\?&]"+key+"=([^&#]*)");
  var qs = regex.exec(window.location.href);
  return (qs)
    ? qs[1].replace('/\+/g', ' ')
    : dflt;
}

/*
 * Fetch a response string from a specified URL.  The string is in
 * CSV format.  Split the string into an array, removing the leading
 * element (which has the response code).  Remove bracketing quotes
 * from any response elements.  Return the resultant array.
 *
 * Any error returns null.
 */
function fetch_response_csv(url, silent) {
  var response = null;
  var xo = GetXmlHttpObject();
  if (xo) {
    xo.open("GET", url, false);
    xo.send(null);
    if (xo.status == 200) {
      response = format_response_csv(xo);
    }
    else if (!silent) alert('Request returned status ' + xo.status + ' from the server.');
  }
  else if (!silent) alert('Failed to get request memory; cannot send request.');
  return response;
}

/*
 * Given an XML object that is fully materialized and which represents
 * a CSV string, format the string into an array.  The first element
 * is a response status; we remove that from the result, and if it is
 * not '200' we return nothing.  From the rest of the elements, we
 * remove any enclosing double quotes, and convert %22 into '"' and
 * %2C into ','.
 *   This function does not deal with multi-line responses, commas
 * embedded in quoted strings, or escaped quotes ("").
 */
function format_response_csv(xo)
{
  if (xo.readyState != 4) return null;
  if (xo.status != 200) return null;

  // split on newlines; throw away a trailing empty element
  var t = xo.responseText.split('\n');
  if (t[t.length - 1] == '') t.length--;

  // any unsuccessful response returns null; remove the response
  var i;
  if (t.length == 1) {
    var x = /^(200),(.*)/.exec(t[0]);
    if (x.length != 3 || x[1] != '200') return null;
    t[0] = x[2];
  }
  else {
    if (t[0] != '200') return null;
    for (i = 0; i < t.length - 1; i++)
      t[i] = t[i+1];
    t.length--;
  }

  var r = new Array();
  var rct = 0;
  for (var tl = 0 ; tl < t.length; tl++) {
    var str = t[tl];
    var c;
    i = -1;
    while (++i < str.length) {
      var v = '';
      c = str.substring(i,i+1);
      if (c == '"') {
        // Extract a quoted string; include everything up to the
        // end-of-value quote.  Intervening quotes should be doubled,
        // but we are lax about enforcing that.
        while (++i < str.length) {
          c = str.substring(i,i+1);
          if (c != '"')
            v = v + c;
          else if (++i < str.length) {
            c = str.substring(i,i+1);
            if (c == ',') break;
            // not end-of-value, so the preceding quote is a literal
            // that we accept even if it is a formal parse error.
            v = v + '"';
            if (c != '"') v = v + c;
          }
        }
        v = v.replace(/%2C/gi, ',');
        v = v.replace(/%22/gi, '"');
      }
      else if (c != ',') {
        // extract an unquoted value
        v = c;
        while (++i < str.length && (c = str.substring(i,i+1)) != ',')
          v = v + c;
      }
      // else this is an empty value

      r[rct++] = v;
    }

    // A trailing comma indicates an empty final value
    if (c == ',') r[rct++] = '';
  }


  return r;
}

/*
 * Receive a string whose HTML tags have been escaped by converting
 * '<' to &#x3C; and '>' to &#x3E;.  Reconvert those, and return the
 * resultant string.
 */
function unEscape(s) {
  if (!s) return '';
  s = s.replace('/\&#x3C;/g', '<');
  return s.replace('/\&#x3E;/g', '>');
}
