/**
 * Fonction anonyme de déclaration des classes utiles de
 * manipulation du DOM.<br>
 * Certaines fonctions ne sont visible que par les classes
 * concernées (déclarées ici).<br>
 * Ce mécanisme de fonction anonyme permet de reproduire un
 * système d'encapsulation digne d'un langage de programmation
 * évolué (comme le Java).<br>
 * <br>
 * Classes et éléments visibles seront stockés dans le
 * namespace 'ev.dom'.
 */
(function (){
	// Si les namespaces/classes nécessaires ne sont pas chargées : exception
	if(!window.ev){throw new Error("Le namespace 'ev' doit exister");}
	if(!ev.tools){throw new Error("Le namespace 'ev.tools' doit exister");}
	if(!ev.log){throw new Error("Le namespace 'ev.log' doit exister");}
	// 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} e : 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(e){
		var l=arguments.length;
		if(typeof(e)==='string'){
			e=window.document.getElementById(e);
		}
		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 e;
		}
		// sinon, on se prépare à retourner un tableau d'éléments DOM (dont le premier est celui déjà récupéré)
		e=[e];
		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
				e.push(window.document.getElementById(arguments[i]));
			}
			else{
				// sinon, on insère le paramètre lui-même
				e.push(arguments[i]);
			}
		}
		// Enfin, on retourne le tableau d'éléments DOM
		return e;
	};

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

	/**
	 * 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} tg : nom des tags à retrouver dans l'élément donné (par défaut le document racine)
	 * @param {String} e (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(tg, e){
		return (ev.dom.element(e)||window.document).getElementsByTagName(tg);
	};

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

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

	/**
	* Ajoute une classe dans l'attribut class d'un élément
	 * @param {HTMLElement} e : Element DOM (ou id d'élément)
	 * @param {String} _classToAdd : nom de la classe à ajouter
	**/
	ev.dom.addClass=function(e, _classToAdd){
		e=ev.dom.element(e);
		if(!e){return;}
		var className=e.className;
		var classList=className.split(/\s+/);
		for(var i=0;i<classList.length;i++){
			if(classList[i]==_classToAdd){return;}
		}
		e.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} e : Element DOM (ou id d'élément)
	 * @param {String} _classToRemove : nom de la classe à enlever
	**/
	ev.dom.removeClass=function(e, _classToRemove){
		e=ev.dom.element(e);
		if(!e){return;}
		var className=e.className;
		var classList=className.split(/\s+/);
		var newClassName='';
		for(var i=0;i<classList.length;i++){
			if(classList[i]!=_classToRemove){
				newClassName+=classList[i]+' ';
			}
		}
		e.className=newClassName.trim();
	};

	/**
	 * 
	 * Effectue la permutation des classes passées en paramètre (cls1 et cls2) pour
	 * l'élément lui passé en paramètre (e)
	 * 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">
	 * @param {Object} e élément DOM concerné
	 * @param {Object} cls1 première classe de l'échange
	 * @param {Object} cls2 deuxième classe de l'échange
	 */
	ev.dom.swapClasses=function(e, cls1, cls2) {
		e=ev.dom.element(e);
		if(!e){return;}
		if(ev.dom.hasClass(e, cls1)){
			ev.dom.removeClass(e, cls1);
			ev.dom.addClass(e, cls2);
		}
		else if(ev.dom.hasClass(e, cls2)){
			ev.dom.removeClass(e, cls2);
			ev.dom.addClass(e, cls1);
		}
	};

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

	/**
	 * @param {Object} e : é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 (e){
		e=ev.dom.element(e);
		var t="", j, l;
		// If an element was passed, get it's children, 
		// otherwise assume it's an array
		e=e.childNodes||e;
		// Look through all child nodes
		for(j=0, l=e.length; j<l; ++j){
			if(e[j].nodeType!=1){
				// If it's not an element, append its text value
				t+=e[j].nodeValue;
				continue;
			}
			// Otherwise, recurse through all the element's children
			t+=arguments.callee(e[j].childNodes);			
		}
		// Return the matched text
		return t;
	};

	/**
	 * Méthode de nettoiement du conteneur donné
	 * @param {Object} e un conteneur DOM à vider (ou son id)
	 */
	ev.dom.clear=function (e){
		e=ev.dom.element(e);
		if(!e){return;}
		// On ne peut supprimer tous les éléments du conteneur autrement que un à un.
		while(e.firstChild){
			e.removeChild(e.firstChild);
		}
	};

	/**
	* 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(e,className){
		e=ev.dom.element(e);
		if(!e){return false;}
		var classNameElement=e.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 (cls1 et cls2) pour
	* l'élément lui passé en paramètre (e)
	* 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">
	* @deprecated FIXME utiliser ev.dom.swapClasses()
	**/
	window.swapClasses=ev.dom.swapClasses;
}());
