/*************************************************************************

  Implements popup/rollover menu functionality using a set
  of nested HTML lists to specify the menu structure.  This version is
  not object-oriented and only allows one popup menu panel per page.

  Original Author:  Brian Stucky, February 22, 2006
  Added menu animation code:  Brian Stucky, February 24, 2007

  Tested and works with:
	Firefox 1.5.0.1, WinXP
	Mozilla 1.7.8, WinXP
	MS IE 6.0, 5.5 and 5.0 WinXP
	Opera 8.52 and 7.5.4, WinXP
	Netscape 8.1, 7.2 and 6.2.3, WinXP
	MS IE 5.5, Win98
	MS IE 5.0 and 5.1, MacOS 9.2
	Mozilla 1.2, MacOS 9.2

  Tested and does not work with:
	MS IE 4.5, MacOS 9.2
	MS IE 4, Win98
	Netscape 4.61, MacOS 9.2
	Netscape 4.8, WinXP

  Needs to be tested:
	MS IE 5.0/Win98
	Firefox 1.0.7/MacOS 10.3
	Safari 1.3.1
	Konquerer 3.3.2

  Copyright(c) Bethel College, 2006

**************************************************************************/

var timers = new Array();
var popups = new Array();

function Animator()
{
	var self = this;
	this.notIE = navigator.appName.indexOf("Microsoft") == -1;
	this.frames = 6;
	this.isVisible = false;
	this.revealTimer = null;
	this.initialDelay = 400;

	//if (this.notIE)
		this.animate = function() { self.revealScroll(); };
	//else
	//	this.animate = function() { self.revealBasic(); };
	this.cancelAnimate = this.cancelRevealScroll;
}

Animator.prototype.reveal = function(element)
{
	this.currFrame = 0;
	this.element = element;

	this.revealTimer = setTimeout(this.animate, 400);
}

Animator.prototype.revealBasic = function()
{
	this.element.style.visibility = 'visible';
	this.isVisible = true;
}

Animator.prototype.cancelRevealBasic = function()
{
	if (this.revealTimer != null)
		clearTimeout(this.revealTimer);

	this.revealTimer = null;

	this.element.style.visibility = 'hidden';
	this.isVisible = false;
}

Animator.prototype.revealScroll = function()
{
	if (this.currFrame == 0)
	{
		this.revealSize = 0;
		this.element.style.clip = 'rect(auto auto 0px auto)';
		this.element.style.visibility = 'visible';
	}

	this.revealSize += (this.element.offsetHeight / this.frames);
	this.element.style.clip = 'rect(auto auto ' + this.revealSize + 'px auto)';
	this.currFrame++;

	if (this.currFrame == this.frames)
	{
		if (this.notIE)
			this.element.style.clip = 'auto';
		else
			this.element.style.clip = 'rect(auto auto auto auto)';

		this.isVisible = true;
		this.revealTimer = null;
	}
	else
		this.revealTimer = setTimeout(this.animate, 10);
}

Animator.prototype.cancelRevealScroll = function()
{
	if (this.revealTimer != null)
		clearTimeout(this.revealTimer);

	this.revealTimer = null;

	this.element.style.visibility = 'hidden';
		
	if (this.notIE)
		this.element.style.clip = 'auto';
	else
		this.element.style.clip = 'rect(auto auto auto auto)';

	this.isVisible = false;
	this.revealTimer = null;
}

var menuAnimator = new Animator();

function mouseOver(element)
{
	var parentElement = element.parentNode;
	var menuLevel = parentElement.parentNode.getAttribute('menulevel');

	// cancel the timer to hide all popups, if there is one
	if (timers.length > 0)
	{
		//alert("clearing timeouts: " + this.timers.length);
		clearTimeout(timers.shift());
	}

	// popup a submenu if this item has one
	var revealElement;
	for (i = 0; i < parentElement.childNodes.length; i++)
	{
		if (parentElement.childNodes[i].nodeName == 'UL')
		{
			revealElement = parentElement.childNodes[i];

			// if no menu items are visible, then animate the menu reveal
			if (!menuAnimator.isVisible)
				menuAnimator.reveal(revealElement);
			else
				revealElement.style.visibility = 'visible';

			revealElement.style.zIndex = '500';
			popups.push(revealElement);
		}
	}

	// only hide the visible popups that are at a deeper menulevel
	for (var i = popups.length - 1; i >= 0; i--)
	{
		if ((popups[i].getAttribute('menulevel') > menuLevel) && (popups[i] != revealElement))
		{
			popups[i].style.visibility = 'hidden';
			popups[i].style.zIndex = '-1';
			popups.splice(i, 1);
		}
	}
}	

function mouseOut()
{
	if (menuAnimator.revealTimer != null)
		menuAnimator.cancelAnimate();

	// set a timer to hide all visible popups
	timers.push(setTimeout('hideAll(); timers.shift(); menuAnimator.isVisible = false;', 400));
}
	
function hideAll()
{
	var item;

	// hide all visible popups
	while (popups.length > 0)
	{
		item = popups.shift();
		item.style.visibility = 'hidden';
		item.style.zIndex = '-1';
	}
}

function parseMenu(rootNode, depth)
{
	var node;

	for (var i = 0; i < rootNode.childNodes.length; i++)
	{
		node = rootNode.childNodes[i];
		if (node.nodeName == 'A')
		{
			node.onmouseover = function() { mouseOver(this); };
			node.onmouseout = mouseOut;
		}
		else if (node.nodeName == 'UL')
		{
			node.setAttribute('menulevel', depth);
			parseMenu(node, depth + 1);
		}
		else if (node.nodeName == 'LI')
		{
			parseMenu(node, depth);
		}
	}
}

function initMenu(id)
{
	var menuElement = document.getElementById(id);

	if (menuElement)
	{
		parseMenu(document.getElementById(id), 1);
		document.getElementById(id).setAttribute('menulevel', 0);
	}
}


/********
  Fix IE 5.0's broken Javascript support by implementing the array functions we need.
  Note that these array functions are not full implementations of the standard Javascript
  array methods; they simply provide the functionality we need for the menus.
********/

// this simplified implementation of push adds a single element at the end of the array
if (!Array.prototype.push)
{
	Array.prototype.push = function(data)
	{
		this[this.length] = data;
	}
	// alert("IE 5 sucks!");
}

if (!Array.prototype.shift)
{
	Array.prototype.shift = function()
	{
		var data = this[0];

		if (this.length > 0)
		{
			for (var i = 0; i < this.length - 1; i++)
				this[i] = this[i+1];

			this.length--;
		}

		return data;
	}
}

// this simplified implementation of splice ignores the len argument and only removes the array entry at
// the location specified by index and returns it
if (!Array.prototype.splice)
{
	Array.prototype.splice = function(index, len)
	{
		var data = this[index];

		for (var i = index; i < this.length - 1; i++)
			this[i] = this[i+1];

		this.length--;

		return data;
	}
}


