﻿(function ($) {

    //Agrego otras funciones que voy a utilizar.
    $.fn.completeHeight = function (borders) {
        var _this = $(this),
        paddingTop = 0,
        paddingBottom = 0,
        h = _this.height(),
        cssHeight = parseInt(_this.css('height')),
        borderTop = 0,
        borderBottom = 0;

        if (!isNaN(parseInt(_this.css('paddingTop'))))
            paddingTop = parseInt(_this.css('paddingTop'));

        if (!isNaN(parseInt(_this.css('paddingBottom'))))
            paddingBottom = parseInt(_this.css('paddingBottom'));

        if (h == 0 && !isNaN(cssHeight) && cssHeight != 0)
            h = cssHeight;

        if (borders) {
            if (!isNaN(parseInt(_this.css('borderTopWidth'))))
                borderTop = parseInt(_this.css('borderTopWidth'));
            if (!isNaN(parseInt(_this.css('borderBottomWidth'))))
                borderBottom = parseInt(_this.css('borderBottomWidth'));
        }

        return h + paddingTop + paddingBottom + borderTop + borderBottom;
    };

    $.fn.completeWidth = function (borders) {
        var _this = $(this),
        paddingLeft = 0,
        paddingRight = 0,
        w = _this.width(),
        cssWidth = parseInt(_this.css('width')),
        borderLeft = 0,
        borderRight = 0;

        if (!isNaN(parseInt(_this.css('paddingLeft'))))
            paddingLeft = parseInt(_this.css('paddingLeft'));

        if (!isNaN(parseInt(_this.css('paddingRight'))))
            paddingRight = parseInt(_this.css('paddingRight'));

        if (w == 0 && !isNaN(cssWidth) && cssWidth != 0)
            w = cssWidth;

        if (borders) {
            if (!isNaN(parseInt(_this.css('borderLeftWidth'))))
                borderLeft = parseInt(_this.css('borderLeftWidth'));
            if (!isNaN(parseInt(_this.css('borderRightWidth'))))
                borderRight = parseInt(_this.css('borderRightWidth'));
        }

        return w + paddingLeft + paddingRight + borderLeft + borderRight;
    };

    $.fn.blindToggle = function (speed, callback) {
        var _this = $(this),
            h = _this.height(),
            cssHeight = parseInt(_this.css('height')),
            marginTop = parseInt(_this.css('marginTop')),
            margenMenu = 5;

        if (h == 0 && !isNaN(cssHeight) && cssHeight != 0)
            h = cssHeight;

        if (parseInt(_this.css('marginTop')) < 0)
            marginTop += h + margenMenu;
        else
            marginTop -= h + margenMenu;

        return _this.animate({ marginTop: marginTop }, speed, undefined, callback);
    };

    $.fn.menuPrincipal = function (options) {
        ///	<summary>
        ///     Convierte un ul común en un menu.
        ///	</summary>
        ///	<param name="opciones" type="event object">
        ///     Configuración
        ///	</param>
        ///	<returns type="jQuery">
        ///     jQuery Object
        /// </returns>

        // extiende las opciones con las default
        options = $.extend({
            headerItemsClass: "header",
            showSpeed: 300,
            timeToCloseWhenMouseOut: 500
        }, options);

        //Referencias y otras variables:
        var _menu = $(this),
            headerElements = _menu.find(">li"), //Todos los li de primer nivel
            parentElements = _menu.find("li:has(ul)"), //Todos los li que tienen un submenu
            openingMenu = false, //Indica si en este momento se está abriendo un menu.
            closingMenu = false, //Indica si en este momento se está cerrando un menu.
        //Funciones:
            openMenu = function (headerElement) {
                var _this = $(this),
                    containerBox = _this.find(">div"),
                    menu = containerBox.find(">ul");

                if (openingMenu || closingMenu)
                    return;

                //Cerrar todos los menues abiertos (del mismo nivel) que no sean este.
                _this.siblings().each(function () {
                    closeMenu.call($(this), headerElement);
                });

                //Asignar clase de over al li
                _this.addClass("selected");

                if (!headerElement) {
                    //Si no es header tengo que agrandar todos los div.containerBox padres.
                    var hTotal = containerBox.height() + _this.position().top + 1;
                    containerBox.parents("div.containerBox").each(function () {
                        var container = $(this),
                            containerH = container.height();

                        if (containerH < hTotal)
                            container.height(hTotal);
                    });
                }

                //Verificar si tengo que mover el menu para que entre en la pantalla.
                var offsetWidth = $(window).width(), //window.document.body.offsetWidth,
                    hScrolled = $(window).scrollLeft(),
                    margin;

                if (headerElement) {
                    if ((_this.position().left + containerBox.width() > offsetWidth + hScrolled) && (_this.position().left < hScrolled))
                    //El menu no entra para ninguno de los dos lados, no hago nada.
                        margin = 0;
                    else if (_this.position().left + containerBox.width() > offsetWidth + hScrolled)
                    //El menu no entra para la derecha, lo desplazo a la izquierda.
                        margin = Math.max(offsetWidth + hScrolled - (_this.position().left + containerBox.width() + 2), hScrolled - (_this.position().left + 2));
                    else if (_this.position().left < hScrolled)
                    //El menu no entra para la izquierda, lo desplazo a la derecha.
                        margin = hScrolled - (_this.position().left + 2);
                    else
                        margin = 0;

                    containerBox.css("marginLeft", margin);
                }


                //Abrir este menu.
                openingMenu = true;

                if (!containerBox.is(":visible"))
                    containerBox.css("visibility", "visible").css("display", "block");

                if (parseInt(menu.css("marginTop")) < 0)
                    menu.blindToggle(options.showSpeed, function () {
                        openingMenu = false;
                    });
                else
                    openingMenu = false;

                //Usamos los eventos Over y out para mantiener un flag actualizado segun si el mouse esta adentro del menu o no.
                var mouseIntoMenu = true, //El mouse empieza en el li padre del menu que se está abriendo que lo contamos como adentro del menu.
                    tiempoFuera = null, //Cada vez que el mouse sale se inicia un timer, si el mouse no vuelve y termina el timer se cierra el menu.
                    mouseOutFn = function () {
                        mouseIntoMenu = false;
                        if (tiempoFuera == null) {
                            tiempoFuera = setTimeout(function () {
                                //Se cumplió el tiempo necesario con el mouse afuera como para cerrar el menu.
                                closeMenu.call(_this, headerElement);
                            }, options.timeToCloseWhenMouseOut);
                        }
                    },
                    mouseInFn = function () {
                        mouseIntoMenu = true;
                        clearTimeout(tiempoFuera); //Borro el timer porque el mouse volvió a entrar al menu.
                        tiempoFuera = null;
                    };

                //Le asigno las funciones al li porque incluye tanto el li mismo (el li "padre") como el menu (el ul).
                _this.hover(mouseInFn, mouseOutFn);

                //Si se hace un click en cualquier lugar del body y el mouse está afuera del menu, se cierra el menu.
                //Sirve solo para los headerElements porque si sucede esto se tiene que cerrar todo.
                if (headerElement) {
                    //El setTimeout es para que el evento no se ejecute con este mismo click que está abriendo el menu.
                    setTimeout(function () {
                        //Este evento se va a eliminar en el close del menu asique le pongo un namespace.
                        var namespace = "." + "evClickParaCerrarMenuPrincipalEq" + headerElements.index(_this);
                        $("body").bind("click" + namespace, function () {
                            if (!mouseIntoMenu) {
                                clearTimeout(tiempoFuera); //Borro el timer porque el ya se esta cerrando el menu, no se necesita mas.
                                tiempoFuera = null;
                                closeMenu.call(_this, headerElement);
                            }
                        });
                    }, 1);
                }
            },
            closeMenu = function (headerElement) {
                var _this = $(this),
                    containerBox = _this.find(">div"),
                    menu = containerBox.find(">ul");

                if (openingMenu || closingMenu)
                    return;

                //Reviso si hay menues "hijos" para cerrar.
                _this.find("li:has(ul:visible)").each(function () {
                    closeMenu.call($(this), false);
                });

                closingMenu = true;

                //Quitar clase de over al li
                _this.removeClass("selected");

                if (containerBox.is(":visible"))
                    containerBox.css("visibility", "hidden").css("display", "none"); //Ocultar el menu.

                //Volverlo a su pocisión "original" (cuando está cerrado).
                if (parseInt(menu.css("marginTop")) >= 0)
                    menu.blindToggle(1, function () {
                        closingMenu = false;
                    });
                else
                    closingMenu = false;

                if (headerElement) {
                    //Elimino el evento click del body.
                    var namespace = "." + "evClickParaCerrarMenuPrincipalEq" + headerElements.index(_this);
                    $("body").unbind("click" + namespace);
                }
            },
            wrapDiv = function (w) {
                var _this = $(this),
                    k_marginTop = 2,
                    k_marginLeft = parseInt(_this.css("marginLeft")),
                    k_top = "";

                if ($.browser.msie && parseInt($.browser.version) >= 9)
                    k_marginTop++;

                if (!_this.parent().hasClass(options.headerItemsClass)) {
                    k_marginTop = 0;
                    k_marginLeft = _this.parent().parent().width();
                    k_top = "top: 0px;";
                }

                var h = _this.completeHeight(true),
                    marginTop = 0,
                    marginBottom = 0,
                    marginRight = 0,
                    marginLeft = 0;

                w += _this.completeWidth(true) + 2;

                if (!isNaN(parseInt(_this.css('marginTop'))))
                    marginTop = parseInt(_this.css('marginTop')) + k_marginTop;

                if (!isNaN(parseInt(_this.css('marginBottom'))))
                    marginBottom = parseInt(_this.css('marginBottom'));

                if (!isNaN(parseInt(_this.css('marginRight'))))
                    marginRight = parseInt(_this.css('marginRight'));

                if (!isNaN(parseInt(_this.css('marginLeft'))))
                    marginLeft = k_marginLeft;

                var m = marginTop + "px " + marginRight + "px " + marginBottom + "px " + marginLeft + "px",
                    ulHeight = _this.height(),
                    zIndex = _this.css('zIndex');

                _this
                    .wrap("<div class=\"containerBox\" style=\"width:" + w + "px;height:" + h + "px;margin:" + m + ";" + k_top + ";z-index:" + zIndex + ";\"></div>")
                    .css('height', ulHeight)
                    .css('marginTop', 0)
                    .css('marginLeft', 0)
                    .css('left', 0);

                if (parseInt(_this.css('marginTop')) >= 0)
                    _this.blindToggle(1);
            },
            wrapUls = function () {
                //Tiene que ser recursivo y de esta forma para empezar desde los de mas adentro e ir subiendo jerarquicamente porque siempre el que esta haciendo el wrapDiv tiene que estar visible.
                var _this = $(this),
                    bigger = 0;

                if (window["wrapUlWidth"] == undefined)
                    window["wrapUlWidth"] = 0;

                _this.find(">li:has(ul)").each(function () {
                    var ul = $(this).find(">ul");
                    //Necesito que el menu este visible para poder obtener las medidas de width, height y margins correctamente.
                    if (ul.is(":hidden"))
                        ul.show();

                    wrapUls.call(ul);

                    if (ul.width() + wrapUlWidth > bigger)
                        bigger = ul.width() + wrapUlWidth;

                    wrapDiv.call(ul, wrapUlWidth);
                });
                wrapUlWidth = bigger;
            };

        parentElements.click(function (ev) {
            var _this = $(this);

            if (_this.hasClass("clickable")) {
                if (_this.find(">div >ul").is(":visible")) {
                    closeMenu.call(_this, _this.hasClass(options.headerItemsClass));
                } else {
                    openMenu.call(_this, _this.hasClass(options.headerItemsClass));
                }
            }
        });

        _menu.find("a").click(function (ev) {
            var _this = $(this),
                href = _this.attr("href");

            if (href != undefined && href != "#") {
                ev.stopPropagation();
                closeMenu.call(_this.closest(".header"), true);
            }
            else {
                ev.preventDefault();
            }

            return true;
        });

        _menu.find("li").hover(function () {
            var _this = $(this);

            if (!_this.hasClass("dont-select") && !_this.hasClass("separator"))
            //Asignar clase de over al li
                _this.addClass("selected");
        }, function () {
            var _this = $(this);

            //Quitar clase de over al li
            _this.removeClass("selected");
        }).click(function (ev) {
            var _this = $(this),
                a = _this.find(">a");

            if (a.attr("href") != undefined && a.attr("href") != "#")
                a.runClick();
        });

        wrapUls.call(_menu);

        parentElements.addClass("clickable");

        // retorna jQuery object de objeto
        return _menu;
    };

})(jQuery);
