var FadeMenu = new Class({    initialize: function fadeMenu(elem,options) {        // Retrieve the element properly        this.parentElem = $(elem);        if($type(this.parentElem) != 'element') {throw new Error('invalid element or id passed as first argument');}        // Add the options        this.options = new Abstract({ // Defaults            fadeRecursive: true,            fadeInSpeed: 150,            fadeOutSpeed: 100,            fadeOutDelay: 300,            ignoreClass: false        });        if($type(options) == 'object') {this.options.extend(options);}        // Retrieve the child items        this.menuItems = this.parentElem.getChildren().filterByTag('li');        // Setup all sub menus        for(var i=0; i<this.menuItems.length; i++) {            // Find this item's sub menu            var subMenu = this.menuItems[i].getElement('ul');            // If we have a sub menu, set it up            if(                $type(subMenu) == 'element' // Make sure it's an element                && !($defined(this.ignoreClass) && subMenu.className != this.ignoreClass) // And that it doesn't have the "ignoreClass"            ) {                this.menuItems[i].subMenu = subMenu;                this.setupSubMenu(subMenu);                subMenu.create(this.options);            }        }    },    setupSubMenu: function(subMenu) {        if($type(subMenu) == 'element') {            new Abstract(subMenu).extend({                create: function(options) {                    // Get options                    this.options = options;                    // Retrieve elements                    this.parentElem = this.getParent();                    // Make sure the element and all it's items are hidden                    this.menuItems().setOpacity(0);                    this.setOpacity(0);                    // Create new events for fadein and fadeout                    this.addEvent('fadein',this.showMenu);                    this.addEvent('fadeout',this.hideMenu);                    // Add mouseenter and mouseleave events to the sub menu                    var thisMenu = this;                    this.parentElem.addEvent('mouseenter',function(evt) {thisMenu.latestEvent = 'mouseenter'; thisMenu.fireEvent('fadein',evt)});                    this.parentElem.addEvent('mouseleave',function(evt) {thisMenu.latestEvent = 'mouseleave'; thisMenu.fireEvent('fadeout',evt,thisMenu.options.fadeOutDelay)});                                        // If recursive, run fadeMenu on this list                    if(this.options.fadeRecursive) {new FadeMenu(this,this.options)};                },                showMenu: function(evt) {                    if(this.latestEvent == 'mouseleave') {return false;}                    // Show each item                    this.fadeInQueue = this.menuItems(); // get our menu items                    this.setOpacity(1); // Make sure the list is visible                    this.fadeInItems();                },                hideMenu: function(evt) {                    if(this.latestEvent == 'mouseenter') {return false;}                    // Hide all items                    this.fadeOutQueue = this.menuItems(); // get out menu items                    this.fadeOutItems();                },                fadeInItems: function() {                    // Get the list element                    var thisMenu;                    if(this.element) {thisMenu = this.element.getParent();}                    else {thisMenu = this;}                    // If there are still menu items left, and we're still fading in, fade in the first one                    if(thisMenu.fadeInQueue.length > 0 && thisMenu.latestEvent == 'mouseenter') {                        thisItem = thisMenu.fadeInQueue.shift();                        // Stop any fade outs going on                        if($defined(thisItem.fadeout)) {thisItem.fadeout.stop();}                        // Check it needs fading in - otherwise move onto the next item                        if(thisItem.getStyle('opacity') < 1) {                            // Fade this item, then chain on to the next                            thisItem.fadein = new Fx.Style(thisItem, 'opacity', {duration: thisMenu.options.fadeInSpeed}).start(thisItem.getStyle('opacity'),0.9999).chain(thisMenu.fadeInItems);                        } else {thisMenu.fadeInItems();}                    } else {return false;}                },                fadeOutItems: function() {                    // Get the list element                    var thisMenu;                    if(this.element) {thisMenu = this.element.getParent();}                    else {thisMenu = this;}                    // If there are still menu items left, fade out the last one                    if(thisMenu.fadeOutQueue.length > 0) {                        // If we are still fading out...                        if(thisMenu.latestEvent == 'mouseleave') {                            thisItem = thisMenu.fadeOutQueue.pop();                            // Stop any fade ins going on                            if($defined(thisItem.fadein)) {thisItem.fadein.stop();}                            // Check it needs fading out - otherwise move on                            if(thisItem.getStyle('opacity') > 0) {                                // Fade this item, then chain on to the next                                thisItem.fadeout = new Fx.Style(thisItem, 'opacity', {duration: thisMenu.options.fadeOutSpeed}).start(thisItem.getStyle('opacity'),0).chain(thisMenu.fadeOutItems);                            } else {thisMenu.fadeOutItems();}                        } else {return false;}                    } else {                        if(thisMenu.latestEvent == 'mouseleave') {thisMenu.setOpacity(0);}                    }                },                menuItems: function() {return this.getChildren().filterByTag('li')}            });        }    }
    });

