var nav = { state: [], positions: { hidden: 0, hiding: 1, showing: 2, shown: 3 }, childAnimations: [], childTimeout: null};

nav.show = function(element, isClick)
{
	var panelObj = jQuery(".nav-panel", element).first();

    // show mask
	jQuery("> .nav-mask", element).css({display: "block"});

	// retrieve state for the element
	var navState = null;
	var index = null;
	for (var i = 0; i < nav.state.length; i++)
	{
		if (nav.state[i].element == element)
		{
			navState = nav.state[i];
			index = i;
			break;
		}
	}
	// if there's no state for the element, create one
	if (navState == null)
	{
		index = nav.state.length;
		var selectedElement = nav.getSelectedElementRecursive(panelObj.get(0));
		navState = {element: element, timeout: null, animation: null, position: nav.positions.hidden, selectedElement: selectedElement};
		nav.state.push(navState);
	    panelObj.css({top: -panelObj.height() + "px"});
	}

	// clear timeout
	if (navState.timeout != null)
	{
		clearTimeout(navState.timeout);
		navState.timeout = null;
	}

	// add selected class to anchor
	jQuery("> a", element).addClass("selected");

	// hide any showing panels that aren't this one
	for (var i = 0; i < nav.state.length; i++)
	{
		if (nav.state[i].element == element) continue;
		if (nav.state[i].position >= nav.positions.showing)
		{
			nav.hide(nav.state[i].element);
		}
	}

    if (navState.position == nav.positions.hidden)
    {
	    // if panel is hidden, set stuff up
	    
	    // un-pin all items
	    jQuery("li", element).removeClass("pinned");
	    
	    if (navState.selectedElement != null)
	    {
	        // if there's a default selected state, restore it
            nav.showSelectedElement(navState.selectedElement);
	    }
	    else
	    {
            // hide all second-generation items
            jQuery(".nav-panel > ul > li > a", element).removeClass("selected");
            jQuery(".nav-panel > ul > li > ul", element).css({display: "none"});
	    }
		panelObj.css(
			{
				opacity: 1,
				top: -panelObj.height() + "px"
			});
        jQuery(".close", element).css({display: isClick ? "block" : "none"});
    }
    else
    {
        if (isClick)
        {
            jQuery(".close", element).css({display: "block"});
        }
	    // if the panel is already showing or shown, there's nothing to do
	    if (navState.position >= nav.positions.showing) return;
        if (navState.position === nav.positions.hiding && navState.animation != null)
	    {
	        // if state of navigation is a hide animation, stop it
		    navState.animation.stop();
		    navState.animation = null;
	    }
    }


	// show the panel by sliding
	navState.animation = panelObj.animate(
		{
			top: 0,
			opacity: 1
		},
		"slow",
		function()
		{
			var thisObj = jQuery(this);

			// update state in nav.state
			var element = thisObj.closest("li").get(0);
			for (var i = 0; i < nav.state.length; i++)
			{
				if (nav.state[i].element != element) continue;
				nav.state[i].animation = null;
				nav.state[i].position = nav.positions.shown;
				break;
			}
			
			thisObj.css({opacity: "auto"});
		});

	// update the state
	navState.position = nav.positions.showing;
}

nav.preHide = function(element)
{
	// retrieve state from nav.state for element
	var navState = null;
	var index = null;
	for (var i = 0; i < nav.state.length; i++)
	{
		if (nav.state[i].element == element)
		{
			navState = nav.state[i];
			index = i;
			break;
		}
	}

    // if there is no state for the element, there's nothing to do
	if (navState == null) return;

	// clear timeout
	if (navState.timeout != null)
	{
		clearTimeout(navState.timeout);
		navState.timeout = null;
	}

    // if the panel is already hiding or hidden, there's nothing to do
	if (navState.position <= nav.positions.hiding) return;
	
    // if the close button is showing, then the nav was shown by click - don't hide automatically
	if (jQuery(".close", element).css("display") == "block") return;

	// set timeout
	navState.timeout = setTimeout(
		function()
		{
			nav.hide(element);
		}, 500);
}

nav.hide = function(element)
{
	// retrieve state from nav.state for element
	var navState = null;
	var index = null;
	for (var i = 0; i < nav.state.length; i++)
	{
		if (nav.state[i].element == element)
		{
			navState = nav.state[i];
			index = i;
			break;
		}
	}

    // if there is no state for the element, there's nothing to do
	if (navState == null) return;

	// clear timeout
	if (navState.timeout != null)
	{
		clearTimeout(navState.timeout);
		navState.timeout = null;
	}

	// if the panel is hiding or hidden, there's nothing to do
	if (navState == null || navState.position <= nav.positions.hiding) return;

	// remove selected class from anchor
	jQuery("> a", element).removeClass("selected");

	// if animation is in progress, stop it
	if (navState.position === nav.positions.showing && navState.animation != null)
	{
		navState.animation.stop();
		navState.animation = null;
	}

	// hide the panel by fading
	var panelObj = jQuery(".nav-panel", element).first();
	navState.animation = panelObj.animate(
		{
			opacity: 0
		},
		"fast",
		function()
		{
			var thisObj = jQuery(this);
			var itemObj = thisObj.closest("li");

			// update state in nav.state
			for (var i = 0; i < nav.state.length; i++)
			{
			    // update state object
				if (nav.state[i].element != itemObj.get(0)) continue;
				nav.state[i].animation = null;
				nav.state[i].position = nav.positions.hidden;
				break;
			}

			// hide mask
			itemObj.find("> .nav-mask").css({display: "none"});
		});

	// update the state
	navState.position = nav.positions.hiding;
}

nav.positionChildrenRecursive = function(element)
{
    var elementObj = jQuery(element);
    var offset = elementObj.nextAll("ul").length;
    var width = elementObj.width() + parseInt(elementObj.css("marginRight"), 10);
    var top = 0;
    var itemsObj = jQuery("> li", element);
    itemsObj.each(
        function()
        {
            var thisObj = jQuery(this);
            thisObj.css({top: top + "px"});
            var childListsObj = jQuery("> ul", this);
            childListsObj.css({display: "block"});
            childListsObj.each(
                function(index)
                {
                    //alert((offset + index + 1) * width);
                    jQuery(this).css({left: ((offset + index + 1) * width) + "px", top: -top + "px"});
	                nav.positionChildrenRecursive(this);
                });
            childListsObj.css({display: "none"});
            top += thisObj.find("> a").outerHeight();
        });
}

nav.hiliteElement = function(element)
{
    // find all item ancestors up to the panel element
    var elementsObj = jQuery(element);
    elementsObj = elementsObj.add(elementsObj.parentsUntil(".nav-panel")).filter("li");
    
    // at each generation, select the ancestor of the selected item, deselect its siblings
    elementsObj.each(
        function()
        {
            jQuery("> ul > li > a", this.parentNode.parentNode).removeClass("selected");
            jQuery("> a", this).addClass("selected");
        });
}

nav.preShowChildren = function(element, noAnim)
{
	// clear timeout
	if (nav.childTimeout != null)
	{
		clearTimeout(nav.childTimeout);
		nav.childTimeout = null;
	}

	// set timeout
	nav.childTimeout = setTimeout(
		function()
		{
			nav.showChildren(element, noAnim);
	//	}, 250);        }, 700);
}

nav.showChildren = function(element, noAnim)
{
	// clear timeout
	if (nav.childTimeout != null)
	{
		clearTimeout(nav.childTimeout);
		nav.childTimeout = null;
	}

    // if we've clicked on a tab, return true
    if (jQuery(element).hasClass("nav-tab")) return true;

    // if there are ongoing animations, stop them
    for (var i = 0; i < nav.childAnimations.length; i++)
    {
        nav.childAnimations[i].animation.stop();
    }
	nav.childAnimations = [];
    
    // remove hiliting on children
    jQuery("> ul > li > a", element).removeClass("selected");

    // remove pinning on children
    jQuery("> ul > li", element).removeClass("pinned");

    // hide grand-children
    jQuery("> ul > li > ul", element).css({display: "none"});

    var siblingsObj = jQuery("> ul > li", element.parentNode.parentNode);
    siblingsObj.each(
        function()
        {
            var childListsObj = jQuery("> ul", this);
            if (element != this)
            {
                // hide siblings' children
                childListsObj.css({display: "none"});
                return;
            }
            
            // if there are no children to display, or we've already displayed the children, there's nothing to do
            if (childListsObj.length == 0 ||
                (childListsObj.first().css("display") == "block" && childListsObj.first().css("opacity") == 1)) return;

            if (noAnim)
            {
                // show children
                childListsObj.css({display: "block", opacity: "auto"});
                return;
            }

            // fade in children
            childListsObj.css({display: "block", opacity: 0});
            childListsObj.each(
                function()
                {
                    var animation = jQuery(this).animate(
                            {
                                opacity: 1
                            },
                            "fast",
                            function()
                            {
                                jQuery(this).css({opacity: "auto"});
                                for (var i = 0; i < nav.childAnimations.length; i++)
                                {
                                    if (nav.childAnimations[i].element != this) continue;
                                    nav.childAnimations[i].element = null;
                                    nav.childAnimations.splice(i, 1);
                                    break;
                                }
                            });
                    nav.childAnimations.push({element: this, animation: animation});
                });
        });

    // return false for leaf nodes (allowing the link URL to be loaded)
    return jQuery("> ul", element).length > 0;

}

nav.getSelectedElementRecursive = function(element)
{
    var childElement = jQuery("> ul > li > a.selected", element).closest("li").get(0);
    if (childElement == null)
    {
        return null;
    }
    var descendantElement = nav.getSelectedElementRecursive(childElement);
    if (descendantElement != null)
    {
        return descendantElement;
    }
    return childElement;
}

nav.showSelectedElement = function(element)
{
    // find all item ancestors up to the panel element
    var elementsObj = jQuery(element);
    elementsObj = elementsObj.add(elementsObj.parentsUntil(".nav-panel")).filter("li");
    
    // at each generation, show the selected element, hide its sibling's children
    elementsObj.each(
        function()
        {
            jQuery("> ul > li > ul", this.parentNode.parentNode).css({display: "none"});
            jQuery("> ul", this).css({display: "block", opacity: "auto"});
            jQuery("> ul > li > a", this.parentNode.parentNode).removeClass("selected");
            jQuery("> a", this).addClass("selected");
        });
}

nav.doSearch = function(query)
{
    if (query == null) return;
    if (jQuery.trim(query) == "") return;
    document.location = "SearchPage.aspx?searchRequest=" + escape(query);
}

jQuery(document).ready(
	function()
	{
	    // add hover handler to tabs
	    var tabsObj = jQuery(".navigation > li > a");
	    tabsObj.hover(
			function()
			{
			    var element = jQuery(this).closest("li").get(0);
			    nav.show(element, false);
			},
			function()
			{
			    var element = jQuery(this).closest("li").get(0);
			    nav.preHide(element);
			});

	    // add click handler to tabs
	    tabsObj.click(
			function()
			{
			    var element = jQuery(this).closest("li").get(0);
			    nav.show(element, true);
			});

	    // add click handler to close buttons
	    var closeButtonsObj = jQuery(".navigation .close");
	    closeButtonsObj.click(
			function()
			{
			    var element = jQuery(this).closest("li").get(0);
			    nav.hide(element, true);
			});

	    // add hover handler to panels
	    var panelsObj = jQuery(".navigation .nav-panel");
	    panelsObj.hover(
			function()
			{
			    var element = jQuery(this).closest("li").get(0);
			    nav.show(element);
			},
			function()
			{
			    var element = jQuery(this).closest("li").get(0);
			    nav.preHide(element);
			});

	    // add hover handler to panel list items
	    var anchorsObj = jQuery(".navigation li > a");
	    anchorsObj.mouseover(
			function()
			{
			    var element = jQuery(this).closest("li").get(0);

			    // hilite ancestors
			    nav.hiliteElement(element);

			    // see if any siblings are currently pinned
			    var pinnedObj = jQuery("> ul > li.pinned", element.parentNode.parentNode).not(element);
			    if (pinnedObj.length > 0) return;

			    nav.preShowChildren(element);
			});

	    // add click behavior to panel list items
	    anchorsObj.click(
            function(evt)
            {
                var elementObj = jQuery(this).closest("li");
                var element = elementObj.get(0);
                elementObj.addClass("pinned");

                // hilite ancestors
                nav.hiliteElement(element);

                var success = nav.showChildren(element);
                if (success)
                {
                    evt.preventDefault();
                }
            });

	    jQuery(".navigation .input input[type='button']").click(
	        function(evt)
	        {
	            evt.preventDefault();
	            nav.doSearch(jQuery("input[type='text']", this.parentNode).val());
	        });
	    jQuery(".navigation .input input[type='text']").keydown(
            function(evt)
            {
                if (evt.keyCode != "13") return;
                evt.preventDefault();
                nav.doSearch(jQuery(this).val());
            });
	});
	
jQuery(window).load(
    function()
    {
        // show mask long enough to work with it
		var masksObj = jQuery(".navigation .nav-mask");
        masksObj.css({display: "block"});

        // position children
		var childListsObj = jQuery(".navigation .nav-panel > ul");
		childListsObj.each(
		    function()
		    {
		        nav.positionChildrenRecursive(this);
		    });

        // hide mask
        masksObj.css({display: "none"});
    });


jQuery(window).unload(
    function()
    {
	    for (var i = 0; i < nav.state.length; i++)
	    {
	        if (nav.state[i].timeout != null)
	        {
	            clearTimeout(nav.state[i].timeout);
	            nav.state[i].timeout = null;
	        }
            if (nav.state[i].animation != null)
	        {
		        nav.state[i].animation.stop();
		        nav.state[i].animation = null;
	        }
	        nav.state[i].element = null;
	    }
	    
	    for (var i = 0; i < nav.childAnimations.length; i++)
	    {
	        nav.childAnimations[i].animation.stop();
	        nav.childAnimations[i].animation = null;
	        nav.childAnimations[i].element = null;
	    }
	    
	    if (nav.childTimeout != null)
	    {
	        clearTimeout(nav.childTimeout);
	        nav.childTimeout = null;
	    }

        nav = null;
    });
