﻿FFD = {}; //implied global 

/*----------------------------------------------------------------------------\
|                                 XML Extras                                  |
|-----------------------------------------------------------------------------|
|                         Created by Erik Arvidsson                           |
|                  (http://webfx.eae.net/contact.html#erik)                   |
|                      For WebFX (http://webfx.eae.net/)                      |
|-----------------------------------------------------------------------------|
| XML and XML HTTP request abstraction.                                       |
|-----------------------------------------------------------------------------|
|             Copyright (c) 2001, 2002, 2003, 2006 Erik Arvidsson             |
|-----------------------------------------------------------------------------|
| Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| use this file except in compliance with the License.  You may obtain a copy |
| of the License at http://www.apache.org/licenses/LICENSE-2.0                |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| Unless  required  by  applicable law or  agreed  to  in  writing,  software |
| distributed under the License is distributed on an  "AS IS" BASIS,  WITHOUT |
| WARRANTIES OR  CONDITIONS OF ANY KIND,  either express or implied.  See the |
| License  for the  specific language  governing permissions  and limitations |
| under the License.                                                          |
|-----------------------------------------------------------------------------|
| 2001-09-27 | Original Version Posted.                                       |
| 2006-05-29 | Changed license to Apache Software License 2.0.                |
|-----------------------------------------------------------------------------|
| Created 2001-09-27 | All changes are in the log above. | Updated 2006-05-29 |
\----------------------------------------------------------------------------*/

//<script>
//////////////////
// Helper Stuff //
//////////////////

// used to find the Automation server name
function getDomDocumentPrefix() {
	if (getDomDocumentPrefix.prefix)
		return getDomDocumentPrefix.prefix;

	var prefixes = ["MSXML2", "Microsoft", "MSXML", "MSXML3"];
	var o;
	for (var i = 0; i < prefixes.length; i++) {
		try {
			// try to create the objects
			o = new ActiveXObject(prefixes[i] + ".DomDocument");
			return getDomDocumentPrefix.prefix = prefixes[i];
		}
		catch (ex) {};
	}

	throw new Error("Could not find an installed XML parser");
}

function getXmlHttpPrefix() {
	if (getXmlHttpPrefix.prefix)
		return getXmlHttpPrefix.prefix;

	var prefixes = ["MSXML2", "Microsoft", "MSXML", "MSXML3"];
	var o;
	for (var i = 0; i < prefixes.length; i++) {
		try {
			// try to create the objects
			o = new ActiveXObject(prefixes[i] + ".XmlHttp");
			return getXmlHttpPrefix.prefix = prefixes[i];
		}
		catch (ex) {};
	}

	throw new Error("Could not find an installed XML parser");
}

//////////////////////////
// Start the Real stuff //
//////////////////////////


// XmlHttp factory
function XmlHttp() {}

XmlHttp.create = function () {
	try {
		if (window.XMLHttpRequest) {
			var req = new XMLHttpRequest();

			// some versions of Moz do not support the readyState property
			// and the onreadystate event so we patch it!
			if (req.readyState == null) {
				req.readyState = 1;
				req.addEventListener("load", function () {
					req.readyState = 4;
					if (typeof req.onreadystatechange == "function")
						req.onreadystatechange();
				}, false);
			}

			return req;
		}
		if (window.ActiveXObject) {
			return new ActiveXObject(getXmlHttpPrefix() + ".XmlHttp");
		}
	}
	catch (ex) {}
	// fell through
	throw new Error("Your browser does not support XmlHttp objects");
};

// XmlDocument factory
function XmlDocument() {}

XmlDocument.create = function () {
	try {
		// DOM2
		if (document.implementation && document.implementation.createDocument) {
			var doc = document.implementation.createDocument("", "", null);

			// some versions of Moz do not support the readyState property
			// and the onreadystate event so we patch it!
			if (doc.readyState == null) {
				doc.readyState = 1;
				doc.addEventListener("load", function () {
					doc.readyState = 4;
					if (typeof doc.onreadystatechange == "function")
						doc.onreadystatechange();
				}, false);
			}

			return doc;
		}
		if (window.ActiveXObject)
			return new ActiveXObject(getDomDocumentPrefix() + ".DomDocument");
	}
	catch (ex) {}
	throw new Error("Your browser does not support XmlDocument objects");
};

// Create the loadXML method and xml getter for Mozilla
if (window.DOMParser &&
	window.XMLSerializer &&
	window.Node && Node.prototype && Node.prototype.__defineGetter__) {

	// XMLDocument did not extend the Document interface in some versions
	// of Mozilla. Extend both!
	//XMLDocument.prototype.loadXML =
	Document.prototype.loadXML = function (s) {

		// parse the string to a new doc
		var doc2 = (new DOMParser()).parseFromString(s, "text/xml");

		// remove all initial children
		while (this.hasChildNodes())
			this.removeChild(this.lastChild);

		// insert and import nodes
		for (var i = 0; i < doc2.childNodes.length; i++) {
			this.appendChild(this.importNode(doc2.childNodes[i], true));
		}
	};


	/*
	 * xml getter
	 *
	 * This serializes the DOM tree to an XML String
	 *
	 * Usage: var sXml = oNode.xml
	 *
	 */
	// XMLDocument did not extend the Document interface in some versions
	// of Mozilla. Extend both!
	/*
	XMLDocument.prototype.__defineGetter__("xml", function () {
		return (new XMLSerializer()).serializeToString(this);
	});
	*/
	Document.prototype.__defineGetter__("xml", function () {
		return (new XMLSerializer()).serializeToString(this);
	});
}


// name:        loadXmlFile
// purpose:     This will load an xml file in a cross browser environment
//              code sample pulled from web
//              see http://webfx.eae.net/dhtml/xmlextras/xmlextras.html
// params:      
//              sUri:       the uri of the xml document you are getting
//              bAsync:     synchronicity
//              sOutFormat: only the 'plainText' output format is tested 
//                          will output plain xml text into the text area
//              textAreaId: The text area id where you want to load the
//                          resultant xml
// returns:     null, but loads the text area and sets a global FFD 
//              property that can be referenced when it is time to use
//              the xml to ensure it exists
// date:        2008-06-24
// dev:         mcd modified pattern from web
// 2006-06-25:  mcd: this assumes the FFD implied global variable has already been declared
//              and creates a new property off the FFD by the name of the textArea
//              where the result was loaded see the 'executeWhenLoaded' function 
//              below
//2008-11-03:   mcd: added plainTextGlobal case for situaltions where you can't load the text into a text area
function loadXmlFile(sUri, bAsync, sOutFormat, textAreaId) {
	var done = function () {
		try {
			switch (sOutFormat) {
				case "plainText":
					document.getElementById(textAreaId).value = xmlHttp.responseText;
					FFD[textAreaId] = "done2";
					break;
				case "plainTextGlobal":
					FFD[textAreaId] = xmlHttp.responseText;					
					break;					
				case "plainTextForDiv":
					document.getElementById(textAreaId).innerHTML = stripHtml(xmlHttp.responseText);
					break;	
				case "xmlText":
					document.getElementById(textAreaId).innerHTML = stripHtml(xmlHttp.responseXML.xml);
					break;
				case "xmlTree":
					document.getElementById(textAreaId).innerHTML = createXmlTree(xmlHttp.responseXML, 0);
					break;
			}
		}
		catch (exc) {
			document.getElementById(textAreaId).value = "An error occured <br/>" +
				"Message: " + exc.message + "\n" +
				"File: " + exc.fileName + "\n" +
				"Line: " + exc.lineNumber + "\n" +
				"Name: " + exc.name;
		}
	};
	
	var xmlHttp = XmlHttp.create();
	xmlHttp.open("GET", sUri, bAsync);
	if (bAsync) {
		xmlHttp.onreadystatechange = function () {
			if (xmlHttp.readyState == 4)
				done();
		}
		//document.getElementById("out").innerHTML = progressText;
	}
	xmlHttp.send(null);
	if (!bAsync) {
		done();
	}
}

function loadXmlText(sText, sOutFormat, outputElementId) {
	var xmlDoc = XmlDocument.create();
	xmlDoc.loadXML(sText);
	try {
		switch (sOutFormat) {
			case "plainText":
				document.getElementById(outputElementId).innerHTML = "Not valid for XmlDocument";
				break;
			case "xmlText":
				document.getElementById(outputElementId).innerHTML = stripHtml(xmlDoc.xml);
				break;
			case "xmlTree":
				document.getElementById(outputElementId).innerHTML = createXmlTree(xmlDoc, 0);
				break;
		}
	}
	catch (exc) {
		document.getElementById(outputElementId).innerHTML = "An error occured <br/>" +
			exc.message;
	}
}

// this function takes an xml node and returns an html string that displays that node
// the indent argument is used to describe what depth the node is at so that
// the html code can create a nice indention
function createXmlTree(node, indent) {

	if (node == null)
		return "";
	var str = "";
	
	switch (node.nodeType) {
		case 1:	// Element
			str += "<div class='element'>&lt;<span class='elementname'>" + node.nodeName + "</span>";
			
			var attrs = node.attributes;
			for (var i = 0; i < attrs.length; i++)
				str += createXmlAttribute(attrs[i]);
			
			if (!node.hasChildNodes())
				return str + "/&gt;</div>";
			
			str += "&gt;<br />";
			
			var cs = node.childNodes;
			for (var i = 0; i < cs.length; i++)
				str += createXmlTree(cs[i], indent + 3);
			
			str += "&lt;/<span class='elementname'>" + node.nodeName + "</span>&gt;</div>";
			break;
	
		case 9:	// Document
			var cs = node.childNodes;
			for (var i = 0; i < cs.length; i++)
				str += createXmlTree(cs[i], indent);
			break;
	
		case 3:	// Text
			if (!/^\s*$/.test(node.nodeValue))
				str += "<span class='text'>" + node.nodeValue + "</span><br />";
			break;
	
		case 7:	// ProcessInstruction
			str += "&lt;?" + node.nodeName;
		
			var attrs = node.attributes;
			for (var i = 0; i < attrs.length; i++)
				str += createXmlAttribute(attrs[i]);
			
			str+= "?&gt;<br />"
			break;
	
		case 4:	// CDATA
			str = "<div class='cdata'>&lt;![CDATA[<span class='cdata-content'>" + 
				node.nodeValue +
			"</span>]" + "]></div>";
			break;
			
		case 8:	// Comment
			str = "<div class='comment'>&lt;!--<span class='comment-content'>" + 
				node.nodeValue +
			"</span>--></div>";
			break;
		
		case 10:
				str = "<div class='doctype'>&lt;!DOCTYPE " + node.name;
				if (node.publicId) {
					str += " PUBLIC \"" + node.publicId + "\"";
					if (node.systemId) 
						str += " \"" + node.systemId + "\"";
				}
				else if (node.systemId) {
					str += " SYSTEM \"" + node.systemId + "\"";
				}
				str += "&gt;</div>";
				
				// TODO: Handle custom DOCTYPE declarations (ELEMENT, ATTRIBUTE, ENTITY)
				
				break;
		
		default:
			//alert(node.nodeType + "\n" + node.nodeValue);
			inspect(node);
	}
	
	return str;
}

function createXmlAttribute(a) {
	return " <span class='attribname'>" + a.nodeName + "</span><span class='attribvalue'>=\"" + a.nodeValue + "\"</span>";
}

function stripHtml(s) {
	return s.replace(/\&/g, "&amp;").replace(/\</g, "&lt;").replace(/\>/g, "&gt;").replace(/\t/g, "&nbsp;&nbsp;&nbsp;").replace(/\n/g, "<br />");
}

function alertXml(src) {
	var xmlHttp = XmlHttp.create();
	xmlHttp.open("GET", src, false);
	xmlHttp.send(null);
	alert(xmlHttp.responseXML.xml);
}

function doLoad(outputElementId, sourceElementId, srcTextElementId, xmlFormElementId) {
	var f = document.getElementById(xmlFormElementId);
	var bAsync = f.async[1].checked;
	var select = document.getElementById(sourceElementId);
	var value = select.value;
	
	if (value == "url") {
		var sUri = select.options[select.selectedIndex].text;

		if (f.outFormat[0].checked)
			loadXmlFile(sUri, bAsync, "plainText", outputElementId);
		else if (f.outFormat[1].checked)
			loadXmlFile(sUri, bAsync, "xmlText", outputElementId);
		if (f.outFormat[2].checked)
			loadXmlFile(sUri, bAsync, "xmlTree", outputElementId);
	}
	else {
		var sText = document.getElementById(srcTextElementId).value;

		if (f.outFormat[0].checked)
			loadXmlText(sText, "plainText");
		else if (f.outFormat[1].checked)
			loadXmlText(sText, "xmlText");
		if (f.outFormat[2].checked)
			loadXmlText(sText, "xmlTree");
	}
}

function inspect(obj) {
	var str = "";
	for (var k in obj)
		str += "obj." + k + " = " + obj[k] + "\n";
	window.alert(str);
}

function updateSrcUI(sourceElementId, sourceTextContainerElementId) {
	var value = document.getElementById(sourceElementId).value;
	var srcTextContainer = document.getElementById(sourceTextContainerElementId);	
	srcTextContainer.style.display = value == "text" ? "block" : "none";
}

//mcd note:         the function(s) below is unrelated to the functions above
//                  but they compliment one another and so I put them 
//                  into the same file (2008-06-25)
//2008-06-25 note:  they are related now in that they both sometimes use the
//                  FFD implied global variable see the 'loadXmlFile' function 
//                  above
//***********************************************
// name:        executeWhenLoaded
// purpose:     this fixes race condition when executing several xmlHttpRequests 
//              and you dont want to do something (like an xslt) untill all the 
//              requests are complete
// results:     executes function (specified in parameters first argument) when 
//              the global namespace variables (specified in parameters >=1)
//              exist.
// params
//              argument 1: name of the function to call when all the following
//                          parameters exist in the global namespace
//              argument 2: name of a global variable that will be set when a 
//                          step is done
//              ...
//              argument n: Name of global variable...
// dev:         from http://paulirish.com/2008/best-practice-poll-instead-of-a-settimeout-hack/
// date:        2008-06-25
// notes:       increased setInterval to 100ms because I thought 50ms was too quick
// notes1:      make sure your parameters 2, 3, ... , n have very unique names
//              as they will be polluting the global namespace
// example:     onload="executeWhenLoaded(doXslt, "textAreaId1", "textAreaId2", "textAreaId3");"
//              and functions for loading the text areas would, upon completion
//              set a global namespace variable like so: FFD.textAreaId1 = "done";
//
function executeWhenLoaded(func){  
    var intRef, argRef = arguments; 
    function executeCheck()	{
	    // for loop starts at 1 to skip the function argument.
	    for (var i = 1; i<argRef.length; i++){
		    if (! FFD[ argRef[i] ]){
			    if (!intRef) intRef = setInterval(executeCheck,100);      
			    return;
		    }
	    } 
	    clearInterval(intRef);
	    func(); // only reaches here when for loop is satisfied.
    };
    executeCheck();
}
