$(function () {

    var menuGetAnimateContainer = function ($elm) {

        var $aniContainer = $elm.parent(".animate-container");

        if (!$aniContainer.length) {

            $elm.before("<div class='animate-container' />");
            $aniContainer = $elm.prev(".animate-container");
            $aniContainer.append($elm);

            $aniContainer.hide();

        }

        return $aniContainer;

    };

    var menuRemoveAnimateContainer = function ($elm) {

        var $aniContainer = $elm.parent(".animate-container");

        if ($aniContainer.length)
            $elm.unwrap();

    };

    var menuSlideDown = function ($menuItem, callback) {

        var $subMenu = $(">ul.sub, >.animate-container>ul.sub", $menuItem);

        if (!$subMenu.is(".open")) {

            var $aniContainer = menuGetAnimateContainer($subMenu);

            if (!$aniContainer.is(":visible")) {

                $aniContainer.height(0).show();
                $subMenu.show();

            }

            $subMenu.addClass("open");

            $aniContainer.stop(true).animate({

                height: $subMenu.data("height")

            }, {

                duration: 200,
                complete: function () {

                    if ($aniContainer.height() == $subMenu.data("height")) {

                        menuRemoveAnimateContainer($subMenu);

                        if (callback)
                            callback();

                    }

                }

            });

        }

    };

    var menuSlideUp = function ($menuItem, callback) {

        var $subMenu = $(">ul.sub, >.animate-container>ul.sub", $menuItem);

        if ($subMenu.is(".open")) {

            var $aniContainer = menuGetAnimateContainer($subMenu);

            if (!$aniContainer.is(":visible")) {

                $aniContainer.height($subMenu.data("height")).show();
                $subMenu.show();

            }

            $aniContainer.stop(true).animate({

                height: 0

            }, {

                duration: 100,
                complete: function () {

                    if ($aniContainer.height() == 0) {

                        $subMenu.removeClass("open").hide();

                        menuRemoveAnimateContainer($subMenu);

                        if (callback)
                            callback();

                    }

                }

            });

        }

    };

    var menuFadeItemsIn = function ($subMenu, callback) {

        if (!$subMenu.is(":visible"))
            $subMenu.css("opacity", 0).show();

        $subMenu.stop(true).animate({

            opacity: 1

        }, {

            duration: 100,
            complete: function () {

                if ($subMenu.css("opacity") == 1) {

                    $subMenu.css("opacity", "");

                    if (callback)
                        callback();

                }

            }

        });

    };

    var menuFadeItemsOut = function ($subMenu, callback) {

        $subMenu.stop(true).animate({

            opacity: 0

        }, {

            duration: 100,
            complete: function () {

                if ($subMenu.css("opacity") == 0) {

                    $subMenu.css("opacity", "").hide();

                    if (callback)
                        callback();

                }

            }

        });

    };

    var menuSlideOut = function ($menuItem, callback) {

        var $subMenu = $(">ul.sub, >.animate-container>ul.sub", $menuItem);

        if (!$subMenu.is(".open")) {

            var yDiff = $subMenu.data("offsetTop") - $menuItem.closest("ul.sub").offset().top;

            var $aniContainer = menuGetAnimateContainer($subMenu);
            var parentHeight = $menuItem.closest("ul.sub").outerHeight(true);
            var grow = $subMenu.data("height") > (parentHeight - yDiff);

            if (!$aniContainer.is(":visible")) {

                $aniContainer.height($subMenu.data("height")).width(0).show();
                $subMenu.show().css("opacity", 0);

                if (grow)
                    $aniContainer.height(parentHeight - yDiff);

            }

            $subMenu.addClass("open");

            $aniContainer.stop(true);

            $aniContainer.animate({

                width: $subMenu.data("width")

            }, {

                duration: 100,
                complete: function () {

                    if (!grow && $aniContainer.width() == $subMenu.data("width")) {

                        menuFadeItemsIn($subMenu, function () { menuRemoveAnimateContainer($subMenu); });

                        if (callback)
                            callback();

                    }

                }

            });

            if (grow) {

                $aniContainer.animate({

                    height: $subMenu.outerHeight(true)

                }, {

                    duration: 100,
                    complete: function () {

                        if (grow && $aniContainer.height() == $subMenu.data("height")) {

                            menuFadeItemsIn($subMenu, function () { menuRemoveAnimateContainer($subMenu); });

                            if (callback)
                                callback();

                        }

                    }

                });

            }

        }

    };

    var menuSlideIn = function ($menuItem, callback) {

        var $subMenu = $(">ul.sub, >.animate-container>ul.sub", $menuItem);

        if ($subMenu.is(".open")) {

            var yDiff = $subMenu.data("offsetTop") - $menuItem.closest("ul.sub").offset().top;

            var $aniContainer = menuGetAnimateContainer($subMenu);
            var parentHeight = $menuItem.closest("ul.sub").outerHeight(true);
            var shrink = $subMenu.data("height") > (parentHeight - yDiff);

            if (!$aniContainer.is(":visible"))
                $aniContainer.height($subMenu.data("height")).width($subMenu.data("width"));

            $aniContainer.show().stop(true);

            menuFadeItemsOut($subMenu, function () {

                if (shrink) {

                    $aniContainer.animate({

                        height: parentHeight - yDiff

                    }, {

                        duration: 100

                    });

                }

                $aniContainer.animate({

                    width: 0

                }, {

                    duration: 100,
                    complete: function () {

                        if ($aniContainer.width() == 0) {

                            menuRemoveAnimateContainer($subMenu);

                            $subMenu.removeClass("open");

                            if (callback)
                                callback();

                        }

                    }

                });

            });

        }

    };

    $("#menu").each(function () {

        var $mainMenu = $(">ul", $(this));
        var $mainMenuItem = $(">li:first", $mainMenu);

        var subOpen = false;

        $("ul.sub", $mainMenuItem).css("visibility", "hidden").show().each(function () {

            $(this).data("height", $(this).outerHeight(true)).data("width", $(this).width()).data("offsetTop", $(this).offset().top);

        }).hide().css("visibility", "");

        $mainMenuItem.mouseenter(function () {

            clearTimeout($mainMenuItem[0].hoverTimeout);

            if (!$mainMenuItem.is(".hover")) {

                $mainMenuItem.addClass("hover");
                menuSlideDown($mainMenuItem);

            }

        }).mouseleave(function () {

            clearTimeout($mainMenuItem[0].hoverTimeout);

            if ($mainMenuItem.is(".hover")) {

                $mainMenuItem[0].hoverTimeout = setTimeout(function () {

                    $mainMenuItem.removeClass("hover");

                    var $openSubMenus = $(">ul.sub.open ul.sub.open", $mainMenuItem);

                    if (!$openSubMenus.length) {

                        menuSlideUp($mainMenuItem);

                    } else {

                        var startIdx = -1;

                        for (var i = 0; i < $openSubMenus.length; i++) {

                            if (!$openSubMenus.eq(i).find("ul.sub.open").length) {

                                startIdx = i;
                                i = $openSubMenus.length;

                            }

                        }

                        var slideNextMenu = function (idx) {

                            menuSlideIn($openSubMenus.eq(idx).closest("li"), function () {

                                if (idx > 0)
                                    slideNextMenu(idx - 1);
                                else
                                    menuSlideUp($mainMenuItem);


                            });

                        };

                        slideNextMenu(startIdx);

                    }

                }, 750);

            }

        });

        $("ul.sub>li", $mainMenuItem).mouseenter(function () {

            var $menuItem = $(this);

            if (!$menuItem.is(".hover")) {

                $menuItem.addClass("hover");

                if ($menuItem.children("ul.sub").length) {

                    clearTimeout($menuItem[0].hoverTimeout);

                    $menuItem[0].hoverTimeout = setTimeout(function () {

                        menuSlideOut($menuItem);

                    }, 350);

                }

            }

        }).mouseleave(function (evt) {

            var $menuItem = $(this);

            $menuItem.removeClass("hover");

            if ($(">ul.sub, >.animate-container>ul.sub", $menuItem)) {

                clearTimeout($menuItem[0].hoverTimeout);

                if ($(evt.relatedTarget).closest("#menu").length)
                    menuSlideIn($menuItem);

            }

        });

    });

});
