// This work is licensed under the Creative Commons Attribution-Share Alike 3.0 License. 
// To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ 

// This script implements an object oriented javascript dropdown menu system that uses HTML unordered
// lists as it's 

// **********************************************************
// this is a simple class that we will be using to create a menu system based off unordered lists
function menuItem ( elementId, oneSubmenu, detachSubmenu, showFirst )
	{
	// this is a method that is called to hide / show the sub-menu that is associated with this object
	this.toggle = function ( closure )
		{
		// if the function is called with a parameter then we return a closure for use in event handlers
		if( closure )
			{
			return (function() {thisObj.toggle();});		
			}
			
		// if the oneSubmenu option is set then we need to go through and hide all the other submenus
		if( this.oneSubmenu )
			{
			if( walden_vertical_menu_array[0] )
				{
				for( subMenu = 1; subMenu < walden_vertical_menu_array.length; subMenu++ )
					{
					if( subMenu == this.registrationNumber )
						{
						continue;	
						}
					walden_vertical_menu_array[subMenu]();
					}
				}
			}
			
		// tell the other menu objects that at least one subMenu is visible
		walden_vertical_menu_array[0] = true;
		
		// check to see if the jQuery object is available and if it is then we will use it's animation
		// effects for displaying and hiding the submenu
		if( typeof( jQuery ) == "function" )
			{
			if( this.shown )
				{
				jQuery( this.submenu ).slideUp("slow");
				this.shown = !this.shown;
				this.createCookie ( this.element.id, "false" );
				this.element.className = this.oldclass;
				}
			else
				{
				flash();
				jQuery( this.submenu ).slideDown("slow");	
				this.shown = !this.shown;
				this.createCookie ( this.element.id, "true" );
				this.element.className = this.oldclass + " submenu_active";
				}	
			}
		// if jQuery is not available then we will fall back on instantly hiding or displaying the submenu
		// via manipulation of the submenu CSS properties
		else
			{
			if( this.shown )
				{
				this.submenu.style.display = "none";
				this.shown = !this.shown;
				this.createCookie ( this.element.id, "false" );
				this.element.className = this.oldclass;
				}
			else
				{
				flash();
				this.submenu.style.display = "block";	
				this.shown = !this.shown;
				this.createCookie ( this.element.id, "true" );
				this.element.className = this.oldclass + " submenu_active";
				}
			}
		}
		
	// this method is simply called to hide this objects submenu
	this.hide = function ( closure )
		{
		// if the function is called with a parameter then we return a closure for use in event handlers
		if( closure )
			{
			return (function() {thisObj.hide();});		
			}
			
		if( typeof( jQuery ) == "function" )
			{
			if( this.shown )
				{
				jQuery( this.submenu ).slideUp("slow");
				this.shown = !this.shown;
				this.createCookie ( this.element.id, "false" );
				this.element.className = this.oldclass;
				}
			}
		else
			{
			if( this.shown )
				{
				this.submenu.style.display = "none";
				this.shown = !this.shown;
				this.createCookie ( this.element.id, "false" );
				this.element.className = this.oldclass;
				}
			}
		}
		
	// this method is designed to be assigned as an event handler to cancel event bubbling
	// this is used to stop the submenus from triggering their parents element's onclick event handlers
	// when they are clicked
	this.cancelBubble = function ( closure, e )
		{
		if( closure )
			{
			return (function(e) {thisObj.cancelBubble(false, e);});		
			}
		if (!e) var e = window.event;
		e.cancelBubble = true;
		if (e.stopPropagation) e.stopPropagation();		
		}
		
	// some functions to get and set cookie values
	// first a function to create the cookies
	this.createCookie = function (name,value,days) 
		{
		if (days) 
			{
			var date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			var expires = "; expires="+date.toGMTString();
			}
		else var expires = "";
		document.cookie = name+"="+value+expires+"; path=/";
		}

	// then a function to read previously created cookies
	this.readCookie = function (name) 
		{
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++) 
			{
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
			}
		return null;
		}
		
	// assign a static instance of THIS for use later in providing closures for event handlers
	var thisObj = this;
	// declare the variable that will hold the status of this objects menu
	this.shown = true;
	// if the oneSubmenu property was supplied then we need to assign it to an internal property so that 
	// the methods will have access to it
	this.oneSubmenu = oneSubmenu;
	// if the detachSubmenu property was supplied the we need to assign it to an internal property so that
	// the methods will have access to it
	this.detachSubmenu = detachSubmenu;
	// if the showFirst parameter was supplied then we need to assign it to and internal property
	// so that the methods of this object will have access to it
	this.showFirst = showFirst;

	
	// find the submenu that is attached to this object's LI element and assign it to a variable
	this.element = document.getElementById( elementId );
	if( typeof( this.element ) == "object" )
		{
		for( x = 0, no_children = this.element.childNodes.length; x < no_children; x++ )
			{
			if( this.element.childNodes[x].tagName && ( this.element.childNodes[x].tagName == "UL" ))
				{
				this.submenu = this.element.childNodes[x];
				}
			}
			
		// if detachSubmenu = true then we need to move the submenu so that it is a child of the element containing 
		// the menu rather than a child of the LI element
		if( this.detachSubmenu )
			{
			this.parent = this.element.parentNode.parentNode;
			var tempElement = "";
			tempElement = this.submenu;
			this.element.removeChild( this.submenu );
			this.parent.appendChild( tempElement );
			this.submenu = tempElement;
			this.submenu.className = "is_submenu";
			}
			
		// this next part deals with registering the object so that we can display only one submenu
		// at a time if we wish
	
		// first check to see if any objects of this type have already been registered
		if( typeof(walden_vertical_menu_array) != "undefined" )
			{
			// and if so then we need to register this object's toggle() method
			this.registrationNumber = walden_vertical_menu_array.length;
			walden_vertical_menu_array[ walden_vertical_menu_array.length ] = this.hide( true );
			}
		else
			{
			// if no objects have been registered yet then we need to create the registration array and register this
			// object with it
			walden_vertical_menu_array = new Array();
			walden_vertical_menu_array[0] = false;
			walden_vertical_menu_array[1] = this.hide( true );
			this.registrationNumber = 1;
			}
			
		// make note of the class attribute of the the element containing the submenu
		this.oldclass = this.element.className;
		// assign the necessary event handlers to the parent menu element	
		this.element.onclick = this.toggle( true );
		// and also to the child submenu
		this.submenu.onclick = this.cancelBubble( true );
		
		// check whether a cookie has been set for this menu item
		this.cookie = this.readCookie ( this.element.id );
		// if not then create a cookie that says that the menu item is close
		if( this.cookie == null )
			{
			this.shown = false;
			this.submenu.style.display = "none";
			this.createCookie ( this.element.id, "false" );
			}
		// if there is a cookie for the menuItem then restore the menuItem to it's previous state
		else
			{
			if( this.cookie == "false" )
				{
				this.shown = false;
				this.submenu.style.display = "none";	
				}
			else
				{
				walden_vertical_menu_array[0] = true;
				this.element.className = this.oldclass + " submenu_active";
				}
			}
		}
		
	
	}
	
// **********************************************************
// this function hides all open submenus
function hideAll ()
	{
	if( walden_vertical_menu_array[0] )
		{
		for( subMenu = 1; subMenu < walden_vertical_menu_array.length; subMenu++ )
			{
			walden_vertical_menu_array[subMenu]();
			}
		}	
	}
	
// **********************************************************
// this function flashes the menubar to get the users attention
function flash ()
	{
	jQuery("#sub_menu").show().slideUp("slow");
	}
	
	
// **********************************************************
function waldenMenuInit () 
	{
	// first grab any list items that have been marked with the class has_submenu
	menuItems = jQuery(".has_submenu");
	menuArray = new Array();
	for( var x = 0, menuLength = menuItems.length; x < menuLength; x++ )
		{
		// If the menu item has no ID then we need to assign
		// it one so the we can identify it
		if( menuItems[x].id == '')
			{
			menuItems[x].id = "waldenMenuItem" + x;	
			}
		
		menuArray[x] = new menuItem( menuItems[x].id, true, false);
		}
	// now grab any menu items marked with the class no_submenu. When clicked on these need to hide all the other submenus
	noSubmenus = jQuery( ".no_submenu" );
	for( var x = 0 , submenuLength = noSubmenus.length; x < submenuLength; x++ )
		{
		noSubmenus[x].onclick = hideAll;
		}
		
	// hide the sub_meu div for the moment
	jQuery("#sub_menu").hide();
	
	
	}


// initialize the menu system
jQuery(document).ready( waldenMenuInit );


