function xmlDocument (root,namespace) {
	var doc;
	if(root) {
		doc = newDocument(root,namespace);
	}

	/*
	 * this.cleanNode
	 * Converts canvas to canvas[0]
	 * and canvas@size to canvas[0]/@size
	 */
	 
	this.cleanNode = function (node) {
		/* Split on the attribute symbol */
		var attr = node.split('@');
		
		/* If it fails to match the format it will just return 0 and move on with life. */
		var workingString = attr[0];
		if(!attr[0].match(/[a-zA-Z]+\[[0-9]+\]/)) {
			var split = attr[0].split(/\[/);
			if(split[0] && split[0] != 'null') {
				workingString = (split[0]+'[0]');
			}
		}
		if(attr[1] && attr[1] != 'null') {
			var attrDelim = '/@';
			if(!attr[0] || attr[0] == 'null') {
				attrDelim = '@';
			}
			workingString = workingString + attrDelim + attr[1];
		}
		return workingString;
	}
	
	/*
	 * this.cleanPath(path) 
	 * Will take a path like:
	 *	/canvas/print/input
	 * And Convert it to:
	 *	/canvas[0]/print[0]/input[0]
	 * or /canvas[0]/print@size ==> /canvas[0]/print[0]/@size
	 */

	this.cleanPath  = function(path) {
		var splitPath = path.split('/');
		var newPath = "/";
		/* The first node should be null. */
		for(var i = 1; i < splitPath.length; i++) {
			newPath = newPath+this.cleanNode(splitPath[i])+'/';
		}
		newPath = newPath.substring(0, newPath.length - 1);
		return newPath;
	}

	/* These functions assume the path has been CLEANED! */
	this.getNodeIndex = function(node) {
		var nodeIndex = node.split('[')[1];
		nodeIndex = nodeIndex.replace(']','','g');
		return parseInt(nodeIndex);
	}

	this.getNodeTag = function(node) {
		return node.split('[')[0];
	}

	this.buildPath = function (path,value) {
		var splitPath = path.split('/');
		if(!doc) {
			doc = newDocument(this.getNodeTag(splitPath[1]));
		}
		var currentNode = doc.documentElement;
		for(var depth = 2; depth < splitPath.length; depth++) {
			var tagName = this.getNodeTag(splitPath[depth]);
			if(!tagName.match(/^@/)) {
				var index = this.getNodeIndex(splitPath[depth]);
				var children = currentNode.getElementsByTagName(tagName);
				for(var i = 0; i <= index; i++) {
					if(!children[index]) {
						currentNode.appendChild(doc.createElement(tagName));
					}
				}
				currentNode = currentNode.getElementsByTagName(tagName)[index];
			}

		}
		var finalTag = this.getNodeTag(splitPath[splitPath.length-1]);
		if(value) {
			if(finalTag.match(/^@/)) {
				finalTag = finalTag.replace('@','','g');
				currentNode.setAttribute(finalTag,value);
			} else {
				currentNode.appendChild(doc.createTextNode(value));
			}
		}
	}

	this.getXMLText = function () {
		var xmlText = '';
		if(doc.documentElement.xml) { /* Should be the MSXML Parser */
			xmlText = doc.documentElement.xml;
		} else {		  /* Assuming FF but need to verify Safari and Opera */
			var serializer = new XMLSerializer();
			xmlText = serializer.serializeToString(doc);
		}
		return xmlText;
	}

	this.stripNamespace = function (nameSpacePrefix) {
		nameSpacePrefix = nameSpacePrefix + ':';
		var xmlText = this.getXMLText();
		var regExp = new RegExp('<'+nameSpacePrefix, 'gi');
		var closeRegExp = new RegExp('</'+nameSpacePrefix, 'gi');
		xmlText = xmlText.replace(regExp, '<');
		xmlText = xmlText.replace(closeRegExp, '</');
		return xmlText;
	}
	
	this.createXMLFromString = function(xmlString) {
		if(xmlString.indexOf('<?xml') < 0) {
			xmlString = '<?xml version="1.0"?>'+xmlString;
		}
		if(window.ActiveXObject) {
			var obj = new ActiveXObject("Msxml2.DOMDocument");
			obj.loadXML(xmlString);
			doc = obj;
		} else if(XMLSerializer) {
			doc = (new DOMParser()).parseFromString(xmlString, "text/xml");
		}
		return doc.documentElement;
	}

	this.getDocument = function () {
		return doc;
	}

	this.setRoot = function (nRoot) {
		if(!doc) {
			doc = newDocument();
		}
		doc.appendChild(nRoot);
	}
}

function processXMLWithXSLT(responseXML, xslFile, containerId, onFinish) {
		var outputHTML = '';
		var swappableSection = document.getElementById(containerId);
		var xslDoc;
		if(window.XSLTProcessor) {
			xslDoc = document.implementation.createDocument("","",null);
		}
		if(window.ActiveXObject) {
			xslDoc = new ActiveXObject("Microsoft.XMLDOM");
		}
		xslDoc.onreadystatechange = function() {
			if(xslDoc.readyState == 4) {
				if(window.XSLTProcessor) { /* Gecko */
					var xsltProcessor = new XSLTProcessor();
					xsltProcessor.importStylesheet(xslDoc);
					outputHTML = xsltProcessor.transformToFragment(responseXML, document);
					swappableSection.innerHTML = '';
					swappableSection.appendChild(outputHTML);
				} else { /* Assuming IE, probably bad practice */
					outputHTML = responseXML.transformNode(xslDoc);		
					swappableSection.innerHTML = outputHTML;
				}
				if(onFinish) {
					onFinish(containerId);
				}

			}
		}
		if(window.XSLTProcessor) {
			xslDoc.readyState = 4;
			xslDoc.addEventListener('load', xslDoc.onreadystatechange, false);
		}
		xslDoc.load(xslFile);

}

function importNode(toDoc, importedNode, deep) {
	if(typeof toDoc.importNode == "function") {		// Yay! The browser supports importNode!
		return toDoc.importNode(importedNode, deep);
	} else {
		var newNode;
		if(importedNode.nodeType == 1) { // Node.ELEMENT_NODE
		    newNode = toDoc.createElement(importedNode.nodeName);
		    for(var i = 0; i < importedNode.attributes.length; i++){
			var attr = importedNode.attributes[i];
			if (attr.nodeValue != null && attr.nodeValue != '') {
			    newNode.setAttribute(attr.name, attr.nodeValue);
			}
		    }
		    if (typeof importedNode.style != "undefined")
			newNode.style.cssText = importedNode.style.cssText;
		} else if(importedNode.nodeType == 3) { // Node.TEXT_NODE
		    newNode = toDoc.createTextNode(importedNode.nodeValue);
		}
		
		if(deep && importedNode.hasChildNodes()){
		    for(var i = 0; i < importedNode.childNodes.length; i++) {
		    	try {
				newNode.appendChild(
				    importNode(toDoc, importedNode.childNodes[i], true)
				);
			} catch(e) {
				;
			}
		    }
		}
		
		return newNode;
	}
}
