//=================================================================================================
var
	ntElement   = 1, 
	ntAttribute = 2, 
	ntText      = 3, 
	ntCData     = 4, 
	ntComment   = 8, 
	ntDocument  = 9;
//=================================================================================================
var
	charCodes = 
		[
			160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 
			178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 
			196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 
			214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 
			232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 
			250, 251, 252, 253, 254, 255, 338, 339, 352, 353, 376, 402, 710, 732, 8211, 8212, 8216, 
			8217, 8218, 8220, 8221, 8222, 8224, 8225, 8226, 8230, 8240, 8249, 8250, 8364, 8482 
		], 
	specialChar = 
		[
			"&nbsp;", "&iexcl;", "&cent;", "&pound;", "&curren;", "&yen;", "&brvbar;", "&sect;", 
			"&uml;", "&copy;", "&ordf;", "&laquo;", "&not;", "&shy;", "&reg;", "&macr;", "&deg;", 
			"&plusmn;", "&sup2;", "&sup3;", "&acute;", "&micro;", "&para;", "&middot;", "&cedil;", 
			"&sup1;", "&ordm;", "&raquo;", "&frac14;", "&frac12;", "&frac34;", "&iquest;", 
			"&Agrave;", "&Aacute;", "&Acirc;", "&Atilde;", "&Auml;", "&Aring;", "&AElig;", 
			"&Ccedil;", "&Egrave;", "&Eacute;", "&Ecirc;", "&Euml;", "&Igrave;", "&Iacute;", 
			"&Icirc;", "&Iuml;", "&ETH;", "&Ntilde;", "&Ograve;", "&Oacute;", "&Ocirc;", "&Otilde;", 
			"&Ouml;", "&times;", "&Oslash;", "&Ugrave;", "&Uacute;", "&Ucirc;", "&Uuml;", "&Yacute;", 
			"&THORN;", "&szlig;", "&agrave;", "&aacute;", "&acirc;", "&atilde;", "&auml;", "&aring;", 
			"&aelig;", "&ccedil;", "&egrave;", "&eacute;", "&ecirc;", "&euml;", "&igrave;", 
			"&iacute;", "&icirc;", "&iuml;", "&eth;", "&ntilde;", "&ograve;", "&oacute;", "&ocirc;", 
			"&otilde;", "&ouml;", "&divide;", "&oslash;", "&ugrave;", "&uacute;", "&ucirc;", 
			"&uuml;", "&yacute;", "&thorn;", "&yuml;"
		];
//-------------------------------------------------------------------------------------------------
	specialChar[178]  = "&OElig;";
	specialChar[179]  = "&oelig;";
	specialChar[192]  = "&Scaron;";
	specialChar[193]  = "&scaron;";
	specialChar[216]  = "&Yuml;";
	specialChar[242]  = "&fnof;";
	specialChar[550]  = "&circ;";
	specialChar[572]  = "&tilde;";
	specialChar[8051] = "&ndash;";
	specialChar[8052] = "&mdash;";
	specialChar[8056] = "&lsquo;";
	specialChar[8057] = "&rsquo;";
	specialChar[8058] = "&sbquo;";
	specialChar[8060] = "&ldquo;";
	specialChar[8061] = "&rdquo;";
	specialChar[8062] = "&bdquo;";
	specialChar[8064] = "&dagger;";
	specialChar[8065] = "&Dagger;";
	specialChar[8066] = "&bull;";
	specialChar[8070] = "&hellip;";
	specialChar[8080] = "&permil;";
	specialChar[8089] = "&lsaquo;";
	specialChar[8090] = "&rsaquo;";
	specialChar[8204] = "&euro;";
	specialChar[8322] = "&trade;";
//=================================================================================================
var Xml = 
{
	Class: "Xml"
};
//-------------------------------------------------------------------------------------------------
Xml.compare = function( node1, node2 )
{
	if ( typeof( node1 ) == "undefined")
		throw new Error("Illegal argument. First argument is not defined.");
	
	if ( typeof( node2 ) == "undefined")
		throw new Error("Illegal argument. Second argument is not defined.");
	
	if ( node1 == null && node2 == null )
		return null;
	else if ( node1 == null && node2 != null )
		return {node1: node1, node2: node2, reason: "First node is null, second is not null.", reasons: []};
	else if ( node1 != null && node2 == null )
		return {node1: node1, node2: node2, reason: "First node is not null, second is null.", reasons: []};
	
	if ( typeof( node1.ownerDocument ) == "undefined" && ( node1.documentElement ) == "undefined")
		throw new Error(
				"Illegal argument. First argument is not an XmlNode (" + valueString( node1 ) + ").");
	
	if ( typeof( node2.ownerDocument ) == "undefined" && ( node2.documentElement ) == "undefined")
		throw new Error(
				"Illegal argument. Second argument is not an XmlNode (" + valueString( node2 ) + ").");
	
	var
		doc1, doc2, 
		//-------------------------------------------------------------
		getNode = function( node, type )
		{
			for ( var i = 0; i < node.childNodes.length; i++ )
				if ( node.childNodes[i].nodeType == type )
					return node.childNodes[i];
			
			return null;
		}
		//-------------------------------------------------------------
		getXPath = function( node )
		{
			var
				xPath = "";
			
			for ( var i = 0; i < node.attributes.length; i++ )
			{
				var
					attr = node.attributes.item( i );
				
				xPath += " and @" + attr.nodeName + " = '" + String( attr.value ).replace( /\'/gi, "&#39;") + "'";
			}
			
			if ( xPath != "")
				xPath = node.nodeName + "[" + xPath.substr( 5 ) + "]";
			else
				xPath = node.nodeName;
			
			return xPath;
		}, 
		//-------------------------------------------------------------
		compare = function( node1, node2 )
		{
			var
				reasons = [];
			
			for ( var i = 0; i < node1.childNodes.length; i++ )
			{
				var
					child1 = node1.childNodes[i], 
					child2 = null;
				
				switch ( child1.nodeType )
				{
					case ntElement:
					{
						var
							xPath = getXPath( child1 ), 
							nodes = Xml.selectNodes( node2, xPath );
						
						if ( nodes.length == 0 )
							return {node1: node1, node2: node2, reason: "Couldn't find child: \"" + xPath + "\".", reasons: reasons};
						
						for ( var k = 0; k < nodes.length && child2 == null; k++ )
						{
							var
								reason = compare( child1, nodes[k] );
							
							if ( reason == null )
								child2 = nodes[k];
							else
								reasons.add( reason );
						}
						
						if ( child2 == null )
						{
							if ( reasons.length == 1 )
								return reasons[0];
							else
								return {node1: node1, node2: node2, reason: "Couldn't find correct child: \"" + xPath + "\".", reasons: reasons};
						}
						
						break;
					}
					case ntText:
					case ntCData:
					{
						var
							child2 = getNode( node2, child1.nodeType );
						
						if ( child2 == null )
							return {node1: node1, node2: node2, reason: "Couldn't find text or CDATA node.", reasons: reasons};
						
						if ( child1.nodeValue != child2.nodeValue )
							return {node1: child1, node2: child2, reason: "text or CDATA nodes are not the same.", reasons: reasons};
						
						break;
					}
				}
			}
			
			return null;
		};
		//-------------------------------------------------------------
	
	if ( typeof( node1.documentElement ) != "undefined")
	{
		doc1  = node1;
		node1 = doc1.documentElement;
	}
	else 
		doc1 = node1.ownerDocument;
	
	if ( typeof( node2.documentElement ) != "undefined")
	{
		doc2  = node2;
		node2 = doc2.documentElement;
	}
	else 
		doc2 = node2.ownerDocument;
	
	if ( node1.nodeName != node2.nodeName )
		return {node1: node1, node2: node2, reason: "Root nodes are not the same.", reasons: []};
	
	if ( node1.nodeType != node2.nodeType )
		return {node1: node1, node2: node2, reason: "Root nodes are not of the same type.", reasons: []};
	
	if ( node1.nodeType == ntElement )
	{
		for ( var i = 0; i < node1.attributes.length; i++ )
		{
			var
				attr1 = node1.attributes.item( i ), 
				attr2 = node2.getAttributeNode( attr1.nodeName );
			
			if ( attr2 == null )
				return {node1: node1, node2: node2, reason: "Attribute \"" + attr1.nodeName + "\" does not exists in second node.", reasons: []};
			
			if ( attr1.value != attr2.value )
				return {node1: attr1, node2: attr2, reason: "The values of the \"" + attr1.nodeName + "\" attributes are not the same (\"" + attr1.value + "\" and \"" + attr2.value + "\").", reasons: []};
		}
		
		for ( var i = 0; i < node2.attributes.length; i++ )
		{
			var
				attr2 = node2.attributes.item( i ), 
				attr1 = node1.getAttributeNode( attr2.nodeName );
			
			if ( attr1 == null )
				return {node1: node1, node2: node2, reason: "Attribute \"" + attr2.nodeName + "\" does not exists in first node.", reasons: []};
			
			if ( attr1.value != attr2.value )
				return {node1: attr1, node2: attr2, reason: "The values of the \"" + attr1.nodeName + "\" attributes are not the same (\"" + attr1.value + "\" and \"" + attr2.value + "\").", reasons: []};
		}
	}
	
	var
		reason = compare( node1, node2 );
	
	if ( reason != null )
		return reason;
	
	return compare( node2, node1 );
}
//-------------------------------------------------------------------------------------------------
Xml.create = function( value, xml, onload )
{
	var
		doc = null;
	
	function created( request )
	{
		var
			xml = Xml.create( Xml.toString( request.responseXML ) );
		
		onload( {status:request.status, statusText:request.statusText, responseText:request.responseText, responseXML:xml} );
	}
	
	if ( typeof( value ) != "undefined" && value != null )
	{
		if ( Url.isUrl( value ) )
		{
			if ( typeof( onload ) == "string")
				onload = new Function("request", onload );
			
			if ( typeof( onload ) == "function")
			{
				value.onreadystatechange = created;
				
				if ( typeof( xml ) != "undefined" && xml != null )
					value.async("post", Xml.toString( xml ) );
				else
					value.async();
				
				return;
			}
			else
			{
				if ( typeof( xml ) != "undefined")
					doc = Xml.create( Xml.toString( value.request("post", Xml.toString( xml ), true, true ) ) );
				else
					doc = Xml.create( Xml.toString( value.request("get", null, true, true ) ) );
			}
		}
		else if ( typeof( value.documentElement ) != "undefined")
			doc = value;
		else if ( typeof( value.ownerDocument ) != "undefined")
			return Xml.create( Xml.toString( value ) );
		else if ( typeof( value ) != "string")
			throw new Error(
					"Illegal argument. First argument is not an Xml object, a string or an url (" + 
					valueString( value ) + ").");
	}
	
	try
	{
		if ( doc == null || typeof( doc.documentElement ) == "undefined")
		{
			if ( window.ActiveXObject )
			{
				var
					i       = 0, 
					objects = [
							"MSXML2.DOMDocument.6.0", 
							"MSXML2.DOMDocument.3.0", "MSXML2.DOMDocument", 
							"MSXML.DOMDocument", "Microsoft.XMLDOM"];
				
				while ( doc == null && i < objects.length )
				{
					try
					{
						doc = new ActiveXObject( objects[i] );
					}
					catch ( error )
					{
					}
					
					i++;
				}
				
				if ( doc == null )
					throw new Error("Capability not supported. Cannot create DOMDocument object.");
				
				if ( typeof( value ) == "string")
					doc.loadXML( value );
			}
			else
			{
				if ( typeof( value ) == "string")
					doc = new DOMParser().parseFromString( value, "text/xml");
				else
					doc = document.implementation.createDocument("", "", null );
			}
		}
	}
	catch ( error )
	{
		Log.error( error );
		
		if ( typeof( value ) != "undefined")
			doc = new Xml( value );
		else
			doc = new Xml();
	}
	
	try
	{
		doc.setProperty("SelectionLanguage", "XPath");
	}
	catch ( error )
	{
		Log.write("doc.setProperty(\"SelectionLanguage\", \"XPath\"); not supported...");
	}
	
	return doc;
}
//-------------------------------------------------------------------------------------------------
Xml.createPath = function( node, xPath, value, nodeType )
{
	if ( node == null || typeof( node.ownerDocument ) == "undefined")
		throw new Error(
				"Illegal argument. First argument is expected to be a XmlNode (" + 
				valueString( node ) + ").");
	
	if ( typeof( xPath ) != "string")
		throw new Error(
				"Illegal argument. xPath expression expected to be of type string (" + 
				valueString( xPath ) + ").");
	
	if ( xPath.length == 0 )
		throw new Error("Illegal argument. xPath expression's length is zero.");
	
	if ( typeof( value ) == "undefined")
		return null;
	
	if ( node.nodeType == ntDocument )
	{
		if ( xPath.contains("/") )
			xPath = xPath.substr( xPath.indexOf("/") + 1 );
		
		return Xml.createPath( node.documentElement, xPath, value, nodeType );
	}
	
	var
		doc = node.ownerDocument,
		nodes = xPath.split("/");
	
	if ( node.nodeType == ntDocument )
		doc = node;
	
	for ( var i = 0; i < nodes.length; i++ )
	{
		var
			newNode = Xml.selectSingleNode( node, nodes[i] );
		
		if ( newNode == null )
		{
			if ( nodes[i].startsWith("@") )
			{
				var
					name = nodes[i].substr( 1 );
				
				newNode = doc.createAttribute( name );
				node.setAttributeNode( newNode );
			}
			else
			{
				var
					create = function( xPath )
					{
						var
							newNode = null;
						
						if ( xPath.indexOf("[") > -1 )
						{
							var
								attribs = xPath.substr( xPath.indexOf("[") + 1 ), 
								name    = xPath.substr( 0, xPath.indexOf("[") );
							
							newNode = doc.createElement( name );
							
							attribs = attribs.substr( 0, attribs.length - 1 );
							attribs = attribs.replace( /(\s+or\s+|\s+and\s+)/gi, "|");
							attribs = attribs.split("|");
							
							for ( var x = 0; x < attribs.length; x++ )
							{
								var
									attrib = attribs[x];
								
								if ( attrib.contains("=") )
								{
									attrib = attrib.split("=");
									
									var
										name  = attrib[0].trim(), 
										value = attrib.length > 1 ? attrib[1].trim() : null;
									
									if ( name.startsWith("@") )
										name = name.substr( 1 );
									
									if ( value != null )
									{
										if ( value.startsWith("\"") || value.startsWith("\'") )
											value = value.substr( 1 );
										
										if ( value.endsWith("\"") || value.endsWith("\'") )
											value = value.substr( 0, value.length - 1 );
									}
									
									newNode.setAttribute( name, value );
								}
							}
						}
						else
							newNode = doc.createElement( xPath );
						
						return newNode;
					};
				
				newNode = node.appendChild( create( nodes[i] ) );
			}
		}
		
		node = newNode;
	}
	
	if ( node.nodeType == ntElement && nodeType == ntCData )
	{
		while ( node.firstChild != null )
			node.removeChild( node.firstChild );
		
		node.appendChild( doc.createCDATASection( value ) );
	}
	else
		Xml.setText( node, value );
	
	return node;
}
//-------------------------------------------------------------------------------------------------
Xml.find = function( node, nodeType )
{
	if ( node == null || typeof( node.ownerDocument ) == "undefined")
		throw new Error(
				"Illegal argument. First argument is expected to be a XmlNode (" + 
				valueString( node ) + ").");
	
	if ( typeof( node.childNodes ) != "undefined")
	{
		for ( var i = 0; i < node.childNodes.length; i++ )
			if ( node.childNodes[i].nodeType == nodeType )
				return node.childNodes[i];
	}
	
	return null;
}
//-------------------------------------------------------------------------------------------------
Xml.getText = function( node )
{
	if ( node == null )
		return null;
	
	if ( typeof( node.ownerDocument ) == "undefined")
		throw new Error(
				"Illegal argument. First argument is expected to be a XmlNode (" + 
				valueString( node ) + ").");
	
	if ( node.nodeType == ntElement )
	{
		var
			child = Xml.find( node, ntText );
		
		if ( child == null )
			child = Xml.find( node, ntCData );
		
		if ( child != null )
			return child.nodeValue;
		else
			return null;
	}
	else
		return node.nodeValue;
}
//-------------------------------------------------------------------------------------------------
Xml.moveBottom = function( node )
{
	if ( node == null || typeof( node.ownerDocument ) == "undefined")
		throw new Error(
				"Illegal argument. First argument is expected to be a XmlNode (" + 
				valueString( node ) + ").");
	
	if ( node.nextSibling )
	{
		node.parentNode.appendChild( node );
		return true;
	}
	
	return false;
}
//-------------------------------------------------------------------------------------------------
Xml.moveDown = function( node )
{
	if ( node == null || typeof( node.ownerDocument ) == "undefined")
		throw new Error(
				"Illegal argument. First argument is expected to be a XmlNode (" + 
				valueString( node ) + ").");
	
	if ( node.nextSibling )
	{
		if ( node.nextSibling.nextSibling )
			node.parentNode.insertBefore( node, node.nextSibling.nextSibling );
		else
			node.parentNode.appendChild( node );
		
		return true;
	}
	
	return false;
}
//-------------------------------------------------------------------------------------------------
Xml.moveTop = function( node )
{
	if ( node == null || typeof( node.ownerDocument ) == "undefined")
		throw new Error(
				"Illegal argument. First argument is expected to be a XmlNode (" + 
				valueString( node ) + ").");
	
	if ( node.previousSibling )
	{
		node.parentNode.insertBefore( node, node.parentNode.firstChild );
		return true;
	}
	
	return false;
}
//-------------------------------------------------------------------------------------------------
Xml.moveUp = function( node )
{
	if ( node == null || typeof( node.ownerDocument ) == "undefined")
		throw new Error(
				"Illegal argument. First argument is expected to be a XmlNode (" + 
				valueString( node ) + ").");
	
	if ( node.previousSibling )
	{
		node.parentNode.insertBefore( node, node.previousSibling );
		return true;
	}
	
	return false;
}
//-------------------------------------------------------------------------------------------------
Xml.removeNode = function( node )
{
	if ( node == null || typeof( node.ownerDocument ) == "undefined")
		throw new Error(
				"Illegal argument. First argument is expected to be a XmlNode (" + 
				valueString( node ) + ").");
	
	if ( node.parentNode )
		node.parentNode.removeChild( node );
	else
		throw new Error("Illegal argument. The given node has no parent.");
}
//-------------------------------------------------------------------------------------------------
Xml.selectNodes = function( node, xPath )
{
	if ( node == null || typeof( node.ownerDocument ) == "undefined")
		throw new Error(
				"Illegal argument. First argument is expected to be a XmlNode (" + 
				valueString( node ) + ").");
	
	if ( typeof( xPath ) != "string")
		throw new Error(
				"Illegal argument. Second argument is not a string (" + valueString( xPath ) + ").");
	
	if ( typeof( node.selectNodes ) != "undefined")
		return node.selectNodes( xPath );
	
	var
		doc = node.ownerDocument;
	
	if ( node.nodeType == ntDocument )
		doc = node;
	
	var
		nodelist = [], 
		result   = doc.evaluate( xPath, node, null, XPathResult.ANY_TYPE, null ), 
		child    = result.iterateNext();
	
	while ( child )
	{
		nodelist.add( child );
		child = result.iterateNext();
	}
	
	return nodelist;
}
//-------------------------------------------------------------------------------------------------
Xml.selectSingleNode = function( node, xPath, ns )
{
	if ( node == null || typeof( node.ownerDocument ) == "undefined")
		throw new Error(
				"Illegal argument. First argument is expected to be a XmlNode (" + 
				valueString( node ) + ").");
	
	if ( typeof( xPath ) != "string")
		throw new Error("Illegal argument. Second argument is not a string (" + valueString( xPath ) + ").");
	
	if ( typeof( node.selectSingleNode ) != "undefined")
		return node.selectSingleNode( xPath );
	
	var
		doc = node.ownerDocument;
	
	if ( node.nodeType == ntDocument )
		doc = node;
	
	var
		result = doc.evaluate( xPath, node, null, XPathResult.ANY_TYPE, null ), 
		child  = result.iterateNext();
	
	if ( child )
		return child;
	else
		return null;
}
//-------------------------------------------------------------------------------------------------
Xml.setText = function( node, text )
{
	if ( node == null || typeof( node.ownerDocument ) == "undefined")
		throw new Error(
				"Illegal argument. First argument is expected to be a XmlNode (" + 
				valueString( node ) + ").");
	
	if ( node.nodeType == ntElement )
	{
		var 
			nodeType = node.firstChild ? node.firstChild.nodeType : ntText;
		
		for ( var i = 0; i < node.childNodes.length; i++ )
		{
			var
				child = node.childNodes[i];
			
			if ( child.nodeType == ntText || child.nodeType == ntCData )
			{
				node.removeChild( child );
				i--;
			}
		}
		
		if ( text != null && String( text ) != "")
		{
			if ( nodeType == ntCData )
				node.insertBefore( node.ownerDocument.createCDATASection( text ), node.firstChild );
			else
				node.insertBefore( node.ownerDocument.createTextNode( text ), node.firstChild );
		}
	}
	else if ( text != null && String( text ) != "")
		node.nodeValue = text;
	else
		node.nodeValue = "";
}
//-------------------------------------------------------------------------------------------------
Xml.setValue = function( node, xPath, value, datatype, nodetype )
{
	if ( node == null || typeof( node.ownerDocument ) == "undefined")
		throw new Error(
				"Illegal argument. First argument is expected to be a XmlNode (" + 
				valueString( node ) + ").");
	
	var
		target   = Xml.selectSingleNode( node, xPath ), 
		parent   = node, 
		nodeName = xPath;
	
	if ( xPath.contains("/") )
	{
		nodeName = xPath.substr( xPath.lastIndexOf("/") + 1 );
		parent   = Xml.selectSingleNode( node, xPath.substr( 0, xPath.lastIndexOf("/") ) );
	}
	
	if ( nodeName.startsWith("@") )
	{
		nodeName = nodeName.substr( 1 );
		nodetype = ntAttribute;
	}
	
	if ( value == null || String( value ) == "")
	{
		if ( target != null )
		{
			if ( nodetype == ntAttribute )
				parent.removeAttribute( nodeName );
			else if ( node != null )
				parent.removeChild( target );
			
			target = null;
		}
	}
	else
	{
		if ( typeof( nodetype ) == "undefined")
			nodetype = ntElement;
			
		var
			xmlValue = datatype == dtString ? value : toXml( value, datatype );
		
		if ( target == null )
			target = Xml.createPath( node, xPath, xmlValue, nodetype );
		else
			Xml.setText( target, xmlValue );
	}
	
	return target;
}
//-------------------------------------------------------------------------------------------------
Xml.show = function( node, name )
{
	if ( node == null || (typeof( node ) != "string" && typeof( node.childNodes ) == "undefined") )
		throw new Error(
				"Illegal argument. First argument is expected to be a XmlNode or a string " + 
				"containing Xml (" + valueString( node ) + ").");
	
	showXml( Xml.create( node ), name );
}
//-------------------------------------------------------------------------------------------------
Xml.toString = function( node )
{
	if ( node == null || typeof( node.childNodes ) == "undefined" )
		throw new Error(
				"Illegal argument. First argument is expected to be a XmlNode (" + 
				valueString( node ) + ").");
	
	if ( typeof( node.xml ) != "undefined")
		return node.xml;
	else
		return ( new XMLSerializer().serializeToString( node ) );
}
//-------------------------------------------------------------------------------------------------
Xml.toHtml = function( node )
{
	if ( node == null )
		return "";
	
	if ( node == null || (typeof( node ) != "string" && typeof( node.childNodes ) == "undefined") )
		throw new Error(
				"Illegal argument. First argument is expected to be a XmlNode or a string " + 
				"containing Xml (" + valueString( node ) + ").");
	
	var
		emptyTags = new RegExp(
				"<(a|b|blockquote|body|button|center|code|colgroup|div|em|fieldset|font|frame|" + 
				"frameset|head|h[\dn]|html|iframe|label|legend|li|object|ol|optgroup|option|p|q|" + 
				"s|script|select|span|strike|strong|sub|sup|table|tbody|td|textarea|tfoot|th|thead|" + 
				"title|tr|u|ul)(\\b[^>]*?)?\\s*/>", "gi"), 
		xmlDecl   = new RegExp(
				"<\\?xml version=\"1\\.0\" encoding=\"[^\"]*\"\\?>", "gi"), 
		str;
	
	if ( typeof( node ) == "string")
		str = node;
	else
		str = Xml.toString( node );
	
	return str.replace( emptyTags, "<$1$2></$1>" ).replace( xmlDecl, "");
}
//-------------------------------------------------------------------------------------------------
Xml.transform = function( xml, xsl )
{
	if ( xml == null || typeof( xml.childNodes ) == "undefined")
		throw new Error(
				"Illegal argument. First argument is expected to be a XmlNode (" + 
				valueString( xml ) + ").");
	
	if ( xsl != null && typeof( xsl ) == "string")
		xsl = Xml.create( new Url( xsl ) );
	
	if ( xsl == null || typeof( xsl.documentElement ) == "undefined")
		throw new Error(
				"Illegal argument. Second argument is expected to be a XmlDocument or an Url " + 
				"to a valid Xsl file (" + valueString( xsl ) + ").");
	
	if ( typeof( xml.transformNode ) != "undefined")
		return xml.transformNode( xsl );
	else
	{
		var
			xslt = new XSLTProcessor();
		
		xslt.importStylesheet( xsl );
		
		return Xml.toString( xslt.transformToFragment( xml, xml.ownerDocument ) );
	}
}
//=================================================================================================
function toXml( value, type )
{
	if ( typeof( type ) != "undefined")
	{
		switch ( type )
		{
			case dtBoolean:
				if ( value )
					return "1";
				else
					return "0";
				
				break;
			case dtDateTime:
				return value.formatString("s");
				break;
			case dtDouble:
			case dtMoney:
			case dtPercentage:
				return value.formatString("0.0#########").replace(",", ".");
				break;
			case dtInteger:
			case dtLong:
				break;
			case dtString:
			case dtGuid:
				value = value.replace( /\&/gi, "&amp;");
				value = value.replace( /\"/gi, "&quot;");
				value = value.replace( /\</gi, "&lt;");
				value = value.replace( /\>/gi, "&gt;");
				
				break;
			default:
				throw new Error("Illegal assignment to parameter \"type\" (" + type + ").");
		}
	}
	else
	{
		// TODO: bep welk type...
	}
	
	return value;
}
//=================================================================================================
function fromXml( value, type )
{
	if ( typeof( value ) != "undefined" && typeof( value.firstChild ) != "undefined")
		value = value.firstChild ? value.firstChild.data : null;
	
	if ( value == null || String( value ) == "")
		return null;
	
	switch ( type )
	{
		case dtBoolean:
			if ( String( value ) == "0" || String( value ).toLowerCase() == "false" || String( value ).toLowerCase() == "no")
				return false;
			else
				return true;
			
			break;
		case dtDateTime:
			value = parseXmlDate( value );
			
			if ( isNaN( value ) )
				return null;
			else
				return value;
			
			break;
		case dtDouble:
		case dtMoney:
		case dtPercentage:
			value = parseFloat( value );
			
			if ( isNaN( value ) )
				return null;
			else
				return value;
			
			break;
		case dtInteger:
		case dtLong:
			value = parseInt( value );
			
			if ( isNaN( value ) )
				return null;
			else
				return value;
			
			break;
		case dtString:
		case dtGuid:
			value = value.replace( /&amp;/gi,  "&");
			value = value.replace( /&quot;/gi, "\"");
			value = value.replace( /&lt;/gi,   "<");
			value = value.replace( /&gt;/gi,   ">");
			
			return value;
			break;
		default:
			throw new Error("Illegal assignment to parameter \"type\" (" + type + ").");
	}
}
//=================================================================================================
function parseXmlDate( value )
{
	var
		regex = /^(\d{4}-\d{2}-\d{2})(T\d{2}:\d{2}:\d{2}(\.\d{1,7})?)?$/i;
	
	if ( regex.test( value ) )
	{
		var
			date = value.split( /[-T:\.]/g );
		
		for ( var i = 0; i < date.length; i++ )
			while ( date[i].startsWith("0") && String( date[i] ).length > 1 )
				date[i] = date[i].substr( 1 );
			
		var
			year   = parseInt( date[0] );
			month  = parseInt( date[1] );
			day    = parseInt( date[2] );
			hour   = parseInt( date[3] );
			minute = parseInt( date[4] );
			second = parseInt( date[5] );
		
		if ( isNaN( hour ) ) 
			hour = 0;
		
		if ( isNaN( minute ) ) 
			minute = 0;
		
		if ( isNaN( second ) ) 
			second = 0;
		
		return new Date( year, month - 1, day, hour, minute, second );
	}
	else
		return parseInt("knaag");
}
//=================================================================================================
function htmlEncode( HTML )
{
	var
		retVal;
	
	try
	{
		retVal = new String( HTML );
		retVal = retVal.replace( /&/g, "&amp;");
		
		for ( var i = 0; i < charCodes.length; i++ )
		{
			var
				code = charCodes[i], 
				html = specialChar[code - 160];
			
			retVal = retVal.replace( new RegExp( String.fromCharCode( code ), "g"), "&amp;" + html.substr( 1 ) );
		}
		
		retVal = retVal.replace( /</g, "&lt;");
		retVal = retVal.replace( />/g, "&gt;");
		retVal = retVal.replace( /"/g, "&quot;");	//"
	}
	catch ( error )
	{
		try
		{
			retVal = HTML.join(", ");
		}
		catch ( error )
		{
			retVal = HTML;
		}
	}
	
	return retVal;
}
//=================================================================================================
function showXml( node, name )
{
	if ( typeof( name ) == "undefined" || name == null )
		name = "";
	
	if ( typeof( node ) != "object" || typeof( node.childNodes ) == "undefined")
		throw new Error("Illegal argument. First argument is not an XmlNode.");
	
	if ( typeof( node.documentElement ) != "undefined")
		node = node.documentElement;
	
	var
		bytes = String("<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n").length, 
		win   = window.open("", "frmXML" + name, "toolbar=0,location=0,directories=0,status=0,menubar=0,scrollbars=1,resizable=1,width=788,height=570");
	
	if ( typeof( Xml ) != "undefined" && typeof( Xml.toString ) == "function")
		bytes += Xml.toString( node ).length;
	
	if ( win )
	{
		win.document.open();
		win.document.writeln("<html><head><title>Xml " + name + "</title></head><body style=\"font-family: Lucida Console, Courier New; font-size:11px; color:navy; margin:5px; background-color:white;\">&lt;?xml version=\"1.0\" encoding=\"Windows-1252\"?&gt;");
		win.document.writeln( buildXml( node ) );
		
		if ( typeof( Xml ) != "undefined" && typeof( Xml.toString ) == "function")
		{
			if ( bytes < 2048 ) 
				win.document.writeln("<div style=\"clear:both; margin-top:10px; font-family:Verdana; color:black;\"><b>XML size:</b> " + bytes + " bytes</div></html>");
			else if ( bytes / 1024 < 2048 ) 
				win.document.writeln("<div style=\"clear:both; margin-top:10px; font-family:Verdana; color:black;\"><b>XML size:</b> " + (Math.round(bytes / 1024 * 100) / 100) + " kB</div></html>");
			else if ( bytes / 1024 / 1024 < 2048 ) 
				win.document.writeln("<div style=\"clear:both; margin-top:10px; font-family:Verdana; color:black;\"><b>XML size:</b> " + (Math.round(bytes / 1024 / 1024 * 100) / 100) + " MB</div></html>");
		}
		
		win.document.writeln("</body></html>");
		win.document.close();
		
		win.focus();
	}
}
//=================================================================================================
function nodeName( node )
{
	var
		retVal = node.nodeName;
	
	if ( node.attributes )
	{
		for ( var i = 0; i < node.attributes.length; i++ )
		{
			var
				attr = node.attributes.item( i );
			
			if ( attr.nodeName != "xmlns:sql" && attr.value != "null" && attr.value != "" && attr.value != null )
				retVal += " " + attr.nodeName + "=<font color=\"blue\">\"" + attr.value.replace(/\"/g, "&amp;quot;") + "\"</font>";
		}
	}
		
	return retVal;
}
//=================================================================================================
function buildXml( node, tabs )
{
	var
		retVal = "";
	
	if ( tabs == undefined )
		tabs = "";
	
	if ( node.childNodes.length ) 
	{
		retVal += "<br/>" + tabs + "&lt;" + nodeName( node ) + "&gt;";
		
		for ( var i = 0; i < node.childNodes.length; i++ )
			retVal += buildXml( node.childNodes[i], tabs + "&nbsp;&nbsp;&nbsp;");
		
		var
			length = node.childNodes.length;
		
		for ( var i = 0; i < node.childNodes.length; i++ )
			if ( node.childNodes[i].nodeType == ntText || node.childNodes[i].nodeType == ntCData )
				length--;
		
		if ( length > 0 )
			retVal += "<br/>" + tabs;
		
		retVal += "&lt;/" + node.nodeName + "&gt;";
	}
	else if ( node.nodeType == ntText )
	{
		var
			text = null;
		
		if ( typeof( node.value ) != "undefined")
			text = node.value;
		else if ( typeof( node.text ) != "undefined")
			text = node.text;
		else if ( typeof( node.nodeValue ) != "undefined")
			text = node.nodeValue;
		
		if ( typeof( text ) != "undefined" && text != "" && text != null )
			retVal += "<font color=\"black\"><b>" + toXml( text, dtString ).htmlEncode().replace(/[\r\n]/gi, "<br/>") + "</b></font>";
	}
	else if ( node.nodeType == ntCData )
	{
		var
			text = null;
		
		if ( typeof( node.value ) != "undefined")
			text = node.value;
		else if ( typeof( node.text ) != "undefined")
			text = node.text;
		else if ( typeof( node.nodeValue ) != "undefined")
			text = node.nodeValue;
		
		retVal += "<font color=\"red\">&lt;![CDATA[<br/><div style=\"margin-left:" + (tabs.length / 6) * 7 + "px; padding-left:3px; border-left:1px solid #C00000;\"><font color=\"black\">";
		retVal += toXml( text, dtString ).replace(/[\r\n]/gi, "<br/>").replace(/[\t]/gi, "&nbsp;&nbsp;&nbsp;");
		retVal += "</font><br/></div>" + tabs + "]]&gt;</font>";
	}
	else if ( node.nodeType == ntElement )
		retVal += "<br/>" + tabs + "&lt;" + nodeName( node ) + "/&gt;";
	
	return retVal;
}
//=================================================================================================
