var isMSIE = navigator.userAgent.indexOf('MSIE') != -1 && navigator.userAgent.indexOf('Opera') == -1;

function createDocument(a, b, c) {
	if (isMSIE) {
		return MSIE_createDocument(a, b, c);
	} else {
		return document.implementation.createDocument(a, b, c);
	}
}


var selectedNodes = [];

var processor;
var xmlDoc, xslDoc;
var xslIsLoaded = false;
var xmlIsLoaded = false;

xslDoc = createDocument(null, null, null);
//xsl.addEventListener('load', xslLoaded, false);
xslDoc.async = false;
xslDoc.load('prettyprint.xsl?' + (new Date).getTime());
xslLoaded();

function xslLoaded() {
	if (!isMSIE) {
		processor = new XSLTProcessor();
		processor.importStylesheet(xslDoc);
	}
	xslIsLoaded = true;
	if (xmlIsLoaded) {
		transform();
	}
}

function xmlLoaded(event) {
	xmlIsLoaded = true;
	if (xslIsLoaded) {
		findNamespaces();
		transform();
	}
}

// FIXME: We still haven't found namespaces declared on other elements than the document element.
var namespaceURIs = {};
function findNamespaces() {
	for (var i = 0; i < xmlDoc.documentElement.attributes.length; i++) {
		var attr = xmlDoc.documentElement.attributes[i];
		if (attr.prefix == 'xmlns') {
			namespaceURIs[attr.localName] = attr.value;
		}
	}
}

// Callback-function from xmlDoc.evaluate()
function namespaceResolver(prefix) {
	return typeof namespaceURIs[prefix] == 'string' ? namespaceURIs[prefix] : null;
}

function transform() {
	var div = document.getElementById('xml');
	if (isMSIE) {
	   div.innerHTML = xmlDoc.transformNode(xslDoc.documentElement);
	} else {
		if (div.firstChild) {
			div.removeChild(div.firstChild);
		}
		var fragment = processor.transformToFragment(xmlDoc, document);
		div.appendChild(fragment);
	}
}

function fetchXml() {
	var url = 'source.php?url=' + encodeURIComponent(document.getElementById('url').value);

	xmlDoc = createDocument(null, null, null);
	xmlDoc.async = false;
	//xmlLoaded({currentTarget:url});
	//xml.addEventListener('load', xmlLoaded, false);
	xmlDoc.load(url);
	xmlLoaded({currentTarget:xmlDoc});
	//alert(xml.documentElement);
}

function expressionOnInput(input) {
	for (var i = 0; i < selectedNodes.length; i++) {
		selectedNodes[i].style.backgroundColor = '';
		selectedNodes[i].style.outline = '';
	}

	var spanResult = document.getElementById('result');
	var spanResultType = document.getElementById('resultType');

	if (!spanResult.firstChild) {
		spanResult.appendChild(document.createTextNode(''));
	}

	var result, nodes = [];
	try {
		if (isMSIE) {
			result = xmlDoc.selectNodes(input.value);
			for (var i = 0; i < result.length; i++) {
				nodes.push(result.item(i));
			}
		} else {
			result = xmlDoc.evaluate(input.value, xmlDoc, namespaceResolver, XPathResult.ANY_TYPE, null);
			if (result.resultType >= XPathResult.UNORDERED_NODE_ITERATOR_TYPE) {
				for (var node; node = result.iterateNext(); ) {
					nodes.push(node);
				}
			}
		}
	} catch (e) {
		spanResult.firstChild.data = '';
		if (e.message.indexOf('Expression does not return a DOM node') == 0) {
			spanResultType.innerHTML = 'Only expressions that returns a nodelist is currently supported.';
		} else {
			spanResultType.innerHTML = '-';
			spanResultType.firstChild.data = input.value ? e.message : '';
		}
		return;
	}

	spanResult.style.backgroundColor = 'yellow';
	if (result.resultType == XPathResult.NUMBER_TYPE) {
		spanResultType.innerHTML = 'number: ';
		spanResult.firstChild.data = result.numberValue;
		return;
	}
	if (result.resultType == XPathResult.STRING_TYPE) {
		spanResultType.innerHTML = 'string:';
		spanResult.firstChild.data = result.stringValue;
		return;
	}
	if (result.resultType == XPathResult.BOOLEAN_TYPE) {
		spanResultType.innerHTML = 'boolean:';
		spanResult.firstChild.data = result.booleanValue;
		return;
	}
	for (var i = 0; i < nodes.length; i++) {
		var node = nodes[i];
		var indexes = [];
		if (node.nodeType == Node.ATTRIBUTE_NODE) {
			indexes.push(node.name);
		}
		if (node.nodeType == Node.ATTRIBUTE_NODE) {
			node = isMSIE ? node.selectSingleNode('..') : node.ownerElement;
		}
		for (; node.nodeType != Node.DOCUMENT_NODE; node = node.parentNode) {
			var childNodes = node.parentNode.childNodes;
			var j = 0;
			for (var n = node.parentNode.firstChild; n != node; n = n.nextSibling) {
				// Skip <?xml ?> as they are not visible from XPath
				if (!(n.nodeType == Node.PROCESSING_INSTRUCTION_NODE && n.target == 'xml')) {
					j++;
				}
			}
			indexes.unshift(j + 1);
		}
		var id = 'e-' + indexes.join('-');
		var div = document.getElementById(id);
		if (div) {
			div.style.outline = '1px dotted black';
			div.style.backgroundColor = 'yellow';
			selectedNodes.push(div);
		}
	}
	spanResultType.innerHTML = i == 1 ? '1 node' : i + ' nodes';
	spanResult.innerHTML = '';
}

function toggle(expander) {
	var element = expander.nextSibling;
	if (element.className.indexOf('collapsed') == -1) {
		element.className += ' collapsed';
		expander.firstChild.data = '+';
	} else {
		element.className = element.className.replace('collapsed', '');
		expander.firstChild.data = '\u2212';
	}
}
