/********************************************************************************
	This script is copyrighted by Orange Street Softare
		www.orangestreetsoftware.com
	
********************************************************************************/

// ============================================ THE CONTAINER OBJECT ============================================

function menu_container ( metrics, image_urls, allow_render_caching )
{
	// set the object properties...
	this.metrics = metrics;								// the scpacial metrics of the menu system accessed via the constant indxes above
	this.image_urls = image_urls;						// an array of image urls (not full tags) that the menu system uses
	this.allow_render_caching = allow_render_caching;	// a flag to indicate whether render caching is enabled
	
	// set up caching variables 
	//   caches the result of a menu run see: menu_run.js
	//   these variablkes are not dealt with by this object 
	//   but are proveded here so as to be global accross all web pages
	//   the rendering implementation can use them as it sees fit (see: menu_run.js)
	this.render_cache = "";
	this.is_cached = false;

	// set up default values for state variables
	this.root_menu_index = -1;
	
	// create the menu array container variable 
	this.menus = new Array();

	// the is the name of the bottom most divlayer
	this.active_window = null;
	this.bottom_tag = null;
	
	// define methods
	// ==============
	
	// --------------------------------------------------------------------------------------------------------
	// call to create a new menu object, returns a pointer to the new menu object
	function add_menu ( parent_menu, menu_system_number, tree_depth )
	{
		// if no parent supplied, then this must be the root
		if (!parent_menu) 
			this.root_menu_index = this.menus.length;

		// create the menu, add it to array and return it
		return ( this.menus[this.menus.length] = new menu ( this, parent_menu, menu_system_number, tree_depth ) );
	}
	this.add_menu = add_menu;
	
	// --------------------------------------------------------------------------------------------------------
	// returns a pointer to the roor menu object (the one that has no ancestor)
	function get_root_menu ()
	{
		if (this.root_menu_index == -1)
			return 0;
		else
			return this.menus[this.root_menu_index];
	}
	this.get_root_menu = get_root_menu;
	
	// --------------------------------------------------------------------------------------------------------
	// returns a pointer to menu object corresponding to DB menu_system number
	function find_menu ( menu_system_number )
	{
		var n_menu_index;

		// search thru all menus registered with the container and return the pointer to the menu if found
		for (n_menu_index = 0 ; n_menu_index < this.menus.length ; n_menu_index++)
			if (this.menus[n_menu_index].menu_system_number == menu_system_number)
				return this.menus[n_menu_index];
				
		// if the menu is not found return null
		return 0;
	}
	this.find_menu = find_menu;
	
	// --------------------------------------------------------------------------------------------------------
	// call to make the appropriate menu current corresponding to DB menu_system number
	function show_menu ( menu_system_number )
	{
		var open_menu;

		// if the menu is found ask it show itself
		if (open_menu = this.find_menu ( menu_system_number ))
			open_menu.show_menu ();
	}
	this.show_menu = show_menu;
	
	// --------------------------------------------------------------------------------------------------------
	// call to hide a menu
	function hide_menu ( menu_system_number )
	{
		var close_menu;

		// if the menu is found ask it show itself
		if (close_menu = this.find_menu ( menu_system_number ))
			close_menu.hide_menu ();

	}
	this.hide_menu = hide_menu;

	// --------------------------------------------------------------------------------------------------------
	// this is called to repostion the trailing layer at the end of the menu
	function position_bottom ()
	{
		var root_menu;
		var position;
		
		if ((this.bottom_tag) && (this.active_window) && (root_menu = this.get_root_menu()))
		{
			position = 	root_menu.get_expanded_height() + this.metrics[0];
		
			if (document.layers)
				this.active_window.eval ( "document." + this.bottom_tag + ".top = " + String.fromCharCode(34) + position.toString() + String.fromCharCode(34)); 
			else if (document.getElementById)
				eval ( "this.active_window.document.getElementById('" + this.bottom_tag + "').style.top='" + position.toString() + "'"); 
			else
				this.active_window.eval ( "document.all." + this.bottom_tag + ".style.top='" + position.toString() + "'"); 
		}
	}
	this.position_bottom = position_bottom;
	
}

// ============================================== THE MENU OBJECT ===============================================

function menu ( container, parent_menu, menu_system_number, tree_depth )
{
	// set the object properties...
	this.container = container;						// the menu container
	this.parent_menu = parent_menu;					// the parent menu
	this.menu_system_number = menu_system_number;	// the menu system number in the db
	this.tree_depth = tree_depth;					// the depth this menu is in the menu tree

	// set up default values for state variables
	this.visible = false;							// true if the menu is visible (will then also be expanded)
	this.expanded = false;							// true if the menu is expnded (may not be vivible if its parent is not)
	this.contractable = true;
	this.total_height = 0;
	this.top = 0;
	
	// create the menu item array container variable 
	this.menu_items = new Array();

	// define methods
	// ==============
	
	// --------------------------------------------------------------------------------------------------------
	// use to add a menu item to the menu
	function add_item ( sub_menu, name, id, url, frame, popup_parameters )
	{
		// create the menu_item, add it to array and return it
		return ( this.menu_items[this.menu_items.length] = new menu_item ( this, sub_menu, name, id, url, frame, popup_parameters ) );
	}
	this.add_item = add_item;
	
	// --------------------------------------------------------------------------------------------------------
	// this returns the total height of the menu including expanded sub-menus
	function get_expanded_height ()
	{
		if (!this.total_height)
			this.total_height = get_total_item_height ();
			
		return this.total_height;
	}
	this.get_expanded_height = get_expanded_height;

	// --------------------------------------------------------------------------------------------------------
	// this returns the height of all items in the menu added together (= total hight if has no expanded subs)
	function get_total_item_height ()
	{
		return (this.menu_items.length * this.item_height);
	}
	this.get_total_item_height = get_total_item_height;

	// --------------------------------------------------------------------------------------------------------
	// method collapses all the open sub-menus on this menu...
	function collapse_all_open ()
	{
	var n_item_index;
	
		for ( n_item_index = 0 ; n_item_index < this.menu_items.length ; n_item_index++ )
			if (this.menu_items[n_item_index].sub_menu)
				if (this.menu_items[n_item_index].sub_menu.visible)
					this.menu_items[n_item_index].sub_menu.hide_menu ();
	}
	this.collapse_all_open = collapse_all_open;

	// --------------------------------------------------------------------------------------------------------
	// method collapses all the open sub-menus from the root menu
	function collapse_all_from_root ()
	{
		if (this.parent_menu)
		{
			this.parent_menu.collapse_all_from_root ();
		}
		else
		{
			this.collapse_all_open();
		}
	}
	this.collapse_all_from_root = collapse_all_from_root;

	// --------------------------------------------------------------------------------------------------------
	// call this to set this menu as active
	function show_menu ()
	{
		this.collapse_all_and_show_menu ( true );
		this.container.position_bottom();
	}
	this.show_menu = show_menu;
	
	function collapse_all_and_show_menu ( b_collapse )
	{
	var n_item_index;

//alert ("Showing menu: " + this.menu_system_number.toString());
		
		if ( b_collapse )
			this.collapse_all_from_root();
		
		// dont re-show if visible aready
		if (!this.visible)
		{
			// if it has a parent menu make sure it is open (visible) and has no other sub-menus open
			if (this.parent_menu)
			{
				if (!this.parent_menu.visible)
					this.parent_menu.collapse_all_and_show_menu ( false );
	
				this.expand_items ( this.parent_menu.move_items ( this.get_total_item_height (), this ) );
				this.parent_menu.show_item_expanded_for_sub (this);
			}
			// this is the root menu, so just expand it starting at its normal top position
			else
			{
				this.expand_items ( this.top );
			}

			// loop thru all sub-menus and show expanded
			for ( n_item_index = 0 ; n_item_index < this.menu_items.length ; n_item_index++ )
				if (this.menu_items[n_item_index].sub_menu)
					if (this.menu_items[n_item_index].sub_menu.expanded)
						this.menu_items[n_item_index].sub_menu.collapse_all_and_show_menu ( false );
		}
	
	}	
	this.collapse_all_and_show_menu = collapse_all_and_show_menu;
	
	// --------------------------------------------------------------------------------------------------------
	// use this to hide this menu and all its sub-menus
	// note: this means that this menus parent will be the active menu
	function hide_menu ()
	{
	var n_item_index;
	
		// dont hide if already hidden (duh)
		if (this.visible)
		{
			// if this menu has open sub_menus first hide them
			for ( n_item_index = 0 ; n_item_index < this.menu_items.length ; n_item_index++ )
				if (this.menu_items[n_item_index].sub_menu)
					this.menu_items[n_item_index].sub_menu.hide_menu ();
					
			// close the gap in the parent menu if thier is one, else just hide the items
			if (this.parent_menu)
			{
				this.parent_menu.move_items ( this.contract_items () * -1, this )
				this.parent_menu.show_item_normal_for_sub (this);
			}
			else
				this.contract_items ();
		}

		this.container.position_bottom();
	}
	this.hide_menu = hide_menu;

	// --------------------------------------------------------------------------------------------------------
	// use this to make an item change to expanded mode for a sub-menu
	function show_item_expanded_for_sub (sub_menu)
	{
		var n_item_index;

		// find the item to show expanded
		for (n_item_index = 0 ; this.menu_items[n_item_index].sub_menu != sub_menu; n_item_index++);
		
		this.menu_items[n_item_index].set_expanded_item ();
	}
	this.show_item_expanded_for_sub = show_item_expanded_for_sub;
	
	// --------------------------------------------------------------------------------------------------------
	// use this to make an item change to normal mode for a sub-menu
	function show_item_normal_for_sub (sub_menu)
	{
		var n_item_index;

		// find the item to show normal
		for (n_item_index = 0 ; this.menu_items[n_item_index].sub_menu != sub_menu; n_item_index++);
		
		this.menu_items[n_item_index].set_normal_item ();
	}
	this.show_item_normal_for_sub = show_item_normal_for_sub;
	
	// --------------------------------------------------------------------------------------------------------
	// use this to move some or all of the items in this menu up or down.
	// if sub menu is passed in then only menu items following the menu item that points to the sub will be moved
	function move_items ( n_move_by, sub_menu )
	{
		var n_item_index;
		var n_item_index_move_from;
		var n_gap_start_pos;
		var expanded_sub_menu;
	
		// only move if visible
		if (!this.visible)
			return 0;
		
		if (sub_menu)
		{
			// if this is a sub menu then the parent must move the items following it first to make space
			if (this.parent_menu)
				this.parent_menu.move_items ( n_move_by, this );

			// because not moving the whole menu total_height changes
			this.total_height += n_move_by;

			// find the item to start moving from
			for (n_item_index_move_from = 0 ; this.menu_items[n_item_index_move_from].sub_menu != sub_menu; n_item_index_move_from++);
			
			// only start moving at the item after the item containing the sub-menu
			n_item_index_move_from++;
		}
		// if we are moving everything then the top position changes, and we must move the sub menus
		else
		{
			// because moving the whole menu top changes
			this.top += n_move_by;

			// start moving at the top of the menu
			n_item_index_move_from = 0;
		}
		
		// if the sub-menu dangles off the end of this menu, 
		// then don't need to reposition sub menus or any items 
		// but do need to calculate the position of the gap created differently
		if (n_item_index_move_from == this.menu_items.length)
			n_gap_start_pos = this.menu_items[(this.menu_items.length - 1)].position + this.item_height;
		else
		{
			// calculate where the gap in this menu will start so that it can be returned
			n_gap_start_pos = this.menu_items[n_item_index_move_from].position;
			
			// move sub_menus
			for ( n_item_index = n_item_index_move_from ; n_item_index < this.menu_items.length ; n_item_index++ )
				if (this.menu_items[n_item_index].sub_menu)
					this.menu_items[n_item_index].sub_menu.move_items ( n_move_by, 0 );
	
			// go thru each menu_item in the menu and re-position them
			for ( n_item_index = n_item_index_move_from ; n_item_index < this.menu_items.length ; n_item_index++ )
			{
				// position the item
				this.menu_items[n_item_index].move_item_by ( n_move_by );
			}
		}

		// return the starting position of the newly made gap in the menu
		return n_gap_start_pos;
	}
	this.move_items = move_items;

	// --------------------------------------------------------------------------------------------------------
	// expands (makes visible) the items in this menu, places them one after another, using item_height property
	function expand_items ( n_start_pos )
	{
		var n_item_index;
		var n_current_pos;

		this.expanded = true;

		// expanding the menu means it will be visible
		this.visible = true;

		// the top of the menu will be the start pos
		this.top = n_start_pos;
			
		// go thru each menu_item in the menu, position them and make them visible
		n_current_pos = n_start_pos;

		for (n_item_index = 0 ; n_item_index < this.menu_items.length; n_item_index++)
		{
//alert ("n_current_pos: " + n_current_pos.toString());
			// position the item
			this.menu_items[n_item_index].move_item_to ( n_current_pos );
			
			// make the item visible
			this.menu_items[n_item_index].show_item ();

			// increment the position to the end of this item
			n_current_pos += this.item_height;
		}

		// update the total height of the menu, was 0 now = total item_height
		this.total_height += this.get_total_item_height ();
		
		// return the ending position 
		return n_current_pos;
	}
	this.expand_items = expand_items;
	
	// --------------------------------------------------------------------------------------------------------
	// contracts (hides) all the menu items in this menu
	function contract_items ()
	{
		var n_item_index;

		// contracting the menu means it will be hidden
		this.visible = false;
		this.expanded = false;

		// go thru each menu_item in the menu and hide it
		for (n_item_index = 0 ; n_item_index < this.menu_items.length; n_item_index++)
		{
			// hide the item (note diff for NN and IE: NN must ref via container div tag)
			this.menu_items[n_item_index].hide_item ();
		}

		// update the total height of the menu, now 0 
		this.total_height = 0;

		// return the height of the menu, the amount contracted by
		return this.get_total_item_height ();
	}
	this.contract_items = contract_items;
	
}

// ============================================ THE MENU ITEM OBJECT ============================================

function menu_item ( menu, sub_menu, name, id, url, frame, popup_parameters )
{
	// link object into object structure
	this.menu = menu;
	this.sub_menu = sub_menu
	
	// set the object properties...
	this.name = name;	
	this.id = id;	
	this.url = url;
	this.frame = frame;
	this.popup_parameters = popup_parameters;
	
	// set up default values for state variables
	this.position = 0;
	this.active_window = null;
	this.visible = false;
	this.show_normal = true;
	this.expanded_name = null;
	this.contracted_name = null;
	
	// define methods
	// ==============

	// --------------------------------------------------------------------------------------------------------
	// this is called when the menu item itself is clicked
	function item_click ()
	{
		// if is has a sub-menu expand it now
		if (this.sub_menu)
			sub_menu.show_menu ();
			
		// if the menu item points to a page, open it
		if ( this.url != '' )
		{
			// if popup parms are supplied, open in popup window
			if ( this.popup_parameters != '' )
				window.open ( this.url, this.frame, this.popup_parameters );
			else
				window.open ( this.url, this.frame );
		}
	}
	this.item_click = item_click;

	// --------------------------------------------------------------------------------------------------------
	// this is called when the + is clicked
	function expand_sub ()
	{
		this.sub_menu.show_menu();
	}
	this.expand_sub = expand_sub;

	// --------------------------------------------------------------------------------------------------------
	// this is called when the + is clicked
	function contract_sub ()
	{
		this.sub_menu.expanded = false;
		this.sub_menu.hide_menu();
	}
	this.contract_sub = contract_sub;

	// --------------------------------------------------------------------------------------------------------
	// this is used internal to this object to move the div tag to the set position
	function do_item_move ()	
	{
//alert ("moving: " + this.name + " to: " + this.position.toString() );
		// position the tag
		if (document.layers)
		{
			if (this.contracted_name)
				this.active_window.eval ( "document." + this.contracted_name + ".top = " + String.fromCharCode(34) + this.position.toString() + String.fromCharCode(34)); 

			// if there is sub-menu then there is also an expanded tag for this item
			if (this.expanded_name)
				this.active_window.eval ( "document." + this.expanded_name + ".top = " + String.fromCharCode(34) + this.position.toString() + String.fromCharCode(34)); 
		}
		else if (document.getElementById)
		{
			if (this.contracted_name)
				eval ( "this.active_window.document.getElementById('" + this.contracted_name + "').style.top='" + this.position.toString() + "'"); 

			// if there is sub-menu then there is also an expanded tag for this item
			if (this.expanded_name)
				eval ( "this.active_window.document.getElementById('" + this.expanded_name + "').style.top='" + this.position.toString() + "'"); 
		}
		else
		{
			if (this.contracted_name)
				this.active_window.eval ( "document.all." + this.contracted_name + ".style.top='" + this.position.toString() + "'"); 

			// if there is sub-menu then there is also an expanded tag for this item
			if (this.expanded_name)
				this.active_window.eval ( "document.all." + this.expanded_name + ".style.top='" + this.position.toString() + "'"); 
		}
	}
	this.do_item_move = do_item_move;

	// --------------------------------------------------------------------------------------------------------
	// this is used to move the item to a location
	function move_item_to ( n_move_to )
	{
		this.position = n_move_to;
		
		this.do_item_move ();
	}
	this.move_item_to = move_item_to;
	
	// --------------------------------------------------------------------------------------------------------
	// this is used to move the item by an amount
	function move_item_by ( n_move_by )
	{
		this.position += n_move_by;
		
		this.do_item_move ();
	}
	this.move_item_by = move_item_by;

	// --------------------------------------------------------------------------------------------------------
	// this is used to make the item visible
	function show_item ()
	{
//alert ("making: " + this.name + " visible." );
		// show the item (note diff for NN and IE: NN must ref via container div tag)
		if (document.layers)
		{
			if ((this.show_normal && this.contracted_name) || !this.expanded_name)
				this.active_window.eval ( "document." + this.contracted_name + ".visibility = " + String.fromCharCode(34) + "visible" + String.fromCharCode(34)); 
			else
				this.active_window.eval ( "document." + this.expanded_name + ".visibility = " + String.fromCharCode(34) + "visible" + String.fromCharCode(34)); 
		}
		else if (document.getElementById)
		{
			if ((this.show_normal && this.contracted_name) || !this.expanded_name)
				eval ( "this.active_window.document.getElementById('" + this.contracted_name + "').style.visibility='visible'"); 
			else
				eval ( "this.active_window.document.getElementById('" + this.expanded_name + "').style.visibility='visible'"); 
		}
		else
		{
			if ((this.show_normal && this.contracted_name) || !this.expanded_name)
				this.active_window.eval ( "document.all." + this.contracted_name + ".style.visibility='visible'"); 
			else
				this.active_window.eval ( "document.all." + this.expanded_name + ".style.visibility='visible'"); 
		}
		this.visible = true;
	}
	this.show_item = show_item;

	// --------------------------------------------------------------------------------------------------------
	// this is used to make the item invisible
	function hide_item ()
	{
//alert ("making: " + this.name + " invisble." );
		// hide the item (note diff for NN and IE: NN must ref via container div tag)
		if (document.layers)
		{
			if ((this.show_normal && this.contracted_name) || !this.expanded_name)
				this.active_window.eval ( "document." + this.contracted_name + ".visibility = " + String.fromCharCode(34) + "hidden" + String.fromCharCode(34)); 
			else
				this.active_window.eval ( "document." + this.expanded_name + ".visibility = " + String.fromCharCode(34) + "hidden" + String.fromCharCode(34)); 
		}
		else if (document.getElementById)
		{
			if ((this.show_normal && this.contracted_name) || !this.expanded_name)
				eval ( "this.active_window.document.getElementById('" + this.contracted_name + "').style.visibility='hidden'"); 
			else
				eval ( "this.active_window.document.getElementById('" + this.expanded_name + "').style.visibility='hidden'"); 
		}
		else
		{
			if ((this.show_normal && this.contracted_name) || !this.expanded_name)
				this.active_window.eval ( "document.all." + this.contracted_name + ".style.visibility='hidden'"); 
			else
				this.active_window.eval ( "document.all." + this.expanded_name + ".style.visibility='hidden'"); 
		}
		this.visible = false;
	}
	this.hide_item = hide_item;

	// --------------------------------------------------------------------------------------------------------
	// use this to make the item show its normal (unexpanded) div tag
	function set_normal_item ()
	{
		if (this.show_normal)
			return;
			
		if (this.visible)
		{
			this.hide_item();
			this.show_normal = true;
			this.show_item();
		}
		else
			this.show_normal = true;
	}
	this.set_normal_item = set_normal_item;

	// --------------------------------------------------------------------------------------------------------
	// use this to make the item show its normal (unexpanded) div tag
	function set_expanded_item ()
	{
		if (!this.show_normal)
			return;
			
		if (this.visible)
		{
			this.hide_item();
			this.show_normal = false;
			this.show_item();
		}
		else
			this.show_normal = false;
	}
	this.set_expanded_item = set_expanded_item;

}
