/**
 * JYAML - Template Framework includes YAML for Joomla!
 *
 * All rights reserved. The JYAML project is a template to manage and
 * configure Joomla!-Templates with the YAML XHTML/CSS Framework
 * - http://www.yaml.de
 *
 * -----------------------------------------------------------------------------
 *
 * @version       $Id: menu.js 1123 2011-11-19 04:09:32Z hieblmedia $
 *
 * @author        Reinhard Hiebl
 * @copyright     Copyright (C) 2006-2011, Reinhard Hiebl, HieblMedia
 * @license       Creative Commons Attribution 3.0 Unported License
 *                  > http://www.jyaml.de/en/license-conditions.html
 * @link          http://www.jyaml.de
 * @package       JYAML
 * @subpackage    JYAML.Dropdown.Javascript
 *
 */

// Only define the JYAML namespace if not defined.
if (typeof(JYAML) === 'undefined') {
  var JYAML = {};
}

JYAML.DropdownMenusGlobal = new Class({
  menus: [],
  initHoverDone: false, // to check the mouse cursor is currently over a item

  registerMenu: function(object) {
    this.menus.include(object);
  },

  hideAllOtherMenus: function(activeMenu){
    this.menus.each(function(menu){
      if(menu !== activeMenu) {
        menu.hideSubMenus();
      }
    });
  }
});
myJYAMLDropdownMenusGlobal = new JYAML.DropdownMenusGlobal();

JYAML.Dropdown = new Class({
  Implements: [Options, Events],
  options: {
    // submenu selector
    subMenuSelector: 'ul, ol',
    // link elements
    linksSelector: 'a, div.menuitem-content',
    // id of the div that will be generated to hold the submenus for absolute
    // positioning
    subMenusContainerId:'dropdownSubMenusContainer',

    // hover class for dropitem {@link this.options.subMenuSelector}.
    // for old browsers (like IE<7)
    hoverClass: 'hover',

    // horizontal or vertical
    orientation: 'horizontal',

    // submenu direction (x=left or right, y=up or down).
    // will be automaticly detected by position defined in css
    direction: {x: 'right', y: 'down'},
    // if true the submenu becomes 100% of the width
    linearSubMenu: false,
    // reverse the direction of the submenu (no the root items), if its out of the browsers viewport
    reverseDirectionOutOfViewport: true,

    // hide delay on mouseout in milliseconds
    hideDelay: 750,
    // determine to hide all submenus are not in this menu assigned on hover/focus
    hideSubMenusOnAllMenus: true,
    // transparency of submenus (0.00-1) - affected on effect fade or null
    opacity: 0.90,
    // effect: ['slide'], ['fade'], ['slide', 'fade'] or null
    // use fullslide instead of slide to slide in 'both' directions
    effects: ['slide', 'fade'],

    // effect: duration of the effect in milliseconds
    showDuration: 750,
    hideDuration: 250,
    // effect: Fx.Transitions onShowMenu
    showTransition: Fx.Transitions.Pow.easeOut,
    // effect: Fx.Transitions onHideMenu
    hideTransition: Fx.Transitions.Pow.easeIn,

    // stretch the first submensus width of his parent of smaller
    stretchWidthFistSubMenu: true,

    // if true the submenu position is move to left/right instead of direction change (dont work if linearSubMenu=true)
    moveHorizPosOutOfViewport: false

    /*
     * Events:
     *
     * onBeforeInitialize: function(){},
     * onAfterInitialize: function(){},
     *
     * onBeforeSubMenuInitialize: function(item){},
     * onAfterSubMenuInitialize: function(item){},
     *
     * onBeforeHideChildSubMenus: function(){},
     * onAfterHideChildSubMenus: function(){},
     *
     * onBeforeHideAllSubMenus: function(){},
     * onAfterHideAllSubMenus: function(){},
     *
     * onAddEffectOnShowSubMenu: function(effect, effectStyles, fx){},
     * onAddEffectOnHideSubMenu: function(effect, effectStyles, fx){},
     *
     */

    /*
     * Custom Effects Example:
     * -----------------------
        onAddEffectOnShowSubMenu: function(effect, effectStyles, fx){
          switch (effect){
            case 'myfade': // e.g. effects: ['slide', 'myfade']
              if(this.initShow) {
                // do some things that required an init value or something else
                this.childMenuElement.setStyle('opacity', 0.01); // required for IE on opacity effects
              }
              Object.append(effectStyles, {'opacity': 0.75});
              break;
          }
          return effectStyles; // Effect Properties Object - see: http://mootools.net/docs/core/Fx/Fx.Morph#Fx-Morph:start
        },
        onAddEffectOnHideSubMenu: function(effect, effectStyles, fx){
          switch (effect){
            case 'myfade': // e.g. effects: ['slide', 'myfade']
              Object.append(effectStyles, {'opacity': 0});
              break;
          }
          return effectStyles; // Effect Properties Object - see: http://mootools.net/docs/core/Fx/Fx.Morph#Fx-Morph:start
        }
     * -----------------------
     */
  },

  menu: null,
  subMenusHideTimeout: null,
  subMenus: [],

  initialize: function(id, options){
    this.setOptions(options);
    this.options.id = id;

    this.menu = $(this.options.id);
    // stop if the menu id not exists in DOM
    if(!this.menu) { return; }

    if(this.menu.retrieve('jyaml-dropdown') !== null) {
      return; // prevent double creation
    }
    this.menu.store('jyaml-accordion', this);

    this.fireEvent('beforeInitialize');
    this.create();
    this.fireEvent('afterInitialize');
  },

  create: function(){
    // add js class to body for css adjustments, if javascript enabled
    $(document.body).addClass('js');

    // initialize directions
    this.options.direction.x = this.options.direction.x.toLowerCase();
    this.options.direction.y = this.options.direction.y.toLowerCase();

    // create helper container, if not exists in DOM
    var parentContainerClass = this.menu.getParent().get('class');
    var subMenusContainerOuter = $(this.options.subMenusContainerId);
    if(!subMenusContainerOuter) {
      subMenusContainerOuter = new Element('div', {'id': this.options.subMenusContainerId}).inject($(document.body), 'bottom');
    }
    // create subcontainer helper from parent with class to hold css styles for better fallback support without js
    var subMenusContainer = new Element('div', {
      'class': parentContainerClass
    }).inject(subMenusContainerOuter, 'bottom');

    // parse links by linksSelector (before inject into helper container)
    var links = this.menu.getElements(this.options.linksSelector);

    // if linear mode filter all 3rd level submenus out
    if(this.options.linearSubMenu) {
      links = links.filter(function(item, index){
        var parentList = $(item.getParent('ul') || item.getParent('ol'));
        if(parentList && parentList.get('id')===this.options.id) {
          return true;
        } else {
          return false;
        }
      }.bind(this));
    }

    links.each(function(item, index){
      // store parent links
      item.store('parentLinks', item.getParent().getParents('li').getFirst(this.options.linksSelector));
      item.store('parentLinks', item.retrieve('parentLinks').erase(item.retrieve('parentLinks').getFirst()));

      // store child menu
      var childMenus = item.getParent('li').getChildren(this.options.subMenuSelector);
      if(childMenus && childMenus.length) {
        var childMenu = childMenus[0];
        item.store('childMenu', childMenu);
        item.store('childMenuCoordinates', childMenu.getCoordinates());
      } else {
        item.store('childMenu', null);
        item.store('childMenuCoordinates', {});
      }

      // determine menu type (is child/dropitem or root/first-level item)
      var isRootMenu = false;
      var parentList = $(item.getParent('ul') || item.getParent('ol'));
      if(parentList && parentList.get('id')===this.options.id) {
        isRootMenu = true;
      } else {
        // prevent tabing of the (hidden) child/dropitem links
        item.set('tabindex','-1');
      }
      item.store('isRootMenu', isRootMenu);
    }.bind(this));

    // insert all childs/dropitems by subMenuSelector into helper subcontainer
    var linearSelector = (this.options.linearSubMenu ? '> li > ' : '');
    this.menu.getElements(linearSelector+this.options.subMenuSelector).each(function(item, index){
      item.addEvent('click', function(e){
        e.stopPropagation();
      });

      var helperClasses = ['dropdownHelperContainer'];
      // add additional helper classes for more layouting
      item.get('class').split(' ').each(function(c){
        helperClasses.push(c.trim()+'-Helper');
      });

      new Element('div', {'class': helperClasses.join(' ')}).inject(subMenusContainer).grab(item);
    });

    // parse links by linksSelector (after inject into helper container)
    links.each(function(item,index){
      if (item.retrieve('childMenu')) {
        // change the childMenu from submenu self into parent helper container
        item.store('childMenu', item.retrieve('childMenu').getParent('div'));

        // add to allSubMenus array
        this.subMenus.include(item.retrieve('childMenu'));

        // store the parentSubMenus
        item.store('parentSubMenus',item.retrieve('parentLinks').retrieve('childMenu'));

        // store a subcontainer helper as reference
        item.store('subMenusContainer', subMenusContainer);

        // WAI-ARIA
        item.set('aria-haspopup', 'true');
        if(!item.get('id')){
          item.set('id', 'aria-dip_' + String.uniqueID());
        }

        // create a JYAML.DropdownDropitem
        new JYAML.DropdownDropitem(item, this);
        /////////////////////////////////
      } else if(item.retrieve('isRootMenu')) {
        item.addEvents({
          'mouseenter': function(){
            this.hideSubMenus();
            myJYAMLDropdownMenusGlobal.hideAllOtherMenus(this);
          }.bind(this),

          'focus': function(){
            this.hideSubMenus();
            myJYAMLDropdownMenusGlobal.hideAllOtherMenus(this);
          }.bind(this),

          'keydown' : function(e){
            // accessible features
            if (e.key=='up' || e.key=='down' || e.key=='left' || e.key=='right') {
              e.stop();
            }

            var parent = item.getParent('li');
            var prev = parent.getPrevious('li');
            var next = parent.getNext('li');

            if((this.options.orientation=='horizontal' && e.key=='left') || (this.options.orientation === 'vertical' && e.key=='up')) {
              if(prev) {
                prev.getFirst(this.options.linksSelector).focus();
              } else {
                // if first item focus the last item now
                parent.getParent().getLast('li').getFirst(this.options.linksSelector).focus();
              }
            }else if((this.options.orientation=='horizontal' && e.key=='right') || (this.options.orientation=='vertical' && e.key=='down')) {
              if(next){
                next.getFirst(this.options.linksSelector).focus();
              }else{
                // if last item focus the first item now
                parent.getParent().getFirst('li').getFirst(this.options.linksSelector).focus();
              }
            }
          }.bind(this)
        });
      }
    }.bind(this));

    // hide submenus onclick document
    $(document.body).addEvent('click', function(){
      // Note: stopPropagation attached on this.options.subMenuSelector
      this.hideSubMenus();
    }.bind(this));

    if(this.options.hideSubMenusOnAllMenus) {
      myJYAMLDropdownMenusGlobal.registerMenu(this);
    }

    myJYAMLDropdownMenusGlobal.menusLoaded = true;
  },

  hideSubMenus: function(){
    this.fireEvent('beforeHideAllSubMenus');

    clearTimeout(this.subMenusHideTimeout);
    this.subMenus.each(function(submenu){
      submenu.fireEvent('hide');
    });

    this.fireEvent('afterHideAllSubMenus');
  }
});

JYAML.DropdownDropitem = new Class({
  Extends: JYAML.Dropdown,
  options: {},

  global: null,
  item: null,
  childMenu: null,
  myEffect: null,

  //fallbackDirection: {x:null, y:null},

  stretchWidthComplete: false,

  initialize: function(item, parentClass){
    this.global = parentClass;
    this.setOptions(this.global.options);

    // initialize variables
    this.initShow = true;
    this.holdDirection = false;
    this.onExecHide = false;

    this.item = item;
    // stop if the item is not available
    if(!this.item) { return; }

    // initialize elements
    this.childMenu = this.item.retrieve('childMenu');
    this.isRootMenu = this.item.retrieve('isRootMenu');
    this.childMenu = this.item.retrieve('childMenu');
    this.childMenuElement = this.childMenu.getFirst();
    this.parentSubMenus = $$(this.item.retrieve('parentSubMenus'));
    this.parentLinks = $$(this.item.retrieve('parentLinks'));

    this.parentSubMenu = null;
    if(this.parentSubMenus && this.parentSubMenus.length) {
      this.parentSubMenu = $(this.parentSubMenus[0]);
    }
    if(this.parentSubMenu){
      this.parentSubMenu = this.parentSubMenu.retrieve('class');
    }

    if(this.childMenu.getElement('div').hasClass('dropitem-forceLinear')){
      this.options.linearSubMenu = true;
    }

    // WAI-ARIA
    this.childMenu.set('aria-hidden', 'true');
    this.childMenu.set('aria-labelledby', this.item.get('id'));

    // store this on bind for retrieve class recursive
    this.childMenu.store('class', this);
    this.item.store('class',this);

    // set default/starting status to closed
    this.childMenu.store('status','closed');

    this.fireEvent('beforeSubMenuInitialize', [item]);

    // add hide Event
    this.childMenu.addEvent('hide', function(){
      this.hideSubMenu();
    }.bind(this));

    // add show Event
    this.childMenu.addEvent('show', function(){
      this.showSubMenu();
    }.bind(this));

    this.childMenu.setStyles({
      'top': this.item.retrieve('childMenuCoordinates').top,
      'left': this.item.retrieve('childMenuCoordinates').left,
      'width': this.item.retrieve('childMenuCoordinates').width,
      //'height': this.item.retrieve('childMenuCoordinates').height,
      'bottom': 'auto',
      'right': 'auto'
    });

    // dectect directions by original css position
    var coChild = this.item.retrieve('childMenuCoordinates');
    var coItem = this.item.getCoordinates();

    if(coChild.left < coItem.left) {
      this.options.direction.x = 'left';
      this.childMenu.store('acPos', 'left');
    } else {
      this.options.direction.x = 'right';
      this.childMenu.store('acPos', 'right');
    }

    if(coChild.top < coItem.top) {
      this.options.direction.y = 'up';
      this.childMenu.store('acPos', 'up');
    } else {
      this.options.direction.y = 'down';
      this.childMenu.store('acPos', 'down');
    }


    /////////////////////

    // set default styles
    this.childMenu.setStyles({
      'display': 'block',
      'visibility': 'hidden',
      'overflow': 'hidden'
    });
    this.childMenuElement.setStyles({
      'display': 'block',
      'visibility': 'hidden',
      'opacity': this.options.opacity
    });
    // set/init dimensions
    this.setSubMenuDimensions();
    // and now we hide it absolute to ignore scrollbars in the browser
    this.childMenu.setStyle('display', 'none');

    // create effect handler
    if(this.options.effects && this.options.effects.length) {
      this.myEffect = new JYAML.DropdownFxMorph(this.childMenuElement, {
        duration: this.options.showDuration,
        transition: this.options.showTransition,
        link: 'cancel',
        onduration: (function(){
          this.setSubMenuPosition();
        }).bind(this)
      });
    }

    // required to foreground the menu of all other menus (defaults for all)
    this.item.retrieve('subMenusContainer').setStyle('z-index', 5000);

    // events for links without child menu
    var linksWithoutChildMenu = $(this.childMenu).getElements(this.options.linksSelector).filter(function(submenu, index){ return !submenu.retrieve('childMenu'); });
    linksWithoutChildMenu.each(function(item, index){
      item.addEvents({
        'mouseenter': function(){
          //this.childMenu.fireEvent('show');
          this.cancelHideSubMenus();
          this.hideChildSubMenus();
          myJYAMLDropdownMenusGlobal.hideAllOtherMenus(this.global);
        }.bind(this),

        'focus': function(){
          //this.childMenu.fireEvent('show');
          this.cancelHideSubMenus();
          this.hideChildSubMenus();
          myJYAMLDropdownMenusGlobal.hideAllOtherMenus(this.global);
        }.bind(this),

        'mouseleave': function(){
          this.cancelHideSubMenus();
          if(this.isRootMenu) {
            // this.hideSubMenus();
          }
        }.bind(this),

        'blur': function(){
          if(item.nodeName == 'DIV') {
            // allow tabbing if the item not a link
            return;
          }

          this.cancelHideSubMenus();
          this.hideSubMenus();
        }.bind(this),

        'keydown': function(e){
          // accessible features
          if (e.key=='up' || e.key=='down' || e.key=='left' || e.key=='right') {
            e.stop();
          }

          if(e.key=='tab' && item.nodeName != 'DIV' && !item.getParent('li').getElement(this.options.subMenuSelector)) {
            // fallback on press tab or shift+tab to prevent focus lost on current submenu
            e.stop();
            e.key = (e.shift ? 'left' : 'right');
          }

          var parent = item.getParent('li');
          var prev = parent.getPrevious('li');
          var next = parent.getNext('li');

          var parentLinks = item.retrieve('parentLinks');
          var parentLink = null;
          if(parentLinks && parentLinks.length) {
            parentLink = parentLinks[0];
          }

          var menuPosition = this.childMenu.retrieve('acPos');
          var jumpToParent = false;

          if(parentLink && parentLink.retrieve('isRootMenu')) {
            jumpToParent = true;
          } else {
            // if reverse direction jump to parent
            if((e.key=='left' && menuPosition=='right') || (e.key=='right' && menuPosition=='left')) {
              jumpToParent = true;
            } else if((e.key=='up' && menuPosition=='down') || (e.key=='down' && menuPosition=='up')) {
              jumpToParent = true;
            }
          }

          if(e.key=='left' || e.key=='up') {
            if(prev) {
              prev.getFirst(this.options.linksSelector).focus();
            } else {
              if(jumpToParent) {
                this.item.focus();
              } else {
                // if first item focus the last item now
                parent.getParent().getLast('* '+this.options.linksSelector.replace(',', ',* ')).focus();
              }
            }
          } else if(e.key=='right' || e.key=='down') {
            if(next){
              next.getFirst(this.options.linksSelector).focus();
            }else{
              if(jumpToParent) {
                this.item.focus();
              } else {
                // if last item focus the first item now
                parent.getParent().getFirst('* '+this.options.linksSelector.replace(',', ',* ')).focus();
              }
            }
          }
        }.bind(this)
      });

    }, this);

    // events for the parent item

    this.childMenu.addEvents({
      'mouseenter': function(){
        this.cancelHideSubMenus();
        // foreground the menu of all other menus
        this.item.retrieve('subMenusContainer').setStyle('z-index', 5001);
      }.bind(this),

      'mouseleave': function(){
        // on hide set the menu on same level of all other menus
        // only foreground currently open menus
        this.item.retrieve('subMenusContainer').setStyle('z-index', 5000);

        this.hideSubMenus();
      }.bind(this)
    });


    // events for the parent item
    this.item.addEvents({
      'click': function(e){
        // do not hide submenus on click
        e.stopPropagation();
      }.bind(this),

      'mousemove': function(e){
        // trigger showSubMenu if the mouse cursor over the item on pageload
        if(myJYAMLDropdownMenusGlobal.initHoverDone === false) {
          this.showSubMenu();
          myJYAMLDropdownMenusGlobal.initHoverDone = true;
        }
      }.bind(this),

      'mouseenter': function(){
        myJYAMLDropdownMenusGlobal.initHoverDone = true; // disable mousemove on pageload if mouseenter before was executed
        this.cancelHideSubMenus();
        this.hideChildSubMenus();
        this.showSubMenu();
        myJYAMLDropdownMenusGlobal.hideAllOtherMenus(this.global);
      }.bind(this),

      'focus': function(){
        this.cancelHideSubMenus();
        this.hideChildSubMenus();
        this.showSubMenu();
        myJYAMLDropdownMenusGlobal.hideAllOtherMenus(this.global);
      }.bind(this),

      'mouseleave': function(){
        this.cancelHideSubMenus();
        if(this.isRootMenu) {
          this.hideSubMenus();
        }
      }.bind(this),

      'blur': function(){
        if(this.item.nodeName == 'DIV') {
          // allow tabbing if the item not a link
          return;
        }

        this.cancelHideSubMenus();
        this.hideSubMenus();
      }.bind(this),

      'keydown': function(e){
        // accessible features
        if (e.key=='up' || e.key=='down' || e.key=='left' || e.key=='right') {
          e.stop();
        }

        if(e.key=='tab' && !this.isRootMenu && this.item.nodeName != 'DIV' && !this.item.getParent('li').getElement(this.options.subMenuSelector)) {
          // fallback on press tab or shift+tab to prevent focus lost on current submenu
          e.stop();
          e.key = (e.shift ? 'left' : 'right');
        }

        var menuPosition = this.childMenu.retrieve('acPos');

        if(menuPosition == e.key) {
          var focusElement = null;

          if(e.key == 'down' || e.key == 'right') {
            focusElement = this.childMenu.getFirst('* '+this.options.linksSelector.replace(',', ',* '));
          } else if(e.key == 'up' || e.key == 'left') {
            focusElement = this.childMenu.getLast('* '+this.options.linksSelector.replace(',', ',* '));
          }

          if(focusElement) {
            focusElement.focus();
          }
        } else {
          var jumpToParent = false;

          if(this.parentSubMenu && this.parentSubMenu.isRootMenu) {
            jumpToParent = true;
          } else {
            // if reverse direction jump to parent
            if((e.key=='left' && menuPosition=='right') || (e.key=='right' && menuPosition=='left')) {
              jumpToParent = true;
            } else if((e.key=='up' && menuPosition=='down') || (e.key=='down' && menuPosition=='up')) {
              jumpToParent = true;
            }
          }

          var parent = this.item.getParent('li');
          var prev = parent.getPrevious('li');
          var next = parent.getNext('li');
          var stop = false;

          if(this.isRootMenu && (e.key=='up' || e.key=='down') && this.options.orientation=='horizontal') {
            stop = true;
          }
          if(this.isRootMenu && (e.key=='left' || e.key=='right') && this.options.orientation=='vertical') {
            stop = true;
          }

          if((e.key=='left' || e.key=='up') && !stop) {
            if(prev) {
              prev.getFirst(this.options.linksSelector).focus();
            } else {
              if(jumpToParent) {
                this.parentSubMenu.item.focus();
              } else {
                // if first item focus the last item now
                parent.getParent().getLast('* '+this.options.linksSelector.replace(',', ',* ')).focus();
              }
            }
          } else if((e.key=='right' || e.key=='down') && !stop) {
            if(next){
              next.getFirst(this.options.linksSelector).focus();
            }else{
              if(jumpToParent) {
                this.parentSubMenu.item.focus();
              } else {
                // if last item focus the first item now
                parent.getParent().getFirst('* '+this.options.linksSelector.replace(',', ',* ')).focus();
              }
            }
          }
        }
      }.bind(this)
    });

    this.fireEvent('afterSubMenuInitialize', [item]);
  },

  stretchWidthFistSubMenu:function(){
    if (!this.options.stretchWidthFistSubMenu || !this.isRootMenu || this.stretchWidthComplete){
      return;
    }

    var parentWidth = this.item.getSize().x;
    var doStretch = false;
    //var coChild = this.item.retrieve('childMenuCoordinates');

    $(this.childMenu).getElements(this.options.linksSelector).each(function(item, index){
      var itemWidth = item.getSize().x;

      if(itemWidth < parentWidth) {
        var newWidth = parentWidth;

        item.setStyle('width', newWidth);

        // cross-browser correction for padding and border offset
        if(item.getSize().x > parentWidth) {
          item.setStyle('width', newWidth - (item.getSize().x - parentWidth));
        }
        doStretch = true;
      }
    }.bind(this));

    if(doStretch) {
      this.childMenu.setStyle('width', 'auto');
      this.width = this.childMenu.getFirst().getSize().x;
    }
    this.stretchWidthComplete = true;
  },

  hideSubMenu: function() {
    if(this.childMenu.retrieve('status') === 'closed'){return;}

    this.fireEvent('beforeHideSubMenu');
    this.onExecHide = true;

    // on hide set the menu on same level of all other menus
    // only foreground currently open menus {@link showSubMenu}
    if(this.isRootMenu) {
      this.item.retrieve('subMenusContainer').setStyle('z-index', 5000);
    }

    if(this.options.effects && this.options.effects.length) {
      var effectStyles = {};

      // set hide transaction
      this.myEffect.options.transition = this.options.hideTransition;
      this.myEffect.options.duration = this.options.hideDuration;

      Object.each(this.options.effects, function(effect){
        effect = ''+effect.trim().toLowerCase();

        var addEffects = this.fireEvent('addEffectOnHideSubMenu', [effect, effectStyles, this.myEffect]) || {};
        if(addEffects !== this && typeof addEffects == 'object') {
          Object.append(effectStyles, addEffects);
        }

        switch (effect) {
          case 'slide':
          case 'fullslide':
            if(this.isRootMenu && this.options.orientation == 'horizontal') {
              if(this.options.direction.y == 'down') {
                Object.append(effectStyles, {'margin-top': -this.height});
              } else if (this.options.direction.y == 'up') {
                Object.append(effectStyles, {'margin-top': this.height});
              }
              if(effect=='fullslide') {
                if(this.options.direction.x == 'left') {
                  Object.append(effectStyles, {'margin-left': this.width});
                } else if (this.options.direction.x == 'right') {
                  Object.append(effectStyles, {'margin-left': -this.width});
                }
              }
            } else if(!this.isRootMenu || this.options.orientation == 'vertical') {
              if(this.options.direction.x == 'left') {
                Object.append(effectStyles, {'margin-left': this.width});
              } else if (this.options.direction.x == 'right') {
                Object.append(effectStyles, {'margin-left': -this.width});
              }
              if(effect=='fullslide') {
                if(this.options.direction.y == 'down') {
                  Object.append(effectStyles, {'margin-top': -this.height});
                } else if (this.options.direction.y == 'up') {
                  Object.append(effectStyles, {'margin-top': this.height});
                }
              }

              if(this.options.orientation == 'vertical' && this.options.direction.y == 'up') {
                // nothing else
              }
            }
            break;
          case 'fade':
            Object.append(effectStyles, {'opacity': 0});
            break;
        }
      }, this);

      this.myEffect.start(effectStyles).chain(function(){
        this.childMenu.setStyle('display', 'none');
        this.onHideSubMenu();
      }.bind(this));

    } else {
      this.childMenu.setStyle('display', 'none');
      this.onHideSubMenu();
    }

    // remove hover class
    this.item.removeClass(this.options.hoverClass);
    this.item.getParent('li').removeClass(this.options.hoverClass);

    this.childMenu.store('status','closed');
  },

  onHideSubMenu: function(){
    // WAI-ARIA
    this.childMenu.set('aria-hidden', 'true');

    this.onExecHide = false;
    this.fireEvent('afterHideSubMenu');
  },

  hideSubMenus: function(){
    this.global.fireEvent('beforeHideAllSubMenus');

    clearTimeout(this.global.subMenusHideTimeout);
    this.global.subMenusHideTimeout = (function(){
      clearTimeout(this.subMenusHideTimeout);
      this.global.subMenus.each(function(submenu){
        submenu.fireEvent('hide');
      });
    }).bind(this).delay(this.options.hideDelay);

    this.global.fireEvent('afterHideAllSubMenus');
  },

  hideChildSubMenus: function() {
    this.fireEvent('beforeHideChildSubMenus');

    if(!this.item.retrieve('childSubMenus')){
      this.item.store('childSubMenus', $$(this.global.subMenus.filter(function(item){
        return !this.item.retrieve('parentSubMenus').contains(item) && item != this.childMenu;
      }.bind(this)) ));
    }
    this.parentSubMenus.fireEvent('show');
    this.item.retrieve('childSubMenus').fireEvent('hide');

    this.fireEvent('afterHideChildSubMenus');
  },

  cancelHideSubMenus: function(){
    clearTimeout(this.global.subMenusHideTimeout);
  },

  showSubMenu: function(){
    if(this.childMenu.retrieve('status') === 'open'){return;}

    this.onExecHide = false;
    this.fireEvent('beforeShowSubMenu');

    // foreground the menu of all other menus
    this.item.retrieve('subMenusContainer').setStyle('z-index', 5001);

    // set width/height
    this.setSubMenuDimensions();

    // set position always new on show
    this.setSubMenuPosition();

    // add hover class
    this.item.addClass(this.options.hoverClass);
    this.item.getParent('li').addClass(this.options.hoverClass);

    if(this.options.effects && this.options.effects.length) {
      var effectStyles = {};

      // set show transaction
      this.myEffect.options.transition = this.options.showTransition;
      this.myEffect.options.duration = this.options.showDuration;

      Object.each(this.options.effects, function(effect){
        effect = ''+effect.trim().toLowerCase();

        var addEffects = this.fireEvent('addEffectOnShowSubMenu', [effect, effectStyles, this.myEffect]) || {};
        if(addEffects !== this && typeof addEffects == 'object') {
          Object.append(effectStyles, addEffects);
        }

        switch (effect){
          case 'slide':
          case 'fullslide':

            if(this.isRootMenu && this.options.orientation == 'horizontal') {
              if(this.options.direction.y == 'down') {
                if(this.initShow) { this.childMenuElement.setStyle('margin-top', -this.height); }
                Object.append(effectStyles, {'margin-top': 0});
              } else if (this.options.direction.y == 'up') {
                if(this.initShow) { this.childMenuElement.setStyle('margin-top', this.height); }
                Object.append(effectStyles, {'margin-top': 0});
              }
              if(effect=='fullslide') {
                if(this.options.direction.x == 'left') {
                  if(this.initShow) { this.childMenuElement.setStyle('margin-left', this.width); }
                  Object.append(effectStyles, {'margin-left': 0});
                } else if (this.options.direction.x == 'right') {
                  if(this.initShow) { this.childMenuElement.setStyle('margin-left', -this.width); }
                  Object.append(effectStyles, {'margin-left': 0});
                }
              }
            } else if(!this.isRootMenu || this.options.orientation == 'vertical') {
              if(this.options.direction.x == 'left') {
                if(this.initShow) { this.childMenuElement.setStyle('margin-left', this.width); }
                Object.append(effectStyles, {'margin-left': 0});
              } else if (this.options.direction.x == 'right') {
                if(this.initShow) { this.childMenuElement.setStyle('margin-left', -this.width); }
                Object.append(effectStyles, {'margin-left': 0});
              }
              if(effect=='fullslide') {
                if(this.options.direction.y == 'down') {
                  if(this.initShow) { this.childMenuElement.setStyle('margin-top', -this.height); }
                  Object.append(effectStyles, {'margin-top': 0});
                } else if (this.options.direction.y == 'up') {
                  if(this.initShow) { this.childMenuElement.setStyle('margin-top', this.height); }
                  Object.append(effectStyles, {'margin-top': 0});
                }
              }
              if(this.options.orientation == 'vertical' && this.options.direction.y == 'up') {
                // nothing else
              }
            }
            break;
          case 'fade':
            if(this.initShow) {
              // it is important to use 0.01, because IE dont show the submenu on first time if its 0
              this.childMenuElement.setStyle('opacity', 0.01);
            }
            Object.append(effectStyles, {'opacity': this.options.opacity});
            break;
        }
      }, this);

      this.childMenu.setStyles({
        'display':'block',
        'visibility':'visible'
      });
      this.childMenuElement.setStyles({
        'display':'block',
        'visibility':'visible'
      });

      this.myEffect.start(effectStyles).chain(function(){
        this.fireEvent('afterShowSubMenu');
        this.setSubMenuPosition(); // finaly set the position of the child menu if the effect is complete
      }.bind(this));

    } else {
      this.childMenu.setStyles({
        'display':'block',
        'visibility':'visible'
      });
      this.childMenuElement.setStyles({
        'display':'block',
        'visibility':'visible'
      });

      this.fireEvent('afterShowSubMenu');
    }

    // WAI-ARIA
    this.childMenu.set('aria-hidden', 'false');

    if(this.initShow) { this.initShow = false; }
    this.childMenu.store('status','open');
  },

  setSubMenuDimensions: function(){
    // if(!this.width || !this.height || this.initShow){
    if(!this.width || !this.height || this.options.linearSubMenu){
      this.childMenu.setStyle('display', 'block');

      if(this.options.linearSubMenu && this.isRootMenu && this.options.orientation == 'horizontal') {
        this.childMenuElement.setStyle('width', $(this.options.id).getCoordinates().width);
      }

      this.width = this.childMenuElement.getSize().x;
      this.height = this.childMenuElement.getSize().y;

      this.stretchWidthFistSubMenu();

      this.childMenu.setStyle('width', this.width);
      this.childMenu.setStyle('height',this.height);
      this.childMenuElement.setStyle('width',this.width);

      this.childMenu.setStyle('display', 'none');
    }
  },

  setSubMenuPosition: function(){
    //return;
    if(this.onExecHide) {
      // do not set the position if the menu is currently on hiding
      return;
    }

    // this.fireEvent('beforePositionSubMenu');

    // inherit direction from parent menu
    if(!this.holdDirection && this.parentSubMenu && !this.isRootMenu && !this.parentSubMenu.isRootMenu) {
      this.options.direction.x = this.parentSubMenu.options.direction.x;
      this.options.direction.y = this.parentSubMenu.options.direction.y;
    }

    if(this.isRootMenu && this.options.orientation == 'horizontal') {
      if(this.options.direction.y == 'down') {
        this.childMenu.setStyles({
          'top': this.item.getCoordinates().bottom
        });
        this.childMenu.store('acPos', 'down');
      } else if (this.options.direction.y == 'up') {
        this.childMenu.setStyles({
          'top': (this.item.getCoordinates().top - this.height)
        });
        this.childMenu.store('acPos', 'up');
      }
      if(this.options.direction.x == 'right') {
        this.childMenu.setStyles({
          'left': this.item.getCoordinates().left
        });
      } else if (this.options.direction.x == 'left') {
        this.childMenu.setStyles({
          'left': this.item.getCoordinates().right - this.width
        });
      }

      if(this.options.linearSubMenu) {
        this.childMenu.setStyles({
          'left': $(this.options.id).getCoordinates().left
        });
      } else {
        if(this.options.moveHorizPosOutOfViewport){
          if(!this.inViewportRange(this.childMenu, 'right')){
            var diff = this.childMenu.getCoordinates().right - ($(window).getSize().x + $(window).getScroll().x);
            var left = this.childMenu.getCoordinates().left - diff;
            if(left < 0){ left = 0; }
            this.childMenu.setStyles({
              'left': left
            });
          } else if(!this.inViewportRange(this.childMenu, 'left')){
            // TODO: check if required for RTL read direction?
          }
        }
      }

    } else if(!this.isRootMenu || this.options.orientation == 'vertical') {
      if(this.options.direction.x == 'left') {
        this.childMenu.setStyles({
          'top': this.item.getCoordinates().top,
          'left': this.item.getCoordinates().left - this.width
        });
        this.childMenu.store('acPos', 'left');

        if(!this.inViewportRange(this.childMenu, 'left')) {
          // reverse direction if outside of viewport
          this.options.direction.x = 'right';
          this.holdDirection = true;
          this.childMenu.setStyles({
            'top': this.item.getCoordinates().top,
            'left': this.item.getCoordinates().right
          });
          //this.fallbackDirection.x = 'left';
          this.childMenu.store('acPos', 'right');
        }
      } else if (this.options.direction.x == 'right') {
        this.childMenu.setStyles({
          'top': this.item.getCoordinates().top,
          'left': this.item.getCoordinates().right
        });
        this.childMenu.store('acPos', 'right');

        if(!this.inViewportRange(this.childMenu, 'right')) {
          // reverse direction if outside of viewport
          this.options.direction.x = 'left';
          this.childMenu.setStyles({
            'top': this.item.getCoordinates().top,
            'left': this.item.getCoordinates().left - this.width
          });
          //this.fallbackDirection.x = 'right';
          this.childMenu.store('acPos', 'left');
          this.holdDirection = true;
        }
      }

      if(this.options.direction.y == 'up') {
        this.childMenu.setStyles({
          'top': (this.item.getCoordinates().bottom - this.height)
        });
      }
    }

    // this.fireEvent('afterPositionSubMenu');
  },

  inViewportRange: function(element, position){
    if(!this.options.reverseDirectionOutOfViewport) {
      // if the feature disabled return always true(=in range)
      return true;
    }

    switch (position){
      case 'bottom':
        return ($(element).getCoordinates().bottom <= ($(window).getSize().y + $(window).getScroll().y));
        break;
      case 'top':
        return ($(element).getCoordinates().top >= $(window).getScroll().y);
        break;
      case 'right':
        return ($(element).getCoordinates().right <= ($(window).getSize().x + $(window).getScroll().x));
        break;
      case 'left':
        return ($(element).getCoordinates().left >= $(window).getScroll().x);
        break;
      default:
        return (this.inViewportRange(element, 'right') && this.inViewportRange(element, 'left') && this.inViewportRange(element, 'bottom') && this.inViewportRange(element, 'top'));
        break;
    }
  }
});

JYAML.DropdownFxMorph = new Class({
  Extends: Fx.Morph,

  compute: function(from, to, delta){
    if(this.options.onduration) {
      this.options.onduration();
    }

    return this.parent(from, to, delta);
  }
});

