var XML = {
	version: "0.9",
	author: "Jan-Erik Pedersen"
}

XML = Class.create({
 	initialize: function(obj, options) {
 		var doc;
		if (obj.documentElement) {
			doc = obj;
		} else if (obj instanceof XML.Qname) {
			doc = XML.createDocumentQName(obj);
		} else {
			throw new Error("invalid arguments in XML constructor");
		}
		this.document = doc;
		this.root = doc.documentElement;
		this.namespaces = XML.namespacesMap(this.root);
		// setup NS resolver for xpath
		if (Prototype.Browser.IE) {
			var strVal = (options && options.nsMap) ?  XML.namepacesToString(options.nsMap) :  XML.namepacesToString(this.namespaces);
			doc.setProperty("SelectionNamespaces", strVal);
		} else {
			doc._nsResolver = (options && options.nsMap) ? XML.createNSResolver(options.nsMap) : this.namespaces ? doc.createNSResolver(this.root) : null;
		}
		this.document = doc;
 	}
});

XML.QName = Class.create({
  initialize : function(localpart) {
    this.localpart = localpart;
    if (arguments[1]) this.namespace = arguments[1];
    if (arguments[2]) this.prefix = arguments[2];
  },
  to_string : function() {
    return (this.namespace) ? 
      '{' + this.namespace + '}' + this.localpart : 
        this.localpart;
  },
  value_of : function() {
    return ((this.prefix)?this.prefix + ':':'') + this.localpart;
  },
  equals : function(obj) {
    return (obj instanceof WS.QName &&
            obj.localpart == this.localpart &&
            obj.namespace == this.namespace);
  }
});

XML.createDocument = function(namespace,nodename) {
	return Try.these(
		function() {
      		return document.implementation.createDocument(namespace, nodename, null);
		},
		function() {
        	var doc = new ActiveXObject('Microsoft.XMLDOM');
        	var root = XML.createElementNS(doc, nodename, namespace);
        	doc.documentElement = root;
        	return doc;
    	},
 		function() {
        	var doc = new ActiveXObject('MSXML2.XMLDOM');
        	var root = XML.createElementNS(doc, nodename, namespace);
        	doc.documentElement = root;
        	return doc;
    	}
 	) || false;
}

XML.importNode = function(doc, node, deep) {
	if (Prototype.Browser.IE) {
		return XML.importNodeIE(doc, node, deep);
	} else {
		return doc.importNode(node, deep);
	}
}

XML.importNodeFrom = function(node, root) {
	var doc = node.ownerDocument;
	var docRoot = doc.documentElement;
	// find path
	var xpath = '';
	while (!node.documentElement) {
		var siblings = node.parentNode.childNodes, i = 0, j = Prototype.Browser.IE ? 0 : 1, sibling;
		while (sibling = siblings[i++]) {
			if (sibling == node) break;
			(sibling.tagName == node.tagName) && (j++);
		}
		if (node.parentNode.documentElement) {
			xpath = '/' + node.tagName + xpath;
		}
		else {
			xpath = '/' + node.tagName + '[' + j + ']' + xpath;
		}
		node = node.parentNode;
	}
	// get new node
	newNode = XML.selectNode(root, xpath);
	return newNode ? XML.importNode(node, newNode, true) : node;
}

XML.importNodeIE = function(doc, node, deep) {
	var nodeType = node.nodeType;
	if (nodeType == Node.ELEMENT_NODE) {
		var result = XML.createElementNS(doc, node.tagName, node.namespaceURI);
		var attrs = node.attributes;
		for (var i = 0, n = attrs.length; i < n; i++) {
			var value = attrs[i].value;
			result.setAttribute(attrs[i].name, attrs[i].value);
		}
	} else if (nodeType == Node.TEXT_NODE) {
		result = doc.createTextNode(node.nodeValue);
	}
	if (deep) {
		var nodes = node.childNodes, i = 0;
		while (node = nodes[i++]) {
			result.appendChild(XML.importNode(doc, node, true));
		}
	}
	return result;
}

XML.createDocumentQName = function(qname) {
	XML.createDocument(qname.namespace, qname.value_of());
}

XML.createTreeWalker = function(doc, root, whatToShow, filter, expandEntityReferences){
	return Try.these(
		function(){return doc.createTreeWalker(root, whatToShow, filter, expandEntityReferences)},
		function(){return new mozile.dom.TreeWalker(root, whatToShow, filter, expandEntityReferences)}
	) || false;
}

XML.createElementNS = function(doc,nodename,namespace) {
	return Try.these(
		function() {return doc.createElementNS(namespace, nodename)},
		function() {return doc.createNode(1,nodename,namespace)}
    ) || false;
}

XML.createElementQName = function(doc,qname) {
    return XML.createElementNS(doc, qname.value_of(),qname.namespace);
}

XML.createAttributeNS = function(doc,nodename,namespace,value) {
	var attr = Try.these(
		function() {return doc.createAttributeNS(nodename,namespace)},
		function() {return doc.createNode(2,nodename,namespace)}
    ) || false;
    attr.nodeValue = value;
    return attr;
}

XML.createAttributeQName = function(doc,qname,value) {
    	return XML.createAttributeNS(doc,qname.value_of(), qname.namespace, value);
}

XML.namespacesMap = function(root) {
  	var i = 0, attr, attrs = root.attributes, result = {};
  	while(attr = attrs[i++]) {
  		var name = attr.nodeName;
  		if (name.indexOf("xmlns:") == 0) {
			result[name.split(":")[1]] = attr.nodeValue;
  		}
  	}
  	return result;
}

XML.namepacesToString = function(obj) {
	var p, a = [];
	for (p in obj) {
		a.push("xmlns:" + p + "='" + obj[p] + "'");
	}
	return a.join(" ");
}

XML.createNSResolver = function(nsMap) {
	var body = "var map =" + Object.toJSON(nsMap) + ";return map[uri]";
	return new Function("uri", body);
}

XML.selectNode = function(node, xpath) {
	if (Prototype.Browser.IE) {
		return node.selectSingleNode(xpath);
	} else {
  		var doc = node.ownerDocument;
  		var nsResolver = doc._nsResolver ? doc._nsResolver : (XML.hasNamespaces(doc.documentElement) ? doc.createNSResolver(doc.documentElement) : null);
  		var nodes = doc.evaluate(xpath, node, nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
  		return nodes ? nodes.singleNodeValue : null;
	}
}

XML.selectNodes = function(node, xpath) {
	var result  = []
	if (Prototype.Browser.IE) {
		var nodes = node.selectNodes(xpath), i = 0, node;
		while(node = nodes.item(i++)) {
			result.push(node);
		}
	} else {
   	  	var doc = node.ownerDocument;
  		var nsResolver = doc._nsResolver;
		var nodes = doc.evaluate(xpath, node, nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
        if (nodes != null) {
            var node = nodes.iterateNext();
            while(node) {
                result.push(node);
                node = nodes.iterateNext();
            }
        }
	}
	return result;
}



console.info("xml loaded");

