/// Adds an event handler to an object, allowing for multiple handlers
/// @param element The element to add the handler to
/// @param eventName The name of the event, e.g. "onmouseup"
/// @param newFunction The function to call on the event
function addHandler(element, eventName, newFunction)
{
	// Tell the event handler to handle all events
	if (!element[eventName])
	{
		element[eventName] = function(event)
		{
			var value;
			var i = 0;
			while (element[eventName + "_" + i] != null)
			{
				value = element[eventName + "_" + i](event);
				i++;
			}
			
			return value;
		};
	}

	// Add the event
	var i = 0;	
	while (element[eventName + "_" + i] != null)
	{
		i++;
	}
	element[eventName + "_" + i] = newFunction;
}

/// Determines whether an object has a given object somewhere above it
/// @param object The object (not an identifier) to check for a parent of
/// @param testParentId The id of the object to see if 'object' is a nested child of
function hasAsParent(object, testParentId)
{
	while (object.parentNode)
	{
		if (object.parentNode.id == testParentId)
		{
			return true;
		}
		else
		{
			object = object.parentNode;
		}
	}

	return false;
}

/// Cross-browser compatible method for getting an XMLHttp[Request] object
/// @returns Either an XMLHTTP object or an XMLHttpRequest object
function getAJAXObject()
{
	try
	{
		if (window.ActiveXObject)
		{
			return new ActiveXObject("MSXML2.XMLHTTP.3.0");
		}
		else
		{
			return new XMLHttpRequest();
		}
	}
	catch (e)
	{
		return null;
	}
}

/// Recurses up to calculate an element's offset from the top-left of the page
/// @param element The element to get the left offset of
function calcOffsetX(element)
{
	var offsetX = 0;

	while (element != null)
	{
		if (!isNaN(element.offsetLeft))
		{
			offsetX += element.offsetLeft;
		}
		if (element.style && element.style.position == "absolute")
		{
			break;
		}

		element = element.offsetParent;
	}

	return offsetX;
}

/// Recurses up to calculate an element's offset from the top-left of the page
/// @param element The element to get the top offset of
function calcOffsetY(element)
{
	var offsetY = 0;

	while (element != null)
	{
		if (!isNaN(element.offsetTop))
		{
			offsetY += element.offsetTop;
		}
		if (element.style && element.style.position == "absolute")
		{
			break;
		}

		element = element.offsetParent;
	}

	return offsetY;
}

/// Starts fading an element in
/// @param item The element to fade in (not an identifier)
/// @param stopAt The opacity to fade to
/// @param change The change in opacity per iteration
function startFadeIn(item, stopAt, change)
{
	item.fading = "in";
	if (item.fadingTimeout)
	{
		window.clearTimeout(item.fadingTimeout);
	}
	fadeInOpacity(item, stopAt, change);
}

/// Starts fading an element out
/// @param item The element to fade out (not an identifier)
/// @param stopAt The opacity to fade to
/// @param change The change in opacity per iteration
/// @param hide If the opacity hits zero, hide the element
function startFadeOut(item, stopAt, change, hide)
{
	item.fading = "out";
	if (item.fadingTimeout)
	{
		window.clearTimeout(item.fadingTimeout);
	}
	fadeOutOpacity(item, stopAt, change, hide);
}

function isMSIE()
{
	if (window.navigator.appName == "Microsoft Internet Explorer")
	{
		return true;
	}
	
	return false;
}

/// Handles the fade in loop
/// @param item The element to fade in (not an identifier)
/// @param stopAt The opacity to fade to
/// @param change The change in opacity per iteration
function fadeInOpacity(item, stopAt, change)
{
	if (item.fading == "in")
	{
		item.style.opacity = parseFloat(item.style.opacity) + change;

		// *Sigh*
		if (isMSIE())
		{
			item.style.filter = "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + (item.style.opacity * 100) + ")";
		}
		
		if (item.style.opacity < stopAt)
		{
			item.fadingTimeout = window.setTimeout(function() { fadeInOpacity(item, stopAt, change); }, 10);
		}
	}
}

/// Handles the fade out loop
/// @param item The element to fade out (not an identifier)
/// @param stopAt The opacity to fade to
/// @param change The change in opacity per iteration
/// @param hide If the opacity hits zero, hide the element
function fadeOutOpacity(item, stopAt, change, hide)
{
	if (item.fading == "out")
	{
		item.style.opacity = parseFloat(item.style.opacity) - change;
		
		// *Sigh*
		if (isMSIE())
		{
			item.style.filter = "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + (item.style.opacity * 100) + ")";
		}
		
		if (item.style.opacity > stopAt)
		{
			item.fadingTimeout = window.setTimeout(function() { fadeOutOpacity(item, stopAt, change, hide); }, 10);
		}
		
		if (parseFloat(item.style.opacity) <= 0.01 && hide == true)
		{
			item.style.visibility = "hidden";
		}
	}
}

/// Mimics the PHP function of the same name, and converts special characters
/// into their HTML entity equivalent.
/// @param str The string encode
function htmlentities(str)
{
	var i, output = "", len, chr = '';
	len = str.length;
	
	for (i = 0; i < len; i++)
	{
		chr = str.charCodeAt(i);
		if ((chr > 47 && chr < 58) || (chr > 62 && chr < 127))
		{
			output += str.charAt(i);
		}
		else
		{
			output += "&#" + chr + ";";
		}
	}
	
	return output;
}

function showPopup(parent, menu)
{
	var menuEl = document.getElementById(menu);
	if (menuEl == null)
	{
		return;
	}
	
	// If the element is being hidden, stop it
	window.clearTimeout(menuEl.timeout);

	// Work out where the menu element is going
	var x = calcOffsetX(document.getElementById(parent)) + 1;
	var y = calcOffsetY(document.getElementById(parent)) + parseInt(document.getElementById(parent).offsetHeight) + 4;
	
	// Set up the menu element
	menuEl.style.position = 'absolute';
	menuEl.style.left = x + "px";
	menuEl.style.top = y + "px";
	menuEl.style.display = 'block';
	menuEl.style.zIndex = 3;
	
	// Fade the menu in
	if (!menuEl.style.opacity)
	{
		menuEl.style.opacity = 0.0;
	}
	menuEl.style.visibility = "visible";
	startFadeIn(menuEl, 1.0, 0.075);
	
	// If the mouse enters the menu DIV, stop it from being hidden
	menuEl.onmouseover = function() { window.clearTimeout(menuEl.timeout); };

	// If the mouse leaves the menu DIV, hide it
	menuEl.onmouseout = function() { hidePopup(menu); };
}

function hidePopup(menu)
{
	// Hide the menu in 100msec
	document.getElementById(menu).timeout = window.setTimeout("doHidePopup('" + menu + "')", 100);
}

function doHidePopup(menu)
{
	startFadeOut(document.getElementById(menu), 0.0, 0.075, true);
}

function bi(id, type, pair)
{
	var el = document.getElementById('bi' + pair + '_' + id);
	el.src = el.src.substr(0, el.src.length - 5) + type + '.jpg';
}

function bannerStep()
{
	window.setTimeout(function() { bannerStep(); }, 160);
	
	bStep++;
	
	if (bStep >= 20)
	{
		nextBannerImage(bStep - 20);
	}
	if (bStep == 23)
	{
		bStep = 0;
	}
}

function nextBannerImage(idx)
{
	// Jump to next image for this banner image index
	bImgIdx[idx] += 4;
	if (bImgIdx[idx] > bImgs) { bImgIdx[idx] -= bImgs; }
	
	// Get the element
	var el = document.getElementById('bi' + bImgNext[idx] + '_' + (idx + 1));
	
	// Update it's src
	el.src = el.src.substr(0, el.src.lastIndexOf('/') + 1) + bImgIdx[idx] + 'b.jpg';		
	
	// Ensure it's ready to fade in
	el.style.opacity = 0.0;
	el.style.filter = "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
	el.style.visibility = "visible";
	
	// Fade it in
	startFadeIn(el, 1, (isMSIE() ? 0.055 : 0.02));

	// Fade out the other in the image pair
	if (bImgNext[idx] == 2) { bImgNext[idx] = 1; } else { bImgNext[idx] = 2; }
	var el2 = document.getElementById('bi' + bImgNext[idx] + '_' + (idx + 1));
	el2.style.opacity = 1.0;
	startFadeOut(el2, 0, (isMSIE() ? 0.055 : 0.02), true);
}

var bStep = 0;							// Current animation step
var bImgIdx = new Array(1, 2, 3, 4);	// Next image index to display
var bImgNext = new Array(2, 2, 2, 2);	// Next of image pair to use
var bImgs = 12;							// Number of images

// Start when ready
addHandler(window, "onload", function () { bannerStep(); });
