
  // --------------------------------------------------
  //  DOCUMENTATION
  // --------------------------------------------------
  // 
  //  Create a Menu:
  //  <ul class='ctxMenu cached click' id='ctxMenuDemo' style='display: none;' onclick="return CtxMenuManager.ajaxCallback('PublicationCtxMenu.getCtxMenu');">
  //  <ul class='ctxTooltip cached click idle' id='ctxMenuDemo2' style='display: none;' onclick="return CtxMenuManager.handleTooltip();">
  //
  //  - Class ctxMenu:    Indicate this is a Contextual Menu
  //  - Class ctxTooltip: Indicate this is a Contextual Tooltip
  //  - Class cached:     Indicate to cache ajax request
  //  - Class click:      Indicate to show on click (Values: click, rightclick, idle)
  //  - Class aligned:    Indicate to align the menu with the bottom left corner of the target element
  //  - Set an Id used by links
  //  - Set display to none
  //  - Set onclick with CtxMenuManager.ajaxCallback function
  //  --- First param is JSON callback (Java Method)
  //  --- Second param is parameters to call with JSON  method 
  //
  //  Bind it with multiple links, 
  //  <input type='button' value='Click Me !' class="ctxMenuDemo ID_c_6222" />
  //  <a href='index.jsp'><img src='icon.gif' class="ctxTooltipDemo" title="Hello World" /></a>
  //
  //  - Class ctxMenuDemo is the Id of te menu to use
  //  - Class ID_c_6222 is the id of the current targeted JCMS Object
  //  -> first parameter of the call back will be the id c_6222
  // 
  //  IE Leaks: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/IETechCol/dnwebgen/ie_leak_patterns.asp
  //

  // --------------------------------------------------
  //  EVENT OBSERVER
  // --------------------------------------------------


  Event.observe(window, 'load'  , function() { CtxMenuManager.initCtxtMenu();   });
  
  
  // --------------------------------------------------
  //  CONTEXTUAL MENU MANAGER
  // --------------------------------------------------

  if (!window.CtxMenuManager) {
    var CtxMenuManager = new Object();
  }
  
  Object.extend(CtxMenuManager,{
    
    latestElement: null,
    initDone : false,
    
   // -------------------------------------
   //  Functions
   // -------------------------------------
    
    /**
     * Create a ContextMenu for each UL.ctxMenu
     * Bind CtxMenuManager.openCtxtMenu() as Event Observer on contextmenu event
     */
    initCtxtMenu: function(){
      if (CtxMenuManager.initDone){
        return;
      }
      
      JcmsLogger.info("CtxMenuManager","Init Context Menu Manager");
      
      CtxMenuManager.initDone = true;
      CtxMenuManager.ctxmenus = new Object();
      
      // Bind right-click event
      Event.observe(document, 'contextmenu' , CtxMenuManager.openCtxtRightClickMenu.bindAsEventListener(this));
      
      // Bind click event
      Util.observeDocument('click', CtxMenuManager.openCtxtLeftClickMenu.bindAsEventListener(this));
      
      // Bind idle event
      new Notifier(500,'ctxmnu', true, 'ctxTooltipMenu');
      Event.observe(document, 'ctxmnu:idle' ,  CtxMenuManager.openCtxtIdleMenu.bindAsEventListener(this));
      Event.observe(document, 'ctxmnu:active', CtxMenuManager.prepareTooltip.bindAsEventListener(this));

    },
    
    /**
     * Display a context menu on left click
     * Respond for  document.contextmenu event
     * 
     * @param event the event
     * @param kind the event kind
     */
    _openCtxtClickMenu: function(event, kind){
     
      var isClick = kind != 'idle';
      var elm = event.memo ? event.memo.target : Event.element(event);
      
      JcmsLogger.debug("CtxMenuManager"," kind: ",kind," type: ",event.type," which: ",event.which, " button: ",event.button, " detail: ",event.detail," elm: ",elm);
      
      if (!elm && isClick){
        CtxMenuManager.hideAllCtxtMenus();
        return;
      }
      
      elm = $(elm);
      if (!elm) {
        return;
      }
      
      // When we want to provide a JSP tooltip on a link
      // which does not have any visible image => use an
      // invisible image inside the link so we can provide
      // the jsp inside the longdesc attribute
      // <a ...><img ... class='ctxTooltip' longdesc='...jsp...'/>Idle on me</a>
      if (!isClick) {
        var a = elm.fastUp('A', null, true, 6);
        if (a) {
          var tooltipImg = a.down('IMG.ctxTooltipMenu');
          if (tooltipImg) {
            elm = tooltipImg;
          }
        }
      }
      // Otherwise do not allow click on a tooltip which is a link
      else if(elm.tagName == 'A' && elm.hasClassName('ctxTooltipMenu')) {
        return;
      }
      
      
      // 1. Look on EXACT element class name (do not move up)
      if (!elm.ctxmenu){ // Dynamically bind Menu to Element
        elm.ctxmenu = CtxMenuManager.findCtxMenu(elm,kind);
      }
      
      // 2. If find and wrapped by click  and is click skip
      if (elm.ctxmenu && kind == 'click' && elm.fastUp('A')){
          return;
      }
      
      // 3. Then move UP for ONLY for A on CLICK
      if (!elm.ctxmenu && kind == "click"){
        for (var i = 0 ; i < 6 && elm && elm.tagName != "A"; i++){
          elm = elm.parentNode;
        }
        
        elm = $(elm)
        if (!elm || elm.tagName != "A"){
          CtxMenuManager.hideAllCtxtMenus();
          return;
        }

        elm.ctxmenu = CtxMenuManager.findCtxMenu(elm,kind);
      }
      
      // 4. Show the menu if found
      if (elm.ctxmenu && $(elm.ctxmenu.id).hasClassName(kind)){
        JcmsLogger.debug('CtxMenuManager','_openCtxtClickMenu',"isClick: ",isClick,elm);
        elm.ctxmenu.showMenuEvent(event,elm);
        CtxMenuManager.latestElement = elm.identify();
        Event.stop(event);
        return;
      }
      
      if (isClick){
        CtxMenuManager.hideAllCtxtMenus();
      }
    },
    
    /**
     * Display a context menu on left click
     * Respond for  document.contextmenu event
     * 
     * @param event the event
     */
    openCtxtLeftClickMenu: function(event){
      if (Util.isLeftClick(event)){ // Strange prototype issue
        CtxMenuManager._openCtxtClickMenu(event,'click');
      }
    },
    
    /**
     * Display a context menu on right click
     * Respond for  document.contextmenu event
     * 
     * @param event the event
     */
    openCtxtRightClickMenu: function(event){
      CtxMenuManager._openCtxtClickMenu(event,'rightclick');
    },
    
    /**
     * Display a context menu on idle
     * Respond for  Notifier event
     * 
     * @param event the event
     */
    openCtxtIdleMenu: function(event){
      CtxMenuManager._openCtxtClickMenu(event,'idle');
    },
    
    
    /**
     * Hide all contextual menu
     */
    hideAllCtxtMenus: function(){
      $H(CtxMenuManager.ctxmenus).each(function(entry,idx){
        entry.value.hideMenu($(entry.value.id));
      });
    },
    
    /**
     * Returns the CtxMenu bind to the given element or null.
     * 
     * @param elm the element to work with
     * @param event then event kind to look for
     */
    findCtxMenu: function(elm, kind){
      
      if (!elm.className){
        return;
      }
      var entry = $H(CtxMenuManager.ctxmenus).find(function(entry,idx){

        // Check Id
        if (elm.className.indexOf(entry.value.id) < 0){ 
          return false;
        }
        
        // Check Event
        var ctxMenuElm = $(entry.value.id);
        if (kind && ctxMenuElm.className.indexOf(kind) < 0){
          return false;
        }
        return true;
      });
      
      if (entry){
        return entry.value;
      }
      
      var elm = $(elm);
      
      // Lazy retrieve of the UL.ctxMenu with an id matching one of the class of elm
      var ctxMenuULid = elm.classNames().find(function(classname){
        var curElm = $(classname);
        return (curElm && (curElm.hasClassName('ctxMenu') || curElm.hasClassName('ctxTooltip')) && curElm.hasClassName(kind));
      });
      
      var ctxMenuUL = $(ctxMenuULid);
      if (!ctxMenuUL) {
        return;
      }
      
      // Lazy initialize : fill CtxMenu the hash map
      ctxMenuUL.parentNode.removeChild(ctxMenuUL);
      document.body.appendChild(ctxMenuUL);
      var ctxMenuInst = new CtxMenu(ctxMenuUL);
      CtxMenuManager.ctxmenus[ctxMenuUL.id] = ctxMenuInst;

      return ctxMenuInst;
    },
    
    /**
     * Callback of active tootlip notifier used to remove the title attribute
     * for tooltip elements 
     */
    prepareTooltip: function(event){
      var memo = event.memo;
      if (!memo || !memo.target || !memo.target.title){ return; }
      
      memo.target._title = memo.target.title;
      memo.target.title  = '';
    },
    
    /**
     * Convenient callback for tooltip menu. Retrieve tooltip info from
     * title, nexSibling, longdesc, ...
     */
    handleTooltip: function(){
      var callback = function(link,menu){
        JcmsLogger.debug('CtxMenuManager','handleTooltip():',link,menu);
        
        if (!link || !link.className){
          return;
        }
        
        // Set wait icon
        menu.clearMenu(); // Very important (TODO: dig why it is so important)
        var elm = $(this.id);
        elm.appendChild(menu.getWaitMenu());
        elm.ctxmenu.showMenu(elm);
        
        // Retrieve content
        var link = $(link);
        var returnValue = "";
        if (link.longDesc){
          var url = link.longDesc;
          new Ajax.Request(url, {
            method: 'get',
            onSuccess: function(transport) { menu.initMenu(link, "<li class='tt'>"+transport.responseText+"</li>"); }
          });
        }
        else if (link.title){ // Default on title
          link._title = link.title; // Backup and remove
          link.title  = '';
          menu.initMenu(link, "<li class='tt'>"+link._title+"</li>");
        }
        else if (link._title){ // Use backuped title
          menu.initMenu(link, "<li class='tt'>"+link._title+"</li>");
        }
        else { // Default on inline data 
          var next = $(link).next();
          if (next && $(next).hasClassName('ctxTooltip')){
            menu.initMenu(link, "<li class='tt'>"+next.innerHTML+"</li>");
            next.innerHTML = "";
          } else{
            return;
          }
        }
      }
      return callback;
    },
    
    /**
     * Return a Callback function that will:
     * 
     * @param jcmsrpc Call an RPC JSON Func
     * @param params for the json request
     * @param hook Javascript func called to update rpc params
     */
    ajaxCallback: function(jcmsrpc, params, hook){
      
      JcmsLogger.debug('CtxMenuManager','ajaxCallback():',jcmsrpc,params,hook);
      
      var callback = function(link,menu){
        
        // Check Link
        if (!link || !link.className){
          return;
        }
        
        // Init Eval String for RPC
        var evalrpc = "JcmsJsContext.getJsonRPC()."+jcmsrpc+"(";
        
        // Append json callback (must be first parameter)
        evalrpc += "function(value){jsonRequest.asyncJsonCallBack(value);}";
        
        
        // Add JCMS Id to RPC
        var jcmsid = $(link).getJcmsId();
        if (jcmsid){
          evalrpc += ",'"+jcmsid+"'";
        } else { return; }
        
        // Add custom parameters
        if (params){
          evalrpc += ","+params;
        }
        
        // Add hook params
        if (hook){
          var tmp = hook(link,menu);
          evalrpc += tmp ? ",'"+tmp+"'" : "";
        }
        
        // Close parenthesis
        evalrpc += ");";
        
        
        // Init JsonRequest
        var jsonRequest = new JcmsJsonRequest($(menu.id)); 
        
        
        // Init Effect with jsonRequest callback
        var functEffect = function(){ 
          
          menu.clearMenu(); // Very important !
          var elm = $(this.id);
          elm.appendChild(menu.getWaitMenu());
          elm.ctxmenu.showMenu(elm);
          
          jsonRequest.asyncEffectCallBack({});
        }.bind(menu);
        
        // Init RPC with jsonRequest callback
        var funcRPC = function(){
          eval(evalrpc);
        };
        
        // Init CallBack
        var funcCallBack = function(returnValue, returnEffect){ 
          menu.initMenu(link, returnValue);
        };
        
        // Run JSON Request
        jsonRequest.effect   = functEffect;
        jsonRequest.rpc      = funcRPC;
        jsonRequest.callback = funcCallBack;
        jsonRequest.asyncJsonCall();
      }
      return callback;
    },
    
    /**
     * Returns classes of the clicked link to be append
     * to JSON Request for server side actions
     * 
     * @param link 
     * @param menu
     * @return String the link classes
     */
    fillElmClassesHook: function(link,menu){
      if (!link.className){
        return;
      }
      return link.className;
    }
  });
  
  
  // --------------------------------------------------
  //  CONTEXTUAL MENU
  // --------------------------------------------------

  CtxMenu = Class.create();
	CtxMenu.prototype = {
	  
	  // --------------------
	  //  CONSTRUCTOR
	  // --------------------
	  
	  /**
	   * Initialise the ContextualMenu for the given UL element quickly.
	   * The second part of the initialisation is done by _initializeLazy()
	   * 
	   * @param elm the UL element
	   */
	  initialize: function(elm) {
	    this.id  = elm.id;
	    this.timeout     = 1000;
	    this.isCached    = elm.hasClassName('cached');
	    this.isAligned   = elm.hasClassName('aligned');
	    this.isIE        = /MSIE/.test(navigator.userAgent);
	    this.useIframe   = this.isIE;
	    this.isTooltip   = elm.hasClassName('ctxTooltip');
	    
	    elm.ctxmenu = this;
	    elm.hide();
	    elm.cleanWhitespace();
	  },
	  
	  _initLazy: false,
	  
	  /**
	   * Second part of the Initialisation
	   */
	  _initializeLazy: function(){
	    if (this._initLazy){
	      return;
	    }
	    
	    JcmsLogger.info("CtxMenu","Init Context Menu: ",this.id," isTooltip: ",this.isTooltip);
	    this._initLazy = true;
	    
	    if (!this.isTooltip){
        this._initHover();           // Setup "hover" behavior on LI
        this._initImages();          // Setup "mnuicon" behavior on IMG
        this._initSubMenu();         // Setup submenu behavior on UL
        this._initLinks();           // Wrap all <A> into a function checking parent LI is not disabled
	    }
	    
	    this._initIFrame($(this.id)); // Setup IFrame hack
	    this._initCallBack();          // Setup callback
	  },
	  
	  // --------------------
	  //  INTERNAL
	  // --------------------
	  
	  /**
	   * NB: Couldn't make a function because code is slightly different with display 
	   * menu in viewport (not the same behavior).
	   * 
	   * @param link the link to align with
	   */
	  _initMenuPosition: function(link){
      var elm = $(this.id);
      
       // Dedieu's trick
	    elm.removeClassName('ctxSmall');
	    elm.removeClassName('ctxTiny'); 
	    
	    elm.show(); // Cannot use style visible because it do not wwork with IE6
	    Position.prepare(); 
	    var pageScroll = [Position.deltaX, Position.deltaY];
	    var pageBounds = Util.getViewportBounds();
	    var menuPos    = Position.cumulativeOffset(elm);  
	    var menuDim    = elm.getDimensions();
	    var linkDim    = link.getDimensions();

      var menuPosWidth    = menuPos[0]+menuDim.width;
      var pageScrollBound = pageScroll[0]+pageBounds.width-20;
      var overflow        = menuPosWidth-pageScrollBound;
      
      if (JcmsLogger.isDebug && JcmsLogger.CtxMenuTrace){
        JcmsLogger.debug('CtxMenuTrace','--- _initMenuPosition -------------------------- ');
	      JcmsLogger.debug('CtxMenuTrace','Pos: '   ,menuPos[0],menuPos[1]);
	      JcmsLogger.debug('CtxMenuTrace','Bound: ' ,menuDim.width,menuDim.height);
	      JcmsLogger.debug('CtxMenuTrace','Window: ',(pageScroll[0]+pageBounds.width));
	      JcmsLogger.debug('CtxMenuTrace','overflow: ',overflow);
      }

      // Check pop right
	    if (overflow > 0){ 
	      // Dedieu's trick  (Cannot set it dynamically)
	      if (this.isTooltip && overflow < 150){
          elm.addClassName('ctxSmall');
        } 
	      else if (this.isTooltip && overflow < 250){
	        elm.addClassName('ctxTiny');
        } 
        else {
	        elm.style.left = this.isAligned ? menuPosWidth+linkDim.width+'px' : menuPos[0]-menuDim.width+'px';
        }
	    }
	    
	    // Check pop left
	    if ((menuPos[1]+menuDim.height > pageScroll[1]+pageBounds.height-20) && (menuPos[1]-menuDim.height > 0)){
	      elm.style.top  = this.isAligned ? menuPos[1]-menuDim.height-linkDim.height+'px' : menuPos[1]-menuDim.height+'px';
	    }
	  },
	  
	  /**
	   * Inits all LI, bind onmouseover and onmouseout events
	   */
	  _initHover: function(){  
      var elm = $(this.id);
	    $A(elm.getElementsByTagName('LI')).each(function(li,idx){
	     var li = $(li);
	     
	     if (li.hasClassName('hr'))
	       return;
	       
	     li.ctxmenu = this;
	     
	     // Fix mouse over/out function
	     Event.observe(li,'mouseover', this._eventShowSubMenu.bindAsEventListener(li));
	     Event.observe(li,'mouseout' , this._eventHideSubMenu.bindAsEventListener(li));
       
	    }.bind(this));
	  },
	  
	  /**
	   * Wrap all <A> with onclick function checking if parent node LI 
	   * has classname disabled.
	   */
	  _initLinks: function(){
      var elm = $(this.id);
	    $A(elm.getElementsByTagName('A')).each(function(ahref,idx){
	      
	      if (!Element.hasClassName(ahref.parentNode,'disabled')){
	        return;
	      }
	      
	      // ahref._onclick = ahref.onclick; // We never enable it
	      ahref.onclick  = function(){	          
	        return false;
	      }
	      
	    });
	    
	  },
	  
	  /**
	   * Inits all IMG, wrap DIV
	   */
	  _initImages: function(){
      var elm = $(this.id);
	    $A(elm.getElementsByTagName('IMG')).each(function(elm,idx){
	      
	      var img = $(elm);

        // Update imgage src if disabled
        if ($(img.parentNode.parentNode).hasClassName('disabled')){
          var path = img.src; 
          var pos  = path.lastIndexOf('.');
          img.src = "s.gif"; // path.substring(0,pos)+'_gray'+path.substring(pos);
          img.width = 16;
          img.height = 16;
        }
	      
	    }.bind(this));
	  },
	  
	  /**
	   * Inits all sub menus, hide bind 
	   */
	  _initSubMenu: function(){
      var elm = $(this.id);
      
	    $A(elm.getElementsByTagName('UL')).each(function(ul,idx){
	      var ul = $(ul);
	      
	      // Set node
	      if (!Element.hasClassName(ul.parentNode,'node'))
  	      Element.addClassName(ul.parentNode,'node');
	      
	      ul.parentNode.timeout = this.timeout;
	      ul.parentNode.submenu = ul;
	      ul.ctxmenu = this;
	      ul.cleanWhitespace();
	      
	      // IFrame
	      this._initIFrame(ul);
	      
	      // Hide submenu
	      ul.hide();
	      
	    }.bind(this));
	  },
	  
	  /**
	   * Use the onclick function of UL as callback to call 
	   * while displaying the menu.
	   */
	  _initCallBack: function(){
	    
      var elm = $(this.id);
	    if (!elm.onclick){
	      return;
	    }
	    
	    this.callback = elm.onclick(); // Retrieve the callback function from onclick
	    elm.onclick = null;
	  },
	  
	  /**
	   * IFrame hack under the menu for IE
	   */
	  _initIFrame: function(elm){
  	  
  	  if (!elm.style.zIndex){ // Set the zIndex
  	    elm.style.zIndex = 20000;
  	  }
  	  
  	  if (!this.useIframe){
  	    JcmsLogger.debug('CtxMenuTrace','No IFrame');
  	    return;
  	  }
  	  
  	  var bodyNode = document.getElementsByTagName('body')[0];
  	  if (!bodyNode){ return; }
  	  
  	  if (!elm.iframe){
  	    elm.iframe = $(document.createElement('IFRAME'));
  	    elm.iframe.src='s.gif';
  	    elm.iframe.style.position = 'absolute';
  	    elm.iframe.style.display = 'none';
  	    elm.iframe.style.zIndex = elm.style.zIndex-1;
	      elm.iframe.frameBorder = 'no';
	      elm.iframe.scrolling = 'no';
	      bodyNode.appendChild(elm.iframe);
  	  }
	  },
	  
	  /**
	   * Display IFrame with the size of the elm
	   */
	  _setupIFrame: function(elm){
	    
	    if (!elm.iframe)
	      return;
	    
	    var elm = $(elm); 
	    var pos = Position.cumulativeOffset(elm);
	    
	    elm.iframe.style.left   = pos[0]+1+'px';
	    elm.iframe.style.top    = pos[1]+1+'px';
	    elm.iframe.style.width  = elm.offsetWidth-2+'px';
	    elm.iframe.style.height = elm.offsetHeight-2+'px';
	    elm.iframe.show();
	  },
	  
	  /**
	   * Hide IFrame
	   */
	  _hideIFrame: function(elm){
	    if (!elm.iframe)
	      return;
	      
	    elm.iframe.hide();
	    
	    // Hide sub iframes
	    $(elm).select('UL').each(function(elm,idx){
        if (!elm.iframe) return;
        elm.iframe.hide();
      });
	  },
	  
	  /**
	   * Convenient function used to handle event to show sub menus. 
	   * If LI is a node then call hideSubMenu otherwise use simple code
	   * Should be called with bindAsEventListener()
	   */
	  _eventHideSubMenu: function(event){
	    //var func = function(event){ 
	      
	      Event.stop(event);
	      var item    = this;
	      var cxtmenu = item.ctxmenu;
	      
	      // Delete timer and create new one
	      if (item.activeTimeout)    clearTimeout(item.activeTimeout);
	      if (cxtmenu.activeTimeout) clearTimeout(cxtmenu.activeTimeout);
	      
	      if (item.timeout || cxtmenu.activeTimeout){
	        item.activeTimeout    = setTimeout( function(){this.ctxmenu.hideSubMenu($(this))}.bind(item),item.timeout);   // Wait mouse come back or open a submenu
	        cxtmenu.activeTimeout = setTimeout( function(){this.ctxmenu.hideMenu($(this.ctxmenu.id))}.bind(item),cxtmenu.timeout); // Wait mouse come back or open a menu
	      } else {
	        cxtmenu.hideSubMenu(item);
	        cxtmenu.activeTimeout = setTimeout( function(){this.ctxmenu.hideMenu($(this.ctxmenu.id))}.bind(item),cxtmenu.timeout); // Wait mouse come back or open a menu
	      }
	    //};
	    //return func;
	  },
	  
	  /**
	   * Convenient function used to handle event to show sub menus.
	   * Should be called with bindAsEventListener()
	   * @param event the event
	   */
	  _eventShowSubMenu: function(event){
	    this.ctxmenu.showSubMenu(this);
	  },
	  
	  // --------------------
	  //  FUNCTIONS
	  // --------------------
	  
	  initMenu: function(link, innerHTML){
              
	    var elm = $(this.id);
	    if ((!link || !link.ctxmenuCache) && (innerHTML)){
        JcmsLogger.debug('CtxMenu','InitMenu: ','Set innerHTML');
        elm.hide();
        elm.innerHTML = innerHTML;
  	    elm.cleanWhitespace();
  	    if (!this.isTooltip){
  	      this._initHover();    // Setup "hover" behavior on LI
  	      this._initImages();   // Setup "mnuicon" behavior on IMG
  	      this._initSubMenu();  // Setup submenu behavior on UL
  	      this._initLinks();    // Wrap all <A> into a function checking parent LI is not disabled
  	    }
  	    this._initMenuPosition(link); // Display menu in viewport
  	    elm.ctxmenu.showMenu(elm); // Show the menu
  	    
  	    if (!this.isCached){
          return;
        }
        
        link.ctxmenuCache = $A(elm.childNodes).clone();
      }
      else if (link.ctxmenuCache){
        JcmsLogger.debug('CtxMenu','InitMenu: ','Use cached DOM');
        
        // Remove previous elements
        this.clearMenu();
        
        // Append new children
        link.ctxmenuCache.each(function(node, idx){
          elm.appendChild(node);
        }.bind(this));
        
        this._initMenuPosition(link); // Display menu in viewport
        elm.ctxmenu.showMenu(elm); // Show the menu
      }
      else{
	      alert(I18N.glp('warn.json.sessiontimeout'));
	      elm.hide();
	      return;
      }        
	  },
	   
	  /**
	   * Remove all child from current elmenet
	   */
	  clearMenu: function(){
      var elm = $(this.id);
	    Util.cleanDOMElements(elm,false);
	  },
	  
	  /**
	   * Returns a <li><img/></li> DOM elements to wait ajax request
	   * Build is lazy and cached in menu.
	   * TODO: Should it removed from previous parent ?
	   */
	  getWaitMenu: function(){
	    if (this.waitmenu){
	      return this.waitmenu;
	    }
	    
	    var li  = document.createElement('li');
      li.className = 'wait';
      
      var img = document.createElement('img');
      img.src = 'images/jalios/icons/waitsmall.gif';
      li.appendChild(img);
      
      this.waitmenu = li;
      return li;
	  },
	  
	  /**
	   * Wrap into a funcion  the onclick of target elements
	   */
	  showMenuEvent: function(event, link, eX, eY){ 

		  //if (!eX) {
	    //  Event.stop(event);
	    //}

	    var link     = link ? link : this;
	    var ctxmenu  = link.ctxmenu;
      var elm      = $(ctxmenu.id);
      
      // Lazy Initialise
      ctxmenu._initializeLazy();
      if (elm.style.display == '' && link == ctxmenu.lastLink && !event.memo){
        ctxmenu.hideMenu(elm);
        return false;
      } 
      else {
        CtxMenuManager.hideAllCtxtMenus();
        
        // Align menu with opener link
        if (ctxmenu.isAligned){
    	  var linkPos = Position.cumulativeOffset(link);
    	  var linkDim = link.getDimensions();
          elm.style.left = linkPos[0]+'px';
          elm.style.top  = linkPos[1] + linkDim.height+'px';
        }
        // Align on mouse click 
        else {
          elm.style.left = (eX || (event.memo && event.memo.eX) || Event.pointerX(event))+'px';
          elm.style.top  = (eY || (event.memo && event.memo.eY) || Event.pointerY(event))+'px';
        }
        
        // Backup last opened element
        ctxmenu.lastLink = link;
        
        // Use cached version of the menu
        if (link.ctxmenuCache){
          ctxmenu.initMenu(link);
        }
        else if (ctxmenu.callback){ 
          ctxmenu.callback(link, ctxmenu);
        }
        else{
          ctxmenu._initMenuPosition(link);
          ctxmenu.showMenu(elm);
        }
      }
      
      return false;
	  },
	  
	  showMenu: function(elm){
	    elm.show();
      elm.ctxmenu._setupIFrame(elm);
	  },
	  
	  /**
	   * Show a submenu. 
	   * A boolean "active" indicate if mouse come back on LI (this limit job)
	   * Set the right position of the submenu
	   */
	  showSubMenu: function(li){
	    // Remove runing timeout
	    if (li.activeTimeout)         clearTimeout(li.activeTimeout);         // If mouse go back on LI
	    if (li.ctxmenu.activeTimeout) clearTimeout(li.ctxmenu.activeTimeout); // If mouse go back on menu
	    
	    // Do not open if disabled
	    if (li.hasClassName('disabled'))
	      return;
	      
	    // Do not do job twice
	    if (li.cxtshow)
	      return;
	      
	    li.cxtshow = true;
	    li.cxthide = false;
	    
	    // Hide all sibling LI
	    var firstChild = $(li.parentNode.firstChild);
	    while (firstChild){
	      if (firstChild.hasClassName('hover')){
	        firstChild.ctxmenu.hideSubMenu(firstChild);
	        break;
	      }
	      firstChild = $(firstChild.nextSibling);
	    }
	    
	    
	    li.addClassName('hover');
	    if (!li.submenu){
	      return;
	    }
	      
	    // Compute position
	    //var ul = li.parentNode;
	    li.makePositioned();
	    var liPos = Position.positionedOffset(li);
	    var liDim = li.getDimensions();
	    li.undoPositioned();
	    
	    JcmsLogger.debug('CtxMenuTrace','liPos: '+liPos[0]+','+liPos[1]);
	    JcmsLogger.debug('CtxMenuTrace','liDim: '+liDim.width+','+liDim.height);
	    
	    // Set position
	    li.submenu.style.left = liPos[0]+liDim.width+'px';
	    li.submenu.style.top  = liPos[1]+'px';
	    
	    
	    // Start to display even if call showMenu at the end
	    li.submenu.show();
	    
	    // Display submenu in viewport
	    // NB: Couldn't make a function because code is slightly different with display 
	    //     menu in viewport (not the smae behavior).
	    
	    Position.prepare();
	    var pageScroll = [Position.deltaX, Position.deltaY];
	    var pageBounds = Util.getViewportBounds();
	    var menuPos    = Position.cumulativeOffset(li.submenu);
	    var menuDim    = li.submenu.getDimensions();
	    
	    if (JcmsLogger.isDebug && JcmsLogger.CtxMenuTrace){
	      JcmsLogger.debug('CtxMenuTrace','--- showSubMenu -------------------------- ');
	      JcmsLogger.debug('CtxMenuTrace','pageScroll: ',pageScroll[0],pageScroll[1]);
	      JcmsLogger.debug('CtxMenuTrace','pageBounds: ',pageBounds.width,pageBounds.height);
	      JcmsLogger.debug('CtxMenuTrace','menuPos: '   ,menuPos[0],menuPos[1]);
	      JcmsLogger.debug('CtxMenuTrace','menuDim: '   ,menuDim.width,menuDim.height);
      }
	    
	    if (menuPos[0]+menuDim.width > pageScroll[0]+pageBounds.width-20){
	      li.submenu.style.left = liPos[0]-menuDim.width+'px';
	    }
	    
	    if ((menuPos[1]+menuDim.height > pageScroll[1]+pageBounds.height-20)){
	      var tmp = (pageScroll[1]+pageBounds.height-20) - (menuPos[1]+menuDim.height);
	      //li.submenu.style.top  = liPos[1]+liDim.height-menuDim.height+'px';
	      li.submenu.style.top  = liPos[1]+tmp+'px';
	    }
	    
	    JcmsLogger.debug('CtxMenuTrace','SubMenu2: '+li.submenu.style.left+' , '+li.submenu.style.top);
	    
	    // Show menu
	    li.ctxmenu.showMenu(li.submenu);
	  },
	  
	  /**
	   * Hide a submenu.
	   */
	  hideSubMenu: function(li){ 
	    
	    // Remove runing timeout
	    if (li.activeTimeout){
	      clearTimeout(li.activeTimeout);
	    }
	    
	    // Do not do job twice
	    if (li.cxthide)
	      return;
	    li.cxthide = true;
	    li.cxtshow = false;
	    
	    
	    li.removeClassName('hover');
	    
	    if (li.submenu){
	      li.ctxmenu.hideMenu(li.submenu);
	    }
	  },
	  
	  /**
	   * Hide menu, sub iframe, ...
	   */
	  hideMenu: function(elm){
	    JcmsLogger.debug('CtxMenu','hideMenu('+elm.id+')');
	    
	    var ctxmenu = elm.ctxmenu;
	    
	    // Menu already closed
	    if (elm.style.display == 'none'){
	      return;
	    }
	    
	    // Main menu is closed
      var ctxMenuElm = $(ctxmenu.id);
	    if (elm == ctxMenuElm){
	      // Clear timeout to do stuff once
	      if (elm.ctxmenu.activeTimeout){
	        clearTimeout(elm.ctxmenu.activeTimeout);
	      }
	    }
	    	    
	    var elm = $(elm);
	    elm.hide();
	    elm.ctxmenu._hideIFrame(elm);
	  }
	}
	