var XML_DEBUG = false;			// true : renvoie les étapes du parsage en alert( )


/* ---[ XML_Parser ]------------------------------------------------------------------- */

function XML_Parser( )
{
	this._oDoc	= XML_create_Document( );
	this.rootDocument	= this._oDoc;
	this._xml	= null;
	this.errorMessage = "";
	this.errorState = 0;
	
	this.parseXML = function( p_xml )
	{
		this.errorState = 0;
		this.errorMessage = "";

		p_xml = this.removeProlog( p_xml );			// On remove <?XML ...></?XML> & le Doctype
		this.processXML( this._oDoc, p_xml );
		
		if ( this.errorState )
		{
			this._oDoc = null;
			alert( "XML_Parser::parseXML Erreur : " + this.errorMessage );
			__DEBUG__( "XML_Parser::parseXML Erreur : " + this.errorMessage );
		}
		
		return this._oDoc;
	}
	
	this.processXML = function( oNode, xml )
	{
		var oNewNode = null;
		var nodeType = 0;
		var nodeValue;
		var endTagName;
		var i = 0;

		// keep circling and eating the xml
		while( 1 )
		{
			// when the xml is empty, return the fragment
			if( xml.length == 0 )
			{
				return "";
			}
	
			var TagStart = xml.indexOf( "<" );
			var TagEnd = 0;
	
			if( TagStart != 0 )		nodeType= XML_Node.ENTITY_NODE;
			else
			{
				// determine what the next section is, and process it
				if( xml.substring( 1, 2 ) == "?" )		nodeType	= XML_Node.PROCESSING_INSTRUCTION_NODE;
				else
				{
					if(xml.substring(1,4) == "!--")		nodeType	= XML_Node.COMMENT_NODE;
					else
					{
						if(xml.substring(1,9) == "![CDATA[")		nodeType	= XML_Node.CDATA_SECTION_NODE;
						else										nodeType = XML_Node.ELEMENT_NODE;
					}
				}
			}
			
			switch( nodeType )
			{
				case XML_Node.ENTITY_NODE :

						if( TagStart == -1 )
						{
							nodeValue = xml;
							xml = "";
						}
						else
						{
							nodeValue = xml.substring( 0, TagStart );
							xml = xml.substring( TagStart, xml.length );
						}

						if ( internalTrim( nodeValue ) != "" )
						{
							// On crée l'element
							oNewNode = oNode.getOwnerDocument( ).createTextNode( nodeValue );
							oNode.appendChild( oNewNode );
						}

					break;

				case XML_Node.PROCESSING_INSTRUCTION_NODE :
						TagEnd		= xml.indexOf( "?>" );
						nodeValue	= xml.substring( 2, TagEnd );
						
						// On crée l'element
						oNewNode = oNode.getOwnerDocument( ).createProcessingInstruction( nodeValue );
						oNode.appendChild( oNewNode );
						
						xml = xml.substring( TagEnd + 2, xml.length );
					break;

				case XML_Node.COMMENT_NODE :
						TagEnd		= xml.indexOf( "-->" );
						nodeValue	= xml.substring( 4, TagEnd );

						// On crée l'element
						oNewNode = oNode.getOwnerDocument( ).createComment( nodeValue );
						oNode.appendChild( oNewNode );

						xml = xml.substring( TagEnd + 3, xml.length );
					break;

				case XML_Node.CDATA_SECTION_NODE :
						TagEnd		= xml.indexOf( "]]>" );
						nodeValue	= xml.substring( 9, TagEnd );

						// On crée l'element
						oNewNode = oNode.getOwnerDocument( ).createCDATASection( nodeValue );
						oNode.appendChild( oNewNode );

						xml = xml.substring( TagEnd + 3, xml.length );
					break;

				case XML_Node.ELEMENT_NODE :
						TagEnd	 	= xml.indexOf( ">" );

						endTagName = oNode.nodeName;
						if ( XML_DEBUG ) alert("we are in <" + endTagName + ">");

						if( xml.substring( 1, endTagName.length + 3 ) == "/" + endTagName + ">" || internalTrim( xml.substring( 1, endTagName.length + 3 ) ) == "/" + endTagName )
						{
							if ( XML_DEBUG ) alert( "fin de tag trouvée : </" + endTagName + ">" );
							return xml.substring( endTagName.length + 3, xml.length );
						}
						else
						{
							// On parse l'Element
							var isEmpty = ( xml.substring( TagEnd - 1, TagEnd ) == "/" );
							if( isEmpty )	TagEnd--;
	
							var tagDatas = internalTrim( xml.substring( TagStart + 1, TagEnd ) );
							var nextSpace = tagDatas.indexOf( " " );
							var tagName = "";
							var aAttributes = Array( );
							
							if ( nextSpace != - 1 )
							{
								tagName = tagDatas.substring( 0, nextSpace );

								// On lance une expression régulière pour récupérer les attributes ( soit de type '...' ou "..." )
								var oRegExp = new RegExp( "\\w+\\s*=(\"[^\"]*\"|'[^']*'|\\w+)", "gi" );	
								aAttributes = tagDatas.match( oRegExp );
								//aAttributes = tagDatas.substring( nextSpace, tagDatas.length ).split( " " );
							}
							else
							{
								tagName = tagDatas;
							}
							
							// On crée l'element
							if ( tagName.substring( 0, 1 ) == "/" )
							{
								this.errorMessage = "invalide XML near : '" + xml.substring( 0, tagName.length + 25 ) + "'";
								this.errorState = 1;
								return "";
							}
							
							if ( XML_DEBUG ) alert( "create <" + tagName + ">" );
							oNewNode = oNode.getOwnerDocument( ).createElement( tagName );
							oNewNode = oNode.appendChild( oNewNode );
							
							if ( aAttributes )
							{
								for ( i = 0 ; i < aAttributes.length ; i++ )
								{
									var sTmp = internalTrim( aAttributes[ i ] )
									var aTmp = Array( );
									var posTmp = sTmp.indexOf( "=" );
									aTmp[ 0 ] = sTmp.substring( 0, posTmp );
									aTmp[ 1 ] = sTmp.substring( posTmp + 1, sTmp.length );

									if ( aTmp.length > 1 )
									{
										var attributeValue = internalTrim( aTmp[1] );
										// On vire les quotes ou doubles quote ( on sait quel type en se placant au charAt( 0 ) de la valeur de l'attribut
										attributeValue = internalReplace( attributeValue, attributeValue.charAt( 0 ), "" );
										oNewNode.setAttribute( internalTrim( aTmp[ 0 ] ), attributeValue );
									}
								}
							}
							
							// On parse le reste
							if ( !isEmpty )
							{
								xml = xml.substring( TagEnd + 1, xml.length );
								xml = this.processXML( oNewNode, xml );
							}
							else
							{
								xml = xml.substring( TagEnd + 2, xml.length );
							}
						}
						
					break;
			}
			
			nodeType = 0;
			
		}
		return xml;
	}

	
	this.removeProlog = function( str )
	{
		var close, start;
		// On vire le <?xml ...... ?>
		start = str.indexOf( "<" );
		if( str.substring( start, start + 3 ) == "<?x" || str.substring( start, start + 3 ) == "<?X" )
		{
			close = str.indexOf( "?>" );
			str = str.substring( close + 2, str.length );
		}

		// On vire le doctype
		start = str.indexOf( "<!DOCTYPE" );
		if( start != -1 )
		{
			close = str.indexOf( ">", start ) + 1;
			var dp = str.indexOf( "[", start );
			if( dp < close && dp != -1 )
			{
				close = str.indexOf( "]>", start ) + 2;
			}
			str = str.substring( close, str.length );
		}

		return str;
	}
}






/* ---[ NODES ]------------------------------------------------------------------- */

XML_Node.ELEMENT_NODE                =  1;
XML_Node.ATTRIBUTE_NODE              =  2;
XML_Node.TEXT_NODE                   =  3;
XML_Node.CDATA_SECTION_NODE          =  4;
XML_Node.ENTITY_REFERENCE_NODE       =  5;
XML_Node.ENTITY_NODE                 =  6;
XML_Node.PROCESSING_INSTRUCTION_NODE =  7;
XML_Node.COMMENT_NODE                =  8;
XML_Node.DOCUMENT_NODE               =  9;
XML_Node.DOCUMENT_TYPE_NODE          = 10;
XML_Node.DOCUMENT_FRAGMENT_NODE      = 11;
XML_Node.NOTATION_NODE               = 12;

function XML_Node( )
{
	this.attributes = null;
	this.childNodes = new XML_NodeList( );
	this.firstChild = null
	this.lastChild = null;
	this.nextSibling = null;
	this.nodeName = null;
	this.nodeType = null;
	this.nodeValue = null;
	this.ownerDocument = null;
	this.parentNode = null;
	this.previousSibling = null;
	
	this.getAttributes = function( )		{ return this.attributes; }
	this.getChildNodes = function( )		{ return this.childNodes; }
	this.getFirstChild = function( )		{ return this.firstChild; }
	this.getLastChild = function( )			{ return this.lastChild; }
	this.getNextSibling = function( )		{ return this.nextSibling; }
	this.getNodeName = function( )			{ return this.nodeName; }
	this.getNodeType = function( )			{ return this.nodeType; }
	this.getNodeValue = function( )			{ return this.nodeValue; }
	this.getOwnerDocument = function( )		{ return this.ownerDocument; }
	this.getParentNode = function( )		{ return this.parentNode; }
	this.getPreviousSibling = function( )	{ return this.previousSibling; }
	
	this.clearChilds = function( )
	{
		this.childNodes = new XML_NodeList();
		this.firstChild = null;
		this.lastChild = null;
	}
	
	this.hasChildNodes = function( )		{ return ( this.childNodes.getLength( ) > 0 ); }

	this.getNodeValue = function( p_tagName )
	{
		for ( var i = 0 ; i < this.childNodes.getLength( ) ; i++ )
		{
			if ( this.childNodes[ i ].nodeName == p_tagName )
				return this.childNodes[ i ].innerHtml( );
		}
	}
	
	this.innerHtml = function( )
	{
		return this.nodeName;
	}

	this.getElementsByTagName = function( p_tagName )
	{
		var contextNode = this;
		var nextNode;
		var aReturnNodes = new XML_NodeList( );
		
		while ( contextNode != null ) 
		{
			if ( contextNode.hasChildNodes( ) ) 
			{
				contextNode = contextNode.firstChild;
			}
			else if ( contextNode != this && ( next = contextNode.nextSibling ) != null ) 
			{
				contextNode = next;
			}
			else
			{
				next = null;
				for ( ; contextNode != this; contextNode = contextNode.parentNode ) 
				{
					next = contextNode.nextSibling;
					if ( next != null ) 
					{
						break;
					}
				}
				contextNode = next;
			}
			if ( contextNode != this && contextNode != null && contextNode.nodeType == Node.ELEMENT_NODE ) 
			{
				if ( p_tagName == "*" || contextNode.tagName == p_tagName ) 
				{
					aReturnNodes.add( contextNode );
				}
			}
		}
		return aReturnNodes;
	}

	this.appendChild = function( p_oChildNode ) 
	{
		if ( this.nodeType == XML_Node.ELEMENT_NODE || this.nodeType == XML_Node.ATTRIBUTE_NODE || this.nodeType == XML_Node.DOCUMENT_NODE || this.nodeType == XML_Node.DOCUMENT_FRAGMENT_NODE ) 
		{
			this.childNodes.add( p_oChildNode );
		}
		else
		{
			return null;
		}

		// Si ajout d'un node inclu dans un autre document, ERREUR
		if ( this.ownerDocument != p_oChildNode.ownerDocument ) return null;

		if ( this.childNodes.length == 1 ) 
		{
			this.firstChild = p_oChildNode;
		}

		this.lastChild = p_oChildNode;
		p_oChildNode.parentNode = this;

		var prevSibling = this.childNodes.item( -2 );
		p_oChildNode.previousSibling = prevSibling;

		if ( prevSibling != null )
		{
			prevSibling.nextSibling = p_oChildNode;
		}

		return p_oChildNode;
	}
	
	
	this.insertBefore = function( p_oNewChild, p_oRefChild ) 
	{
		var currentChildren = this.childNodes;
		this.childNodes = new XML_NodeList( );
		for ( var i = 0 ; i < currentChildren.length ; i++ ) 
		{
			var child = currentChildren.item( i );
			if ( child == p_oRefChild && p_oRefChild != null ) 
			{
				this.appendChild( p_oNewChild );
				refChild = null;
			}
			else 
			{
				this.appendChild( child );
				i++;
			}
		}
		return p_oNewChild;
	}

	this.removeChild = function( p_oOldChild ) 
	{
		var currentChildren = this.childNodes;
		this.childNodes = new XML_NodeList( );
		for ( var i = 0 ; i < currentChildren.length; i++ ) 
		{
			var child = currentChildren.item( i )
			if ( child != p_oOldChild ) 
			{
				this.appendChild( child );
			}
		}
	}

	this.replaceChild = function( p_oNewChild, p_oOldChild )
	{
		var oldChildren = this.childNodes;
		this.childNodes = new XML_NodeList( );
		for ( var i = 0; i < oldChildren.length; i++ ) 
		{
			if ( oldChildren.item( i ) == p_oOldChild ) 
			{
				this.appendChild( p_oNewChild );
			}
			else 
			{
				this.appendChild( p_oOldChild );
			}
		}
	}	
	
	
}




/* ---[ NODE LIST ]------------------------------------------------------------------- */

function XML_NodeList( )
{
	this.length = 0;

	this.getLength = function( )
	{
		return this.length;
	}

	this.item = function( p_index )
	{
		var oItem;
		if ( p_index < 0 )
		{
			oItem = this[ this.length + p_index ];
		}
		else
		{
			oItem = this[ p_index ];
		}
		return ( oItem || null );
	}
	
	this.add = function( p_oNode ) 
	{
		this[ this.length++ ] = p_oNode;
	}

}


/* ---[ DOCUMENT ]----------------------------------------------------------------------- */


function XML_create_Document() 
{
	var oElement = new XML_Node( );
	oElement.nodeName = "#document";
	oElement.nodeType = XML_Node.DOCUMENT_NODE;
	oElement.ownerDocument = oElement;
	oElement.createCDATASection = function( p_datas )			{	return XML_createNode( this, "", p_datas, "#cdata-section", XML_Node.CDATA_SECTION_NODE, p_datas );	}
	oElement.createComment = function( p_datas )				{	return XML_createNode( this, "", p_datas, "#comment", XML_Node.COMMENT_NODE, p_datas );				}
	oElement.createDocumentFragment = function( )				{	return XML_createNode( this, "", null, "#document-fragment", XML_Node.DOCUMENT_FRAGMENT_NODE, null );	}
	oElement.createEntityReference = function( p_name )			{	return XML_createNode( this, "", null, p_name, XML_Node.ENTITY_REFERENCE_NODE, null );				}
	oElement.createProcessingInstruction = function( p_target, p_datas )	{	return XML_createNode( this, "", null, p_target, XML_Node.PROCESSING_INSTRUCTION_NODE, p_datas );				}
	oElement.createTextNode = function( p_datas )				{	return XML_createNode( this, "", null, "#text", XML_Node.TEXT_NODE, p_datas );						}

	oElement.createElement = function( p_tagName )			
	{	
		var oElement = XML_createNode( this, "", null, p_tagName, XML_Node.ELEMENT_NODE, null );
		
		oElement.attributes = new XML_NamedNodeMap( );
		oElement.getAttribute = function( p_name ) 				{	var oAttribute = this.attributes.getNamedItem( p_name );  if ( oAttribute ) return oAttribute.getValue( ); return null;		}
		oElement.getAttributeNode = function( p_name )			{	return this.attributes.getNamedItem( p_name );	}
		oElement.removeAttribute = function( p_name )			{	this.attributes.removeNamedItem( p_name );		}
		oElement.removeAttributeNode = function( p_oAttribute ) {	return this.attributes.removeNamedItem( p_oAttribute.nodeName );		}
		oElement.setAttribute = function( name, value )			{	var oAttribute = this.ownerDocument.createAttribute( name, value ); oAttribute.setValue( value ); this.attributes.setNamedItem( oAttribute ); }
		oElement.setAttributeNode = function( p_oAttribute )	{	return this.attributes.setNamedItem( p_oAttribute );	}
		
		return oElement;
	}
	
	oElement.createAttribute = function( p_name, p_value )		
	{	
		var oElement = XML_createNode( this, p_name, p_value, p_name, XML_Node.ATTRIBUTE_NODE, null );
		oElement.nodeValue = p_value;	// patch Alex
		oElement.getValue = function( )
		{
			var value = "";
			for ( var i = 0; i < this.childNodes.length; i++ ) 
			{
				value += this.childNodes.item( i ).getNodeValue( );
			}
			return value;
		}

		oElement.setValue = function( p_value )
		{
			this.clearChilds( );
			if ( p_value == null ) return;
			this.appendChild( this.ownerDocument.createTextNode( p_value ) );
		}		
		return oElement;
	}

	return oElement;
}


function XML_createNode( p_oDoc, p_name, p_value, p_nodeName, p_nodeType, p_nodeValue )
{
	var oElement = new XML_Node( );
	oElement.name = p_name;
	oElement.value = p_value;
  
	oElement.nodeName = p_nodeName;
	oElement.nodeType = p_nodeType;
	oElement.nodeValue = p_nodeValue;
	oElement.ownerDocument = p_oDoc;
	
	return oElement;
}



// ---[ XML_NamedNodeMap ]---------------------------------------------------


function XML_NamedNodeMap( )
{
	this.length = 0;

	this.getLength = function( )			{	return this.length;		}
	this.getNamedItem = function( p_name )	{	return ( this[ p_name ] || null );	}

	this.item = function( p_index ) 
	{
		var oItem;
		oItem = ( p_index < 0 ) ? this[ this.length + oItem ] : this[ p_index ];
		return ( oItem || null );
	}

	this.removeNamedItem = function( p_name ) 
	{
		var removed = this[ p_name ];
		if ( !removed ) return null;

		delete this[ p_name ];
		for ( var i = 0; i < this.length - 1; i++ ) 
		{
			if ( !this[ i ] ) 
			{
				this[ i ] = this[ i + 1 ];
				delete this[ i + 1 ];
			}
		}
		this.length--;
		return removed;
	}

	this.setNamedItem = function( p_oNode ) 
	{
		var nodeName = p_oNode.getNodeName();
		var item = this.getNamedItem( nodeName );
		this[ nodeName ] = p_oNode;
		
		if ( item == null ) 
		{
			this[ this.length++ ] = p_oNode;
		}
		return item;
	}
}

function XML_EntitiesToText( p_sXML )
{
	return p_sXML;
//	return internalReplace( p_sXML, "&amp;", "&" );
}

function XML_TextToEntities( p_sXML )
{
	return p_sXML;
//	xml = internalReplace( p_sXML, "&amp;", "&" )
//	return internalReplace( p_sXML, "&", "&amp;" );
}

