/************************************************
**
**	CXmlDomContainer class definition.
**	Developed by FunnyFox Group -FFG Co Ltd-.
**
************************************************/





/* start PROPERTIES declaration */

CXmlDomContainer.nonIE;
	// = true if the browser is not IE, false if yes
	
CXmlDomContainer.prototype.m_oSelfXmlDom;

// Use for FireFox
CXmlDomContainer.prototype.m_sfnNsResolver = "function nsResolver( sPrefix ) { switch( sPrefix ) { default: return null; break; } }";

/* end PROPERTIES declaration */





/* start METHODS declaration */

function CXmlDomContainer( ) {
	if( document.implementation && document.implementation.createDocument ) {
		
		CXmlDomContainer.nonIE = true;
		this.m_oSelfXmlDom = document.implementation.createDocument( "", "", null );
		// createDocument( 	"string containing the namespace URI for the document to use",
		//					"string containing the qualified name of the document's root element",
		//					"type of document -also called doctype- to create" );
		this.m_oSelfXmlDom.async = true;
		
	} else if ( typeof ActiveXObject != "undefined" ) {
		
		CXmlDomContainer.nonIE = false;
		var aVersions = [ "MSXML2.DOMDocument.5.0",
			"MSXML2.DOMDocument.4.0"," MSXML2.DOMDocument.3.0",
			"MSXML2.DOMDocument"," Microsoft.XmlDom"
			];
		
		for ( var i = 0; i < aVersions.length; i++ ) {
			try {
				this.m_oSelfXmlDom = new ActiveXObject( aVersions[i] );
				this.m_oSelfXmlDom.async = true;
				break;
			} catch ( oError ) {
				//Do nothing
			}
		}
		//throw new Error("MSXML is not installed.");	
		
	}	
}

CXmlDomContainer.prototype.registerCallback = function( p_sEvent, p_fnCallback ) {
	var aEvents = new Array( "onreadystatechange" );
	
	for ( var i = 0; i < aEvents.length; i++ ) {
		if( p_sEvent == aEvents[i] ) {
			eval( "this." + p_sEvent + "Callback( " + p_fnCallback + " );" );
		}
	}
}

CXmlDomContainer.prototype.onreadystatechangeCallback =  function( p_fnCallback ) {
	var oSelfXmlDom = this.m_oSelfXmlDom;
	var oSelf = this;
	
	if( oSelf.isNotIE( ) ) {
	
		oSelfXmlDom.onload = function( ) {
			
			eval( p_fnCallback );
			
		};
		
	} 
	else {
		
		oSelfXmlDom.onreadystatechange = function( ) {
			
			if ( oSelfXmlDom.readyState == 4 ) {
				
				eval( p_fnCallback );
				
			}
			
		};
		
	}
}

CXmlDomContainer.prototype.loadXmlFromUri = function( p_sUri ) {
	this.m_oSelfXmlDom.load( p_sUri );
}

CXmlDomContainer.prototype.loadXmlData = function( p_sXml ) {
	if( this.isNotIE( ) ) {
		
		var oParser = new DOMParser( );
		this.m_oSelfXmlDom = oParser.parseFromString( p_sXml,"text/xml" );
		
	}
	else {
		
		this.m_oSelfXmlDom.loadXML( p_sXml );
		
	}
	
	this.determineError( );
}

CXmlDomContainer.prototype.getTextProp = function( p_oNode ) {
	if( this.isNotIE( ) ) {
		
		var sText = "";
		for ( var i = 0; i < p_oNode.childNodes.length; i++ ) {
			if ( p_oNode.childNodes[i].hasChildNodes( ) ) {
				sText += this.getTextProp( p_oNode.childNodes[i] );
			} else {
				sText += p_oNode.childNodes[i].nodeValue;
			}
		}
		return sText;
		
	} else {
		
		return p_oNode.text;
		
	}
}

CXmlDomContainer.prototype.getXmlProp = function( p_oNode ) {
	if( this.isNotIE( ) ) {
		
		var oSerializer = new XMLSerializer( );
    	return oSerializer.serializeToString( p_oNode );
		
	} else {
		
		return p_oNode.xml;
		
	}
}

/*
**	p_aNamespaces = new Array( 	"xmlns:na='http://site1.com'",
**							  	"xmlns:pub='http://site2.com'" );
*/
CXmlDomContainer.prototype.setNamspaces = function( p_aNamespaces ) {
	if( this.isNotIE( ) ) {
		
		var sCmd = "function nsResolver( sPrefix ) { switch( sPrefix ) {";
		for( var i = 0; i < p_aNamespaces.length; i++ ) {
			var aTemp1 = p_aNamespaces[i].split( "=" );
			var aTemp2 = aTemp1[0].split( ":" );
			var sNsUri = aTemp1[1];
			var sPrefixValue = aTemp2[1];
			
			sCmd += "case '" + sPrefixValue + "': return " + sNsUri + "; break;" ;
		}
		sCmd += "default: return null; break; } }";
		this.m_sfnNsResolver = sCmd;
		
	}
	else {
		
		var sNamespaces = p_aNamespaces.join( " " );
		this.m_oSelfXmlDom.setProperty( "SelectionNamespaces", sNamespaces );
		
	}
}

CXmlDomContainer.prototype.selectNodes = function( p_oContextNode, p_sXPath, p_returnType ) {
	if( p_oContextNode == null ) {
		p_oContextNode = this.m_oSelfXmlDom.documentElement
	}
	
	if( this.isNotIE( ) ) {
		
		/*
		Using 'XPathEvaluator' and 'XPathResult' objects:
			XPathResult = XPathEvaluator.evaluate( 	"XPath expression string to be evaluated",
													"context node that the expression should be run against",
													"function that will handle the namespaces in the expression -Namespace resolver-", 
													"the result type you want -10 types-", 
													"XPathResult object to contain the results -if is null, a new XPathResult object is returned-", );
		10 result types:
		XPathResult.ANY_TYPE, which returns no specific type. The method returns the type that naturally results from the evaluation of the expression.
		XPathResult.ANY_UNORDERED_NODE_TYPE, which returns a node set of one node that is accessed through the singleNodeValue property; null is returned if there are no matching nodes. The returned node may or may not be the first occurring node.
		XPathResult.BOOLEAN_TYPE, which returns a Boolean value.
		XPathResult.FIRST_ORDERED_NODE_TYPE, which returns a node set consisting of one node. This node is accessed with the singleNodeValue property of the XPathResult class. The node returned is the first occurring one in the document.
		XPathResult.NUMBER_TYPE, which returns a number value.
		XPathResult.ORDERED_NODE_ITERATOR_TYPE, which returns a document-ordered node set that can be iterated through using the iterateNext() method; therefore, you can easily access each individual node in the set.
		XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, which returns a document-ordered node set that is a snapshot of the result set. Any modifications made to the nodes in the document do not affect the retrieved results.
		XPathResult.STRING_TYPE, which returns a string value.
		XPathResult.UNORDERED_NODE_ITERATOR_TYPE, which returns a node set that can be iterated through; however, the results may or may not be in the same order as they appear in the document.
		XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, which returns an unordered snapshot node set. Any modifications made to the nodes in the document do not affect the result set.	
		*/
		switch( p_returnType ) {
			case 'Single':
				var sReturnType = XPathResult.FIRST_ORDERED_NODE_TYPE;
				break;
			case 'Multiple':
			default:
				var sReturnType = XPathResult.ORDERED_NODE_ITERATOR_TYPE;
				break;
		}
		
		eval( this.m_sfnNsResolver );
		var oEvaluator = new XPathEvaluator( );
		var oResult = oEvaluator.evaluate( 	p_sXPath, p_oContextNode, nsResolver,
											sReturnType, null );
		
		var aNodes = new Array;
		
		if ( oResult != null ) {
			var oElement;
			while ( oElement = oResult.iterateNext( ) ) {
				aNodes.push( oElement );
			}
		}
		
		switch( p_returnType ) {
			case 'Single':
				return aNodes[0];
				break;
			case 'Multiple':
			default:
				return aNodes;
				break;
		}
		
	} else {
		
		switch( p_returnType ) {
			case 'Single':
				return p_oContextNode.selectSingleNode( p_sXPath );
				break;
			case 'Multiple':
			default:
				return p_oContextNode.selectNodes( p_sXPath );
				break;
		}
		
	}
}

// Worked but not accomplished yet
CXmlDomContainer.prototype.determineError = function( ) {
	if( this.isNotIE( ) ) {
		
		if ( this.m_oSelfXmlDom.documentElement.tagName != "parsererror" ) {
			//No error occurred. Do something here.
		} else {
			var oXmlSerializer = new XMLSerializer( );
			var sXmlError = oXmlSerializer.serializeToString( oXmlDom );
			/*
				Error appears like that
				<parsererror xmlns="http://www.mozilla.org/newlayout/xml/parsererror.xml">XML
					Parsing Error: mismatched tag. Expected: </person>.
					Location: http://yoda/fooreader/test.htm
					Line Number 1, Column 43:<sourcetext><root><person><name>Jeremy
					McPeak</name></root>
					------------------------------------------^</sourcetext></parsererror>
			*/
			// Not works
			var reError = />( [\s\S]*? )Location:( [\s\S]* ?)Line Number ( \d+ ), Column ( \d+ ):<sourcetext>( [\s\S]*? )( ?:\-*\^ )/;
			reError.test( sXmlError );
			var str = 	"An error occurred!!\n" +
						"Description: "+ RegExp.$1 + "\n" +
						"File: "+ RegExp.$2 + "\n" +
						"Line: "+ RegExp.$3 + "\n" +
						"Line Position: "+ RegExp.$4 + "\n" +
						"Source Code: "+ RegExp.$5;
			//--
			alert( sXmlError );
		}		
		
	}
	else {
		
		if ( this.m_oSelfXmlDom.parseError.errorCode != 0 ) {
			/*
				The 'parseError' object provides the following properties:
				errorCode: The error code as a long integer
				filePos: A long integer specifying the position in the file where the error occurred
				line: The line number that contains the error as a long integer
				linePos: The character position in the line where the error occurred (long integer)
				reason: A string specifying why the error happened
				srcText: The text of the line where the error happened
				url: The URL of the XML document as a string
			*/
			var str = 	"An error occurred!!\n" +
						"Description: "+ this.m_oSelfXmlDom.parseError.reason + "\n" +
						"File: "+ this.m_oSelfXmlDom.parseError.url + "\n" +
						"Line: "+ this.m_oSelfXmlDom.parseError.line + "\n" +
						"Line Position: "+ this.m_oSelfXmlDom.parseError.linePos + "\n" +
						"Source Code: "+ this.m_oSelfXmlDom.parseError.srcText;
			alert( str );
		
		} else {
			//Code to do for successful load.
		}		
		
	}
}

CXmlDomContainer.prototype.isNotIE = function( ) {
	return CXmlDomContainer.nonIE;
}

CXmlDomContainer.prototype.setAsync = function( p_bAsync ) {
	this.m_oSelfXmlDom.async = p_bAsync;	
}

CXmlDomContainer.prototype.getAsync = function( ) {
	return this.m_oSelfXmlDom.async;
}

CXmlDomContainer.prototype.getXmlDomObject = function( ) {
	return this.m_oSelfXmlDom;	
}

// Do not use this method
CXmlDomContainer.prototype.die = function( ) {
	delete this.m_oSelfXmlDom;
}
/* end METHODS declaration */





/* 	--[ Appendix A ]--
	Traversing XML Data with oXmlDom object

		<?xml version="1.0" encoding=" utf-8"?>
		<books>
			<book isbn="0471777781">Professional Ajax</book>
			<book isbn="0764579088">Professional JavaScript for Web Developers</book>
			<book isbn="0764557599">Professional C#</book>
			<book isbn="1861002025">Professional Visual Basic 6 Databases</book>
		</books>
		
		var oRoot = oXmlDom.documentElement;
		var oFirstBook = oRoot.firstChild;
		var oFirstBook2 = oRoot.childNodes[0];
		var iChildren = oRoot.childNodes.length;
		var oParent = oFirstBook.parentNode;
		var oSecondBook = oFirstBook.nextSibling;
		oFirstBook2 = oSecondBook.previousSibling;
		var sText = oRoot.childNodes[2].firstChild.nodeValue;
		var sAttribute = oFirstChild.getAttribute( "isbn" );
		var cBooks = oRoot.getElementsByTagName( "book" ); // cBooks is a Array
		var cElements = oRoot.getElementsByTagName( "*" ); // Retreive all child elements

	--[ end Appendix A ]-- */





/* 	--[ Appendix B ]--
	Manipulating the DOM with oXmlDom object
		
		// Creating Nodes
		var oNewBook = oXmlDom.createElement( "book" );
		var oNewBookText = oXmlDom.createTextNode( "Professional .NET 2.0 Generics" );
		oNewBook.appendChild( oNewBookText );
		oNewBook.setAttribute("isbn","0764559885");
		oXmlDom.documentElement.appendChild( oNewBook );
		//--
		
		// Removing, Replacing, and Inserting Nodes
		var oRemovedChild = oRoot.removeChild( oRoot.firstChild );
		//--
		
		// replaceChild( "Note to add", "Note to place" )
		var oReplacedChild = oRoot.replaceChild( oRemovedChild, oRoot.childNodes[2] );
		//--
		
		// insertBefore( "Node to insert", "Node to insert before" )
		oRoot.insertBefore( oReplacedChild, oRoot.lastChild );
		//--

	--[ end Appendix B ]-- */