
function CElement_Transformer( p_oRootElement )
{
	this.oRootElement = p_oRootElement;
	this.oCurrentElement = p_oRootElement;
	
	this.oContextNode = null;
	this.oCurrentNode = null;
	
	this.aLoops = Array( );
	this.aLoopsElements = Array( );
	this.currentLoop = 0;
	this.currentElementInLoop = 0;
	
	this.sRendu = "";
	
	this.appendRendu = function( p_mixRendu )	
	{
		switch( typeof p_mixRendu )
		{
/*			case "object" :
				for ( var i = 0 ; i < p_mixRendu.length ; i++ )
					this.sRendu += p_mixRendu[ i ] ;
				break;
*/				
			default:
				this.sRendu += p_mixRendu;	
		}
	}
	
	this.setRootElement = function( p_oRootElement )
	{
		this.oRootElement = p_oRootElement;
	}
	
	this.transform = function( p_sXsl )
	{
		var oXslNode = DOM_htmlToNodes( p_sXsl, true );
		var oNode = oXslNode;
		this.oCurrentNode = oXslNode;
		this.oCurrentElement = this.oRootElement;
		
		this.transformNode( this.oCurrentElement, oNode );
		
		return this.sRendu;
	}
	
	this.transformNode = function ( p_oRootElement, p_oRootNode )
	{
		var oRootNode = p_oRootNode;
		if ( oRootNode.length )
		{
			for( var i = 0 ; i < oRootNode.length ; i++ )
				this.process( oRootNode[ i ] );
		}
		else
			this.process( oRootNode );
	}
	
	// Fonction qui renvoie la valeur d'un noeud xsl, en regardant s'il possède un SELECT="", un VALUE="" ou directement un TEXTNODE
	// si plusieurs objets match, la valeur renvoyŽ est un array, sinon, l'objet est directement renvoyŽ
	this.getXslNodeValue = function ( p_oNode )
	{
		var sSelectParam = p_oNode.getAttribute( "SELECT" );
		var sValueParam = p_oNode.getAttribute( "VALUE" );
		var aArrayElements = null;
		if ( sSelectParam )
		{
			aArrayElements = this.getXPath( this.oCurrentElement, sSelectParam );
			if ( aArrayElements.length == 1 )	// Si un seul element match, on le renvoie direct
				return aArrayElements[0]
			else		// Sinon on renvoie un array d'elements
				return aArrayElements;
		}
		else if ( sValueParam )		return sValueParam ;
		else 						return p_oNode.firstChild.nodeValue;		// A MODIFIER... POUR L'INSTANT CA NE DOIT PAS FONCTIONNER
	}
	
	// Fonction qui renvoie dans un Array les parametres d'un noeud XSL ( exemple <xsl:with-param value="toto"/>, ou <xsl:use-param.../>, ...)
	this.getXslNodeParams = function ( p_oNode, p_paramNodeName )
	{
		var aWithParams = Array( );
		var oNode;
		
		for ( var i = 0 ; i < p_oNode.childNodes.length ; i++ )
		{
			oNode = p_oNode.childNodes[ i ];
			if ( typeof oNode.nodeName != "undefined" && oNode.nodeName == "XSL:" + p_paramNodeName )
			{	
				aWithParams[ aWithParams.length ] = this.getXslNodeValue ( oNode );
			}
		}
		return aWithParams;
	}

	
	this.process = function( p_oNode )
	{
		var tagName = p_oNode.tagName;
		var aElements = Array( );
		var aResults = Array( );

		var sSelectParam = "";
		var sTestParam = "";
		var sValueParam = "";
		var aWithParams = Array( );
		var sEnteteObjet = "";				// Uniquement utile si appelle de method par exec-method

		var i = 0;

		// SI NOEUD TEXTE, ON NE FAIT QUE L'AFFICHER
		if ( p_oNode.nodeType == NODE_TEXT )
		{
			this.appendRendu( p_oNode.nodeValue );
		}
		// SI TAG COMMANDE ( XSL: )
		else if ( tagName.indexOf( "XSL:" ) >= 0 )
		{
			var sCmd = tagName.replace( "XSL:", "" );
	//		alert( sCmd );
			switch( sCmd )
			{
					case "VALUE-OF" :
						sSelectParam = p_oNode.getAttribute( "SELECT" );
						aResults = this.getXPath( this.oCurrentElement, sSelectParam );
						this.appendRendu( aResults );
						break;

					case "SET-VALUE-OF" :
						sSelectParam = p_oNode.getAttribute( "SELECT" );
						sNameParam = p_oNode.getAttribute( "NAME" );
						sNameParam = sNameParam.replace( "@", "" );
						sValueParam = p_oNode.getAttribute( "VALUE" );
						
						
						aElements = this.getXPath( this.oCurrentElement, sSelectParam );
						for ( i = 0 ; i < aElements.length ; i++ )
						{
							eval( "aElements[ i ]." + sNameParam + " = \"" + sValueParam + "\";" )
							aElements[ i ].setModified( true );
						}
						break;

					// EXEC-METHODE & EXEC-COMMAND FONT LA MEME CHOSE, SEULEMENT LE PREMIER APPELLE UNE METHODE DE CELEMENT,
					// L'AUTRE UNE FONCTION QUELCONQUE
					case "EXEC-METHOD" :
						sEnteteObjet = "aElements[ i ].";
					case "EXEC-COMMAND" :
						sSelectParam = p_oNode.getAttribute( "SELECT" );
						sNameParam = p_oNode.getAttribute( "NAME" );
						
						// ON RECUPERE LES PARAMETRES S'IL Y EN A
						aWithParams = this.getXslNodeParams( p_oNode, "WITH-PARAM" );
						var sParams = "";
						for ( i = 0 ; i < aWithParams.length ; i++ )
						{
							if ( sParams != "" ) sParams += ",";
							sParams += " aWithParams[" + i + "] ";
						}
						// ON EXECUTE LA COMMANDE
						if ( sSelectParam )	aElements = this.getXPath( this.oCurrentElement, sSelectParam );
						else				aElements = Array( this.oCurrentElement )
							
						for ( i = 0 ; i < aElements.length ; i++ )
							eval( sEnteteObjet + sNameParam + "(" + sParams + ")" )
//							eval( "aElements[ i ]." + sNameParam + "(" + sParams + ")" )
						break;
					
					case "FOR-EACH" :
						sSelectParam = p_oNode.getAttribute( "SELECT" );
						aElements = this.getXPath( this.oCurrentElement, sSelectParam );
						for ( i = 0 ; i < aElements.length ; i++ )
						{
							this.oCurrentElement = aElements[ i ];
							this.transformNode( this.oCurrentElement, p_oNode.childNodes ) ;
						}
						break;

					case "IF" :
						sTestParam = p_oNode.getAttribute( "TEST" );
//						aElements = this.getXPath( this.oCurrentElement, sSelectParam );
						if ( this.testCondition( this.oCurrentElement, sTestParam ) )
							this.transformNode( this.oCurrentElement, p_oNode.childNodes ) ;
	
					break;
	
				default :
					break;
			} 
		}
		// SI TAG NORMAL ( sans XSL:, GENRE HTML )
		else
		{
			var tmpRenduStartTag = "<" + p_oNode.nodeName;
			var bEmptyTag = false;
			for ( i = 0 ; i < p_oNode.attributes.length ; i++ )
				if( p_oNode.attributes.item( i ).nodeValue != null && p_oNode.attributes.item( i ).nodeValue.length > 0 )
					tmpRenduStartTag += " " + p_oNode.attributes.item(i).nodeName + "=\"" + p_oNode.attributes.item(i).nodeValue + "\"";
			// On regarde s'il s'agit ou non d'un TAG vide (autoclosed) comme BR
			if( p_oNode.childNodes.length == 0 && DOM_Empty_Tags.indexOf( p_oNode.nodeName ) > 0 )	
			{
				tmpRenduStartTag += "/>";
				bEmptyTag = true;
			}
			// On append la premiere partie du tag
			this.appendRendu( tmpRenduStartTag )
				
			if( !bEmptyTag )
			{
				// On Process les tags enfants s'il y en a
				if ( p_oNode.childNodes && p_oNode.childNodes.length > 0 )
					this.transformNode( this.oCurrentElement, p_oNode.childNodes ) ;
				this.appendRendu( ">" );
			}
		}
	}
	
	
	this.getXPath = function( p_oRootElement, p_XPath )
	{
		var aElementsMatch = Array( );
		var pos;
		var currentPath = "";
		var currentCondition = ""; 
		var i;
		var currentAxeRecherche = "";
		var iCurrentAxeRecherche = "";
		
		if ( p_XPath.substring( 0, 2 ) == "//" ) 			pos = 1;
		else if ( ( pos = p_XPath.indexOf( "/" ) ) < 0 ) 	pos = p_XPath.length;
		currentPath = p_XPath.substring( 0, pos );
		p_XPath = p_XPath.substring( pos+1 , p_XPath.length );

//		alert( "2-CMD: '" + currentPath + "'" );
//		alert( "XPath: '" + p_XPath + "'" );

		if ( currentPath.substring( 0, 2 ) == ".." )
		{
			aElementsMatch[0] = this.oCurrentElement.oParent;
		}
		else if ( currentPath.substring( 0, 1 ) == "." )
		{
			aElementsMatch[0] = this.oCurrentElement;
		}
		else if ( currentPath == "/" )
		{
			aElementsMatch[0] = this.oRootElement;
		}
		else
		{
			if ( currentPath.substring( 0, 1 ) == "@" )	
			{
				aElementsMatch[ 0 ] = eval( "p_oRootElement." + currentPath.substring( 1, currentPath.length ) );
				return aElementsMatch;
			}
			else
			{
				// find condition [...]
				if ( ( pos = currentPath.indexOf( "[" ) ) > 0 )
				{
					currentCondition = internalTrim( currentPath.substring( pos + 1 , currentPath.indexOf( "]" ) ) );
					currentPath = internalTrim( currentPath.substring( 0, pos ) );
				}
				// find descendant::
				if ( ( pos = currentPath.indexOf( "::" ) ) > 0 )
				{
					currentAxeRecherche = currentPath.substring( 0, pos );
					currentPath = currentPath.substring( pos+2, currentPath.length );
					switch( currentAxeRecherche )
					{
						case "descendant" 		: iCurrentAxeRecherche = ELEMENT_SEARCH_DESCENDANT; break;
						case "descendant-or-self": iCurrentAxeRecherche = ELEMENT_SEARCH_DESCENDANT_OR_SELF; break;
						case "ancestor"			: iCurrentAxeRecherche = ELEMENT_SEARCH_ANCESTOR; break;
						case "ancestor-or-sef" 	: iCurrentAxeRecherche = ELEMENT_SEARCH_ANCESTOR_OR_SELF; break;
						case "parent" 			: iCurrentAxeRecherche = ELEMENT_SEARCH_PARENT; break;
						case "child"			: iCurrentAxeRecherche = ELEMENT_SEARCH_CHILD; break;
						case "previous-sibling" : iCurrentAxeRecherche = ELEMENT_SEARCH_PREVIOUS_SIBLING; break;
						case "following-sibling": iCurrentAxeRecherche = ELEMENT_SEARCH_FOLLOWING_SIBLING; break;
					}
				}
				// match the tag ? on va voir a !
				switch( currentPath )
				{
					case "document" :
						aElementsMatch = p_oRootElement.getElementByType( ELEMENT_DOCUMENT, iCurrentAxeRecherche );
						break; 
					case "bloc" :
						aElementsMatch = p_oRootElement.getElementByType( ELEMENT_BLOC, iCurrentAxeRecherche );
						break; 
					case "media" :	
						aElementsMatch = p_oRootElement.getElementByType( ELEMENT_MEDIA, iCurrentAxeRecherche );
						break; 
					case ".." :	aElementsMatch[0] = p_oRootElement.oParent; break;
					
				}
			}
		}

		// Si on a des conditions pour cet objet :
		if ( currentCondition != "" )
			aElementsMatch = this.applyCondition( aElementsMatch, currentCondition );
		
		if( p_XPath == "" )
		{
			return aElementsMatch;
		}
		else
		{
			var aResultElements = Array( );
			for ( i = 0 ; i < aElementsMatch.length ; i++ )
			{
				var aTmpResultElements = this.getXPath( aElementsMatch[ i ], p_XPath );
				for ( var ii = 0 ; ii < aTmpResultElements.length ; ii++ )
					if ( aTmpResultElements[ ii ] )
						aResultElements[ aResultElements.length ] = aTmpResultElements[ ii ];
			}
			return aResultElements;
		}
	}
	
	this.applyCondition = function( p_aElements, p_strConditions )
	{
		var aElementsMatch = Array( );

		for ( var i = 0 ; i < p_aElements.length ; i++ )
		{
			this.oCurrentElement = p_aElements[ i ];			// On indique ou l'on est
			if ( this.testCondition( p_aElements[ i ], p_strConditions ) )
				aElementsMatch[ aElementsMatch.length ] = p_aElements[ i ];
		}
		return aElementsMatch;
	}
	
	this.testCondition = function( p_aElement, p_strConditions )
	{
		var p_symbole = "";
		if ( p_strConditions.indexOf( "!=" ) > 0 )		p_symbole = "!=";
		else if ( p_strConditions.indexOf( "=" ) > 0 )	p_symbole = "=";
		else if ( p_strConditions.indexOf( ">=" ) > 0 )	p_symbole = ">=";
		else if ( p_strConditions.indexOf( "<=" ) > 0 )	p_symbole = "<=";
		else if ( p_strConditions.indexOf( ">" ) > 0 )	p_symbole = ">";
		else if ( p_strConditions.indexOf( "<" ) > 0 )	p_symbole = "<";
		
		var value = internalTrim( p_strConditions.substring( p_strConditions.indexOf( p_symbole ) + p_symbole.length, p_strConditions.length ) );
		var variable = internalTrim( p_strConditions.substring( 0, p_strConditions.indexOf( p_symbole ) ) );
//		variable = variable.replace( "@", "" );
		if ( p_symbole == "=" ) p_symbole = "==";		// Transcription en javascript...

		variable = this.getXPath( this.oCurrentElement, variable );
		return eval ( "( \"" + variable + "\" " + p_symbole + " " + value + " ) " );
//		return eval( "( this.oCurrentElement." + variable + " " + p_symbole + " " + value + ")" );
	}
	

	
}