/**
* Global JavaScript Definitions
*
* @author				Matt Gifford
* @copyright			2006 Timeshifting Interactive Limited
* @version			1.4.6 (fork / patched 2007-04)
*/


var viewHandler = WebPage;
var onLoadTasks = [];
var onUnloadTasks = [];


// Execute on load handler
window.onload = function()
	{
	if (viewHandler !== WebPage)
		{
		// Extend the base page class and create the xhtml object
		viewHandler.inheritsFrom( WebPage );
		xhtml = new viewHandler();
		}
	else
		{
		// Create a generic page xhtml object
		xhtml = new WebPage();
		}

	// Main page initialization
	xhtml.init();

	// Execute secondary on load tasks
	for (var x = 0; x < onLoadTasks.length; x++)
		{
		onLoadTasks[x]();
		}
	}


// Execute on unload handler
window.onunload = function()
	{
	// Execute on unload tasks
	for (var x = 0; x < onUnloadTasks.length; x++)
		{
		onUnloadTasks[x]();
		}
	}


/**
* Creates a new WebPage object with methods used by all pages, can be extended to add page specific methods.
*
* @author				Matt Gifford
* @copyright			2006 Timeshifting Interactive Limited
* @version			1.4.6
*/
function WebPage()
	{
	// Step 1. Define Properties

	this.initialized = false;
	this.debug = false;


	// Step 2. Define Public Methods

	/**
	* Sets up the initial page state and event handlers
	*/
	this.init = function()
		{
		this.initExpandableLists();
		this.initAnchors();
		this.initInputButtons();
		this.initMenus();			// Initialize drop down menus (if applicable for this document)
		this.initActiveX();			// Activates activeX controls in IE

		// Set class as initialized
		this.initialized = true;
		}


	/**
	* Adds standard event handlers to process in-page links and offsite links
	*/
	this.initAnchors = function()
		{
		var links = document.getElementsByTagName('a');
		for(x=0; x<links.length; x++)
			{
			// 1. Hide marquee border for inpage links
			links[x].onfocus = function()
				{
				this.blur();
				}

			// 2. Make offsite links and pdfs open in a new tab/window
			if(/\b(offsite|pdf)\b/.exec(links[x].className))
				{
				links[x].onclick = function()
					{
					window.open(this.href,'_blank');
					return false;
					}
				}

			// 3. Make inpage links smooth scroll
			if(/\binpage\b/.exec(links[x].className))
				{
				var url = links[x].href;
				var startPos = url.indexOf('#')+1;
				var endPos = (url.indexOf('?') != -1 ? url.indexOf('?')+1 : url.length);
				var target = url.substring(startPos, endPos);
				links[x].onclick = new Function('xhtml.smoothScroll("' + target + '");');
				links[x].href = 'javascript:javascript:void(1);';
				}

			// 4. If we're in debug mode, make empty links bright green
			if ( this.debug == true && links[x].href.indexOf('#') == (links[x].href.length-1) )
				{
				links[x].style.background = '#0f0';
				links[x].style.color = '#000';

				// Set the transpancy of all child nodes to 50%, so we can see the green
				for (var y = 0; y < links[x].childNodes.length; y++)
					{
					// Check if it's an element node (rather than a text node, etc)
					if (links[x].childNodes[y].nodeType == 1)
						{
						links[x].childNodes[y].style.opacity = '0.5';
						}
					}
				}

			// 5. Disable links to the current page (improves user experience)
			if ( this.debug == true && (links[x].href == window.location.toString() || links[x].href == window.location.toString() + '#') )
				{
				links[x].style.cursor = 'default';
				links[x].style.textDecoration = 'none';
				}
			}
		}



	/**
	* Adds rollover support to input[type=image] elements
	*/
	this.initInputButtons = function()
		{
		var rolloverCache = new Array();
		var inputs = document.getElementsByTagName('input');
		for(x=0; x<inputs.length; x++)
			{
			// Check if it's an image button with a roll over
			if (inputs[x].type == 'image' && inputs[x].className.indexOf('hasRollover') != -1)
				{
				// 1. Add event handlers to swap the images
				inputs[x].onmouseover = function()
					{
					this.src = this.src.replace(/\.(gif|jpg|png)/, '-over.$1');
					}
				inputs[x].onmouseout = function()
					{
					this.src = this.src.replace(/-over\.(gif|jpg|png)/, '.$1');
					}

				// 2. Pre-cache the rollover image
				var newImage = new Image();
				newImage.src = inputs[x].src.replace(/\.(gif|jpg|png)/i, '-over.$1');
				rolloverCache[rolloverCache.length] = newImage;
				}
			}
		}



	/**
	* Initializes the drop down menu system for the document (if applicable)
	*
	* (The menu container is specified by using the "dropDownMenu" class)
	*
	*/
	this.initMenus = function()
		{
		// Search the document for a drop down menu container
		var uls = document.getElementsByTagName('ul');
		var menuContainer = null;
		for (var x = 0; x < uls.length; x++)
			{
			if (uls[x].className.indexOf('dropDownMenu') != -1)
				{
				menuContainer = uls[x];
				break;
				}
			}

		// Check if we're a menu container, if no return (the document contains no menus)
		if (menuContainer == null)
			{
			return;
			}

		// Create menu timeout variable
		this.menuTimeout = null;

		// Find the menus items inside the list
		var childNodesLength = menuContainer.childNodes.length;
		this.menuItems = new Array();
		for (var x = 0; x < childNodesLength; x++)
			{
			// A nodeType of 1 is an XHTML element, as opposed to a comment or white space, etc.
			if(menuContainer.childNodes[x].nodeType == 1 && menuContainer.childNodes[x].tagName.toLowerCase() == 'li')
				{
				this.menuItems[this.menuItems.length] = menuContainer.childNodes[x];
				}
			}

		// Add event handlers to menu headings
		for (var x = 0; x < this.menuItems.length; x++)
			{
			// Add show menu event handler to heading anchor
			this.menuItems[x].getElementsByTagName('a')[0].onmouseover = function()
				{
				try
					{
					xhtml.hideMenus();
					var parentDiv = findParent(this, 'li');
					parentDiv.className = 'active';
					}
				catch (err)
					{
					}
				}

			// Add hide menu event handler to heading anchor
			this.menuItems[x].getElementsByTagName('a')[0].onmouseout = function()
				{
				try
					{
					clearTimeout(xhtml.menuTimeout);
					xhtml.menuTimeout = setTimeout("xhtml.hideMenus();", 1500);
					}
				catch (err)
					{
					}
				}

			// Add event handlers to menu items
			var links = this.menuItems[x].getElementsByTagName('li');
			if ( 0 < links.length )
				{
				for (var y = 0; y < links.length; y++)
					{
					// Clear timed menu hide on mouseover
					links[y].firstChild.onmouseover = function()
						{
						try
							{
							clearTimeout(xhtml.menuTimeout);
							}
						catch (err)
							{
							}
						}

					// Add timed menu hide on mouseout
					links[y].firstChild.onmouseout = function()
						{
						try
							{
							xhtml.menuTimeout = setTimeout("xhtml.hideMenus();", 1250);
							}
						catch (err)
							{
							}
						}
					}
				}
			else
				{
				// If the heading doesn't have a menu below it, make it inactive on mouseout
				this.menuItems[x].getElementsByTagName('a')[0].onmouseout = function()
					{
					try
						{
						xhtml.hideMenus();
						var parentDiv = findParent(this, 'li');
						parentDiv.className = 'inactive';
						}
					catch (err)
						{
						}
					}
				}
			}
		}


	/**
	* Hides all the down drop menus
	*/
	this.hideMenus = function()
		{
		clearTimeout(xhtml.menuTimeout);
		for (var x = 0; x < xhtml.menuItems.length; x++)
			{
			xhtml.menuItems[x].className = 'inactive';
			}
		}


	/**
	* Activate ActiveX controls in Internet Explorer
	*
	* After changes to Internet Explorer made in June 2006, activeX controls need to
	* be clicked on work, this function fixes this issue. For more information see:
	*
	* @url	http://support.microsoft.com/kb/912945
	*/
	this.initActiveX = function()
		{
		// Check if we're using the Trident rendering engine
		if (navigator.appName && navigator.appName == 'Microsoft Internet Explorer' && navigator.userAgent.toLowerCase().indexOf('msie') != -1)
			{
			// Find any object elements in the document
			var objectElements = document.getElementsByTagName('object');
			for (var x = 0; x < objectElements.length; x++)
				{
				// Re-insert the object into the document
				objectElements[x].outerHTML = objectElements[x].outerHTML;
				}
			}
		}


	/**
	* Initializes expandable lists
	*/
	this.initExpandableLists = function()
		{
		// Search the document for expandable lists
		var uls = document.getElementsByTagName('ul');
		for (var x = 0; x < uls.length; x++)
			{
			if (uls[x].className.indexOf('expandable') != -1)
				{
				// Add event handlers to list items
				var anchors = uls[x].getElementsByTagName('a');
				for (var y = 0; y < anchors.length; y++)
					{
					// Only add event handlers to empty anchors
					if (anchors[y].href.indexOf('#') == (anchors[y].href.length-1))
						{
						anchors[y].href = 'javascript:void(0);';
						anchors[y].onclick = function()
							{
							xhtml.toggleExpandableList(this);
							}
						}
					}
				}
			}
		}


	/**
	* Toggles the class name of the parent LI between "active" and "inactive"
	*/
	this.toggleExpandableList = function(obj)
		{
		// Find the parent LI node
		var parentNode = findParent(obj, 'li');

		// Check if we have a valid parent node
		if (parentNode != null)
			{
			// Toggle the class name
			if (parentNode.className == '' || parentNode.className.indexOf('inactive') != -1)
				{
				parentNode.className = parentNode.className.replace(/\s?(inactive)/g, '');
				parentNode.className += ' active';
				}
			else
				{
				parentNode.className = parentNode.className.replace(/\s?(active)/g, '');
				parentNode.className += ' inactive';
				}
			}
		}


	/**
	* Set the font size for the document and save setting to a cookie
	*
	* @param		int		fontsize		A value of either 1 or 2 indicating normal or large fonts
	*/
	this.fontSize = function(newSize)
		{
		// Build the cookie expiry
		var date = new Date();
		date.setTime(date.getTime()+(365*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();

		// Update the font size
		switch (newSize)
			{
			case 1:
				document.getElementById('page').style.fontSize = '78%';
				document.cookie = "fontsize=1"+expires+"; path=/";
				break;
			case 2:
				document.getElementById('page').style.fontSize = '95%';
				document.cookie = "fontsize=2"+expires+"; path=/";
				break;
			}
		}


	/**
	* Scrolls the page to the specified element
	*
	* @param			elementId			The ID of the element to scroll to
	* @param			elementY			The Y position of element (optional, will be calculated if not specified)
	*/
	this.smoothScroll = function(elementId, elementY)
		{
		// If the elements vertical location hasn't been specificed, calculate it
		if (arguments.length != 2)
			{
			// Get it's offset
			obj = document.getElementById(elementId);
			obj.style.display = 'block';	// Make sure it's visible, otherwise we can't get it's location
			elementY = obj.offsetTop;

			// If its parent is relative or absolutely positioned, find it's offset and add it to the total
			while (obj.offsetParent)
				{
				obj = obj.offsetParent;
				elementY += obj.offsetTop;
				}

			// Make the scroll stop just above the target (looks nicer)
			elementY -= 15;
			if (elementY < 0)
				{
				elementY = 0;
				}

			// Check to see we're not trying to scroll off the end of the page
			var contentHeight = document.getElementById('page').offsetHeight;
			var windowHeight = (window.innerHeight ? window.innerHeight : (document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight));
			if ( (contentHeight - windowHeight) < elementY)
				{
				elementY = (contentHeight - windowHeight);
				}
			}

		// Get the current window scroll position
		var yPos = window.scrollY ? window.scrollY : (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);

		// Calculate the pixels remaining to scroll and the scroll step size
		var distanceLeft = Math.abs(yPos - elementY);
		var stepSize = 100;
		if (distanceLeft < 400)
			{
			stepSize = 60;
			}
		if (distanceLeft < 200)
			{
			stepSize = 20;
			}
		if (distanceLeft < 50)
			{
			stepSize = 10;
			}

		// Calculate the scroll
		if (yPos < elementY)
			{
			// Scroll down
			yPos += stepSize;

			// Check if we're scrolled past the target
			if (elementY < yPos)
				{
				yPos = elementY;
				}
			}
		else if (elementY < yPos)
			{
			// Scroll Up
			yPos -= stepSize;

			// Check if we're scrolled past the target
			if (yPos < elementY)
				{
				yPos = elementY;
				}
			}

		// Check for less than zero
		if (yPos < 0)
			{
			yPos = 0;
			}

		// Scroll window
		window.scrollTo(0, yPos);

		// If we haven't reached the target, run the another scroll step
		if (yPos != elementY)
			{
			setTimeout("xhtml.smoothScroll('" + elementId + "', " + elementY + ");", 10);
			}
		}


	/**
	* Sets the current view in a XHTML document
	*
	* Displays and Hides elements and once complete, can optionally scroll to specified element.
	*
	* @param		toDisplay			An array of element ID's to display
	* @param		toHide				An array of element ID's to hide
	* @param		scrollTo			The element ID to scroll to once the view has been set (optional)
	*/
	this.setView = function(toDisplay, toHide, scrollTo)
		{
		// 1. Check if we've been passed an arrays or a strings for display param
		if (typeof(toDisplay) != 'array' && typeof(toDisplay) != 'object')
			{
			var toDisplay = new Array(typeof(toDisplay) != 'undefined' ? toDisplay : '');
			}

		// 2. Check if we've been passed an arrays or a strings for hide param
		if (typeof(toHide) != 'array' && typeof(toHide) != 'object')
			{
			var toHide = new Array(typeof(toHide) != 'undefined' ? toHide : '');
			}

		// 3. Display elements
		for (var x = 0; x < toDisplay.length; x++)
			{
			if (document.getElementById(toDisplay[x]))
				{
				document.getElementById(toDisplay[x]).style.display = 'block';
				}
			}
		
		// 4. Hide elements
		for (var x = 0; x < toHide.length; x++)
			{
			if (document.getElementById(toHide[x]))
				{
				document.getElementById(toHide[x]).style.display = 'none';
				}
			}
		
		// 5. Scroll to specified element, if supplied
		if (arguments.length == 3)
			{
			if (document.getElementById(scrollTo))
				{
				xhtml.smoothScroll(scrollTo);
				}
			}
		}


	
	/**
	* Creates a video player window and loads the specified video into it
	*
	* @param		videoName		The name of the video to load
	*/
	this.playVideo = function(videoName, obj)
		{
		url = 'videos/' + videoName + '.php';

		var width = 320;
		var height = 280;
		var left = (screen.width ? (screen.width/2)-(width/2) : 40);
		var top = (screen.height ? (screen.height/2)-(height/2) : 40);

		if (left < 0)
			{
			left = 0;
			}
		if (top < 0)
			{
			top = 0;
			}

		window.open(url, 'lovaasvideo', 'toolbar=no,location=no,directories=no,status=yes,scrollbars=no,resizable=yes,copyhistory=no,top='+top+',left='+left+',width='+width+',height='+height);
		return false;
		}


	// Step 3. Define Private Methods

	/**
	* Finds the parent of the element with a node type of parentTagName
	*
	* @param		element					The element object to find the parent of
	* @param		parentTagName		The type of parent element to find
	*
	* @return		The element's parent object of the specified type, or null if no parent of that type could be found
	*/
	function findParent(element, parentTagName)
		{
		if (element == null)
			{
			return null;
			}
		else
			{
			if ( element.nodeType == 1 && element.tagName.toLowerCase() == parentTagName.toLowerCase() )
				{
				return element;
				}
			else
				{
				return findParent(element.parentNode, parentTagName);
				}
			}
		}
	}


/**
* Inherts a prototype from the specified class, updates the constructor reference
*
* @param		parent		The parent class or object
* @return		The inherted object
*/
Function.prototype.inheritsFrom = function( baseClass )
	{
	// Inherit the base class
	this.prototype = new baseClass;
	this.prototype.constructor = this;

	// Add access to the base's methods
	this.prototype.base = {};
	for (method in this.prototype)
		{
		// hasOwnProperty test is a workaround for "for..in" bug, see: http://yuiblog.com/blog/2006/09/26/for-in-intrigue/
		if (typeof this.prototype[method] === 'function' && this.prototype.hasOwnProperty(method) && this.prototype[method] !== this.prototype.constructor)
			{
			this.prototype.base[method] = this.prototype[method];
			}
		}
	return this;
	}