wmp.provide("wmp.xml");

wmp.xml.innerXml = function (xmlNode) {
	var code = "";
	var child;
	var i=0;
	if (xmlNode.nodeType == 3)
		return xmlNode.nodeValue;
	while(xmlNode.hasChildNodes && (child = xmlNode.childNodes[i++])) 
		code += wmp.xml.outerXml(child);
	return code;
}
	

	
wmp.xml.outerXml = function (node) {
	if(node.outerHTML) {
		return node.outerHTML;
	}
	else if(node.innerXML) {
		return node.innerXML;
	}
	else if (node.xml) {
		return node.xml;
	}
	else if(typeof XMLSerializer != "undefined") {
		return (new XMLSerializer()).serializeToString(node);
	}
}

wmp.xml.hasContent = function(node) {
	return (node.innerHTML && node.innerHTML.toString().trim() != "") || 
		(node.nodeValue && node.nodeValue.trim() != "");
}

wmp.xml.wrapContent = function (node, nodeName) {
	var newnode = node.ownerDocument.createElement(nodeName);
	wmp.xml.moveContent(node, newnode);
	node.appendChild(newnode);
	return newnode;
}

wmp.xml.unwrapNode = function (node) {
	var start = node.firstChild;
	var end = node.lastChild;
	while (node.firstChild)
		node.parentNode.insertBefore(node.firstChild, node);
	$(node).dispose();
	//return the first and the last unwrapped node
	return {start:start, end:end}
}

wmp.xml.moveContent = function (from, to) {
	while (from.firstChild)
		to.appendChild(from.firstChild);
}

wmp.xml.renameNode = function(node, newName) {
	//debug_changes += "converted '" + node.nodeName.toLowerCase() + "', ";
	var newnode = node.ownerDocument.createElement(newName);
	var parent = node.parentNode;
	wmp.xml.copyAttributes(node,newnode);
	wmp.xml.moveContent(node, newnode);
	if (parent) {
		parent.insertBefore(newnode, node);
		node.parentNode.removeChild(node);
	}
	return newnode;
}

wmp.xml.copyAttributes = function(node, to) {
	if (node.attributes && node.attributes.length > 0) {
		var il;
		for (var i = 0, il = node.attributes.length; i < il;i++) {
			try {
				var v=node.getAttribute(node.attributes[i].nodeName,2);
			}
			catch(e) {
				var v=node.getAttribute(node.attributes[i].nodeName);
			}
			if (v)
				to.setAttribute(node.attributes[i].nodeName, v);
		}
	}
}

wmp.xml.getAncestors = function(node) {
	var nodes = [];
	while (node && node.nodeName.toLowerCase() != "html") {
		nodes.push(node);
		node = node.parentNode;
	}
	return nodes;
}

wmp.xml.isInPage = function(node) {
	while (node = node.parentNode) {
		if (node.nodeName.toLowerCase() == "body")
			return true;
	}
	return false;
}

wmp.xml.getNodesInRange = function(start, end) {
	var n = start, ns = [];
	while (n) {
		ns.push(n);
		if (n == end)
			break;
		if (n.firstChild)
			n = n.firstChild;
		else if (n.nextSibling)
			n = n.nextSibling;
		else if (n.parentNode && n.parentNode!=end)
			n = n.parentNode.nextSibling;
		else
			break;
	}
	return ns;
}

wmp.xml.nextNode = function(node, containerNode) {
	while (node && node != containerNode) {
		if (node.nextSibling)
			return node.nextSibling;
		node = node.parentNode;
	}
	return null;
}

wmp.xml.prevNode = function(node, containerNode) {
	while (node && node != containerNode) {
		if (node.previousSibling)
			return node.previousSibling;
		node = node.parentNode;
	}
	return null;
}

wmp.xml.walkThrough = function(node, containerNode) {
	if (node.firstChild)
		return node.firstChild;
	else
		return wmp.xml.nextNode(node, containerNode);
}

wmp.xml.walkThroughBack = function(node, containerNode) {
	if (node.lastChild)
		return node.lastChild;
	else
		return wmp.xml.prevNode(node, containerNode);
}


wmp.xml.splitNode = function(node, pos, parent) {
	if (pos!==0 && !pos)
		return;
	if (node.nodeType == 3) {
		//console.log("split textnode at " + pos);
		if (!parent)
			parent = node.parentNode.parentNode;
		var splittedNode = node.splitText(pos);
		var p = node.parentNode;
	}
	else if (node.nodeType == 1) {
		//console.log("split node at childNodes[" + pos + "] of " + node.childNodes.length);
		if (!parent)
			parent = node.parentNode;
		var p = node;
		var splittedNode = p.childNodes[pos] || node.ownerDocument.createTextNode("");
	}
	else
		return;
	

	while (p != parent) {
		var clone = p.cloneNode(false);
		//todo: remove this line and let remove arbitary attributes
		clone._module = null;
		clone.removeAttribute("id");
		var c = splittedNode;
		while (c) {
			var next = c.nextSibling;
			clone.appendChild(c);
			var c = next;
		}
		p.parentNode.insertBefore(clone, p.nextSibling);
		var splittedNode = clone;
		var p = p.parentNode;
	}
	return clone;
}

wmp.xml.getPosition = function(node) {
	var c=0;
	while(node = node.previousSibling) {
		if (node.nodeType == 3 && node.previousSibling && node.previousSibling.nodeType == 3)
			c--;
		c++;
	}
	return c;
}



wmp.xml.hasVisibleContent = function(node, moreregex) {
	//
	var c = node.innerHTML;
	if (moreregex) {
		//to remove ul, ol for exaple: moreregex="<ul.+</ul>|<ol.+</ol>"
		var c = c.replace(new RegExp(moreregex, "g"), "");
	}
	if (c.test(new RegExp("<br", "i")) || c.test(new RegExp("\u00A0")))
		return true;
	if (["div","body","p","blockquote","h1","h2","h3","h4","h5","h6","h7", "li"].contains(node.get('tag'))) {
		//blocks are displayed as empty if there are just whitespaces in it
		if (c.replace(new RegExp("(<[^>]+>)|([ \\s\n\r\t\u200B\u00A0]+)", "g"), "").length<1)
			return false;
	}
	else {
		if (c.replace(new RegExp("(<[^>]+>)", "g"), "").length<1)
			return false;
	}
	return true;
}

wmp.xml.hasRealContent = function(node, moreregex) {
	//are there real characters other than whitespaces in this node
	var c = node.innerHTML;
	if (moreregex) {
		//to remove ul, ol for exaple: moreregex="<ul.+</ul>|<ol.+</ol>"
		var c = c.replace(new RegExp(moreregex, "g"), "");
	}
	if (c.replace(new RegExp("(<[^>]+>)|([ \\s\n\r\t\u200B\u00A0]+)", "g"), "").length<1)
		return false;
	return true;
}


wmp.xml.xmlizeSource = function(html) {
	//element names to lowercase
	html = html.replace(new RegExp('<[^> ]*','g'), function(match){return match.toLowerCase();});
	//attributes to lowercase
	html = html.replace(/<[^>]*>/g, function(match) {
			match = match.replace(/ [^=]+=/g, function(match2){return match2.toLowerCase();});
			return match;});
	//quotes around unquoted attributes
	html = html.replace(/<[^?][^>]*>/g, function(match) {
			match = match.replace(/( [^=]+=)([^"][^ >]*)/g, "$1\"$2\"");
			return match;	});
	//remove _attribute=""
	html = html.replace(new RegExp('(<[^>]+?)( _.+?="[^"]*")+(.*>)','g'), "$1$3");
	//replace unclosed BRs
	html = html.replace(new RegExp('<br\\W*?>','g'), '<br />');
	//rplace unclosed <img>
	//html = html.replace(/(<img [^>]+[^\/])>/g, "$1 />");
	//replace &nbsp; with " "
	html = html.replace(new RegExp('\u00A0','g'), ' ');
	html = html.replace(new RegExp('&nbsp;','g'), ' ');
	//remove zero-width-space
	html = html.replace(new RegExp('\u200B','g'), '');
	//remove <br> before the end of blocks
	html = html.replace(new RegExp('<br />\\s*</(h[1-6]|div|legend|li|p)','g'), "</$1");
	//remove empty inline tags,  Bug: removes <??>
	//html = html.replace(/(<[^\/]>|<[^\/][^>]*[^\/]>)\s*<\/[^>]*>/g, "");
	return html.trim();
};

wmp.xml.indent = new Class({
	Implements: Options,
	options: {
		dontIndentEl:	'blockcode',
		indentEl:	'section|head|table|tbody|thead|tfoot|tr|ul|ol|p|blockquote|object|div|compatibility|downloads',
		breakBefore:	'h1|h2|h3|h4|h5|h6|h7|blockimg|blockcode|h|ul|ol|li|meta|option|area|title|link|base|script|td',
		breakAfter:	'br|hr|p|pre|address|div|ul|ol|meta|option|area|link|base|script|title',
		breakBoth:	'section|html|head|body|table|thead|tbody|tfoot|tr|form|ul|ol|blockquote|p|object|param|hr|div|compatibility|downloads|audio|video',
		preserveSpace:	'pre|blockcode',
		indentString:	'\t'
	},
	initialize: function(options){
		this.setOptions(options);
		var o = this.options;
		
		var sp = o.preserveSpace.split("|"),c=0,e,r="";
		while (e = sp[c++]) {
			r += "<"+e+"[^>]*>[^<]*?</"+e+">" + ((sp[c]&&"|")||"");
		}
		this.regex = {
			removeR:	new RegExp('\r', 'g'),
			removeBreaks:	new RegExp('\\n\\s+', 'g'),
			open: 		new RegExp('^<('	+ o.indentEl	+ ')[^>]*','i'),
			close: 		new RegExp('^</('	+ o.indentEl	+ ')[^>]*', 'i'),
			dont:		new RegExp('^</('	+ o.dontIndentEl+ ')[^>]*', 'i'),
			breakBefore:	new RegExp('<('		+ o.breakBefore	+ ')([^>]*)>', 'gi'),
			breakAfter:	new RegExp('<('		+ o.breakAfter	+ ')([^>]*)>', 'gi'),
			breakBoth:	new RegExp('<(/?)('	+ o.breakBoth	+ ')([^>]*)>', 'gi'),
			preserveSpace:	new RegExp(r, "gi")
		}
	},
	format: function(xml) {
		var indention = '', c = 0, oldc = 0, newXml = '', line;
		xml = xml.replace(this.regex.preserveSpace, function(match) {
			return match.replace(/\n/g, '__i am a newline__');
		});
		xml = xml.replace(this.regex.removeR, '');
		xml = xml.replace(this.regex.removeBreaks, '\n');
		xml = xml.replace(this.regex.breakBefore, '\n<$1$2>');
		xml = xml.replace(this.regex.breakAfter, '<$1$2>\n');
		xml = xml.replace(this.regex.breakBoth, '\n<$1$2$3>\n');
		
		xml = '\n' + xml + '\n';
		while ((c = xml.indexOf('\n', c + 1)) != -1) {
			if ((line = xml.substring(oldc + 1, c)).length != 0) {
				//remove indention if closing tag
				if (indention.length !=0 && this.regex.close.test(line))
					indention = indention.substring(1);

				newXml += indention + line + '\n';
				
				//indent if opening tag
				if (this.regex.open.test(line))
					indention += this.options.indentString;
			}
			oldc = c;
		}
		newXml = newXml.replace(new RegExp("__i am a newline__", "g"), '\n');
		return newXml;
	}
});

wmp.xml.decodeHtmlEntities =  function(str) {
	var ta=document.createElement("textarea");
	ta.innerHTML=str.replace(/</g,"&lt;").replace(/>/g,"&gt;");
	return ta.value;
}


wmp.xml.docFromString = function(xml) {
	try {
		if (window.ActiveXObject) {
			var d = new ActiveXObject("Microsoft.XMLDOM");
			d.async="false";
			d.loadXML(xml);
			return d;
		}
		else {
			return (new DOMParser()).parseFromString(xml,"text/xml");
		}
	}
	catch(e) {
		alert(e.message);
	}
}


/* is this stuff defined? */
if (!document.ELEMENT_NODE) {
	document.ELEMENT_NODE = 1;
	document.ATTRIBUTE_NODE = 2;
	document.TEXT_NODE = 3;
	document.CDATA_SECTION_NODE = 4;
	document.ENTITY_REFERENCE_NODE = 5;
	document.ENTITY_NODE = 6;
	document.PROCESSING_INSTRUCTION_NODE = 7;
	document.COMMENT_NODE = 8;
	document.DOCUMENT_NODE = 9;
	document.DOCUMENT_TYPE_NODE = 10;
	document.DOCUMENT_FRAGMENT_NODE = 11;
	document.NOTATION_NODE = 12;
}
