// library to control dynamic menu bars
// by Thomas M. Farrell
//
//
// This code requires the use of several other libraries.
// Before loading this library, you must load:
//    sniff.js by Netscape Communications
//    layercontrol3.js by Thomas M. Farrell

// The following variable defines the suffix which will be appended to the 
// ID strings of layers to obtain the ID string of the matching on 
// version of the layer. For example, if you have a layer called "top0sub0"
// in the menu, and the string to append is "on", the matching on version 
// is "top0sub0on".
var menuBarOnSuffix = "on";

// The following variable holds the menu structure.
var menuBarStructure = null;
// You have to override the value with an array. This array contains 
// the IDs of the layers in the menu (off states only) in a structure 
// representing the structure of the menu bar and its submenus. For example,
// assume that the menu bar has two menus listed on it. The off states of
// the menu items are held in the layers with IDs "top0" and "top1". The 
// submenu for the top0 item has three menu items, with off states in layers
// "top0sub0", "top0sub1", and "top0sub2". The top0sub0 and top0sub1 items 
// are direct links, but the top0sub2 has a sub-submenu with two items, 
// with off states in layers "top0sub2sub0" and "top0sub2sub1". 
//
// Meanwhile, in the top1 menu, the menu items have their off states in the 
// "top1sub0" and "top1sub1" layers. For simplicity, neither has a
// sub-submenu. 
// 
// Note that these layer names are arbitrary: the code doesn't actually care
// what you call your layers as long as you can keep track of them.
//
// Anyway, the above described structure can be represented as:
// menuBarStructure = [
//   "ignore",
//   ["top0",["top0sub0"],["top0sub1"],["top0sub2",["top0sub2sub0"],["top0sub2sub1"]]],
//   ["top1",["top1sub0"],["top1sub1"]]
//   ];
// where "ignore" is a required placeholder, ignored by the software. 

// The following variable specifies which menu items, if any, 
// should default open.
var menuBarDefaultOpen = [];
// If you want to set a default state other than "everything is closed"
// for the menu bar, override the value with an array of strings of
// the IDs of the layers you want to default to visible. When the
// menu returns to its default state, the listed layers will be 
// forced to visibility.

// The following sets the error to display if there is a problem encountered
// while adjusting the menu display. Set it to a blank string ("") if you
// don't want an error.
var menuBarAdjustmentError = "A difficulty was encountered while attempting to adjust navigation display.";

// The following holds the interval for the menu bar's detection loop.
// You shouldn't alter this value, the software handles it.
var menuBarDetectionInterval = null;

// The following holds the number of pixels by which the menu bar is to be
// offset vertically before starting. Override this value with an integer
// if desired.
var menuBarVerticalOffset = 0;

// Call the following function when the page is finished loading.
// THIS FUNCTION KNOWN WORKING.
function startMenuBar() {
	// recurse through the structure and turn it into objects...
	menuBarStructure = turnStructureIntoObjects(menuBarStructure);
	// recurse through the default state arrays and objectify the strings
	for (var x=0; x<menuBarDefaultOpen.length; x++) {
		menuBarDefaultOpen[x] = new layerObject(menuBarDefaultOpen[x]);
		} //end for loop
	// if the user wants the menu bar moved, move it.
	if (menuBarVerticalOffset!=0) {
		offsetMenuBarVertical(menuBarStructure);
		} //end if
	// start detecting the mouse location
	loadMouse();
	// start detecting if the mouse is actually in a menu...
	menuBarDetectionInterval = setInterval("menuBarStatusUpdater()",200);
	// make all of the top-level menu items visible...
	// return the menu to its default state.
	menuBarDefault();
	} //end function

// The following function moves the menu bar along the Y axis by the
// specified number of pixels
function offsetMenuBarVertical(incomingArray) {
	var result = incomingArray;
	if (result[0]!="ignore") {
		result[0].offsetVertical(menuBarVerticalOffset);
		if (menuBarOnSuffix!="") {
			var tempLayerObject = new layerObject(result[0].layerID + menuBarOnSuffix);
			tempLayerObject.offsetVertical(menuBarVerticalOffset);
			} //end if
		} //end if
	if (result.length>1) {
		for (var x=1; x<result.length; x++) {
			offsetMenuBarVertical(result[x]);
			} //end for loop
		} //end if
	} //end function

// The following function returns the menu bar to its default state.
// If you haven't changed the default state, that means all the menus
// are closed and only the items on the bar itself show. If you have
// changed the values of menuBarDefaultOpen and/or menuBarDefaultOn,
// the default state will reflect your specifications.
function menuBarDefault() {
	// hide everything in all the submenus, leaving only the top level
	menuBarCloseAll();
	// recurse through the default open list and show all the layers...
	for (var x=0; x<menuBarDefaultOpen.length; x++) {
		menuBarOpen(menuBarDefaultOpen[x]);
		} //end for loop
	menuBarReturnedToDefault = true;
	} //end function

// The following variable contains an array of all the layer objects
// that are currently open in the menubar.
//
// YOU SHOULD NOT MODIFY THIS ARRAY. THE CODE HANDLES THIS FOR YOU.
var menuBarOpenItems = [];

// The following variable contains an array of all the layer objects
// that are currently on in the menubar in the on state.
//
// YOU SHOULD NOT MODIFY THIS ARRAY. THE CODE HANDLES THIS FOR YOU.
var menuBarOnItems = [];

// The following function is used to display the off state of a menu bar item.
// It is used instead of calling the show function directly to enable tracking
// of what items are being used.
function menuBarOpen(itemToOpen) {
	menuBarOpenItems[menuBarOpenItems.length] = itemToOpen;
	itemToOpen.show();
	} //end function

// The following function is used to display the on state of a menu bar item.
// It is used instead of calling the show function directly to enable tracking
// of what on items are being used.
function menuBarTurnOn(itemToOpen) {
	menuBarOnItems[menuBarOnItems.length] = new layerObject(itemToOpen+menuBarOnSuffix);
	menuBarOnItems[menuBarOnItems.length-1].show();
	} //end function

// The following function is used to remove one item at a time from the menu bar.
// This function is depricated.
function menuBarClose(idOfItemToClose) {
	for (var x=0; x<menuBarOpenItems.length; x++) {
		if (menuBarOpenItems[x].layerID==idOfItemToClose) {
			menuBarOpenItems[x].hide();
			menuBarOpenItems[x] = null;
			break;
			} //end if
		} //end for loop;
	menuBarOpenItems = compressArray(menuBarOpenItems);
	} //end function

// The following function is used to make all menu bar items vanish.
//
// You probably won't need to call it directly unless you want to hide the
// menu bar for some reason.
function menuBarCloseAll() {
	for (var x=0; x<menuBarOpenItems.length; x++) {
		menuBarOpenItems[x].hide();
		} //end for loop
	menuBarOpenItems = [];
	if (menuBarOnSuffix!=null) {
		for (var x=0; x<menuBarOnItems.length; x++) {
			menuBarOnItems[x].hide();
			} //end for loop
		menuBarOnItems = [];
		} //end if
	} //end function

// The following function takes an incoming array and returns a new array
// with the same values as the incoming array, but with null values 
// eliminated.
function compressArray(incomingArray) {
	var result = [];
	for (var x=0; x<incomingArray.length; x++) {
		if (incomingArray[x]!=null) {
			result[result.length] = incomingArray[x];
			} //end if
		} //end for loop
	return result;
	} //end function

// The following function puts the new value onto the front of the 
// array, steps each value in the array one ahead, and keeps the
// array within the specified maximum size. It returns the new
// array.
function pushArray(oldArray,newValue) {
	var newArray = oldArray;
	for (var x=(newArray.length-1); x>0; x--) {
		newArray[x] = newArray[x-1];
		} //end for loop
	newArray[0] = newValue;
	return newArray;
	} //end function

// The following function returns true if all values in the incoming array
// are null, false if not. 
function allNull(incomingArray) {
	var soFar = true;
	for (var x=0; x<incomingArray.length; x++) {
		if (incomingArray[x]!=null) {
			soFar = false;
			break;
			} //end if
		} //end for loop
	return soFar;
	} //end function

// The following variable contains the mouse-in-menu-status. This is an
// array of strings containing the names of the layers the mouse was pointing 
// at it was in the menu during recent update cycles, or null if the mouse was 
// pointing at no menu layer.
//
// YOU SHOULDN'T MODIFY THIS VALUE. THE SOFTWARE DOES IT FOR YOU.
var menuBarMouseStatus = [null,null,null,null,null,null,null,null,null,null];

// The following variable indicates if the menu bar has already been returned 
// to its default state. This prevents the update routine from constantly 
// redrawing the menu bar when the mouse is ignoring it.
//
// YOU SHOULDN'T MODIFY THIS VALUE. THE SOFTWARE DOES IT FOR YOU.
var menuBarReturnedToDefault = true;

// The following variable indicates if the software is adjusting the status
// of the menu now. This prevents the menu from attempting a redraw while a
// redraw is already occurring, possibly resulting in a nonsensical menu 
// state.
//
// YOU SHOULDN'T MODIFY THIS VALUE. THE SOFTWARE DOES IT FOR YOU.
var menuBarAdjustingNow = false;

// The following sets the value of menuBarMouseStatus.
// YOU SHOULDN'T CALL THIS FUNCTION, THE SOFTWARE DOES IT FOR YOU.
function menuBarStatusUpdater() {
	var foundIt=false;
	for (var x=0; x<menuBarOpenItems.length; x++) {
		// If the mouse is in a menu item...
		if (menuBarOpenItems[x].hasMouse()) {
			menuBarMouseStatus = pushArray(menuBarMouseStatus,menuBarOpenItems[x].layerID);
			menuBarReturnedToDefault = false;
			foundIt = true;
			break;
			} //end if
		} //end for loop
	// If the mouse isn't in a menu item...
	if (!foundIt) {
		menuBarMouseStatus = pushArray(menuBarMouseStatus,null);
		} //end if
	// Next we check if the status has just changed. If so, and if the new
	// status is not null, we adjust the menu.
	if ((menuBarMouseStatus[0]!=null) && (menuBarMouseStatus[0]!=menuBarMouseStatus[1]) && (!menuBarAdjustingNow)) {
		menuBarAdjust(menuBarMouseStatus[0]);
		} //end if
	//alert(menuBarMouseStatus);
	// Now we determine if the mouse has been out of the menu bar for
	// long enough to return to default state, and if it's necessary.
	if ( (allNull(menuBarMouseStatus)) && (!menuBarReturnedToDefault) ) {
		menuBarDefault();
		} //end if
	} //end function

// The following function adjusts the appearance of the menu in
// reaction to the current mouse location.
// YOU SHOULDN'T CALL THIS FUNCTION, THE SOFTWARE DOES IT FOR YOU.
function menuBarAdjust(currentItem) {
	menuBarAdjustingNow = true;
	menuBarReturnedToDefault = false;
	// First close everything so extraneous stuff doesn't get left on...
	menuBarCloseAll();
	// then build up the new status of the menus...
	var collectResult = menuBarRecurseStructure(menuBarStructure,currentItem);
	// collectResult should always evaluate to true. If it doesn't, that means
	// something has gone rather wrong in the menu. The following puts an error
	// message in the window status display. This may be commented out. It then
	// stops the menus from attempting to function, which is just a waste of
	// processor time if the menus have this sort of problem.
	if (!collectResult) {
		window.status = menuBarAdjustmentError;
		clearInterval(menuBarDetectionInterval);
		menuBarDetectionInterval = null;
		} //end if
	menuBarAdjustingNow = false;
	} //end function

// The following function works its way through the menu bar structure,
// finds the specified named item, and adjusts the menu accordingly.
// YOU SHOULDN'T CALL THIS FUNCTION, THE SOFTWARE DOES IT FOR YOU.
function menuBarRecurseStructure(incomingArray,searchingForString) {
	// if the primary element of the array is the item being searched for,
	// make all of the elements visible and return true.
	if (incomingArray[0].layerID==searchingForString) {
		if (incomingArray[0]!="ignore") {
			menuBarOpen(incomingArray[0]);
			if (menuBarOnSuffix!="") {
				menuBarTurnOn(incomingArray[0].layerID);
				} //end if
			} //end if
		for (var x=1; x<incomingArray.length; x++) {
			menuBarOpen(incomingArray[x][0]);
			} //end for loop
		return true;
		} //end if
	// Otherwise, check all of the subelements. If any of them contain
	// it, make all of the elements visible and return true.
	else {
		var foundInSubitem = null;
		for (var x=1; x<incomingArray.length; x++) {
			if (menuBarRecurseStructure(incomingArray[x],searchingForString)) {
				foundInSubitem = x;
				break;
				} //end if
			} //end for loop
		if (foundInSubitem!=null) {
			if (incomingArray[0]!="ignore") {
				menuBarOpen(incomingArray[0]);
				if (menuBarOnSuffix!="") {
					menuBarTurnOn(incomingArray[0].layerID);
					} //end if
				} //end if
			for (var x=1; x<incomingArray.length; x++) {
				menuBarOpen(incomingArray[x][0]);
				} //end for loop
			return true;
			} //end if
		} //end else
	// Otherwise this array does not contain the item being searched for.
	// So, return false.
	return false;
	} //end function

// The following function goes through the menu bar structure array and 
// turns all of the strings into layers.
function turnStructureIntoObjects(incomingArray) {
	var result = incomingArray;
	if (result[0]!="ignore") {
		result[0] = new layerObject(result[0]);
		} //end if
	if (result.length>1) {
		for (var x=1; x<result.length; x++) {
			result[x] = turnStructureIntoObjects(result[x]);
			} //end for loop
		} //end if
	return result;
	} //end function

// The following function localizes the menu bar based on the URL of 
// the page the code is used in.
function menuBarLocalize() {
	var current = location.href;
	current = current.split("/");
	var local = current[3];
	menuBarDefaultOpen[menuBarDefaultOpen.length] = local + menuBarOnSuffix;
	} //end function
