var Menu = Class.create() ;
Menu.prototype =
{
  initialize: function( id, config)
  {
    this.oMenu = this ;
    this.Type = "menu" ;
    this.closeDelayTimer = null ;
    this.closingMenuItem = null ;

    // Options
    this.Config = {
      tagMenuItem: "li" ,
      tagMenuContainer: "ul" ,
      collapseBorders: true ,
      quickCollapse: true ,
      closeDelayTime: 500 ,
      classMenuItemp: "" ,
      classMenuContainer: ""
    }
    
    if( typeof( config ) != 'undefined' )
    {
      Object.keys(config).each(this.updateConfig.bindAsEventListener(this,config)) ;
    }
        
    this.oRootContainer = new MenuContainer( id, this ) ;
  },

  // {{{
  updateConfig: function( param, config )
  {
    eval( "if( typeof( config."+param+" ) != 'undefined' ) {this.Config."+param+" = config."+param+" ;}" ) ;
  }
  // }}}
  
}

var MenuContainer = Class.create() ;
MenuContainer.prototype =
{
  initialize: function( id, oParent )
  {
    this.Type = "menuContainer" ;
    this.MenuItems = [] ;
    this.init( id, oParent ) ;
  },

  init: function( id, oParent )
  {
    this.oElement = $(id) ;
    this.oParent = oParent ;
    this.oParentMenu = (this.Type == "menuContainer") ? ((oParent) ? oParent.oParent : null) : oParent ;
    this.oRoot = oParent instanceof Menu ? oParent : oParent.oRoot ;
    this.id = this.oElement.id ;

    if( this.Type == "menuContainer" )
    {
      if( this.oElement.hasClassName( "horizontal" ) )
        this.MenuType = "horizontal" ;
      else
      if( this.oElement.hasClassName( "dropdown" ) )
        this.MenuType = "dropdown" ;
      else
        this.MenuType = "flyout" ;
    
      if( this.MenuType == "flyout" || this.MenuType == "dropdown" )
      {
        this.isOpen = false ;
        Element.setStyle( this.oElement,
        {
          position: "absolute",
          top: "0px",
          left: "0px",
          display: "none"
        }) ;
      }
      else
      {
        this.isOpen = true ;
      }
    }
    else
    {
      this.isOpen = this.oParentMenu.isOpen ;
    }

    var childNodes = this.oElement.childNodes ;
    if( childNodes == null ) return ;
    for( var i = 0 ; i < childNodes.length ; i++ )
    {
      var node = childNodes[i] ;
      if( node.nodeType == 1 )
      {
        if( this.Type == "menuContainer" )
        { 
          if( (node.tagName.toLowerCase() == this.oRoot.Config.tagMenuItem) &&
              ( !this.oRoot.Config.classMenuItem || (this.oRoot.Config.classMenuItem && Element.hasClassName( node, this.oRoot.Config.classMenuItem )) ) )
          {
          
            this.MenuItems.push( new MenuItem(node, this) ) ;
          }
        }
        else
        {
          if( (node.tagName.toLowerCase() == this.oRoot.Config.tagMenuContainer) &&
              ( !this.oRoot.Config.classMenuContainer || (this.oRoot.Config.classMenuContainer && Element.hasClassName( node, this.oRoot.Config.classMenuContainer )) ) )
          {
            this.SubMenu = new MenuContainer(node, this) ;
          }
        }
      }
    }
  },

  getBorders: function(element)
  {
    var ltrb = ["Left","Top","Right","Bottom"] ;
    var result = {} ;
    for( var i = 0 ; i < ltrb.length ; ++i )
    {
      if( this.oElement.currentStyle )
        var value = parseInt( this.oElement.currentStyle["border"+ltrb[i]+"Width"] ) ;
      else
      if( window.getComputedStyle )
        var value = parseInt( window.getComputedStyle( this.oElement, "" ).getPropertyValue( "border-"+ltrb[i].toLowerCase()+"-width" ) ) ;
      else
        var value = parseInt( this.oElement.style["border"+ltrb[i]] ) ;
      result[ltrb[i].toLowerCase()] = isNaN(value) ? 0 : value ;
    }
    return result ;
  },

  open: function()
  {
    if( this.oRoot.closeDelayTimer ) window.clearTimeout( this.oRoot.closeDelayTimer ) ;
    this.oParentMenu.closeAll( this ) ;
    this.isOpen = true ;
    if( this.MenuType == "dropdown" )
    {
      Element.setStyle( this.oElement,
      {
        left: (Position.positionedOffset(this.oParent.oElement)[0]) + "px" ,
        top: (Position.positionedOffset(this.oParent.oElement)[1] + Element.getHeight(this.oParent.oElement)) + "px"
      });
    }
    else
    if( this.MenuType == "flyout" )
    {
      var oParentMenuBorders = this.oParentMenu ? this.oParentMenu.getBorders() : new Object() ;
      var thisBorders = this.getBorders() ;
      if( ( Position.positionedOffset(this.oParentMenu.oElement)[0] + this.oParentMenu.oElement.offsetWidth + this.oElement.offsetWidth + 20 ) > ( window.innerWidth ? window.innerWidth : document.body.offsetWidth ) )
      {
        Element.setStyle( this.oElement,
        {
          left: (- this.oElement.offsetWidth - (this.oRoot.Config.collapseBorders ?  0 : oParentMenuBorders["left"])) + "px"
        }) ;
      }
      else
      {
        Element.setStyle( this.oElement,
        {
          left: (this.oParentMenu.oElement.offsetWidth - oParentMenuBorders["left"] - (this.oRoot.Config.collapseBorders ?  Math.min(oParentMenuBorders["right"], thisBorders["left"]) : 0) +1 ) + "px"
        }) ;
      }
      Element.setStyle( this.oElement,
      {
        top: (this.oParent.oElement.offsetTop - oParentMenuBorders["top"] - this.MenuItems[0].oElement.offsetTop + 5) + "px"
      });
    }
    //Element.setStyle(this.oElement,{visibility: "visible"}) ;
    //Element.setStyle(this.oElement,{display: ""}) ;
    if( this.oElement.style.display == "none" ) new Effect.Appear(this.oElement,{duration:0.3});
  },

  close: function()
  {
    //Element.setStyle(this.oElement,{visibility: "hidden"}) ;
    //Element.setStyle(this.oElement,{display: "none"}) ;
    if( this.oElement.style.display == "" ) new Effect.Fade(this.oElement,{duration:0.5});   
    this.isOpen = false ;
    this.closeAll() ;
  },

  closeAll: function(trigger)
  {
    if( this.MenuItems )
    {
      for( var i = 0 ; i < this.MenuItems.length ; ++i )
      {
        this.MenuItems[i].closeItem(trigger) ;
      }
    }
  }

}


var MenuItem = Class.create();
Object.extend(Object.extend(MenuItem.prototype, MenuContainer.prototype),
{
  initialize: function(id, oParent)
  {
    var menuItem = this ;
    this.Type = "menuItem" ;
    this.SubMenu ;
    this.init( id, oParent ) ;
    if( this.SubMenu )
    {
      this.oElement.onmouseover = menuItem.SubMenu.open.bind(menuItem.SubMenu) ;
    }
    else
    {
      if( this.oRoot.Config.quickCollapse )
      {
        this.oElement.onmouseover = menuItem.oParentMenu.closeAll.bind(menuItem.SubMenu);
      }
    }
    
    var linkTag = this.oElement.getElementsByTagName("A")[0] ;
    if( linkTag )
    {
      linkTag.onfocus = this.oElement.onmouseover ;
      this.link = linkTag ;
      this.text = linkTag.text ;
    }
    
    if( this.SubMenu )
    {
      this.oElement.onmouseout = menuItem.elOnMouseOut.bind( menuItem ) ;
    }
  },

  // {{{
  elOnMouseOut: function()
  {
    if( this.oRoot.openDelayTimer ) window.clearTimeout( this.oRoot.openDelayTimer ) ;
    if( this.oRoot.closeDelayTimer ) window.clearTimeout( this.oRoot.closeDelayTimer ) ;
    this.oRoot.oMenu.closingMenuItem = this;
    this.oRoot.closeDelayTimer = window.setTimeout( this.oRoot.oMenu.closingMenuItem.SubMenu.close.bind(this.oRoot.oMenu.closingMenuItem.SubMenu), this.oRoot.Config.closeDelayTime ) ;
  },
  
  openItem: function()
  {
    this.isOpen = true ;
    if( this.SubMenu ) { this.SubMenu.open(); }
  },

  closeItem: function(trigger)
  {
    this.isOpen = false ;
    if( this.SubMenu )
    {
      if( this.SubMenu != trigger ) this.SubMenu.close() ;
    }
  }
}) ;