/**
* Librairie remote Javascript (rjs)
*
* Fonctionnement:
* Un fichier javascript distant contient par d�finition un ensemble de propri�t�s et de m�thodes. Comme le fichier
* javascript est succeptible d'�tre modifi� dynamiquement l'url concernant ce fichier peut �tre modifi�e sur �v�nement
* lors de la consultation de la page par l'internaute. 
* L'invocation d'une m�thode n�cessite donc � la fois l'url du fichier javascript ansi que la commande. Ceci est r�alis�
* en invoquant la m�thode invoke(src,command) comme suit:
* rs=new RemoteScript();
* rs.invoke("http://scripts.somewhere.js/scripts/theScript.js?p=XXX","theFunction()");
* Dans le morceau de code pr�c�dent la variable rs contient une r�f�rence � un fichier js particulier contenant la 
* fonction theFunction().
* L'instance rs qui a �t� d�clar�e pr�c�demment doit �tre vu comme une source fonctionnelle de javascript. Le changement
* de l'URL doit contribuer avant tout � modifier le comportement javascript sans changer la signature.
* Par exemple, si on doit invoquer theFunction() sur deux fichiers js diff�rents:
*
* IL NE FAUT PAS FAIRE:
* rs1=new RemoteScript();
* rs1.invoke("http://scripts.somewhere.js/scripts/theScript.js?p=XXX","theFunction()");
*	...
* rs2=new RemoteScript();
* rs2.invoke("http://scripts.somewhere.js/scripts/theScript.js?p=YYY","theFunction()");
* Car dans ce cas, deux fonctions theFunction() ont �t� cr��es, il va donc y avoir ambiguit� entre ces deux fonctions
* cr��es par les deux instances.
*
* IL FAUT FAIRE:
* rs=new RemoteScript();
* rs.invoke("http://scripts.somewhere.js/scripts/theScript.js?p=XXX","theFunction()");
*	...
* rs.invoke("http://scripts.somewhere.js/scripts/theScript.js?p=YYY","theFunction()");
* La deuxi�me invocation fait disparaitre la pr�c�dente. Chaque objet RemoteScript doit incarner une fonctionnalit�
* particuli�re.
*
* IMPORTANT: La page HTML utilisant rjs doit contenir une section HEAD.
*
* TODO: Il faut param�trer davantage la partie message d'erreur
* TODO: Il faut envisager �ventuellement la possibilit� d'un valeur de retour sur invocation
**/



var DEFAULT_TIMEOUT=1000;
var DEFAULT_DELAY=100;



/**
* Cet objet maintient le code source li� au fichier d�sign� par le param�tre src utilis� dans la m�thode invoke(),
* il �vite de recharger a diverses reprises un script d�j� charg�. Il g�re aussi le d�lai de chargement du script
* distant, et les cas d'erreur de chargement.
*
* - defaultSrc: contient le fichier de script par d�faut utilis� lorsque le src utilis� dans invoke est inexistant.
* - ptimeout: (optionnel) d�finit le timeout sur l'invocation des m�thodes rjs.
* - pdelay: (optionnel) d�finit l'intervalle d'invocation des m�thodes rjs, limit�es par le timeout.
**/
function RemoteScript(defaultSrc,timeout,delay) {

	// On d�finit les propri�t�s defaultSrc timeout et delay
	this.defaultSrc=defaultSrc;
	this.timeout=DEFAULT_TIMEOUT;
	if(timeout!=undefined) this.timeout=timeout;
	this.delay=DEFAULT_DELAY;
	if(delay!=undefined) this.delay=delay;

	// On ajoute l'instance courante dans le remoteScriptPool	
	this.poolIndex=remoteScriptPool.add(this);
	
	// La propri�t� scriptNode va contenir le node script qui sera ajout� � la page. Pour l'instant il est null, indiquant qu'aucun script n'est charg�.
	this.scriptNode=null;
	
	// Le timestamp correspondant au d�marrage de l'invocation, il sert � interrompre l'execution au del� du tiemout
	this.commandTime=new Date();
	
	// Contiendra le dernier message d'erreur g�n�r� par eval() de la commande invoqu�e dans invoke(), ce message sera affich� par 
	// displayRSErrorMessage() en cas de timeout
	this.cause="No Problem";

	/**
	* M�thode d'invocation d'une fonction rjs.
	* - src: attribut src du node script indiquant o� se trouve le script � charger.
	* - command: commande � invoquer (soumise au timeout)
	* - cbAfter: methode 'callback' � executer apr�s la commande
	* ATTENTION: la commande est contenue dans une chaine de caract�re, elle doit �tre g�r�e comme c'est fait dans les foonctions classiques de 
	* Javascript setTimeout() ou eval(), ces deux fonctions sont d'ailleurs charg�es de g�rer la commande.
	**/
	this.invoke=function(src,command,cbAfter) {
		// Si un script est d�j� charg�, alors il n'est peut �tre pas n�cessaire de le recharger
		if(this.scriptNode!=null){
			// Si le script d�j� pr�sent sur la page a le m�me src que celui demand� en param�tre de la m�thode, il est inutile de le recharger
			// Si en revanche il est diff�rent, on doit effectuer ce chargement
			if(this.scriptNode.src!=src){
				// head r�f�rence le noeud HEAD de la page
				var head=document.getElementsByTagName("HEAD").item(0);

				// Comme un script �tait d�j� pr�sent, il faut le supprimer de la page.
				// Ce n'est pas suffisant, car si le param�tre scr ne correspond � aucun fichier disponible, le fait d'avoir supprim� le node script
				// ne fait pas disparaitre sa trace en "m�moire". Il faut donc charger le script par d�faut, pour s'assurer qu'on a bien remplac� la
				// trace du script pr�c�dent en "m�moire"
				head.removeChild(this.scriptNode);
				this.scriptNode=document.createElement("SCRIPT");
				this.scriptNode.charset="iso-8859-1"
				this.scriptNode.src=this.defaultSrc;
				this.scriptNode.type="text/javascript";
				head.appendChild(this.scriptNode);

				// Puis on enl�ve le script par d�faut et on ajoute le script correspondant au src pass� en param�tre
				head.removeChild(this.scriptNode);
				this.scriptNode=document.createElement("SCRIPT");
				this.scriptNode.charset="iso-8859-1"
				this.scriptNode.src=src;
				this.scriptNode.type="text/javascript";
				head.appendChild(this.scriptNode);
			}
		}
		// Si aucun script n'est d�j� charg� on charge simplement
		else{
			var head=document.getElementsByTagName('head').item(0);
			this.scriptNode=document.createElement("SCRIPT");
			this.scriptNode.charset="iso-8859-1"
			this.scriptNode.src=src;
			this.scriptNode.type="text/javascript";
			head.appendChild(this.scriptNode);
		}
		// On invoque alors la fonction par le biais de la m�thode tryCommand(), on applique d'embl�e un intervalle delay pour laisser au navigateur
		// le temps de charger le script
		this.commandTime=new Date();
		// closure use (definition d'une variable visible seulement ici et par la fonction definie juste en dessous)
		var scopedPoolIndex=this.poolIndex;
		var scopedFunctionCmd=function(){
				remoteScriptPool.get(scopedPoolIndex).tryCommand(command, cbAfter);
			};
		window.setTimeout(scopedFunctionCmd, this.delay);
	}
	
	/**
	* Cette m�thode tente l'excution de la commande pass�e en param�tre, si cette commande n'est pas execut�e correctement, elle est invoqu�e 
	* r�cursivement jusqu'� r�ussite de l'execution ou expiration du timeout.
	* - command: contient la commande (appel de fonction) � effectuer apr�s chargement du rjs
	* - cbAfter: methode 'callback' � executer apr�s la commande
	**/
	this.tryCommand=function(command, cbAfter) {
		// Le bon d�roulement de l'execution de la commande est control� par un try/catch
		try{
			// Si on n'a pas encore d�pass� le timeout, on peut tenter l'excution de la commande
			if((new Date()).getTime()<this.commandTime.getTime()+this.timeout){
				eval(command);
				// Si on arrive ici c'est que la commande a �t� execut�e, et le traitement se termine
			}
			// Sinon on affiche un message d'erreur
			else{
				displayRSErrorMessage("command timeout: "+command+", cause: "+this.cause.lineNumber+" - "+this.cause);
			}
			if(cbAfter){
				cbAfter();
			}
		}
		// Si la commande ne s'est pas execut� correctement, on invoque � nouveau la pr�sente m�thode apr�s l'�coulement de delay
		catch(e){
			this.cause=e;
			// closure use (definition d'une variable visible seulement ici et par la fonction definie juste en dessous)
			var scopedPoolIndex=this.poolIndex;
			var scopedFunctionCmd=function(){
					remoteScriptPool.get(scopedPoolIndex).tryCommand(command, cbAfter);
				};
			window.setTimeout(scopedFunctionCmd, this.delay);
		}
	}
}



/**
* Pas de description, cf. TODO list.
**/
var showErrors=false;
function displayRSErrorMessage(message) {
	if(showErrors){
		var DISPLAY_TIME=3000;
		var divErrorMessage=document.getElementById("rsErrorMessage");
		if(divErrorMessage==undefined){
			divErrorMessage=document.createElement("DIV");
			divErrorMessage.id="rsErrorMessage";
			var body=document.getElementsByTagName("BODY").item(0);
			body.appendChild(divErrorMessage);
			divErrorMessage.style.position="absolute";
			divErrorMessage.style.top="0px";
			divErrorMessage.style.left="0px";
			divErrorMessage.style.backgroundColor="red";
		}
		divErrorMessage.appendChild(document.createTextNode(message));
		divErrorMessage.appendChild(document.createElement("BR"));
		divErrorMessage.style.display="block";
		window.setTimeout("hideRSErrorMessage()",DISPLAY_TIME);
	}
}
function hideRSErrorMessage() {
	var divErrorMessage=document.getElementById("rsErrorMessage");
	if(divErrorMessage!=undefined){
		divErrorMessage.style.display="none";
	}
}



/**
* Pool de RemoteScript qui maintient dans la variable globale remoteScriptPool la totalit� des instances de RemoteScript pouvant �tre pr�sentes sur une
* m�me page.
**/
function RemoteScriptPool() {
	this.pool=new Array();
	this.add=function(remoteScript) {
		this.pool.push(remoteScript);
		return this.pool.length-1;
	}
	this.get=function(poolIndex) {
		return this.pool[poolIndex];
	}
}
var remoteScriptPool=new RemoteScriptPool();

