/******
 ** Harmen christophe 2004 - 2005/05/22
 * Quelques fonctions en rapport avec le DOM
 *		getOuterHTML (Html):
 *		getInnerHTML (Html):
 *		getOuterText (core):
 *		getInnerText (core):  
 *		replaceClassName (Core) :
 *		addClassName (Core):
 *		deleteClassName (Core):
 *		hasClassName (Core):
 *		getElementsByClass (Core):
 *		getElementsByAttribut (Core):
 *		isChildNodeOf (Core): 
 *		addEventLst (Event):
 *		removeEventLst (Event):  
 ******
 *	require_once : lib.string.js [htmlspecialchars(), trim()]
 ******/

function trim(str) {
	return str.replace(/ /gi, '');
}

/**
 * récupère la source HTML (Éléments standard) d'un élément et de l'ensemble de ses enfants
 * Equivalent au outerHTML de MSIE
 * @param : oNode : node de type element : noeud pour lequel il faut récupérer la source HTML
 * @return : _source : string : source HTML de l'élément oNode
**/
function getOuterHTML(oNode) {
	var _source = "";
	switch (oNode.nodeType) {
		case 1: // ELEMENT_NODE
			switch(oNode.tagName.toLowerCase()) {
				case "area" :
				case "base" :
				case "basefont" :
				case "br" :
				case "col" :
				case "frame" :
				case "hr" :
				case "img" :
				case "input" :
				case "isindex" :
				case "link" :
				case "meta" :
				case "param" :
					_source += "<" + oNode.nodeName.toLowerCase();
					var oAttr;
					for (var i=0; oAttr=oNode.attributes[i]; i++) {
						// Si l'attribut est spécifié par l'utilisateur (MSIE retourne des attr par defaut)
						_source += (oAttr.specified)?" " + oAttr.nodeName.toLowerCase() + "=\""+ htmlspecialchars(oAttr.nodeValue,"ENT_QUOTES") + "\"":"";
					}
					oAttr = null;
					_source += ">";
					break;
				case "/ABBR":
				//	 Particularite MSIE :-((
				//	alert("Microsoft Internet Explorer ne supporte pas l'élément ABBR.");
					break;
				default :
					_source += "<" + oNode.nodeName.toLowerCase();
					var oAttr;
					for (var i=0; oAttr=oNode.attributes[i]; i++) {
						// Si l'attribut est spécifié par l'utilisateur (MSIE retourne des attr par defaut)
						_source += (oAttr.specified)?" " + oAttr.nodeName.toLowerCase() + "=\""+ htmlspecialchars(oAttr.nodeValue,"ENT_QUOTES") + "\"":"";
					}
					oAttr = null;
					_source += ">";
					_source += getInnerHTML(oNode);
					_source += "</" + oNode.nodeName.toLowerCase() + ">";
					break;
			}
			break;
		case 3: // TEXT_NODE
		    _source += htmlspecialchars(oNode.nodeValue,"ENT_NOQUOTES");
		    break;
		case 2: // ATTRIBUTE_NODE
		case 4: // CDATA_SECTION_NODE
		case 5: // ENTITY_REFERENCE_NODE
		case 6: // ENTITY_NODE
		case 7: // PROCESSING_INSTRUCTION_NODE
			break;
		case 8: // COMMENT_NODE
			//_source += "<!--" + htmlspecialchars(oNode.nodeValue,"ENT_NOQUOTES") + "-->";
			_source += "<!--" + oNode.nodeValue + "-->";
			break;
		case 9: // DOCUMENT_NODE
		     _source += getInnerHTML(oNode);
		    break;
		case 10: // DOCUMENT_TYPE_NODE
			// LE DOCTYPE PASSE POUR UN COMMENTAIRE SOUS MSIE :
			//_source += "<!DOCTYPE " + oNode.name + " PUBLIC \"" + oNode.publicId + "\" \"" + oNode.systemId + "\">";
			break;
		case 11: // DOCUMENT_FRAGMENT_NODE
			_source += getInnerHTML(oNode);
			break;
		case 12: // NOTATION_NODE
		    break;
	}
	return _source;
}
/**
 * récupère la source HTML de l'ensemble des enfants (et descendants) d'un élément (pas la source de l'élément lui-même)
 * Equivalent au innerHTML de MSIE
 * @param : oNode : node de type element : noeud pour lequel il faut récupérer la source HTML
 * @return : _source : string : source HTML de l'élément oNode
**/
function getInnerHTML(oNode) {
	var _source ="";
	if (oNode.hasChildNodes()) {
		oNode = oNode.firstChild;
	} else {
		return "";
	}
	while (oNode) {
		_source += getOuterHTML(oNode);
		oNode = oNode.nextSibling;
	}
	return _source;
}
function getOuterText(oNode) {
	var _outerText = "";
	if (oNode == null) {return _outerText;}
	switch (oNode.nodeType) {
		case 1: // ELEMENT_NODE
		case 5: // ENTITY_REFERENCE_NODE
			_outerText += getInnerText(oNode);
			break;
		case 3: // TEXT_NODE
		case 2: // ATTRIBUTE_NODE
		case 4: // CDATA_SECTION_NODE
		case 7: // PROCESSING_INSTRUCTION_NODE
		case 8: // COMMENT_NODE
			_outerText += oNode.nodeValue;
			break;
		case 6: // ENTITY_NODE
		case 9: // DOCUMENT_NODE
		case 10: // DOCUMENT_TYPE_NODE
		case 11: // DOCUMENT_FRAGMENT_NODE
		case 12: // NOTATION_NODE
			// skip
			break;
	}
	return _outerText;
}
function getInnerText(oNode) {
	var _innerText ="";
	if (oNode.hasChildNodes()) {
		oNode = oNode.firstChild;
	} else {
		return "";
	}
	while (oNode) {
		_innerText += getOuterText(oNode);
		oNode = oNode.nextSibling;
	}
	return _innerText;
}
/*
 * Fonction retournant true ou false selon que l'élément a une classe affectée
 * @param : oEl : node de type elément : element sur lequel il faut tester une classe
 * @param : sHasClass : string : classe à tester
 * @return : boolen : selon si l'élément à la classe ou pas
**/
function hasClassName(oEl,sHasClass) {
    return ((" " + oEl.className + " ").indexOf(" " + sHasClass + " ")!=-1);
}
/*
 * Fonction ajoutant une classe à un élément si elle n'existe pas encore
 * @param : oEl : node de type elément : element sur lequel il faut ajouter une classe
 * @param : sNewClass : string : nouvelle classe
**/
function addClassName(oEl,sNewClass) {
    if (!hasClassName(oEl,sNewClass)) oEl.className = trim(oEl.className + " " + sNewClass);
}
/*
 * Fonction remplacant une classe d'un élément si elle existe et l'ajoutant sinon
 * @param : oEl : node de type elément : element sur lequel il faut remplacer une classe
 * @param : sOldClass : string : ancienne classe
 * @param : sNewClass : string : nouvelle classe
**/
function replaceClassName(oEl,sOldClass,sNewClass) {
    oEl.className = trim((" " + oEl.className + " ").replace(" " + sOldClass + " "," " + sNewClass + " "));
}
/*
 * Fonction supprimant une classe d'un élément si elle existe
 * @param : oEl : node de type elément : element sur lequel il faut ajouter une classe
 * @param : sRemoveClass : string : classe qu'il faut supprimer
**/
function deleteClassName(oEl,sRemoveClass) {
    oEl.className = trim((" " + oEl.className + " ").replace(" " + sRemoveClass + " "," "));
}
/*
 * récupère une collection d'objet possédant
 * une classe particulière (sClass) au sein de l'élément "el"
 * @sClass : string : nom de classe qu'il faut rechercher
 * @oEl : node de type element : élément racine à partir duquel il faut rechercher
 * @return : outArray : array : tableau contenant l'ensemble des éléments capturés répondant aux critère (ayant la classe)
**/
function getElementsByClass(sClass,oEl) {
	var outArray = new Array();
	function _getElementsByClass(outArray, oEl, sClass) {
		while (oEl) {
			if (oEl.nodeType == 1) {
       			if (hasClassName(oEl,sClass)) outArray[outArray.length] = oEl;
    			_getElementsByClass(outArray, oEl.firstChild, sClass);
			}
			oEl = oEl.nextSibling;
		}
	}
	oEl = ((oEl) && (oEl.nodeType==1))?oEl:document.documentElement;
	_getElementsByClass(outArray, oEl, sClass);
	return outArray;
}
/*
 * récupère une collection d'objet possédant
 * un attribut particulière (sAttr) au sein de l'élément "el"
 * @param : sAttr : string : nom de l'attribut qu'il faut rechercher
 * @param : bSpecified : booleen : optionnel : true (valeur par défaut) s'il ne faut retourner que les éléments dont l'attribut est spécifié par l'utilisateur (par ceux par defaut)
 * @param : oEl : node de type element : optionnel : élément racine à partir duquel il faut rechercher
 * @return : outArray : array : tableau contenant l'ensemble des éléments capturés répondant aux critère (ayant l'attribut)
**/
function getElementsByAttribut(sName, sValue, oEl) {
	//if (sName=="class") {return getElementsByClass(sValue,oEl);}
		function _getElementsByAttribut(outArray, oEl, sName, sValue) {
			while (oEl) {
				// getAttributeNode() >= MSIE 6
				if ( (oEl.nodeType == 1) && (oEl.getAttributeNode(sName)) && (oEl.getAttributeNode(sName).specified) ) {
					if ( (sValue!=null) && (trim(sValue)!="") ) {
						switch(sName.toLowerCase()) {
							case "class":
								//if ((" " + oEl.className + " ").indexOf(" " + sValue + " ")!=-1) outArray[outArray.length] = oEl;
								if (hasClassName(oEl,sValue)) outArray[outArray.length] = oEl;
								break;
							default :
								if (oEl.getAttributeNode(sName).value==sValue) {outArray[outArray.length] = oEl;}
								break;
						}
					} else {
						outArray[outArray.length] = oEl;
					}
				}
				_getElementsByAttribut(outArray, oEl.firstChild, sName, sValue);
				oEl = oEl.nextSibling;
			}
		}
	var outArray = new Array();
	oEl = ((oEl)&&(oEl.nodeType==1))?oEl:document.documentElement;
	_getElementsByAttribut(outArray, oEl, sName, sValue);
	return outArray;
}
/*
 * Détermine si un noeud est enfant d'un autre
 * @nChild : node : Noeud potentiellement enfant
 * @nParent : node : Noeud potentiellement parent
 * @iLevel : integer :  optionnel : profondeur d'exploration : ex : 1=enfants directs, 2= enfants et petits enfants, typeof(iLevel)="undefined"=>tous les niveaux
 * @return : booleen : true si nChild est enfant de nParent (dans iLevel), false sinon
**/
function isChildNodeOf(nChild,nParent,iLevel) {
	var bIsChildNodeOf = false;
	function _isChildNodeOf(nChild,nParent,iLevel) {
		for (var i=0;i<nParent.childNodes.length;i++) {
			if (nParent.childNodes[i]===nChild) {
				bIsChildNodeOf = true;
				return;
			} else if ( (typeof(iLevel)=="undefined") && nParent.childNodes[i].hasChildNodes())
				_isChildNodeOf(nChild,nParent.childNodes[i]);
			else if ( (iLevel>1) && nParent.childNodes[i].hasChildNodes())
				_isChildNodeOf(nChild,nParent.childNodes[i],iLevel-1);
		}
	}
	_isChildNodeOf(nChild,nParent,iLevel);
	return bIsChildNodeOf;
}
/*
 * Ajout un eventListener à un EventTarget
 * @EventTarget : node : Noeud auquel rajouter l'eventListener
 * @type : String : Type d'évennement à gérer
 * @listener : objet function : enventListener à associer
 * @useCapture : booleen : Faut il utiliser la capture d'évennement pour les implémentations le supportant 
**/
function addEventLst(EventTarget,type,listener,useCapture) {
	useCapture = typeof(useCapture)!="undefined"?useCapture:false;
	// si EventTarget==window : Pour Gecko (et KHTML (¡¿))
	if (EventTarget.addEventListener) {
		EventTarget.addEventListener(type,listener, useCapture);
	// Version conforme W3C pour le body[onload] par exemple
	} else if ((EventTarget==window) && window.document.addEventListener) {
		window.document.addEventListener(type,listener, useCapture);
	// Pour MSIE/Win
	} else if (EventTarget.attachEvent) {
		EventTarget.attachEvent("on"+type,listener);
	// Pour MSIE/Mac et autres...
	} else {
		eval("EventTarget.on"+type+" = "+listener);
	}
}
/*
 * Retire un eventListener à un EventTarget
 * @EventTarget : node : Noeud auquel retirer l'eventListener
 * @type : String : Type d'évennement à retirer
 * @listener : objet function : enventListener à retirer
 * @useCapture : booleen : Faut il retirer l'eventListener capturant pour les implémentations le supportant 
**/
function removeEventLst(EventTarget,type,listener,useCapture) {
	useCapture = typeof(useCapture)!="undefined"?useCapture:false;
	if (EventTarget.removeEventListener)
		EventTarget.removeEventListener(type,listener, useCapture);
	else if ((EventTarget==window) && window.document.removeEventListener)
		window.document.removeEventListener(type,listener, useCapture);
	else if (EventTarget.detachEvent)
		EventTarget.detachEvent("on"+type,listener);
	else
		eval("EventTarget.on"+type+" = null");
}
