 /**
 * Cette fonction a pour but de créer une barre de progression contrôlable.
 * Ce script nécessite tools.js pour fonctionner
 * A cela s'ajoute un Listener commenté plus loin
 * 
 * Exemple d'intégration :
 * 
 * entre les balises <head></head> :
 * 		<script type="text/javascript" src="/base/js/tools.js"></script>
 * 		<script type="text/javascript" src="/base/js/effects/progressBar.js"></script>
 * 		<script type="text/javascript" src="/base/js/timeline.js"></script>
 * 		<style type="text/css">
 * 			#progressBarReloadedOneShot {width: 500px; height: 20px; border: 1px solid #c44} 
 * 			#progressBarReloadedOneShot div {width: 0; height: 20px; background: #c44}
 * 			#compteur1 {width: 40px; height: 14px; margin: 5px; line-height: 14px; font-size: 11px; color :#333}
 * 			#progressBarReloadedOneShot.classeFinalDIV {border: 1px solid blue}
 * 		</style>
 * 		
 * 		<script type="text/javascript">
 * 			var progressBar;
 * 			
 * 			function initPB () {
 * 			progressBar=new ProgressBar(document.getElementById("progressBarReloadedOneShot"),0, 1000);
 * 				// On rajoute les comportements désirés sur la page
 * 				addJavascriptToAttribute(document.getElementById("t1"),"onclick","progressBar.setValue(0);");
 * 				addJavascriptToAttribute(document.getElementById("t2"),"onclick","progressBar.setValue(500);");
 * 				addJavascriptToAttribute(document.getElementById("t3"),"onclick","progressBar.setValue(1000);");	
 * 				addJavascriptToAttribute(document.getElementById("t4"),"onclick","progressBar.setMinValue(-2000);");	
 * 				addJavascriptToAttribute(document.getElementById("t5"),"onclick","progressBar.setMaxValue(2000);");				
 * 				
 * 				function MyProgressBarListener(_minValue, _maxValue) {
 * 					//on fixe l'élément qui contiendra un compteur
 * 					var counterNode=document.getElementById("compteur1");
 * 					var flag=false;
 * 					this.throwEvent=function(progressBarEvent) {
 * 						// On inmplémente un compteur en %
 * 						//counterNode.firstChild.nodeValue=progressBarEvent.getValue();
 * 						counterNode.firstChild.nodeValue=Math.round(100*((progressBarEvent.getValue()-_minValue)/(_maxValue-_minValue)))+"%";
 * 						
 * 						// On ajoute une classe arrivé à 100% sur l'élément parent de la barre de progression
 * 						if (counterNode.firstChild.nodeValue == "100%") {
 * 							addClass(progressBarEvent.getElement(), "classeFinalDIV");
 * 							flag=true;
 * 						}
 * 						else if (flag) {
 * 							removeClass(progressBarEvent.getElement(), "classeFinalDIV");
 * 							flag=false;
 * 						}
 * 					}
 * 				}
 * 				MyProgressBarListener.prototype=new ProgressBarListener();
 * 				var myProgressBarListener=new MyProgressBarListener(progressBar.getMinValue(),progressBar.getMaxValue());
 * 				progressBar.addProgressBarListener(myProgressBarListener);
 * 				
 * 				//on implémente un exemple de timeline
 * 				var timeline=new Timeline(10);
 * 				function MyTimelineListener(_pb) {
 * 					var pb=_pb;
 * 					var start;
 * 					var T=10000;
 * 					this.throwEvent=function(timelineEvent) {
 * 						if(timelineEvent.getType()==TimelineEventType().START){
 * 							start=timelineEvent.getDate().getTime();
 * 						}
 * 						var t=timelineEvent.getDate().getTime()-start;
 * 						var value=500.0*Math.sin(6.28*t/T)+500.0;
 * 						pb.setValue(value);
 * 					}
 * 				}
 * 				MyTimelineListener.prototype=new TimelineListener();
 * 				var myTimelineListener=new MyTimelineListener(progressBar);
 * 				timeline.addTimelineListener(myTimelineListener);
 * 				timeline.start();
 * 		
 * 			}
 * 		</script>
 *  entre les balises <body></body> :
 *  <body onload="initPB();">
 *		
 *		<div id="progressBarReloadedOneShot">
 *			<div></div><!-- CETTE DIV DOIT TOUJOURS ETRE LA PREMIERE BALISE ENFANT-->
 *		</div>
 *		
 *		<div id="compteur1">0%</div><!-- CETTE DIV DOIT TOUJOURS AVOIR UN NOEUD TEXTE PRESENT, ELLE PEUT ETRE PLACE N'IMPORTE OU-->
 *		
 *		<input id="t1" type="submit" value="Aller à 0"></input>
 *		<input id="t2" type="submit" value="Aller à 500" ></input>
 *		<input id="t3" type="submit" value="Aller à 0"></input>
 *		<input id="t4" type="submit" value="Set minValue à -2000"></input>
 *		<input id="t5" type="submit" value="Set maxValue à 2000"></input>
 *	</body>
 *
 * @param {Object} paneNode un élément DOM désignant un élément quit doit avoir pour enfant
 * un élément de type DIV. C'est sur ce dernier élément que l'on fera varier la largeur en 
 * fonction d'une valeur transmise.
 * @param {number} _minValue est un nombre définisssant la valeur minimale
 * Ce paramètre est facultatif et prend 0 comme valeur par défaut.
 * @param {number} _maxValue est un nombre définisssant la valeur maximale
 * Ce paramètre est facultatif et prend 100 comme valeur par défaut.
 */
 function ProgressBar(paneNode, _minValue, _maxValue){
 	// Constante stockant le noeud DOM à utiliser
 	var pn=ev.dom.element(paneNode);
	if(!pn){throw new Error("paneNode is not defined ["+paneNode+"]");}
	if(_minValue>=_maxValue){throw new Error("_minValue is superior to _maxValue ["+paneNode+", min:"+_minValue+", max:"+_maxValue+"]");}
	
	/**
	 * This private property defines thisinstance reference in progressBarsList 
	 * that can be used in getProgressBar(int) function.
	 */
	var id=ProgressBarsManager().addProgressBar(this);

	/**
	 * This private field refers to the ProgressBarListener used by this instance 
	 * to manage its behaviour. Each delay ms, #throwEvent(ProgressBarListener) 
	 * method of that instance is invoked, providing this progressbar is running.
	 */
	var progressBarListener=null;
	/**
	 * This method adds a ProgressbarListener instance as a control to this 
	 * Progressbar.
	 * @param listener The ProgressbarListener that will control this 
	 * 	Progressbar.
	 * @throws If listener is not defined.
	 * @throws If listener is not an instance of ProgressbarListener.
	 */
	this.addProgressBarListener=function(listener) {
		if(!listener){throw new Error("progressBarListener is not defined");}
		if(!(listener instanceof ProgressBarListener)){throw new Error("progressBarListener is not a ProgressBarListener listener");}
		progressBarListener=listener;
	};
	// Cette constante fixe la div dont la largeur est amenée à être modifiée.
	var paneNodeFC=firstChildByNodeName(pn,"div") || firstChildByNodeName(pn,"DIV") ;
	
	// Ces constantes sont fixées à 0 par défaut
	// value est la valeur courante
	// widthFC est la largeur appliquée à paneNodeFC
	var value=0;
	var widthFC=0;
	
	// minValue est la valeur minimale
	// par défaut elle vaut 0, hormis si on la déclare en paramètre
	var minValue=_minValue||0;
	
	// maxValue est la valeur maximale
	// par défaut elle vaut 100, hormis si on la déclare en paramètre
	var maxValue=_maxValue||100;
	
	// La Largeur équivalent à 100%
	var maxWidth=getStyleValue(pn,"width");
	if(maxWidth!="none"&&maxWidth.match(/[0-9]+px/)){
		maxWidth=parseInt(maxWidth.replace(/px/,""), 10);
	}
	
	// On récupère les paddings et bordures horizontals quand IE est en quirk mode
	// car ils sont pris en compte dans le calcul de la largeur
	if (genericNavigator.navigator.id==MSIE&&document.compatMode=="BackCompat") {
		var paddingLeft=getStyleValue(pn,"paddingLeft");
		if(paddingLeft!="none"&&paddingLeft.match(/[0-9]+px/)){
			paddingLeft=parseInt(paddingLeft.replace(/px/,""), 10);
		}
		var paddingRight=getStyleValue(pn,"paddingRight");
		if(paddingRight!="none"&&paddingRight.match(/[0-9]+px/)){
			paddingRight=parseInt(paddingRight.replace(/px/,""), 10);
		}
		var paddingHor=paddingLeft+paddingRight;
		
		var borderLeftWidth=getStyleValue(pn,"borderLeftWidth");
		if(borderLeftWidth!="none"&&borderLeftWidth.match(/[0-9]+px/)){
			borderLeftWidth=parseInt(borderLeftWidth.replace(/px/,""), 10);
		}
		var borderRightWidth=getStyleValue(pn,"borderRightWidth");
		if(borderRightWidth!="none"&&borderRightWidth.match(/[0-9]+px/)){
			borderRightWidth=parseInt(borderRightWidth.replace(/px/,""), 10);
		}
		var borderHorWidth=borderLeftWidth+borderRightWidth;
	}
	this.getMinValue=function() {
		return minValue;
	};
	this.getMaxValue=function() {
		return maxValue;
	};
	/**
	 * On fixe la valeur de la largeur de paneNodeFC
	 */
	this.setValue=function(_value){
		value=_value;
		
		//Si on tente de dépasser les 100%
		if (value>maxValue) {
			if(genericNavigator.navigator.id==MSIE&&document.compatMode=="BackCompat"){
				maxWidth=maxWidth-paddingHor-borderHorWidth;
			}
			paneNodeFC.style.width=maxWidth+"px";
			value=maxValue;
		}
		//Si on tente d'aller en dessous de 0%
		else if (value<=minValue) {
			paneNodeFC.style.width=0;
			value=minValue;
		}
		// On recadre la barre de progression
		else {
			widthFC=maxWidth*((value-minValue)/(maxValue-minValue));
			/* On gère ici la différence de traitement des modèles de boîtes sous IE qui intervient quand il passe en mode Quirks */
			if(genericNavigator.navigator.id==MSIE&&document.compatMode=="BackCompat"){
				widthFC=widthFC-paddingHor-borderHorWidth;
			}
			paneNodeFC.style.width=widthFC+"px";
		}
		progressBarListener.throwEvent(new ProgressBarEvent(pn, value, minValue, maxValue,this));
	};
	/**
	 * Cette méthode permet de redéfinir la valeur minimale
	 */
	this.setMinValue=function(_minValue){
		minValue=_minValue;
		this.setValue(value);
	};
	/**
	 * Cette méthode permet de redéfinir la valeur maximale
	 */
	this.setMaxValue=function(_maxValue){
		maxValue=_maxValue;
		this.setValue(value);
	};
}

/**
 * Abstract class designed to control a Progressbar, this controls is a listener 
 * whom #throwEvent(ProgressbarEvent) abstract method is invoked periodically by 
 * the Progressbar instance it controls. All ProgressbarListener subclass must 
 * implement this method.
 */
function ProgressBarListener() {
	/**
	 * The method periodically invoked by the controled Progressbar, it defines the 
	 * Progressbar's behaviour.
	 * @param timlineEvent Contains a ProgressbarEvent instance produced by the 
	 * 	controled Progressbar.
	 * @throws If the method has not been implemented.
	 */
	this.throwTimelineEvent=function(progressBarEvent){
		throw new Error("#throwEvent() has not been implemented");
	};
}

/**
 * Defines the event produced by a Progressbar instance when it invokes 
 * ProgressbarListener#throwEvent().
 * @param _executionCount Step number from Progressbar's instance last #start() 
 * 	invokation.
 * @param _type Instance of ProgressbarEventType holding this ProgressbarEvent's type.
 * @param _source The Progressbar instance who produced this event.
 */
function ProgressBarEvent(_element, _value, _minValue, _maxValue, _source) {
	var source=_source;
	var element=_element;
	var value=_value;
	var minValue=_minValue;
	var maxValue=_maxValue;
	
	this.getSource=function() {
		return source;
	};
	this.getElement=function() {
		return element;
	};
	this.getValue=function() {
		return value;
	};
}

/**
 * Singleton declared to hold progressbar's events type enumeration, i.e.
 * START, RUNNING, and STOP.
 */
function ProgressBarEventType() {
}
new ProgressBarEventType();

/**
 * Singleton declared to hold the list of Progressbar instances generaly used in
 * eval(), or setTimeout(), or setInterval() string commands.
 */
function ProgressBarsManager() {
	// both two following lines defines the singleton.
	if(arguments.callee.singletonInstance){
		return arguments.callee.singletonInstance;
	}
	arguments.callee.singletonInstance=this;
	/**
	 * This private property holds the list of Progressbar instances and used by 
	 * functions addProgressbar(Progressbar) or getProgressbar(int) in order to find a 
	 * specific instance.
	 */
	var progressBarsList=[];
	/**
	 * Method designed to reference each Progressbar returning the reference which 
	 * can be used in method getProgressbar(int) to retreive the matching instance. 
	 * This is the only mean to reference an instance from an eval string for 
	 * example.
	 * @param progressbar a Progressbar instance to be referenced.
	 * @return the index of the given Progressbar instance that can be used in 
	 * 	getProgressbar(int) in order to get back the instance.
	 * @throws If progressbar parameter is not defined.
	 * @throws If progressbar parameter is not a Progressbar instance.
	 */
	this.addProgressBar=function(progressBar) {
		if(!progressBar){throw new Error("progressBar is not defined");}
		if(!(progressBar instanceof ProgressBar)){throw new Error("progressBar is not a ProgressBar");}
		progressBarsList.push(progressBar);
		return progressBarsList.length-1;
	};
	/**
	 * Method designed to return an instance given its index in the 
	 * progressbarsList.
	 * @param index the index of the instance to retrieve.
	 * @return the matching Progressbar instance.
	 * @throws If index is undefined, null or not a number.
	 * @throws If index is negative.
	 * @throws If index is out of bounds (is not a valid reference)
	 */
	this.getProgressBar=function(index) {
		if(typeof(index)!=='number'){throw new Error("index is not a number (maybe null or undefined as well) ["+index+"]");}
		if(index<0||index>=progressBarsList.length){throw new Error("index is out of bounds [0 ; "+progressBarsList.length+"[");}
		return progressBarsList[index];
	};
}
// intanciates singleton.
new ProgressBarsManager();