/**
*
*  The large majority of this code comes from http://www.webreference.com/programming/javascript/definitive2/index.html
*  I assume by posting this code in a tutorial format, the original author expected that the code can be reproduced and redistrubuted.
*  I highly recommend reading the article from which this code came from.  It generates an HTML table from the XML data.
*  Code is meant to be shared.  Knowledge is not intended to be owned.
*  Modifications made by Josh Schumacher <http://blog.joshschumacher.com>
*
*/
var XML = {};
/**
 * Create a new Document object. If no arguments are specified,
 * the document will be empty. If a root tag is specified, the document
 * will contain that single root tag. If the root tag has a namespace
 * prefix, the second argument must specify the URL that identifies the
 * namespace.
 */
XML.newDocument = function(rootTagName, namespaceURL) {
  if (!rootTagName) rootTagName = "";
  if (!namespaceURL) namespaceURL = "";
  if (document.implementation && document.implementation.createDocument) {
    // This is the W3C standard way to do it
    return document.implementation.createDocument(namespaceURL, rootTagName, null);
  }
  else { // This is the IE way to do it
    // Create an empty document as an ActiveX object
    // If there is no root element, this is all we have to do
    var doc = new ActiveXObject("MSXML2.DOMDocument");
    // If there is a root tag, initialize the document
    if (rootTagName) {
      // Look for a namespace prefix
      var prefix = "";
      var tagname = rootTagName;
      var p = rootTagName.indexOf(':');
      if (p != -1) {
        prefix = rootTagName.substring(0, p);
        tagname = rootTagName.substring(p+1);
      }
      // If we have a namespace, we must have a namespace prefix
      // If we don't have a namespace, we discard any prefix
      if (namespaceURL) {
        if (!prefix) prefix = "a0"; // What Firefox uses
      }
      else prefix = "";
      // Create the root element (with optional namespace) as a
      // string of text
      var text = "<" + (prefix?(prefix+":"):"") +  tagname +
          (namespaceURL
           ?(" xmlns:" + prefix + '="' + namespaceURL +'"')
           :"") +
          "/>";
      // And parse that text into the empty document
      doc.loadXML(text);
    }
    return doc;
  }
};

/**
 * Synchronously load the XML document at the specified URL and
 * return it as a Document object
 */
XML.load = function(url) {
    // Create a new document with the previously defined function
    var xmldoc = XML.newDocument();
    xmldoc.async = false;  // We want to load synchronously
    xmldoc.load(url);      // Load and parse
    return xmldoc;         // Return the document
};

/**
 * Asynchronously load and parse an XML document from the specified URL.
 * When the document is ready, pass it to the specified callback function.
 * This function returns immediately with no return value.
 */
XML.loadAsync = function(url, callback) {
    var xmldoc = XML.newDocument();
    // If we created the XML document using createDocument, use
    // onload to determine when it is loaded
    if (document.implementation && document.implementation.createDocument) {
        xmldoc.onload = function() { callback(xmldoc); };
    }
    // Otherwise, use onreadystatechange as with XMLHttpRequest
    else {
        xmldoc.onreadystatechange = function() {
            if (xmldoc.readyState == 4) callback(xmldoc);
        };
    }
    // Now go start the download and parsing
    xmldoc.load(url);
};

/**
 * Parse the XML document contained in the string argument and return
 * a Document object that represents it.
 */
XML.parse = function(text) {
    if (typeof DOMParser != "undefined") {
        // Mozilla, Firefox, and related browsers
        return (new DOMParser()).parseFromString(text, "application/xml");
    }
    else if (typeof ActiveXObject != "undefined") {
        // Internet Explorer.
        var doc = XML.newDocument();  // Create an empty document
        doc.loadXML(text);            // Parse text into it
        return doc;                   // Return it
    }
    else {
        // As a last resort, try loading the document from a data: URL
        // This is supposed to work in Safari. Thanks to Manos Batsis and
        // his Sarissa library (sarissa.sourceforge.net) for this technique.
        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;
    }
};

/**
 * Return a Document object that holds the contents of the <xml> tag
 * with the specified id. If the <xml> tag has a src attribute, an XML
 * document is loaded from that URL and returned instead.
 *
 * Since data islands are often looked up more than once, this function caches
 * the documents it returns.
 */
XML.getDataIsland = function(id) {
    var doc;
    // Check the cache first
    doc = XML.getDataIsland.cache[id];
    if (doc) return doc;
    // Look up the specified element
    doc = document.getElementById(id);
    // If there is a "src" attribute, fetch the Document from that URL
    var url = doc.getAttribute('src');
    if (url) {
        doc = XML.load(url);
    }
    // Otherwise, if there was no src attribute, the content of the <xml>
    // tag is the document we want to return. In Internet Explorer, doc is
    // already the document object we want. In other browsers, doc refers to
    // an HTML element, and we've got to copy the content of that element
    // into a new document object
    else if (!doc.documentElement) {// If this is not already a document.// First, find the document element within the <xml> tag. This is
        // the first child of the <xml> tag that is an element, rather
        // than text, comment, or processing instruction
        var docelt = doc.firstChild;
        while(docelt != null) {
            if (docelt.nodeType == 1 /*Node.ELEMENT_NODE*/) break;
            docelt = docelt.nextSibling;
        }
        // Create an empty document
        doc = XML.newDocument();
        // If the <xml> node had some content, import it into the new document
        if (docelt) doc.appendChild(doc.importNode(docelt, true));
    }
    // Now cache and return the document
    XML.getDataIsland.cache[id] = doc;
    return doc;
};
XML.getDataIsland.cache = {}; // Initialize the cache

/**
 * Extract data from the specified XML document and return as a multidimensional JS array
 *
 * The schema argument is a JavaScript object that specifies what data is to
 * be extracted and how it is to be displayed. The schema object must have a
 * property named "rowtag" that specifies the tag name of the XML elements that
 * contain the data for one row of the table. The schema object must also have
 * a property named "columns" that refers to an array. The elements of this
 * array specify the order and content of the columns of the table. Each
 * array element may be a string or a JavaScript object. If an element is a
 * string, that string is used as the tag name of the XML element that contains
 * table data for the column, and also as the column header for the column.
 * If an element of the columns[] array is an object, it must have one property
 * named "tagname" and one named "label". The tagname property is used to
 * extract data from the XML document and the label property is used as the
 * column header text. If the tagname begins with an @ character, it is
 * an attribute of the row element rather than a child of the row.
 */
XML.xmlToArray = function(xmldoc, schema) {
    // Get the elements that contain our data from the xml document
    var xmlrows = xmldoc.getElementsByTagName(schema.rowtag);
	var xmlArray = Array(xmlrows.length);
    // Loop through these elements. Each one contains a row
    for(var r=0; r < xmlrows.length; r++) {
        // This is the XML element that holds the data for the row
        var xmlrow = xmlrows[r];
        // Loop through the columns specified by the schema object
		xmlArray[r] = Array(schema.columns.length);
        for(var c = 0; c < schema.columns.length; c++) {
            var sc = schema.columns[c];
            var tagname = (typeof sc == "string")?sc:sc.tagname;
            var celltext = "";
            if (tagname.charAt(0) == '@') {
                // If the tagname begins with '@', it is an attribute name
                celltext = xmlrow.getAttribute(tagname.substring(1));
            }
            else {
                // Find the XML element that holds the data for this column
                var xmlcell = xmlrow.getElementsByTagName(tagname)[0];
                // Assume that element has a text node as its first child
				if(xmlcell) {
					celltext = xmlcell.firstChild.data;
				}
            }
			// Append the data into the xmlArray
			xmlArray[r][c] = celltext;
        }
    }
    // Set an HTML attribute on the table element by setting a property.
	return xmlArray;
}