/**
 * Menu déroulant v.8
 * par Romain Berton
 * pour www.alsacreations.com
 * le 27/10/2007
 */

(function() {

var oO =
{
	/**
	 * Label et identifiant du menu associé
	 */
	'Menus': 'menus',


	/**
	 * Labels et intervalles de temps associés
	 */
	'Tempo': 650, // Temps d'ouverture d'un sous-menu (ms)
	'Interval': 10, // Intervalle entre deux itérations de mouvement (ms)


	/**
	 * Label et style associés (définis dans le JS)
	 */
	'Overflow': 'hidden', // sous-menus


	/**
	 * Labels et classes CSS associées (affectées via JS)
	 */
	'Style': 'Surv', // menu
	'Show': 'Montre', // affichage des sous-menus
	'Hide': 'Cache', // masquage des sous-menus
	'FT': 'focusTitle', // entête des sous-menus à la prise de focus
	'BT': 'blurTitle', // entête des sous-menus à la perte de focus

	
	/**
	 * Saleté de navigateur qui ne comprend rien au JS
	 */
	'isSafari': (/webkit/.test(navigator.userAgent.toLowerCase())),


	/**
	 * oO.bTest() -> Teste les méthodes recquises.
	 *               Retourne un booléen
	 */
	bTest: function()
	{
		return document.getElementById && document.getElementsByTagName &&
		document.createElement && document.createTextNode;
	},


	/**
	 * oO.oId(sId) -> Retourne l'élément dont l'id est sId.
	 */
	oId: function(sId)
	{
		return document.getElementById(sId);
	},


	/**
	 * oO.aTag(oEl, sTag) -> Retourne un tableau des éléments
	 *                       nommés sTag et inclus dans l'élément oEl.
	 */
	aTag: function(oEl, sTag)
	{
		return oEl.getElementsByTagName(sTag);
	},


	/**
	 * oO.bElemNotLink(oEl) -> Teste si la valeur de l'élément oEl est non nulle
	 *                         et si c'est un lien.
	 *                         Retourne un booléen
	 */
	bElemNotLink: function(oEl)
	{
		return oEl.nodeValue != null && oEl.nodeName.toLowerCase() != 'a';
	},


	/**
	 * oO.oCreaEl(sTag) -> Retourne un élément nommé sTag.
	 */
	oCreaEl: function(sTag)
	{
		return document.createElement(sTag);
	},


	/**
	 * oO.oCreaTxt(sTxt) -> Retourne un noeud texte de la chaîne sTxt.
	 */
	oCreaTxt: function(sTxt)
	{
		return document.createTextNode(sTxt);
	},


	/**
	 * oO.cancelClick(e) -> Annule la propagation d'un événement
	 */
	cancelClick: function(e)
	{
		if(e && e.stopPropagation && e.preventDefault)
		{
			e.stopPropagation();
			e.preventDefault();
		}
		else if(e && window.event)
		{
			window.event.cancelBubble = true;
			window.event.returnValue = false;
		}

		return false;
	},


	/**
	 * oO.elem() -> Retourne un élément avec différents attributs et un texte
	 *              inclus.
	 */
	elem: function()
	{
		var aArg = arguments,
		    oElem = oO.oCreaEl(aArg[0]),
		    iI = 2,
		    aTab = aArg.length;

		for(iI; iI < aTab; iI++)
		{
			iI % 2 == 0 ?
				oO.sAtt = aArg[iI] :
				oO.sVal = aArg[iI];

			if(oO.sAtt && oO.sAtt == aArg[iI-1])
				oO.sAtt == 'class' ?
					oElem.className = oO.sVal :
					oElem.setAttribute(oO.sAtt, oO.sVal);
		}
		if(aArg[1] && typeof aArg[1] == 'string' && aArg[1] != '')
		{
			var oText = oO.oCreaTxt(aArg[1]);

			oElem.appendChild(oText);
		}

		return oElem;
	},


	/**
	 * oO.connect(oElem, sEvType, fn, bCapture) -> Attache un événement à une
	 *                                             action.
	 */
	connect: function(oElem, sEvType, fn, bCapture)
	{
		return document.addEventListener ?
			oElem.addEventListener(sEvType, fn, bCapture):
			oElem.attachEvent ?
				oElem.attachEvent('on' + sEvType, fn):
				false;
	},


	/**
	 * oO.getSource(e) -> Récupère la source d'un événement.
	 */
	getSource: function(e)
	{
		return e.target || e.srcElement;
	},


	/**
	 * oO.contains(container, containee) -> Teste si un élément est inclus dans
	 *                                      un autre.
	 */
	contains: function(container, containee)
	{
		if(container && containee)
		{
			var n = containee;

			for(n; n && n != container; n = n.parentNode) {}

			return n;
		}

		return false;
	},


	/**
	 * oO.insertCtrlLinks(oMenus) -> Transforme les entêtes des sous-menus en
	 *                               liens de contrôle.
	 */
	insertCtrlLinks: function(oMenus)
	{
		if(!oMenus)
			return;

		var aLis = oO.aTag(oMenus, 'li'),
		    iI = aLis.length;

		if(iI == 0)
			return;

		while(iI-- > 0)
		{
			var oChildLi = aLis[iI].firstChild;

			if(!oChildLi)
				return;

			if(oO.bElemNotLink(oChildLi))
			{
				var oA = oO.elem('a', oChildLi.nodeValue, 'href', '#ssmenu');

				aLis[iI].replaceChild(oA, oChildLi);
				oO.connect(oA, 'click', oO.cancelClick, false);
			}
		}

		return oMenus;
	},


	/**
	 * oO.initCptMenu(oMenus) -> Définit le comportement du menu en fonction
	 *                           de l'événement et de l'élément affecté.
	 */
	initCptMenu: function(oMenus)
	{
		if(!oMenus)
			return;

		oO.connect(oMenus, 'mouseover', oO.ctrlDisplayOn, false);
		oO.connect(oMenus, 'mouseout', oO.ctrlDisplayOut, false);

		var aA = oO.aTag(oMenus, 'a'),
		    iI = aA.length;

		if(iI == 0)
			return;

		while(iI-- > 0)
		{
			oO.connect(aA[iI], 'focus', oO.ctrlDisplayOn, false);
			oO.connect(aA[iI], 'blur', oO.ctrlDisplayOut, false);
		}

		return oMenus;
	},


	/**
	 * oO.initStyle(oMenus) -> Définit les styles affectés au menu lorsque JS
	 *                         est actif.
	 */
	initStyle: function(oMenus)
	{
		if(!oMenus)
			return;

		oMenus.className = oO['Style'];
		
		var oNext = oMenus.nextSibling;
		
		while(oNext.nodeType != 1)
			oNext = oNext.nextSibling;
		
		oNext.className = 'over';
		
		var aUls = oO.aTag(oMenus, 'ul'),
		    iI = aUls.length;

		if(iI == 0)
			return;

		do
		{
			aUls[--iI].id = 'ssmenu' + iI;
			if(!oO.isSafari) {
				aUls[iI].style.overflow = oO['Overflow'];
				aUls[iI].hauteur = aUls[iI].offsetHeight;
				aUls[iI].style.height = 0;
			}
			aUls[iI].className = oO['Hide'];
		}
		while(iI > 0);

		return oMenus;
	},


	/**
	 * oO.actif() -> Affecte des liens de contrôle, des styles et un comportement
	 *               au menu.
	 */
	actif: function()
	{
		var oMenus = oO.oId(oO['Menus']);

		if(!oMenus)
			return;

		if(oO.insertCtrlLinks(oMenus))
			if(oO.initStyle(oMenus))
				return oO.initCptMenu(oMenus);

		return false;
	},


	/**
	 * oO.ctrlDisplayOn(e) -> Repère l'élément d'où l'événement a été généré et
	 *                        renvoie une demande d'affichage.
	 */
	ctrlDisplayOn: function(e)
	{
		if(e)
		{
			var oRelatedNode = e.fromElement;

			return oO.ctrlDisplay(e, oRelatedNode);
		}

		return false;
	},


	/**
	 * oO.ctrlDisplayOut(e) -> Repère l'élément sur lequel on arrive à la suite
	 *                         d'un événement et renvoie une demande de masquage.
	 */
	ctrlDisplayOut: function(e)
	{
		if(e)
		{
			var oRelatedNode = e.toElement;

			return oO.ctrlDisplay(e, oRelatedNode);
		}

		return false;
	},


	/**
	 * oO.compareSsMenu(oMenus, oUl) -> Retourne le numéro d'ordre du sous-menu
	 *                                  demandé parmi l'ensemble des sous-menus.
	 */
	compareSsMenu: function(oMenus, oUl)
	{
		if(!oMenus || !oUl)
			return;

		var aUls = oO.aTag(oMenus, 'ul'),
		    iI = aUls.length;

		for(; iI >= 0; iI--)
			if(aUls[iI] == oUl)
				return iI;

		return false;
	},


	/**
	 * oO.ctrlDisplay(e, oRelatedNode) -> Contrôle l'affichage / masquage
	 *                                   des sous-menus.
	 */
	ctrlDisplay: function(e, oRelatedNode)
	{
		var oRelatedNode = e.relatedTarget || oRelatedNode,
		    oSource = oO.getSource(e),
		    oMenus = oO.oId(oO['Menus']);

		while(oSource && oSource != oMenus)
		{
			var oUl = oO.aTag(oSource, 'ul')[0],
			    oParentSource = oSource.parentNode,
			    iI;

			if(!oO.contains(oSource, oRelatedNode) && oUl &&
			(iI = oO.compareSsMenu(oMenus, oUl)) != null)
				if(e.type == 'mouseout')
				{
					oO.temp = setTimeout
					(
						function()
						{
              			oUl.parentNode.firstChild.className = oO['BT'];
							oO.deroule(oUl.id, oUl.hauteur, 0, oO['Interval']);
						},
						oO['Tempo']
					);
					if(oSource.PostIt != oO.temp)
						oSource.PostIt = oO.temp;
				}
				else if(oO.temp && oSource.PostIt == oO.temp &&
				e.type == 'mouseover')
				{
					oO.deroule(oUl.id, 0, oUl.hauteur, oO['Interval']);
					oO.PostIt = null;
					clearTimeout(oO.temp);
				}
				else
				{
					switch(e.type)
					{
						case 'mouseover':
							oO.deroule(oUl.id, 0, oUl.hauteur, oO['Interval']);
							break;

						case 'focus':
              			oUl.parentNode.firstChild.className = oO['FT'];
							oUl.className = oO['Show'];
							oUl.style.height = oUl.hauteur + 'px';
							break;

						case 'blur':
             			oUl.parentNode.firstChild.className = oO['BT'];
							oUl.className = oO['Hide'];
							oUl.style.height = 0;
							break;
					}
				}

				if(oSource.nodeName.toLowerCase() == 'a')
					if(e.type == 'focus' || e.type == 'mouseover')
						if(oParentSource.parentNode == oMenus)
							oSource.className = oO['FT'];
					else if(e.type == 'blur' || e.type == 'mouseout')
						if(oParentSource.parentNode == oMenus)
							oSource.className = oSource.className;

				oSource = oParentSource;
		}

		return true;
	},


	/**
	 * oO.deroule(id, initHeight, finalHeight, interval) -> Anime la hauteur
	 *                                                      d'un élément.
	 */
	deroule: function(id, initHeight, finalHeight, interval)
	{
		var oElem = oO.oId(id);
		if(!oElem) return;
		if(oO.isSafari && !oElem.testSafari && oElem.className == oO['Hide']) {
			oElem.className = oO['Show'];
			finalHeight = oElem.offsetHeight;
			oElem.style.height = 0;
			oElem.testSafari = true;
		}
		if(oElem.style.overflow != 'hidden') {
			oElem.style.overflow = 'hidden';
			oElem.style.height = oElem.offsetHeight + 'px';
		}
		if(oElem.movement) clearTimeout(oElem.movement);
		var iDimY = parseInt(oElem.style.height);
		if(iDimY == finalHeight) {
			if(!oElem.test) oElem.test = true;
			else {
				oElem.style.height = '';
				oElem.style.overflow = '';
				oElem.test = null;
				oElem.testSafari = null;
			}
		if(iDimY < 2) {
			oElem.className = oO['Hide'];
		}
			return;
		}
		if(finalHeight != 0) {
			oElem.className = oO['Show'];
		}
		if(iDimY < finalHeight) iDimY = iDimY + Math.ceil((finalHeight - iDimY)/10);
		if(iDimY > finalHeight) iDimY = iDimY - Math.ceil((iDimY - finalHeight)/10);
		oElem.style.height = iDimY + 'px';
		var repeat = function() { oO.deroule(id, initHeight, finalHeight, interval); };
		oElem.movement = setTimeout(repeat, interval);
		return;
	}
};

if(oO.bTest)
	oO.connect(window, 'load', oO.actif, false); // Lancement du script

})();
