/**
* Scripts utilitaires généraux.
**/

/**
 * Declarations de variables globales.
 */
/*global window, document, ev */

/**
 * Fonction anonyme de déclaration de classe(s) utiles.
 * Certaines classes/fonctions ne sont visible que par
 * la(es) classe(s) concernée(s) (déclarée(s) ici).
 * Ce mécanisme de fonction anonyme permet de reproduire
 * un système d'encapsulation digne d'un langage de
 * programmation évolué (comme le Java).
 */
(function(){
	// On s'assure que le namespace ev existe
	if(!window.ev){ window.ev={}; }
	// pour compatibilité ascendante (namespace EV)
	window.EV=window.ev;
	// On ne fait rien si le namespace ev.tools a déjà été initialisé
	if(ev.tools){return;}
	// déclaration du namespace ev.tools
	ev.tools={};

	// Patchs pour compatibilité des logs sur les
	// navigateurs sans console
	if(!window.console){ window.console={}; }
	if(!window.console.log){ window.console.log=function(){}; }
	if(!window.console.info){ window.console.info=function(){}; }
	if(!window.console.warn){ window.console.warn=function(){}; }
	if(!window.console.error){ window.console.error=function(){}; }
	// méthodes de logging pour 4 niveau de verbosité
	if(!ev.log){
		ev.log={
			NONE: 0,
			FATAL: 1,
			ERROR: 2,
			WARN: 3,
			INFO: 4,
			DEBUG: 5,
			LEVEL: 2 // niveau par défaut ERROR
//			LEVEL: 5 // pour activer mode DEBUG pendant chargement des ev.tools
		};
	}

	function stacktrace(){
		var caller=arguments.callee.caller&&arguments.callee.caller['arguments'].callee.caller, stackText="", cnt=-1, a, l;
		if(caller){
			stackText+="\n - Stack Trace: ";
			while(caller){
				stackText+="\n    "+(++cnt)+":"+caller.name+"(";
				a=caller['arguments'];
				l=a.length;
				if(l>0){
			  		stackText+=a[0].constructor&&a[0].constructor.name||'?';
					for(var i=1; i<l; ++i){stackText+=","+(a[i].constructor&&a[i].constructor.name||'?');}
				}
				stackText+=")";
				caller=a.callee.caller;
			}
		}
		return stackText;
	}

	/** trace un message d'erreur fatale */
	ev.log.fatal=function(m, st){
		if(ev.log.LEVEL>=ev.log.FATAL){window.console.error(m+(st? stacktrace(): ''));}
	};
	/** trace un message d'erreur */
	ev.log.error=function(m, st){
		if(ev.log.LEVEL>=ev.log.ERROR){window.console.error(m+(st? stacktrace(): ''));}
	};
	/** trace un message d'alerte */
	ev.log.warn=function(m, st){
		if(ev.log.LEVEL>=ev.log.WARN){window.console.warn(m+(st? stacktrace(): ''));}
	};
	/** trace un message d'info */
	ev.log.info=function(m, st){
		if(ev.log.LEVEL>=ev.log.INFO){window.console.info(m+(st? stacktrace(): ''));}
	};
	/** trace un message de debug */
	ev.log.debug=function(m, st){
		if(ev.log.LEVEL>=ev.log.DEBUG){window.console.log(m+(st? stacktrace(): ''));}
	};

	/**
	 * Patch pour la classe String.
	 * Supprime les espaces inutiles en début et fin
	 * de la chaîne.
	 */
	String.prototype.trim=function(){
		 return this.replace(/(^\s+|\s+$)/g, '');
	};

	/**
	 * Patch pour la classe String.
	 * Transforme la première lettre de la chaine
	 * en une majuscule.
	 */
	String.prototype.capitalize=function(){
		 return this.charAt(0).toUpperCase()+this.substring(1);
	};
	
	/**
	 * Fonction permettant, si besoin, de définir une fonction
	 * à exécuter après le chargement du fichier donné.
	 *
	 * A priori cette fonction doit être utilisée en fin de fichier
	 * et prendre en paramètre le nom du fichier en question.
	 *
	 * @param {String} jsFile : nom du fichier js
	 */
	ev.tools.onFileLoad=function(jsFile){
		var PAT_JS_ONLOAD=new RegExp('^.*\\/'+jsFile.replace(/\./g,'\\.').replace(/\//g,'\\/')+'\\?onfileload=(.*)$', 'i');
		var scriptElements=document.getElementsByTagName('head')[0].getElementsByTagName('script');
		var l=scriptElements.length;
		for(var i=0; i<l; ++i){
			var src=scriptElements[i].src;
			if(!src){continue;}
			if(PAT_JS_ONLOAD.test(src)){
				var ldName=src.replace(PAT_JS_ONLOAD,'$1');
				window.setTimeout('if(typeof('+ldName+')==\'function\'){'+ldName+'();}else{throw new Error(\'On file '+jsFile+' load - Fonction non définie : '+ldName+' (\'+typeof('+ldName+')+\')\');}', 0);
			}
		}
	};

	/**
	 * Copie toutes les propriétés d'un objet dans un autre sauf si la propriété existe déjà dans l'objet de destination
	 * @param {Object} c : objet de destination
	 * @param {Object} s : objet source
	 */
	ev.tools.extend=function(c, s){
		if(!c){ev.log.error("ev.tools.extend: NullPointerException(child cannot be null)");}
		if(!s){ev.log.error("ev.tools.extend: NullPointerException(super cannot be null)");}
		// copie de toutes les propriétés de super vers child
		for(var p in s){
			if(c[p]===undefined||!c.hasOwnProperty(p)&&s.hasOwnProperty(p)){
				c[p]=s[p];
			}
		}
		return c;
	};

	/**
	 * Copie toutes les propriétés d'un objet dans un autre.
	 * Cette fonction ne copie pas les méthodes.
	 * @param {Object} d : objet de destination
	 * @param {Object} s : objet source
	 */
	ev.tools.copyProperties=function(d, s){
		if(!d){ev.log.error("ev.tools.copy: NullPointerException(destination object cannot be null)");}
		if(!s){ev.log.error("ev.tools.copy: NullPointerException(source object cannot be null)");}
		// copie de toutes les propriétés de source vers destination
		for(var p in s){
			if(typeof(s[p])!=='function'){
				d[p]=s[p];
			}
		}
		return d;
	};

	/**
	 * Copie toutes les propriétés et méthodes d'un objet
	 * dans un autre.
	 * @param {Object} d : objet de destination
	 * @param {Object} s : objet source
	 */
	ev.tools.copy=function(d, s){
		if(!d){ev.log.error("ev.tools.copy: NullPointerException(destination object cannot be null)");}
		if(!s){ev.log.error("ev.tools.copy: NullPointerException(source object cannot be null)");}
		// copie de toutes les propriétés de source vers destination
		for(var p in s){
			d[p]=s[p];
		}
		return d;
	};

	/**
	 * 
	 * @param {String} chaine formatée en hh:mm
	 */
	ev.tools.convertToMinute=function(chaine){
		if(/^([0-9]{1,2}):([0-9]{2})$/.test(chaine)){
			return parseInt(RegExp.$1, 10)*60+parseInt(RegExp.$2, 10);
		}
		ev.log.error("ev.tools.convertToMinute: Le paramètre "+chaine+" n'est pas formaté selon hh:mm");
		return 0;
	};

	/**
	 * Remplacement de la méthode navigator.cookieEnabled
	 * qui ne marche que sur Firefox (cf test
	 * http://fr.selfhtml.org/javascript/objets/affichage/navigator_cookie_enabled.htm)
	 * <br>
	 * Une technique de "closure" est utilisée pour encapsuler
	 * la vérification de la possibilité d'utilisation des
	 * cookies. Cette partie encapsulée crée un cookie et
	 * essaye d'y accéder pour voir s'il a vraiment été créé.
	 * <br>
	 * Une fois la vérification faite on ne conserve que la
	 * valeur obtenue pour la retourner à chaque appel à la
	 * méthode #isCookieEnabled().
	 * <br>
	 * NB : Testée sur Firefox,IE
	 *
	 * @return true si et seulement si le navigateur courant
	 *   accepte les cookies ; sinon false
	 */
	ev.tools.isCookieEnabled=(function (){ // tech de "closure"
		// Nom du cookie de test.
		var nameCookieTest="ev_testcookie",
		// Expression régulière permettant de retrouver le cookie de test.
		regexCookieTest=new RegExp("(?:; )?"+nameCookieTest+"=([^;]*);?"),
		// création d'un timestamp (nombre considéré comme aléatoire, utilisé juste pour notre test)
		ts=new Date().getTime(),
		// valeur booléenne qui représente la possibilité d'utiliser les cookies sur le navigateur courant
		cookieEnabled,
		// date d'expiration qui permet de supprimer le cookie une fois le test effectué
		dateExpiration;
		// on enregistre le test de cookie pour voir si on peut le relire
		document.cookie=nameCookieTest+'='+ts+';path=/';
		// on regarde si on peut lire le cookie (on vérifie que le timestamp est bien récupéré)
		cookieEnabled=regexCookieTest.test(document.cookie)&&ts===parseInt(RegExp.$1, 10);
		// suppression du cookie (méthode simple : on fixe la date d'expiration pour que le cookie expire dans une seconde) 
		dateExpiration=new Date();
		dateExpiration.setTime(dateExpiration.getTime()+1000);
		document.cookie=nameCookieTest+'=;path=/;expires='+dateExpiration.toGMTString();
		// on retourne une fonction qui renverra seulement la valeur booléenne
		return function (){
			return cookieEnabled;
		};
	}());

///////// DEBUT ev/tools/array.js ///////////
	ev.tools.array={};

	/**
	 * Fonction utile ajoutant les éléments du 2e tableau
	 * donné dans le premier.
	 * @param {Object} a tableau à compléter
	 * @param {Object} b tableau à ajouter à la fin du premier 
	 */
	ev.tools.array.append=function (a, b){
		var i, l=b.length;
		for(i=0; i<l; ++i){
			a.push(b[i]);
		}
		return a;
	};

	ev.tools.array.toString=function (a){
		if(!a){return '¤';}
		var l=a.length, s=l+'[', i;
		if(l){
			for(i=0; i<l; ++i){
				s+='\n'+a[i];
			}
			s+='\n';
		}
		return s+']';
	};

	ev.tools.array.deepToString=function (a, s){
		if(!a){return '¤';}
		s=s||'';
		var l=a.length, r=s+l+' [', i;
		if(l){
			for(i=0; i<l; ++i){
				if(a[i]&&a[i].constructor===Array){
					r+='\n'+arguments.callee(a[i], s+'  ');
				}
				else{
					r+='\n'+s+' '+a[i];
				}
			}
			r+='\n'+s;
		}
		return r+']';
	};

	/**
	 * Méthode utile similaire au indeOf appliquable sur les listes en java
	 * @param {Object} array
	 * @param {Object} object: ATTENTION doit être de type simple (integer, string) 
	 * //TODO[poblin] élargir la méthode aux types complexes
	 * @return le 1er index de array contenant objet, sinon -1
	 */
	ev.tools.array.indexOf=function(array,object){
		//on parcourt le tableau et on retourne le 1er index contenant l'objet, sinon -1
		if(typeof(array.length)!=="number"){
			ev.log.error("ev.tools#indexOf - array doit être un tableau");
			return -1;
		}
		if(typeof(object)!=="string" && typeof(object)!=="number"){
			ev.log.error("ev.tools#indexOf - object doit être de type string ou number");
			return -1;
		}
		for(var i=0;i<array.length;i++){
			el=array[i];
			if(el===object){
				return i;
			}
		}
		return -1;
	};

	/**
	 * retourne un tableau qui est la copie du tableau passé en paramètre. C'est une copie profonde (les éléments du tableau ne sont pas des référence)
	 * @return {Array} : tab copié
	 */
	ev.tools.clone=function(tab){
		if(!tab){ev.log.error("ev.tools: Le paramètre doit etre un tableau");}
		var output=[];
		for(var i=0;i<tab.length;i++){
			output[i]=tab[i];
		}
		return output;
	};

///////// DEBUT ev/dom/utils.js ///////////
	/** On s'assure que le namespace ev.dom existe */
	if(!ev.dom){ ev.dom={}; }

	/**
	 * La méthode ev.dom.element(String/Element) retrouve un ou plusieurs éléments
	 * dans le DOM. Cette méthode retourne autant d'éléments que de
	 * de paramètre reçus.
	 * Si un paramètre est un élément DOM, il est retourné directement.
	 * Si un paramètre est une chaîne, l'élément DOM correspondant sera
	 * recherché.
	 * NOTA : cette méthode a un résultat quasi identique à la méthode $(elt,...)
	 * de Prototype. Notre méthode est itérative plutôt que recursive, mais arrive
	 * au même résultat.
	 * @param {Object} _elt : id du premier élément à retrouver dans le document DOM
	 * @return si 1 paramètre : un élément DOM ; si plus d'1 paramètre : un tableau d'éléments DOM
	 * (chaque élément DOM retourné peut-être null ou undefined si non trouvé)
	 */
	ev.dom.element=function(_elt){
		var l=arguments.length;
		if(typeof(_elt)==='string'){
			_elt=document.getElementById(_elt);
		}
		if(l<=1){
			// si on fourni un seul paramètre ou aucun, on sort ici (le résultat peut être un élément DOM, null ou undefined)
			return _elt;
		}
		// sinon, on se prépare à retourner un tableau d'éléments DOM (dont le premier est celui déjà récupéré)
		_elt=[_elt];
		for(var i=1; i<l; ++i){
			if(typeof(arguments[i])==='string'){
				// si le paramètre est une chaîne, on cherche l'élément DOM correspondant
				// (undefined ou null si non trouvé), et on l'insère dans le tableau
				_elt.push(document.getElementById(arguments[i]));
			}
			else{
				// sinon, on insère le paramètre lui-même
				_elt.push(arguments[i]);
			}
		}
		// Enfin, on retourne le tableau d'éléments DOM
		return _elt;
	};

	/**
	 * Méthode permettant de créer un élément DOM.
	 * L'élément sera créé en XHTML si possible.
	 * @param {String} _tagName : type de balise à créer
	 */
	ev.dom.create=function(_tagName){
		if(document.createElementNS){
			return document.createElementNS('http://www.w3.org/1999/xhtml', _tagName);
		}
		return document.createElement(_tagName);
	};

	/**
	 * Méthode permettant de créer un noeud texte.
	 * @param {String} t contenu du noeud texte
	 */
	ev.dom.createText=function(t){
		return window.document.createTextNode(t);
	};

	/**
	 * Recherche les éléments DOM ayant le nom donné.
	 * @param {String} _tagName : nom des tags à retrouver dans l'élément donné (par défaut le document racine)
	 * @param {String} _elt (optionel) : élément DOM dans lequel il faut effectuer la recherche, par défaut 'document'
	 * @return un tableau d'éléments DOM ayant le nom recherché
	 */
	ev.dom.tags=function(_tagName, _elt){
		return (ev.dom.element(_elt)||document).getElementsByTagName(_tagName);
	};

	/**
	 * @param {Object} _elt : élément DOM
	 * @return l'éléments DOM suivant l'élément donné (ou null si non trouvé)
	 */
	ev.dom.nextElement=function(_elt){
		do{
			_elt=_elt.nextSibling;
		}while(_elt&&_elt.nodeType!==1);
		return _elt;
	};

	/**
	 * Fonction qui indique si le className d'un élément donné
	 * contient la classe passée en paramètre.
	 * @param {HTMLElement} _elt : Element DOM à vérifier
	 * @param {String} _className : nom de la classe recherhée
	 */
	ev.dom.hasClass=function(_elt, _className){
		_elt=ev.dom.element(_elt);
		// Locate the class name (allows for mutliple class names)
		if(_elt&&new RegExp("(^|\\s)"+_className+"(\\s|$)").test(_elt.className)){
			return true;
		}
		return false;
	};

	/**
	* Ajoute une classe dans l'attribut class d'un élément
	 * @param {HTMLElement} _elt : Element DOM (ou id d'élément)
	 * @param {String} _classToAdd : nom de la classe à ajouter
	**/
	ev.dom.addClass=function(_elt, _classToAdd){
		_elt=ev.dom.element(_elt);
		if(!_elt){return;}
		var className=_elt.className;
		var classList=className.split(/\s+/);
		for(var i=0;i<classList.length;i++){
			if(classList[i]==_classToAdd){return;}
		}
		_elt.className=(className+" "+_classToAdd).trim();
	};
	
	/**
	 * Dans une classe contenant "xxx" ou "yyy xxx zzz" on
	 * peut supprimer "xxx" pour obtenir respectivement ""
	 * ou "yyy zzz".
	 * @param {HTMLElement} _elt : Element DOM (ou id d'élément)
	 * @param {String} _classToRemove : nom de la classe à enlever
	**/
	ev.dom.removeClass=function(_elt, _classToRemove){
		_elt=ev.dom.element(_elt);
		if(!_elt){return;}
		var className=_elt.className;
		var classList=className.split(/\s+/);
		var newClassName='';
		for(var i=0;i<classList.length;i++){
			if(classList[i]!=_classToRemove){
				newClassName+=classList[i]+' ';
			}
		}
		_elt.className=newClassName.trim();
	};

	/**
	 * Recherche les éléments DOM ayant la classe demandée, et le cas échéant le type donné.
	 * @param {String} _className : id du premier élément à retrouver dans le document DOM
	 * @param {String} _eltType (optionel) : type d'élément DOM (ex: li, span, ...), par défaut tout type d'élément
	 * @return un tableau d'éléments DOM ayant la classe CSS recherchée
	 */
	ev.dom.elementsWithClass=function(_className, _eltType){
		var resultats=[];
		// Locate the class name (allows for mutliple class names)
		var RE=new RegExp("(^|\\s)"+_className+"(\\s|$)");
		// Limit search by type, or look through all elements
		var elements=ev.dom.tags(_eltType||"*");
		for(var i=0; i<elements.length; ++i){
			if(RE.test(elements[i].className)){
				// If the element has the class, add it for return
				resultats.push(elements[i]);
			}
		}
		// Return the list of matched elements
		return resultats;
	};

	/**
	 * @param {Object} _elt : élément DOM (ou tableau d'éléments)
	 * @return le texte contenu dans le(s) élément(s) DOM donné(s)
	 */
	ev.dom.text=function(_elt){
		_elt=ev.dom.element(_elt);
		var t="";
		// If an element was passed, get it's children, 
		// otherwise assume it's an array
		_elt=_elt.childNodes||_elt;
		// Look through all child nodes
		for ( var j=0; j<_elt.length; j++){
			if(_elt[j].nodeType!=1){
				// If it's not an element, append its text value
				t+=_elt[j].nodeValue;
				continue;
			}
			// Otherwise, recurse through all the element's children
			t+=arguments.callee(_elt[j].childNodes);			
		}
		// Return the matched text
		return t;
	};

	/**
	* Fonction qui indique si le className d'un élément contient la classe passée en paramètre.
	* @deprecated FIXME utiliser ev.dom.hasClass()
	**/
	window.hasClass=function(_elt,className){
		_elt=ev.dom.element(_elt);
		if(!_elt){return false;}
		var classNameElement=_elt.className;
		var classList=classNameElement.split(/\s+/);
		for(var i=0;i<classList.length;i++){
			if(classList[i]==className){return true;}
		}
		return false;
	};

	/**
	* Ajoute une classe dans l'attribut class d'un élément
	* @deprecated FIXME utiliser ev.dom.addClass()
	**/
	window.addClass=ev.dom.addClass;
	
	/**
	 * Dans une classe contenant "xxx" ou "yyy xxx zzz" on
	 * peut supprimer "xxx" pour obtenir respectivement ""
	 * ou "yyy zzz".
	 * @deprecated FIXME utiliser ev.dom.removeClass()
	**/
	window.removeClass=ev.dom.removeClass;

	/**
	* Effectue la permutation des classes passées en paramètre (class1 et class2) pour
	* l'élément lui passé en paramètre (_elt)
	* Les classes permutées sont rejetées à la fin de l'attribut class de l'élément.
	* EXEMPLE: 
	* <tag class="xxx yyy"> 
	* après l'invocation de swapClasses(tagElement,"xxx","zzz"), devient:
	* <tag class="yyy zzz">
	**/
	window.swapClasses=function(_elt,class1,class2) {
		_elt=ev.dom.element(_elt);
		if(!_elt){return;}
		if(ev.dom.hasClass(_elt,class1)){
			ev.dom.removeClass(_elt,class1);
			ev.dom.addClass(_elt,class2);
		}
		else if(ev.dom.hasClass(_elt,class2)){
			ev.dom.removeClass(_elt,class2);
			ev.dom.addClass(_elt,class1);
		}
	};
///////// FIN ev/dom/utils.js /////////////

	// FIXME [ygally] A mettre dans un fichier à part (tout ce qui concerne la langue en js : /base/js/ev/lang.js)
	
	/**
	* Choix langue par défaut si aucune n'est définie
	**/
	if(!window.lang){
		window.lang="fr_FR";
	}
	
	// 4 variables internes : accessibles seulement dans cette
	// fonction anonyme et dans les fonctions créées plus bas.
	// Ceci évite de créer trop de variables globales (seules
	// les 4 fonctions, plus bas, seront accessibles)
	var tabJoursTexteCourt; // jours courts
	var tabJoursTexteLong; // jours entiers
	var tabMoisTexteCourt; // mois courts
	var tabMoisTexteLong; // mois entiers
	// définition du vocabulaire selon la langue
	// par défaut en français.
	switch(window.lang){
		case "es_ES":
			tabJoursTexteCourt=["dom.","lun.","mar.","mié.","jue.","vie.","sáb."];
			tabJoursTexteLong=["domingo","lunes","martes","miércoles","jueves","viernes","sábado"];
			tabMoisTexteCourt=["","ene.","feb.","mar.","abr.","may.","jun.","jul.","ago.","sep.","oct.","nov.","dic."];
			tabMoisTexteLong=["","enero","febrero","marzo","abril","mayo","junio","julio","agosto","septiembre","octubre","noviembre","diciembre"];
			break;
		case "it_IT":
			tabJoursTexteCourt=["dom.","lun.","mar.","mer.","gio.","ven.","sab."];
			tabJoursTexteLong=["domenica","lunedi","martedi","mercoledi","giovedi","venerdi","sabato"];
			tabMoisTexteCourt=["","gen.","feb.","mar.","apr.","mag.","giu.","lug.","ago.","set.","ott.","nov.","dic."];
			tabMoisTexteLong=["","gennaio","febbraio","marzo","aprile","maggio","giugno","luglio","agosto","settembre","ottobre","novembre","dicembre"];
			break;
		case "en_US": // ici en_US = en_GB, mais il peut y avoir des différences pour certains textes
		case "en_GB":
			tabJoursTexteCourt=["sun.","mon.","tue.","wed.","thu.","fri.","sat."];
			tabJoursTexteLong=["sunday","monday","tuesday","wednesday","thursday","friday","saturday"];
			tabMoisTexteCourt=["","jan.","feb.","mar.","apr.","may","jun.","jul.","aug.","sep.","oct.","nov.","dec."];
			tabMoisTexteLong=["","january","febrary","march","april","may","june","july","august","september","october","november","december"];
			break;
		case "de_DE":
			tabJoursTexteCourt=["So.","Mo.","Di.","Mi.","Do.","Fr.","Sa."];
			tabJoursTexteLong=["Sonntag","Montag","Dienstag","Mercredi","Donnerstag","Freitag","Samstag"];
			tabMoisTexteCourt=["","Jan.","Feb.","M\u00E4rz.","Apr.","Mai.","Juni.","Juli.","Aug.","Sep.","Okt.","Nov.","Dez."];
			tabMoisTexteLong=["","Januar","Februar","M\u00E4rz","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"];
			break;
		//case "fr_FR": par défaut fr_FR (français)
		default:
			tabJoursTexteCourt=["dim.","lun.","mar.","mer.","jeu.","ven.","sam."];
			tabJoursTexteLong=["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"];
			tabMoisTexteCourt=["","jan.","fév.","mar.","avr.","mai","jun.","jul.","aoû.","sep.","oct.","nov.","déc."];
			tabMoisTexteLong=["","janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"];
			break;
	}
	
	/**
	* Retourne le texte court correspondant au quantième d'un jour de la semaine. Le quantième est celui utilisé dans la
	* classe date 0=dimanche, 1=lundi, 2=mardi, 3=mercredi, 4=jeudi, 5=samedi
	**/
	window.getJourTexteCourt=function(value){
		return tabJoursTexteCourt[Math.abs(value)];
	};
	
	/**
	* Retourne le texte long correspondant au quantième d'un jour de la semaine. Le quantième est celui utilisé dans la
	* classe date 0=dimanche, 1=lundi, 2=mardi, 3=mercredi, 4=jeudi, 5=samedi
	**/
	window.getJourTexteLong=function(value){
		return tabJoursTexteLong[Math.abs(value)];
	};
	
	/**
	* Retourne le texte court correspondant au quantième d'un mois. Contrairement à ce qui est défini dans la classe date
	* le compte ne débute pas à 0, mais il associe 1=janvier, 2=février, 3=mars, 4=avril, etc...
	**/
	window.getMoisTexteCourt=function(value){
		return tabMoisTexteCourt[Math.abs(value)];
	};
	
	/**
	* Retourne le texte long correspondant au quantième d'un mois. Contrairement à ce qui est défini dans la classe date
	* le compte ne débute pas à 0, mais il associe 1=janvier, 2=février, 3=mars, 4=avril, etc...
	**/
	window.getMoisTexteLong=function(value){
		return tabMoisTexteLong[Math.abs(value)];
	};
	
	/**
	 * Permet de créer une date à partir de l'année, le mois
	 * et le jour sans risque de création de dates erronées.
	 *
	 * Cela évite de se retrouver avec une date imprévue
	 * lorsque la date du jour comporte des irrégularités.
	 * (ex: 31 du mois, mois de février, année bissextile)
	 *
	 * L'objet Date se décalant sur une date correcte dés
	 * qu'un élément entré est incorrect, on peut souvent
	 * avoir ce problème de date imprévue. (ex: une date du
	 * jour étant 14/02/2008, lorsqu'on veut initialiser le
	 * 31/05/2008, si on commence par changer le jour, la
	 * date du 31/02/2008 étant erronée, on bascule
	 * directement sur le 02/03/2008, ensuite le mois vient
	 * et on obtient 02/05/2008 qui n'est pas la date
	 * souhaitée.
	 *
	 * Si la date obtenue après nos manipulations est
	 * différente des paramètres, là on sait que c'est
	 * une mauvaise date.
	 *
	 * @param {Integer} _j : jour de la date (1-31)
	 * @param {Integer} _m : mois de la date (1-12)
	 * @param {Integer} _a : année de la date (en 4 chiffres)
	 */
	window.createDate=function(_j, _m, _a){
		// Les deux lignes suivantes convertissent la chaine jour en nombre
		_j--;
		_j++;
		var d=new Date();
		// On force d'abord à Janvier (pour être dans un mois de 31 jours)
		d.setMonth(0);
		// on supprime toutes les données sur l'heure
		d.setHours(0);
		d.setMinutes(0);
		d.setSeconds(0);
		d.setMilliseconds(0);
		// On positionnera ensuite dans l'ordre l'année, le jour et le mois
		d.setFullYear(_a);
		d.setDate(_j);
		d.setMonth(_m-1);
		return d;
	};
	
	/**
	 * Indique si la date définie par les quantièmes jour mois an est une date
	 * valide. Retourne le booléen true si la date est valide et postérieure
	 * à maintenant, false sinon.
	 *
	 * @param {Integer} _j : jour de la date (1-31)
	 * @param {Integer} _m : mois de la date (1-12)
	 * @param {Integer} _a : année de la date (en 4 chiffres)
	 */
	window.isValidDate=function(_j, _m, _a) {
		var now=new Date();
		var d=window.createDate(_j, _m, _a);
		--_m;
	//	alert('jour  = '+_j+' - '+d.getDate()+'\n'
	//	+'mois  = '+_m+' - '+d.getMonth()+'\n'
	//	+'annee = '+_a+' - '+d.getFullYear());
		if(_j!=d.getDate()){return false;}
		if(_m!=d.getMonth()){return false;}
		if(_a!=d.getFullYear()){return false;}
		if(now.getTime()>d.getTime()){return false;}
		return true;
	};
	
	/**
	* Dans les sélects générés automatiquement, il y a un tag <option> vide (obligatoire pour la validation XHTML). Avant
	* l'insertion des options calculées, il faut supprimer cette option vide. C'est le rôle de cette fonction
	*	- s: l'élément select dans lequel on doit supprimet l'option vide
	*/
	window.removeOptionInSelect=function(s){
		var n=s.firstChild;
		while(n){
			if(n.tagName==="OPTION"){
				s.remove(n);
				return;
			}
			n=n.nextSibling;
		}
	};
	
	/**
	* Retourne un objet option
	*	- v value de l'option
	*	- t texte de l'option
	**/
	window.createOption=function(v, t){
		var o=document.createElement("OPTION");
		o.value=v;
		o.text=t;
		return o;
	};
	
	/**
	* Ajoute une option dans un select à la suite des options précédents
	*	- s: l'élément select dans lequel on insére l'option
	*	- o: l'élément option à insérer
	**/
	window.addOptionInSelect=function(s, o){
		var i=s.length;
		try{
			s.add(o, null);
		}catch(e1){
			try{
				s.add(o, i);
			}catch(e2){}
		}
	};
	
	/**
	* Initialise automatiquement le selecteur d'heures avec toutes ses valeurs
	*	- s: l'élément select à modifier
	**/
	window.initHeures=function(s){
		window.removeOptionInSelect(s);
		for(var j=0;j<24;j++){
			var v=""+j;
			if(v<10){
				v="0"+j;
			}
			v+=":00";
			window.addOptionInSelect(s, window.createOption(v, v));
		}
	};
	
	/**
	* Initialise automatiquement le selecteur de jours avec toutes ses valeurs, précision l'élément SELECT doit être structuré comme suit:
	* <select attributs... ><option></option></select>
	* Si on renseigne defaultTextJours et defaultValueJours dans l'appel de la fonction, un élément option supplémentaire est ajouté au début de la liste d'options. NOTA: Par défaut HTML affiche toujours le premier élément d'une liste select, pour éventuellement modifier, la valeur par défaut on fera appel à des fonctions JS annexes.
	* - s: l'élément select à modifier
	* - defaultTextJours (optionel): un texte éventuel précédant la liste des jours.
	* - defaultValueJours (optionel): la valeur associée au texte précédent. NOTA: Si l'un ou l'autre des deux paramètres defaultTextJours ou defaultValueJours est
	* manquant, aucune option supplémentaire n'est alors affichée en début de liste. 
	**/
	window.initJours=function(s, defaultTextJours, defaultValueJours){
		window.removeOptionInSelect(s);
		if(defaultTextJours!==undefined&&defaultValueJours!==undefined){
			window.addOptionInSelect(s, window.createOption(defaultValueJours, defaultTextJours));
		}
		for(var j=1;j<=31;j++){
			var v=""+j;
			if(v<10){
				v="0"+j;
			}
			window.addOptionInSelect(s, window.createOption(v, j));
		}
	};
	
	/**
	* Initialise automatiquement le selecteur de mois avec toutes ses valeurs, et définit comme mois par défaut 
	* celui défini sur l'ordinateur client
	*	- s: l'élément select à modifier
	**/
	window.initMois=function(s){
		window.removeOptionInSelect(s);
		var d=new Date(), m=d.getMonth()+1, a=d.getFullYear(), j;
		for(j=0;j<=12;j++){
			// FIXME utiliser ev.lang.getMoisTexteCourt(mois) des qu'elle est dispo
			window.addOptionInSelect(s, window.createOption((m<10?'0':'')+m+'/'+a, window.getMoisTexteCourt(m)+' '+a));
			if(++m>12){
				m=1;
				++a;
			}
		}
	};
	
	/**
	* Effectue la permutation des classes folded et unfolded sur l'élément passé en paramètre.
	**/
	window.foldUnfold=function(_elt) {
		window.swapClasses(_elt,"folded","unfolded");
	};
	
	/**
	* Rend un champ input (ou select) inactif et modifie sa classe CSS
	* Fonction utilisée par les scripts formXXX.js
	*	- _elt: element champ à rendre inactif
	**/	
	window.disableInput=function(_elt){
		_elt=ev.dom.element(_elt);
		if(!_elt){return;}
		_elt.disabled="disabled";
		ev.dom.removeClass(_elt,"enabled");
		ev.dom.addClass(_elt,"disabled");
	};
	
	/**
	* Rend un champ input (ou select) actif et modifie sa classe CSS
	* Fonction utilisée par les scripts formXXX.js
	*	- _elt: element champ à rendre actif
	**/	
	window.enableInput=function(_elt){
		_elt=ev.dom.element(_elt);
		if(!_elt){return;}
		_elt.disabled="";
		ev.dom.removeClass(_elt,"disabled");
		ev.dom.addClass(_elt,"enabled");
	};
	
	/**
	* Définit l'opacité d'un élément
	* fonction utilisée par toggle()
	*	- _elt: l'élément dont on doit définir l'opacité
	*	- opacity: opacité donnée à l'élément
	**/
	window.setOpacity=function(_elt,opacity) {
		_elt=ev.dom.element(_elt);
		if(!_elt){return;}
		if(!_elt.style){return;}
		_elt.style.opacity=opacity;
		_elt.style.filter="alpha(opacity="+(opacity*100)+")";
	};
	
	// for use with Navigator + Os
	window.UNKNOWN=0;

	// for use with Navigator
	window.FIREFOX=1;
	window.MSIE=2;
	window.OPERA=3;
	window.SAFARI=4;
	window.NETSCAPE=5;
	window.CAMINO=6;
	window.KONQUEROR=7;

	// for use with Os
	window.WINDOWS_XP=1;
	window.WINDOWS_VISTA=2;
	window.WINDOWS_2000=3;
	window.WINDOWS_SERVER_2003=4;
	window.WINDOWS_98=5;
	window.MAC_OS_X_PPC=6;
	window.MAC_OS_X_INTEL=7;
	window.LINUX=8;

	window.Navigator=function(ua){
		// Initialisation des propriétés
		this.id=window.UNKNOWN;
		this.version=0.0;
		// variable temporaire
		var debut;
	
		// Firefox
		if(ua.indexOf("firefox/")!=-1){
			this.id=window.FIREFOX;
			debut=ua.indexOf("firefox/");
			this.version=""+(ua.substring(debut+8).match(/[0-9]+\.[0-9]+/));
		}
		// MSIE
		else if(ua.indexOf("msie")!=-1){
			this.id=window.MSIE;
			debut=ua.indexOf("msie");
			this.version=""+(ua.substring(debut+4).match(/[0-9]+\.[0-9]+/));
		}
		// Opera
		else if(ua.indexOf("opera")!=-1){
			this.id=window.OPERA;
			debut=ua.indexOf("opera");
			this.version=""+(ua.substring(debut+6).match(/[0-9]+\.[0-9]+/));
		}
		// Safari
		else if(ua.indexOf("safari")!=-1){
			this.id=window.SAFARI;
			debut=ua.indexOf("version");
			this.version=""+(ua.substring(debut+8).match(/[0-9]+\.[0-9]+/));
		}
		// Netscape
		else if(ua.indexOf("netscape")!=-1){
			this.id=window.NETSCAPE;
			debut=ua.indexOf("netscape");
			this.version=""+(ua.substring(debut+9).match(/[0-9]+\.[0-9]+/));
		}
		// Camino
		else if(ua.indexOf("camino/")!=-1){
			this.id=window.CAMINO;
			debut=ua.indexOf("camino/");
			this.version=""+(ua.substring(debut+7).match(/[0-9]+\.[0-9]+/));
		}
		// Konqueror
		else if(ua.indexOf("konqueror/")!=-1){
			this.id=window.KONQUEROR;
			debut=ua.indexOf("konqueror/");
			this.version=""+(ua.substring(debut+10).match(/[0-9]+\.[0-9]+/));
		}
	};
	
	window.Os=function(ua) {
		// Initialisation des propriétés
		this.os=window.UNKNOWN;
		//variables temporaires
		var tab_elt;
		var tab_os;
		var system;
	
		if(ua.indexOf("opera")!=-1){
			//tab_elt = ua.replace(/[^\(]*(\([^\)]*)\).*/,"$1").split(";");
			//this.os=tab_elt[0];
			tab_elt = ua.split("(");
			tab_os = tab_elt[1].split(";");
			//this.os= =tab_os[0];
			system=tab_os[0];
		}
		else{
			tab_elt = ua.replace(/[^\(]*(\([^\)]*)\).*/,"$1").split(";");
			//tab_elt = ua.split(";");
			//this.os=tab_elt[2];
			system=tab_elt[2];
		}
	
		//system = system.replace(/^\s+/, "").replace(/\s+$/, "");
		system = system.trim();
		if(system=="windows nt 5.1"){this.os=window.WINDOWS_XP;}
		else if(system=="windows nt 6.0"){this.os=window.WINDOWS_VISTA;}
		else if(system=="windows nt 5.0"){this.os=window.WINDOWS_2000;}
		else if(system=="windows nt 5.2"){this.os=window.WINDOWS_SERVER_2003;}
		else if(system=="windows 98"){this.os=window.WINDOWS_98;}
		else if(system=="ppc mac os x mach-o"||system=="ppc mac os x"||system=="macintosh"){this.os=window.MAC_OS_X_PPC;}
		else if(system=="intel mac os x mach-o"){this.os=window.MAC_OS_X_INTEL;}
		else if(system=="linux"){this.os=window.LINUX;}
	};
	
	/**
	* Cette classe définit le navigateur de façon générique.
	**/
	window.GenericNavigator=function(){
		var ua=window.navigator.userAgent.toLowerCase();
		this.navigator=new Navigator(ua);
		this.os=new Os(ua);
	
		this.getNameNavigator=function() {
			switch(this.navigator.id){
				case window.FIREFOX: return "Firefox "+this.navigator.version;
				case window.MSIE: return "Microsoft Internet Explorer "+this.navigator.version;
				case window.OPERA: return "Opera "+this.navigator.version;
				case window.SAFARI: return "Safari "+this.navigator.version;
				case window.NETSCAPE: return "Netscape "+this.navigator.version;
				case window.CAMINO: return "Camino "+this.navigator.version;
				case window.KONQUEROR: return "Konqueror "+this.navigator.version;
				default : return "Inconnu";
			}
		};
		//alert(this.getNameNavigator());
		
		this.getNameOs=function() {
			switch(this.os.os){
				case window.WINDOWS_XP: return "WINDOWS XP";
				case window.WINDOWS_VISTA: return "WINDOWS VISTA";
				case window.WINDOWS_2000: return "WINDOWS 2000 ";
				case window.WINDOWS_SERVER_2003: return "WINDOWS SERVER 2003 ";
				case window.WINDOWS_98: return "WINDOWS 98 ";
				case window.MAC_OS_X_PPC: return "MAC OS X PPC ";
				case window.MAC_OS_X_INTEL: return "MAC OS X INTEL ";
				case window.LINUX: return "LINUX ";
				default: return "Inconnu";
			}
		};
		//alert(this.getNameOs());
				
		this.getXMLHttpRequest=function() {
			if(this.id==window.MSIE&&this.version<7.0){// MSIE version <7
				try{
					return new ActiveXObject("Msxml2.XMLHTTP");
				}
				catch(e){
					try{
						return new ActiveXObject("Microsoft.XMLHTTP");
					}
					catch (e2){
					}
				}
			}
			else{
				var xmlHttpRequest=new XMLHttpRequest();
				// Évite un bug du navigateur Safari :
				if(xmlHttpRequest.overrideMimeType){
					xmlHttpRequest.overrideMimeType("text/xml");
				}
				return xmlHttpRequest;
			}
			throw "GenericNavigator.getXMLHttpRequest(): could not retreive an XMLHttpRequest instance";
		};
	};
	window.genericNavigator=new GenericNavigator();
	
	/**
	* Ajoute un comportement javascript dans un attribut d'élément
	*	- _elt: l'élément
	*	- attributeName: nom de l'attribut à modifier
	*	- newJavascript: le code javascript à ajouter
	*	- trail: (optionel) si true le javascript est ajouté en fin d'attribut, si false, il est ajouté au début, par
	*	  défaut true;
	**/
	window.addJavascriptToAttribute=function(_elt,attributeName,newJavascript,trail) {
		_elt=ev.dom.element(_elt);
		if(!_elt){return;}
		if(!attributeName){return;}
		if(newJavascript===undefined){return;}
		if(trail===undefined){
			trail=true;
		}
		
		var idnav=genericNavigator.navigator.id;
		
		//var attribute=_elt.attributes[attributeName];
		var attribute=_elt.getAttributeNode(attributeName);
		newJavascript=newJavascript.replace(/^\s*javascript:\s*/,"");
		newJavascript=newJavascript.replace(/\s*;\s*$/,"");
	
		if(attribute&&attribute.nodeValue){
			var oldJavascript=attribute.nodeValue;
			oldJavascript=oldJavascript.replace(/^\s*javascript:\s*/,"");
			oldJavascript=oldJavascript.replace(/\s*;\s*$/,"");
			if(trail){
				newJavascript=oldJavascript+";"+newJavascript;
			}
			else{
				newJavascript=newJavascript+";"+oldJavascript;
			}
			
			if(idnav==FIREFOX || idnav==OPERA || idnav==SAFARI || idnav==NETSCAPE || idnav==CAMINO || idnav==KONQUEROR){
				attribute.nodeValue=newJavascript;
			}
			else{
				attribute.nodeValue=function(){
					eval(newJavascript);
				};
			}		
		}
		else{
			attribute=document.createAttribute(attributeName);
			_elt.setAttributeNode(attribute);
			if(idnav==FIREFOX || idnav==OPERA || idnav==SAFARI || idnav==NETSCAPE || idnav==CAMINO || idnav==KONQUEROR){
				attribute.nodeValue=newJavascript+";";
			}
			else {
				attribute.nodeValue=function(){
					eval(newJavascript+";");
				};
			}
		}
	};
	
	/**
	* Modifie un comportement javascript dans un attribut d'élément, i.e. crée le code, ou remplace un éventuel Javascript précédemment mis en 
	* place, dans l'attribut désigné.
	*	- _elt: l'élément
	*	- attributeName: nom de l'attribut à modifier
	*	- newJavascript: le code javascript à appliquer
	**/
	window.modifyJavascriptInAttribute=function(_elt,attributeName,newJavascript) {
		_elt=ev.dom.element(_elt);
		if(!_elt){return;}
		if(!attributeName){return;}
		if(newJavascript===undefined){return;}
		
		var idnav=genericNavigator.navigator.id;
		
	//	var attribute=_elt.attributes[attributeName];
		var attribute=_elt.getAttributeNode(attributeName);
		newJavascript=newJavascript.replace(/^\s*javascript:\s*/,"");
		newJavascript=newJavascript.replace(/\s*;\s*$/,"");
		if(attribute&&attribute.nodeValue!==undefined){
			if(idnav==FIREFOX || idnav==OPERA || idnav==SAFARI || idnav==NETSCAPE || idnav==CAMINO || idnav==KONQUEROR){
				attribute.nodeValue=newJavascript;
			}
			else{
				attribute.nodeValue=function(){
					eval(newJavascript);
				};
			}
		}
		else{
			attribute=document.createAttribute(attributeName);
			_elt.setAttributeNode(attribute);
			if(idnav==FIREFOX || idnav==OPERA || idnav==SAFARI || idnav==NETSCAPE || idnav==CAMINO || idnav==KONQUEROR){
				attribute.nodeValue=newJavascript+";";
			}
			else{
				attribute.nodeValue=function(){
					eval(newJavascript+";");
				};
			}
		}
	};
	
	/**
	* Un bug présent sur IE cause un comportement erratique des tableaux chargés par JSON. En effet, la structure suivante:
	* [
	*		{data1},
	* 	{data2},
	* ]
	* ne compte en théorie que DEUX datas. Ceci est correctement géré par Firefox, mais IE en compte TROIS, à cause de la 
	* virgule supplémentaire après {data2}. Il est possible que d'autre navigateurs soient sujets au même bug. La fonction 
	* suivante effectue la correction, en supprimant dans le tableau tout élément null.
	**/
	window.normalizeJSONArray=function(dataArray) {
		var newDataArray=[];
		for(var i=0;i<dataArray.length;i++){
			if(dataArray[i]){
				newDataArray.push(dataArray[i]);
			}
		}
		return newDataArray;
	};
	
	/**
	 * Cette méthode permet de stopper l'évènement en cours sur IE
	 * (Sur les autres navigateurs il suffit de retourner la valeur false)
	 */
	window.cancelIEEvent=function(){
	    // Traitement spécifique pour le blocage de l'évènement sur IE (pour qu'il ne remonte pas au navigateur)
	    if(genericNavigator.navigator.id==MSIE&&window.event){
	        window.event.cancelBubble=true;
	        window.event.returnValue=false;
	    }
	};

	/**
	 * Récuperation d'un parametre de l'url.
	 * @param {String} _name : nom du paramètre à récupérer
	 */
	ev.tools.getParameter=function(_name){
		//on recupere la query, on supprime le ? de depart, et on divise la chaine dans un tableau avec le separateur &, qui est du coup supprimé 
		var pArr=window.location.search.substring(1).split("&"),
		i=pArr.length, idx;
		while(i--){
			// si c'est pas le parametre cherché, on passe au suivant
			if(pArr[i].indexOf(_name)){continue;}
			idx=pArr[i].indexOf("=")+1;
			if(idx){
				return pArr[i].substring(idx);
			}
		}
		return null;
	};

	/**
	* Recuperation de parametre de l'url.
	* @deprecated utiliser ev.tools.getParameter(String) - FIXME méthode window.getParameter(String) à supprimer
	**/
	window.getParameter=ev.tools.getParameter;

	/**
	 * Cette fonction effectue le tranfert d'un jeu de paramètres d'URL vers les propriétés value
	 * d'éléments de la page. Ce transfert s'effectue par le biais de la variable langParamIdArray
	 * qui définit pour chaque paramètre dans une langue donnée l'identifiant de l'élément dont la
	 * value doit être modifié. La liste de ces paramétrage étant stockée dans un tableau d'objets
	 * disposant des propriétés lang, param, et id. lang définit la langue telle qu'elle est définie
	 * pour la page dans window.lang. param, définit pour la langue précédente, le paramètre d'URL
	 * qui va contenir la valeur à affecter à l'élément. id contient sous forme de chaine, l'identifiant
	 * de l'élément à affecter.
	 * Exemple de la déclaration d'un langParamIdArray en format JSon
	 * [
	 *	{lang: "fr_FR", param: "depart", id: "lieuMEVDepartAller", id2: "iataMEVDepartAller"},
	 *	{lang: "es_ES", param: "origen", id: "lieuMEVDepartAller", id2: "iataMEVDepartAller"},	
	 *	{lang: "it_IT", param: "partenza", id: "lieuMEVDepartAller", id2: "iataMEVDepartAller"},
	 *  {lang: "en_EN", param: "departure", id: "lieuMEVDepartAller", id2: "iataMEVDepartAller"},	
	 *  {lang: "de_DE", param: "abflug", id: "lieuMEVDepartAller", id2: "iataMEVDepartAller"},
	 *	{lang: "fr_FR", param: "arrivee", id: "lieuMEVArriveeAller", id2: "iataMEVArriveeAller"},
	 *	{lang: "es_ES", param: "destino", id: "lieuMEVArriveeAller", id2: "iataMEVArriveeAller"},
	 *	{lang: "it_IT", param: "destinazione", id: "lieuMEVArriveeAller", id2: "iataMEVArriveeAller"},
	 *	{lang: "en_EN", param: "arrival", id: "lieuMEVArriveeAller", id2: "iataMEVArriveeAller"},
	 *	{lang: "de_DE", param: "ankunft", id: "lieuMEVArriveeAller", id2: "iataMEVArriveeAller"}
	 * ];
	 */
	window.transfertLangURLToElement=function(langParamIdArray){
		for(var i=0;i<langParamIdArray.length;i++){
			if(langParamIdArray[i].lang==window.lang){
				var value=ev.tools.getParameter(langParamIdArray[i].param);
				if(value){
					ev.dom.element(langParamIdArray[i].id).value=value;
					ev.dom.element(langParamIdArray[i].id2).value=value;
				}
			}
		}
	};
	
	/**
	* Cette fonction récupère le style passé en paramètre de l'élément passé en paramètre
	*
	* Returns the value of a particular style as it actually defined i.e. defined by a
	* style attribute, or a style sheet, or the default navigator's behaviour.
	* @param _elt The element owning the style we get the value.
	* @param styleName The style name to look for. Use the same definition as in css-style
	* not as usually defined in JavaScript (for example: USE border-top-color, 
	* NOT borderTopColor).
	* @return A string containing the value or "none" if the style does not exist.
	*/
	window.getStyleValue=function(_elt,styleName){
		_elt=ev.dom.element(_elt);
		if(!_elt){throw new Error("param elt is not a valid DOM element");}
		if(typeof(styleName)!=='string'){throw new Error("styleName is not a valid property name (string)");}
		//variable temporaire
		var value;
		if(window.getComputedStyle){
			var cssStyleDeclaration=window.getComputedStyle(_elt,null);// should never be null
			value=cssStyleDeclaration.getPropertyValue(styleName);// can be empty if the style does not exist
			if(value===""){throw new Error("unknown style: "+styleName);}
			else{return value;}
		}
		else if(_elt.currentStyle){// Works with MSIE 5+
			// contains the current style of the element (all the styles given by style
			// attribute, css, or default behaviour.
			var currentStyle=_elt.currentStyle;
			value=currentStyle.getAttribute(styleName);
			// can be null for a composed style name (ex: border-top-color) if
			// the attribute is managed by the navigator.
			// Examples: border-top-color is managed by IE6 and IE7 both navigators
			// change it to borderTopColor. max-width is managed by IE7, but is not
			// by IE6. IE7 uses maxWidth, IE6 uses max-width.
			if(typeof(value)!=='string'){
				// While there is a - in styleName, it is removed and the following
				// letter is upper cased.
				// Ex: toto-titi-tutu -> totoTiti-tutu -> totoTitiTutu
				while(styleName.indexOf('-')!= -1){
					// dashAndLetter contains the first - and the following letter
					var dashAndLetter=styleName.charAt(styleName.indexOf('-')+1);
					styleName=styleName.replace(/-\S{1}/,dashAndLetter.toUpperCase());
				}
				value=currentStyle.getAttribute(styleName);
			}
			if(typeof(value)==='string'){return value;}
			else{return "none";}
		}
		else{throw new Error("cannot retrieve styleName: "+styleName);}
	};
	
	
	//DEBUG
	//	var maxWidth=getStyleValue(paneNode,"max-width");
	//log('max width (by easy : \''+maxWidth+'\')', 'info');
	//log('max width (by proto : \''+protoGetStyle(paneNode, "max-width")+'\')', 'info');
	//String.prototype.camelize=function() {
	//    var parts = this.split('-'), len = parts.length;
	//    if (len == 1) return parts[0];
	//
	//    var camelized = this.charAt(0) == '-'
	//      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
	//      : parts[0];
	//
	//    for (var i = 1; i < len; i++)
	//      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
	//
	//    return camelized;
	//  };
	//String.prototype.capitalize=function(){
	//    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
	//  };
	//window.protoGetStyle=function(_elt, style) {
	//    _elt = ev.dom.element(_elt);
	//    if(!_elt){return null;}
	//    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
	//    var value = _elt.style[style];
	//    if (!value && _elt.currentStyle) value = _elt.currentStyle[style];
	//
	//    if (style == 'opacity') {
	//      if (value = (_elt.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
	//        if (value[1]) return parseFloat(value[1]) / 100;
	//      return 1.0;
	//    }
	//
	//    if (value == 'auto') {
	//      if ((style == 'width' || style == 'height') && (_elt.getStyle('display') != 'none'))
	//        return _elt['offset' + style.capitalize()] + 'px';
	//      return null;
	//    }
	//    return value;
	//  };
	
	/**
	* Cette fonction permet de diminuer la valeur de la font-size de l'élément passé en paramètre.
	* Cette valeur doit être exprimé en pixel.
	* @param _elt est un élément (ou un identifiant d'élément) DOM.
	* @param minSize est la valeur minimale que la font-size peut prendre.
	*/
	window.decreaseFontSize=function(_elt,minSize) {
		_elt=ev.dom.element(_elt);
		if(!_elt){return;}
		var fontSize=getStyleValue(_elt,"font-size");
		if(!fontSize.match(/[0-9]+px/)){
			// On ne peut pas traiter des tailles qui ne sont pas exprimées en px
			return;
		}
		var fontSizeValue=parseInt(fontSize.replace(/px/,''),10);
		if(minSize&&fontSizeValue<=minSize){
			// Si une taille minimale est précisée et atteinte, on ne fait rien
			return;
		}
		_elt.style.fontSize=(fontSizeValue-1)+"px";
	};
	
	/**
	* Cette fonction permet d'augmenter la valeur de la font-size de l'élément passé en paramètre.
	* Cette valeur doit être exprimé en pixel.
	* @param _elt est un élément (ou un identifiant d'élément) DOM.
	* @param maxSize est la valeur maximale que la font-size peut prendre.
	*/
	window.increaseFontSize=function(_elt,maxSize) {
		_elt=ev.dom.element(_elt);
		if(!_elt){return;}
		var fontSize=getStyleValue(_elt,"font-size");
		if(!fontSize.match(/[0-9]+px/)){
			// On ne peut pas traiter des tailles qui ne sont pas exprimées en px
			return;
		}
		var fontSizeValue=parseInt(fontSize.replace(/px/,''),10);
		if(maxSize&&fontSizeValue>=maxSize){
			// Si une taille maximale est précisée et atteinte, on ne fait rien
			return;
		}
		_elt.style.fontSize=(fontSizeValue+1)+"px";
	};
	
	/**
	 * expression rationnelle pour extraire le contenu d'une balise XML préfixée par CDATA
	 */
	window.stripCDATA=(function(){
		// Variable privée contenant l'expression régulière compilée
		var innerREGEX=/<!\[CDATA\[([^\]]*)\]\]>/;
		return function(txt){
			var groups=innerREGEX.exec(txt);
			if(groups){return groups[1];}
			return txt;
		};
	})();
	
	window.getChildNodesByNodeName=function(_elt,nodeName) {
		_elt=ev.dom.element(_elt);
		if(!_elt){return[];}
		var childNodes=[];
		for(var i=0;i<_elt.childNodes.length;i++){
			if(_elt.childNodes[i].nodeName==nodeName){
				childNodes.push(_elt.childNodes[i]);
			}
		}
		return childNodes;
	};
	window.firstChildByNodeName=function(_elt,nodeName) {
		_elt=ev.dom.element(_elt);
		if(!_elt){return null;}
		for(var i=0;i<_elt.childNodes.length;i++){
			if(_elt.childNodes[i].nodeName==nodeName){
				return _elt.childNodes[i];
			}
		}
		return null;
	};
	window.firstChildByNodeType=function(_elt,nodeType) {
		_elt=ev.dom.element(_elt);
		if(!_elt){return null;}
		for(var i=0;i<_elt.childNodes.length;i++){
			if(_elt.childNodes[i].nodeType==nodeType){
				return _elt.childNodes[i];
			}
		}
		return null;
	};
	
	/* ########################################## */
	/* ########################################## */
	/* ### OUTILS DE MANIPULATIONS DE CLASSES ### */
	/* ########################################## */
	/* ########################################## */
	
	// Namespace (Espace de nom) des outils pour les classes
	window.Classe={
		/**
		 * Méthode permettant de déclarer un héritage de classe.
		 *
		 * NB : la fonction suivante pourrait ne fonctionner que
		 * sur les navigateur récents :
		 *    function extend(_child, _super){
		 *      _child.prototype.__proto__=_super.prototype;
		 *      _child.prototype.__super=_super;
		 *    }
		 *
		 * @see #isInstanceOf(Object, Class)
		 *
		 * @param {Class} _child : classe fille
		 * @param {Class} _super : classe mère de laquelle hérite la classe fille
		 * @throws si _child est null ou non défini
		 * @throws si _super est null ou non défini
		 * @throws si _child n'est pas une classe (pas de prototype)
		 * @throws si _super n'est pas une classe (pas de prototype)
		 */
		extend: function(_child, _super){
			if(!_child){throw new Error("Classe.extend: NullPointerException(_child class cannot be null)");}
			if(!_super){throw new Error("Classe.extend: NullPointerException(_super class cannot be null)");}
			if(!_child.prototype){throw new Error("Classe.extend: Not a class : "+_child);}
			if(!_super.prototype){throw new Error("Classe.extend: Not a class : "+_super);}
	
			// copie de toutes les propriétés de _super vers _child
			// (pour compatibilite anciens navigateurs ne supportant pas l'utilisation de prototype.__proto__)
			for(var property in _super.prototype){
				if(typeof(_child.prototype[property])==='undefined'){
					_child.prototype[property]=_super.prototype[property];
				}
			}
			// transfert du prototype de _super sur la propriété __proto__ du prototype de _child (nouveaux navigateur)
			_child.prototype.__proto__=_super.prototype;
			// stokage de la référence sur la super classe (pour meilleure performance de la méthode #isInstanceOf
			_child.prototype.__super=_super;
			return _child;
		},
	
		/**
		 * Fonction permettant de savoir si un objet (ou une classe) est
		 * une instance d'une classe donnée ou d'une classe dérivée.
		 *
		 * NOTA : une classe peut être considérée comme une instance d'une
		 * de ses super classes.
		 *
		 * La classe de l'objet _child doit être _super ou avoir été étendue de
		 * la classe _super ou d'une de ses classes filles (sinon le résultat
		 * sera 'faux').
	
		 * Toutes les classes de la chaine doivent avoir été étendues via la
		 * méthode extend(). Dans le cas contraire, l'héritage n'est pas
		 * supporté par cette méthode isInstanceOf(). La méthode renverra alors
		 * 'vrai' seulement si _child est une instance directe de la classe
		 * _super.
		 *
		 * @see #extend(Class, Class)
		 *
		 * @param {Object} _child : instance d'un objet de n'importe quelle classe fille de _super (ou une classe fille de _super, ou _super elle-même)
		 * @param {Class} _super : classe mère de laquelle doit hériter la classe fille
		 * @throws si _child est null ou non défini
		 * @throws si _super est null ou non défini
		 * @throws si _super n'est pas une classe (pas de prototype)
		 * @return true si la classe de _child est étendue (directement ou pas) de la classe _super
		 */
		isInstanceOf: function(_child, _super){
		//FIXME rendre ITERATIVE (c'est quand même plus performant)
			if(!_child){throw new Error("Classe.isInstanceOf: NullPointerException(_child class cannot be null)");}
			if(!_super){throw new Error("Classe.isInstanceOf: NullPointerException(_super class cannot be null)");}
			if(!_super.prototype){throw new Error("Classe.isInstanceOf: Not a class : "+_super);}
	
			// si _child n'est pas une classe, c'est une instance d'objet
			if(!_child.prototype){
				// si la classe de _child est _super, on renvoi vrai (si le navigateur supporte l'héritage de classe, l'opérateur 'instanceof' suffira lorsque la réponse est 'vrai')
				if(_child instanceof _super){return true;}
				// sinon, si la classe de _child a une super classe, on teste si cette super classe est _super (ou étendue de _super)
				// Pour cela, il suffit de tester de façon récursive, si cette super classe est une instance de _super
				if(_child.__super){return _child.__super===_super||arguments.callee(_child.__super, _super);}
				// sinon, on renvoie 'faux' (_child n'est pas une instance de _super)
				return false;
			}
	
			// si _child est une classe, on teste si cette classe est _super (ou étendue de _super)
			if(_child===_super){return true;}
			// ... ou, si elle a une super classe étendue de _super
			if(_child.prototype.__super){return _child.prototype.__super===_super||arguments.callee(_child.prototype.__super, _super);}
			// sinon, on retourne 'faux' (_child n'hérite pas de _super)
			return false;
		},
	
		/**
		 * Permet de verifier qu'une classe est définie.
		 *
		 * @param {Class} _className : nom de classe (ou de namespace) à vérifier
		 */
		checkDefined: function(_className){
//			ev.log.debug("Classe.checkDefined: test d'existance de la classe '"+_className+"'");
			if(typeof(window[_className])==='function'){return;}
			// si nom complet (avec namespace)
			var names=_className.split(/\./);
			var nameCnt=names.length;
			var lastNamespace=window;
			var i=0;
			while(lastNamespace){
				lastNamespace=lastNamespace[names[i++]];
				if(i>=nameCnt){
					if(typeof(lastNamespace)==='function'){return;}
					break;
				}
			}
			//throw new Error("Classe.checkDefined: la classe "+_className+" n'est pas définie!");
			ev.log.error("Classe.checkDefined: la classe "+_className+" n'est pas définie!", true);
		}
	};
	
	/**
	 * Fonction permettant de gerer des onglet de façon générique
	 * 
	 * @param {Object} _IdBaseBlocOnglet : chaine de caractere correspondant a la base commune des ids des bloc correspondant aux onglets
	 * @param {Object} _IdBaseOnglet : chaine de caractere correspondant a la base commune des ids des onglets (permet de mettre la class selected sur l'onglet selectionné)
	 * @param {Object} _numOngletCourant : numero de l'onglet à afficher
	 * @param {Object} _nbOnglets : nombre total d'onglets
	 */
	window.displayOnglet=function(_idBaseBlocOnglet, _idBaseOnglet, _numOngletCourant, _nbOnglets) {
		if(!_idBaseBlocOnglet){throw new Error("_idBaseBlocOnglet is not valid");}
		if(!_idBaseOnglet){throw new Error("_idBaseOnglet is not valid");}
		if(typeof(_numOngletCourant)!=='number'){throw new Error("_numOngletCourant is not a number");}
		if(typeof(_nbOnglets)!=='number'){throw new Error("_nbOnglets is not a number");}
	
		for(var i=1; i<=_nbOnglets; ++i){	
			var blocOnglet=ev.dom.element(_idBaseBlocOnglet + i);
			if(!blocOnglet){throw new Error("l'element html dont l'id est : " + _idBaseBlocOnglet + i + " correspondant au bloc de l'onglet courant n'a pas pu etre trouvé");}
		
			var onglet=ev.dom.element(_idBaseOnglet + i);
			if(!onglet){throw new Error("l'element html dont l'id est : " + _idBaseOnglet + i + " correspondant a l'onglet courant n'a pas pu etre trouvé");}
		
			if(i==_numOngletCourant){//on affiche cet element
				blocOnglet.style.display="block";
				ev.dom.addClass(onglet, "selected");
				ev.dom.removeClass(onglet, "unselected");
			}
			else{//on cache l'element
				blocOnglet.style.display="none";
				ev.dom.addClass(onglet, "unselected");
				ev.dom.removeClass(onglet, "selected");
			}
		}
	};
	
	window.postLoad=function(){
		//on initialise le tableau qui contiendra les objets
		this.arrayPostLoad=[];
		//cette méthode permet de construire le tableau d'objets contenants les urls des images et leurs éléments correspondants
		this.addIMG=function(eltIMG,urlIMG){
			if (!eltIMG) {throw new Error("eltIMG is undefined");}
			this.arrayPostLoad.push({elt:eltIMG, url:urlIMG});
		};
		//cette méthode effectue le remplacement de l'image initiale
		this.load=function(){
			for (var i=0;i<this.arrayPostLoad.length;i++) {
				this.arrayPostLoad[i].elt.src=this.arrayPostLoad[i].url;
			}
		};
	};

	ev.log.debug('ev.tools ok');
	ev.tools.onFileLoad('tools.js');
}()); // exécution de la fonction anonyme, ici
