/**
* Menús desplegables dinámicos, compatibles con varios
* clientes Web, para CSS: resultados instantáneos.
*
* Para que este código funcione es necesaria la biblioteca
* JavaScript cssQuery de Dean Edwards, disponible en la
* dirección http://dean.edwards.name/my/cssQuery/.
*
* Autor: Richard York
* [Referencia a la licencia de código abierto aquí.]
* (c) Copyright 2005, Wiley Publishing, Inc.
*/

// ¡Determinar aquí el nombre de las clases!
var $CSSIR_liNombreClase  = 'menu';
var $CSSIR_ulNombreClase  = 'menu';
var $CSSIR_divNombreClase = 'menu';
var $CSSIR_liResaltado    = 'menu-resaltado';
var $CSSIR_aResaltado     = 'menu-enlace-resaltado';

var $CSSIR_menuUltimo     = null;
var $CSSIR_menuAlternar   = false;
var $CSSIR_padre          = new Array();

var $CSSIR_menus;
var $CSSIR_menu;
var $CSSIR_actual         = -1;
var $CSSIR_enlace         = 0;

function CSSIR_menuAsignarEventos()
{
  // Seleccionar todos los elementos de la lista pertenecientes a una clase
  // cuyo nombre sea menu y asignarles un evento onclick.
  $CSSIR_menus = cssQuery('li.' + $CSSIR_liNombreClase);

  for (var $i in $CSSIR_menus)
  {
    $CSSIR_menus[$i].onclick = CSSIR_menuGestor;

    // La variable $CSSIR_menuAlternar sirve para determinar si el
    // usuario tiene el puntero del ratón sobre un menú.
    $CSSIR_menus[$i].onmouseover = function()
    {
      $CSSIR_menuAlternar = true;
    }

    $CSSIR_menus[$i].onmouseout = function()
    {
      $CSSIR_menuAlternar = false;
    }
  }
}

function CSSIR_menuGestor()
{
  var $etiquetas;
  var $menu = (!$CSSIR_menu)? this : $CSSIR_menu;
  $CSSIR_menu = null;

  $etiquetas = cssQuery('ul.' + $CSSIR_ulNombreClase + ' li.' + $CSSIR_aResaltado);

  for (var $i in $etiquetas)
  {
    $etiquetas[$i].className = null;
  }

  if ($CSSIR_padre.length)
  {
    // Este fragmento oculta los menús desplegables descendientes,
    // siempre que el menú tenga más de un nivel de profundidad.
    //
    // He utilizado cssQuery para solucionar algunos problemas con los que
    // me he encontrado al utilizar parentNode.childNodes en Safari.
    $etiquetas = cssQuery('#' + $menu.parentNode.parentNode.parentNode.parentNode.id + ' > div li.' + $CSSIR_liNombreClase);

    if ($etiquetas.length)
    {
      for (var $i in $etiquetas)
      {
        if ($etiquetas[$i].id)
        {
          // Reasignar el gestor del evento del menú.
          $etiquetas[$i].onclick = CSSIR_menuGestor;

          // Ocultar el menú.
          cssQuery('#' + $etiquetas[$i].id + ' div.' + $CSSIR_divNombreClase)[0].style.visibility = 'hidden';
          $etiquetas[$i].className = $CSSIR_liNombreClase;
        }
      }
    }
  }

  // Evitar burbujas de eventos.
  $menu.onclick = null;
  $menu.className = $CSSIR_liNombreClase + ' ' + $CSSIR_liResaltado;

  // Detectar si un menú tiene más de un nivel de profundidad.
  CSSIR_padre($menu);

  if ($CSSIR_menuUltimo && !(
    $menu.parentNode &&
    $menu.parentNode.parentNode &&
    $menu.parentNode.parentNode.parentNode &&
    $menu.parentNode.parentNode.parentNode.parentNode &&
    $menu.parentNode.parentNode.parentNode.parentNode.id
    )
  ) {
    cssQuery('#' + $CSSIR_menuUltimo.id + ' > div')[0].style.visibility = 'hidden';
    $CSSIR_menuUltimo.onclick   = CSSIR_menuGestor;
    $CSSIR_menuUltimo.className = $CSSIR_liNombreClase;

    if ($CSSIR_padre.length)
    {
      for (var $i in $CSSIR_padre)
      {
        $CSSIR_padre[$i].parentNode.onclick   = CSSIR_menuGestor;
        $CSSIR_padre[$i].parentNode.className = $CSSIR_liNombreClase;
        $CSSIR_padre[$i].style.visibility   = 'hidden';
      }
      $CSSIR_padre = new Array();
    }
  }

  if ($menu.id)
  {
    cssQuery('li#' + $menu.id + ' > div')[0].style.visibility = 'visible';
  }
  $CSSIR_menuUltimo = $menu;

  // Aquí utilizamos la función recalc de la biblioteca IE7 de Dean Edwards.
  if (document.recalc)
  {
    document.recalc();
  }
}

function CSSIR_padre($nodo)
{
  if ($nodo.parentNode && $nodo.parentNode.parentNode.parentNode.parentNode.id)
  {
    $CSSIR_padre.push($nodo.parentNode.parentNode.parentNode);
  }
}

function CSSIR_ocultarTodos()
{
  // Ocultar todos los menús y reiniciar el nombre de la clase.
  var $nodos = cssQuery('li.' + $CSSIR_liNombreClase  + ' > div');

  for (var $i in $nodos)
  {
    $nodos[$i].style.visibility   = 'hidden';
    $nodos[$i].parentNode.className = $CSSIR_liNombreClase;
  }

  // Reiniciar los valores y dejarlo todo como
  // estaba cuando se cargó la página.
  CSSIR_menuAsignarEventos();
  $CSSIR_padre      = new Array();
  $CSSIR_menuUltimo = null;
  $CSSIR_actual     = 0;
  $CSSIR_enlace     = 0;
  $CSSIR_menu       = null;

  // Reiniciar esos enlaces resaltados.
  $etiquetas = cssQuery('ul.' + $CSSIR_ulNombreClase + ' li.' + $CSSIR_aResaltado);

  for (var $i in $etiquetas)
  {
    $etiquetas[$i].className = null;
  }

  if (document.recalc)
  {
    document.recalc();
  }
}

document.onload = CSSIR_menuAsignarEventos;

document.onkeydown = function($e)
{
  if (!$e)
  {
    $e = window.event;
  }

  // Comprobar si se está pulsando la tecla Intro.
  //
  // El acceso mediante teclado debe ser independiente de los accesos rápidos
  // ya existentes en el cliente Web. Parece que, por defecto, ningún
  // acceso rápido utiliza las teclas del cursor, así que las
  // utilizaremos en nuestro sistema de navegación.
  switch ($e.keyCode)
  {
    case 37: // Flecha izquierda.
    case 39: // Flecha derecha.
    {
      // La variable $CSSIR_actual es un contador que sigue
      // la pista del menú desplegado.
      if ($CSSIR_actual == -1)
      {
        // Cuando el usuario llega al final del menú, lo cerramos.
        // Esto le permite volver a empezar desde el principio
        // si así lo desea, y proporciona una forma de acceder a
        // a los enlaces del primer nivel.
        CSSIR_ocultarTodos();

        // Reiniciamos el contador cuando el usuario pulsa de nuevo la tecla
        // Flecha derecha o Flecha izquierda. Se abrirá el primer submenú.
        $CSSIR_actual = 0;
      }
      else
      {
        // Obtener todos los elementos del submenú.
        $CSSIR_menus = cssQuery('li.' + $CSSIR_liNombreClase);

        var $n = 0;

        // Localizar el menú que se corresponde con el contador.
        for (var $i in $CSSIR_menus)
        {
          if ($n == $CSSIR_actual)
          {
            var $menu = $CSSIR_menus[$i];
            break;
          }
          $n++;
        }

        // Hacer esto global para que podamos pasar el menú a su gestor,
        // que se encargará de la parte de abrirlo y cerrarlo
        // así como de mantenerlo en su sitio.
        $CSSIR_menu = $menu;
        CSSIR_menuGestor();

        // Es el último submenú. Reiniciar el contador y volver a empezar.
        if ($CSSIR_actual == ($CSSIR_menus.length - 1))
        {
          $CSSIR_actual = -1;
        }
        else
        {
          // Ir al menú siguiente.
          $CSSIR_actual++;
        }
      }
      break;
    }
    case 38: // Flecha arriba.
    case 40: // Flecha abajo.
    {
      // Comprobar si hay algún menú abierto. En caso contrario,
      // iterar por el menú de nivel superior.
      var $selector = ($CSSIR_menuUltimo)?
        '#' + $CSSIR_menuUltimo.id + ' > div > div > ul > li'
      :
        'ul.' + $CSSIR_ulNombreClase + ' > li';

      var $etiquetas = cssQuery($selector);

      var $n = 0;

      for (var $i in $etiquetas)
      {
        if ($etiquetas[$i].className.indexOf($CSSIR_aResaltado) != -1)
        {
          $etiquetas[$i].className = '';
        }

        if ($etiquetas[$i].className.indexOf($CSSIR_liNombreClase) == -1)
        {
          if ($n == $CSSIR_enlace)
          {
            $etiquetas[$i].className = $CSSIR_aResaltado;

            // Tanto IE como Firefox necesitan que el enlace tenga el foco. Si
            // no, el resultado de pulsar la tecla Intro puede ser inesperado.
            cssQuery($selector + '.' + $CSSIR_aResaltado + ' > a')[0].focus();
          }
        }
        $n++;
      }

      if ($CSSIR_enlace == ($etiquetas.length - 1))
      {
        $CSSIR_enlace = 0;
      }
      else
      {
        $CSSIR_enlace++;
      }

      if (document.recalc)
      {
        document.recalc();
      }
      break;
    }
    case 13: // Tecla Intro.
    {
      // Este fragmento es para aquellos clientes Web que
      // no permitan que un enlace obtenga el foco.

      $enlace = ($CSSIR_menuUltimo)?
        cssQuery('#' + $CSSIR_menuUltimo.id + ' > div > div > ul > li.' + $CSSIR_aResaltado + ' > a')[0]
      :
        cssQuery('ul.' + $CSSIR_ulNombreClase + ' > li.' + $CSSIR_aResaltado + ' > a')[0];

      if ($enlace)
      {
        // Opera no permite que un enlace obtenga el foco.
        document.location.href = $enlace.href;
      }
      break;
    }
  }
}

document.onmousedown = function()
{
  // Cuando el usuario haga clic fuera de un menú, éste
  // se ocultará y volverá a su estado inicial.
  if (!$CSSIR_menuAlternar)
  {
    CSSIR_ocultarTodos();
  }
}
