
var menuContainerDiv = 'browsepropertyby';
var menuArray;
var activeItems;

// set onclick listeners for all links with children
function setLinkListeners(menuDiv)
{
	var menuContainerObj = getObject(menuDiv);
	// get actual ul object
	var menuObj = getFirstOccurenceIn('UL', menuContainerObj);
		
	// build a multidimensional array for the menu
	menuArray = buildArrayFrom(menuObj);
	
	// set up active items
	activeItems = new Object();
	
	// add listeners and register active items
	applyFunctionToTree(menuArray, setupItem);
}

function setupItem(listItemObj, level)
{
	// if listItem has class 'active'
	if (listItemObj.listItem.className == 'active')
	{
		// register in activeItems array
		activeItems['level' + level] = listItemObj.listItem;
	}

	// if link is not a leaf
	if (listItemObj.children != false)
	{
		// get anchor
		var link = getFirstOccurenceIn('A', listItemObj.listItem);
		// set onclick listener
		link.onclick = function() 
			       { 
			       	    setMenuItemActive(listItemObj.listItem, level);
			       	    return false; 
			       };
	}
}

// set menu item as active
function setMenuItemActive(listItem, level)
{
	// if not in activeItems array
	if (listItem != activeItems['level' + level] && !inMotion)
	{
		inMotion = true;
		// close previous item if it exists
		if (activeItems['level' + level])
		{
			closeAndOpenULs(getFirstOccurenceIn('UL', activeItems['level' + level]), 
					getFirstOccurenceIn('UL', listItem)
					);
		}
		else justOpenUL(getFirstOccurenceIn('UL', listItem));
				
		// register as active
		activeItems['level' + level] = listItem;
	}
	// else do nothing - it's already active
}

// variables for close/open ULs code

var closingIntervalID; // for timer
var openingIntervalID; // for timer
var cUL; // for closing UL obj
var oUL; // for opening UL obj
var oULHeight; // for opening UL offsetHeight
var cULHeight; // for closing UL offsetHeight
var inMotion = false; // boolean - are the uls in transition?

// velocity variables
var transitionTime = 0.2; // time taken for menu to transition (in seconds)
var intervalTime = 30; // in millis
// ensure transitionTime is a whole even number of intervalTime to avoid motion sideeffects
var realTime = Math.floor(((transitionTime * 1000) / intervalTime)) * intervalTime;
if (realTime % 2 != 0) realTime++;
var openAccel;
var closeAccel;
var velocity;

function closeAndOpenULs(closeUL, openUL)
{
	setupForClose(closeUL);
	setupForOpen(openUL);
	velocity = 0;
	closingIntervalID = setInterval("closeMenu()", 30);
}

function justOpenUL(openUL)
{
	setupForOpen(openUL);
	velocity = 0;
	openingIntervalID = setInterval("openMenu()", intervalTime);
}

function setupForOpen(openUL)
{
	openUL.style.overflow = 'hidden';
	
	// complete hack to get height - quickly switch on display, get height, then set height as 0 - is there a better way?
	openUL.style.display = 'block';
	// get full openUL height for later
	oULHeight = openUL.offsetHeight;
	openUL.style.height = '0px';
	
	oUL = openUL;
	
	// do speed calculations
	// accel = (distance - initial velocity(0)) / (1/2 time)
	openAccel = oULHeight / (realTime >> 1);
}

function setupForClose(closeUL)
{
	closeUL.style.overflow = 'hidden';
	closeUL.style.height = closeUL.offsetHeight + 'px';
	// get full closeUL height for later
	cULHeight = closeUL.offsetHeight;
	
	cUL = closeUL;
	
	// do speed calculations
	// accel = (distance - initial velocity(0)) / (1/2 time)
	closeAccel = closeUL.offsetHeight / (realTime >> 1);
}
	
function closeMenu()
{
	// still moving
	if (parseInt(cUL.style.height) > 0 && velocity >= 0)
	{
		cUL.style.height = (parseInt(cUL.style.height) - Math.min(velocity, parseInt(cUL.style.height))) + 'px';
		velocity += closeAccel;
		// past halfway
		if (parseInt(cUL.style.height) < Math.floor(cULHeight >> 1)) 
		{
			// time to decellerate -> invert accel
			closeAccel = Math.abs(closeAccel) * -1;
		}
	}
	else 
	{
		// set all attributes as if ul was originally not active
		cUL.style.height = '';
		cUL.parentNode.className = '';
		cUL.style.display = 'none';
		clearInterval(closingIntervalID);
		// open menu ready to be opened
		velocity = 0;
		openingIntervalID = setInterval("openMenu()", intervalTime);
	}
}

function openMenu()
{
	// still moving
	if (parseInt(oUL.style.height) < oULHeight && velocity >= 0)
	{
		oUL.style.height = (parseInt(oUL.style.height) + Math.min(velocity, oULHeight - parseInt(oUL.style.height))) + 'px';
		velocity += openAccel;
		// past halfway
		if (parseInt(oUL.style.height) > (oULHeight >> 1)) 
		{
			// time to decellerate -> invert accel
			openAccel = Math.abs(openAccel) * -1;
		}
	}
	else 
	{
		oUL.style.height = 'auto';
		clearInterval(openingIntervalID);
		inMotion = false;
	}
}

// apply given function (of form func(treeElement, level) ) to every item in the tree
function applyFunctionToTree(tree, func)
{
	var level = 0;
	applyFunctionToSubTree(tree, level, func);	
}

function applyFunctionToSubTree(subTree, baseLevel, func)
{
	for (var i = 0; i < subTree.length; i++)
	{
		var currElement = subTree[i];
		func(currElement, baseLevel);
		if (currElement.children != false)
		{
			applyFunctionToSubTree(currElement.children, baseLevel + 1, func);
		}
	}
}

// build a multidimensional array for a ul/li nested menu
function buildArrayFrom(menuObj)
{
	return buildLevelIn(menuObj);
}

function buildLevelIn(container)
{
	// array of level (and sub-levels) to return
	var retArray = new Array();
	// get top level links
	var topLevelLI = getFirstOccurenceIn('LI', container);
	var index = 0;
	
	while (topLevelLI != false)
	{
		retArray[index] = new Object();
		
		// place list item in array
		retArray[index].listItem = topLevelLI;
		
		
		// if it has valid children build next level
		var childUL = getFirstOccurenceIn('UL', topLevelLI);
		if (childUL != false)
		{
			retArray[index].children = buildLevelIn(childUL);
		}
		else retArray[index].children = false;
		
		// increment index
		index++;
		
		// next LI
		topLevelLI = getNextAdjacentOccurence('LI', topLevelLI);
	}
	
	return retArray;
}

// get first occurence of element with tagName == tagStr and container containingObj
function getFirstOccurenceIn(tagStr, containingObj)
{
	if (containingObj.firstChild) var tempObj = containingObj.firstChild;
	else 
	{
		alert('Container has no children in getFirstOccurence(' + tagStr + ')');
		return;
	}
	
	while (tempObj.tagName != tagStr)
	{
		if (tempObj.nextSibling) tempObj = tempObj.nextSibling;
		else 
		{
			return false;
		}
	}
	
	if (tempObj != null) return tempObj;
}

// get next occurence of element with tagName == tagStr and sibling siblingObj
function getNextAdjacentOccurence(tagStr, siblingObj)
{
	if (siblingObj.nextSibling) var tempObj = siblingObj.nextSibling;
	else
	{
		return false;
	}
	
	while (tempObj.tagName != tagStr)
	{
		if (tempObj.nextSibling) tempObj = tempObj.nextSibling;
		else 
		{
			return false;
		}
	}
	
	if (tempObj != null) return tempObj;
}
	

// basic defensive function to get object
function getObject(id)
{
	if (document.getElementById)
	{
		if (document.getElementById(id))
		{	
			return document.getElementById(id);
		}
		else
		{
			trace('Element with id = ' + id + ' does not exist');
		}
	}
	else
	{
		trace('getElementbyId not supported');
	}
}

function setOnLoad()
{
	if (window.onload != null) 
	{
		var currentLoadFunc = window.onload;
		window.onload = function()
				{
				    currentLoadFunc();

				    // slideMenu setup functions here
				    setLinkListeners(menuContainerDiv);
				};        
	}
	else
	{
	    window.onload = function()
				{
				    // slideMenu setup functions here
				    setLinkListeners(menuContainerDiv);
				};   
	}
}

setOnLoad();

