/*
	======================================================================
	BASIC VARIABLES STUFF
	======================================================================
*/
var dhtmlCapable = (document.getElementById || document.all || document.layers);
var hasCookiesOn = document.cookie;
var cssCachedStyles = new Array ();	// Used as a temporary lookup table to boost performance
var preloadCache = new Array ();	// Used by preloadImg
var sectionStatus = new Array();	// Used on some pages
var helpScreenVisible = false;

/*
	======================================================================
	IMAGE BASED FUNCTIONS
	======================================================================
*/
function preloadCore (filename, filenameClean)
{
	// Clean our timeout entry completely so it can be repurposed
	clearTimeout(preloadCache[filenameClean]);
	delete(preloadCache[filenameClean]);

	preloadCache[filenameClean] = new Image(1, 1);
	preloadCache[filenameClean].src = filename;
}

//	------------------------------------------------------------------------------------------
function preloadImg (filename, fullPath, suppressSkinPath)
{
	if (typeof(fullPath) == 'undefined') {
		if (skinPath && typeof(suppressSkinPath) != 'undefined') {
			fullPath = skinPath +"images/";
			
		} else {
			pageURL = parseURL();
			fullPath = pageURL['path'];
		}
	}

	if (fullPath.charAt(fullPath.length - 1) != '/')
		fullPath = fullPath +"/";
	
	filenameClean = stripFilename(filename);
	
	// Set a timeout which is later repurposed for the proper filename set
	preloadCache[filenameClean] = setTimeout('preloadCore("'+ fullPath + filename +'", "'+ filenameClean +'")', 100);
}

//	------------------------------------------------------------------------------------------
function preloadImgState (fileName, preloadType, fullPath)
{
	if (!preloadType || typeof(preloadType) != 'object')
		preloadType = new Array('n');

	if (!fullPath) {
		pageURL = parseURL();
		fullPath = pageURL['path'];
	}
	
	fileSplit = extractImgState(fileName);
	
	for (var element in preloadType) {
		preloadImg(fileSplit['leading'] + preloadType[element] + fileSplit['trailing'], fullPath);
	}
}

//	------------------------------------------------------------------------------------------
function findStyleRule (styleName)
{
	// If no stylesheets exist, return! This also denies Opera any usability until they implement this property
	if (!document.styleSheets) {
		return false;
	}

	// Check if the CSS style has already been cached (or has been searched for and explicitly set to false)
	if (cssCachedStyles[styleName] || cssCachedStyles[styleName] === false) {
		return cssCachedStyles[styleName];
	}
	
	// If the style has not been cached, continue	
	var theRules = new Array();
	
	// We need to count backwards, so that the last defined CSS takes precedence over the first
  	for (i = document.styleSheets.length - 1; i >= 0; i--) { 
    	// Mozilla/IE W3C compatibility
		if (document.styleSheets[i].cssRules)
			theRules = document.styleSheets[i].cssRules;
  		else if (document.styleSheets[i].rules)
			theRules = document.styleSheets[i].rules;
		
		for (j = 0; j < theRules.length; j++) {
			if (theRules[j].selectorText == styleName) {
				// Add the style to our CSS cache and then return
				cssCachedStyles[styleName] = new Array();
				cssCachedStyles[styleName]['stylesheet'] = i;
				cssCachedStyles[styleName]['element'] = j;
				cssCachedStyles[styleName]['css'] = theRules[j];
				
				return cssCachedStyles[styleName];
			}
		}
    }
	
	// Report to our cache that the style could not be found, then return false
	cssCachedStyles[styleName] = false;
	
	return false;
}

//	------------------------------------------------------------------------------------------
function getObjectRef (objectName)
{
	if (typeof(objectName) == 'object')
	{
		return objectName;
	}
	else if (document.getElementById && document.getElementById(objectName))  
	{
		return document.getElementById(objectName);
	}
	else if (document.all && document.all[objectName])
	{
		return document.all[objectName];
	}
	else if (document.layers && document.layers[objectName])
	{
		return document.layers[objectName];
	}
	
	return false;
}

//	------------------------------------------------------------------------------------------
function setStyle (objectName, styleName, value)
{
	var object = getObjectRef(objectName);
    object.style[styleName] = value;
}

//	------------------------------------------------------------------------------------------
function swapImg (imageSource, mode)
{
	if (typeof(imageSource) == 'object')
		imageID = imageSource.id;
	else
		imageID = imageSource;

	if (document.images[imageID]) {
		// Use the faster document.images array as a first preference
		fileSplit = extractImgState(document.images[imageID].src);
		
		if (mode == 'over')
			insertChar = fileSplit['middle'] +'o';
		else if (mode == 'out')
			insertChar = fileSplit['middle'].charAt(0);
		else if (mode == 'press')
			insertChar = 'p';
		
		document.images[imageID].src = fileSplit['leading'] + insertChar + fileSplit['trailing'];
		return true;
		
	} else if (curImg = new GetObject(imageID)) {
		// If we do not have a document.images entry, test for an object (eg. form submit buttons of type image)
		fileSplit = extractImgState(curImg.object.src);
		
		if (mode == 'over')
			insertChar = fileSplit['middle'] +'o';
		else if (mode == 'out')
			insertChar = fileSplit['middle'][0];
		else if (mode == 'press')
			insertChar = 'p';
		
		curImg.object.src = fileSplit['leading'] + insertChar + fileSplit['trailing'];
		return true;
		
	} else {
		return false;
	}
}

//  ------------------------------------------------------------------------------------------
function findNode(startingNode, tagName)
{
  // on Firefox, the <td> node might not be the firstChild node of the <tr> node
  myElement=startingNode;
  var i=0;
  while (myElement && 
    (!myElement.tagName || (myElement.tagName && myElement.tagName!=tagName)))
  {
    myElement=startingNode.childNodes[i++];
  } 
  if (myElement && myElement.tagName && myElement.tagName==tagName)
  {
    return myElement;
  }
  // On Internet Explorer, the <tr> node might be the firstChild node of the <tr> node 
  else if (startingNode.firstChild)
    return findNode(startingNode.firstChild, tagName);
  return 0;
}
//  ------------------------------------------------------------------------------------------
function swapHighlightRow (objectName, polarity)
{
  if (!objectName)
    return;

  // The following code traverses every <td> within the <tr> and highlights it
  // by changing its style[backgroundColor] property
  if (objectName)
  {
    var tableRow=objectName;

    var tableCell=findNode(objectName, "TD"); 

    i=0;
    // Loop through every sibling.  Theoretically, a sibling of a <td> should be 
    //  another <td>, but this is not always the case on certain browsers, 
    //  so we need to check the tagName to be sure and skip to the next 
    //  sibling if the sibling is not a <td>)
    // We then highlight every siblings
    while (tableCell)
    {
      // Make sure it's actually a cell (a <td>)
      if (tableCell.tagName=="TD")
      {
        // If no style has been assigned, assign it, otherwise Netscape will 
        // behave weird.
        if (!tableCell.style)
        {
          tableCell.style={};
        }

		var colObjectName = getObjectRef(tableCell);
        
		// Attempt to just change the BG colour; if it fails, ignore
		switch (polarity)
		{
			case 'over':
				var changeStyle = findStyleRule('.'+ colObjectName.className +'over');
				if (changeStyle != false)
				{
					setStyle(colObjectName, 'backgroundColor', changeStyle.backgroundColor);
				}
				break;
			case 'out':
				var changeStyle = findStyleRule('.'+ colObjectName.className);
				if (changeStyle != false)
				{
					setStyle(colObjectName, 'backgroundColor', changeStyle.backgroundColor);
				}
				break;
		}

        i++;
      }
      // Go to the next cell in the row
      tableCell=tableCell.nextSibling;
    }
  }
}

//	------------------------------------------------------------------------------------------
function swapHighlight (objectName, polarity)
{	
	var objectName = getObjectRef(objectName);

	// Attempt to just change the BG colour; if it fails, ignore
	switch (polarity)
	{
		case 'over':
			var changeStyle = findStyleRule('.'+ objectName.className +'over');
			if (changeStyle != false)
			{
				setStyle(objectName, 'backgroundColor', changeStyle.backgroundColor);
			}
			break;
		case 'out':
			var changeStyle = findStyleRule('.'+ objectName.className);
			if (changeStyle != false)
			{
				setStyle(objectName, 'backgroundColor', changeStyle.backgroundColor);
			}
			break;
	}
}

//	------------------------------------------------------------------------------------------
function extractImgState (filename, includeServer)
{
	fileSplit = new Array();
	
	leadExtract = filename.lastIndexOf('-') + 1;
	trailExtract = filename.lastIndexOf('.');
	
	fileSplit['leading'] = filename.substr(0, leadExtract);
	
	// Rip out the server prepend, as this often interferes with mouseOver sources
	if (!includeServer && fileSplit['leading'].indexOf('://') != -1)
		// The indexOf '/' here starts at the 9th character in, which is pretty safe for a URL
		fileSplit['leading'] = fileSplit['leading'].substr(fileSplit['leading'].indexOf('/', 9));

	fileSplit['middle'] = filename.substring(leadExtract, trailExtract);
	fileSplit['trailing'] = filename.substr(trailExtract);
	
	return fileSplit;
}

/*
	======================================================================
	ALERT FUNCTIONS
	======================================================================
*/
function confirmEvent (eventType, eventItem, extendedError, joinItem)
{
	if (!extendedError)
		extendedError = '';
	else
		extendedError = ' '+ extendedError;
	
	if (!joinItem)
		joinItem = 'this';
	
	return confirm('Are you sure you want to '+ eventType +' '+ joinItem +' '+ eventItem +'?'+ extendedError);
}

/*
	======================================================================
	CORE DHTML FUNCTIONS
	======================================================================
*/
// Get a reference to an object based on what references are available to the browser
function GetObject (objectName)
{
	if (document.getElementById && document.getElementById(objectName)) {
		this.object = document.getElementById(objectName);
		
		if (document.getElementById(objectName).style)
			this.style = document.getElementById(objectName).style;
	
	} else if (document.all && document.all[objectName]) {
		this.object = document.all[objectName];
		
		if (document.all[objectName].style)
			this.style = document.all[objectName].style;
	
	} else if (document.layers && document.layers[objectName]) {
		this.object = document.layers[objectName];
		this.style = document.layers[objectName];
		
	} else {
		return false;
	}
}

//	------------------------------------------------------------------------------------------
function hook ()
{
	// This function deliberately left blank (-;
}

/*
	======================================================================
	CSS FUNCTIONS
	======================================================================
*/
function swapTableBg (referenceSource, mode, multiple)
{
	if (typeof(referenceSource) == 'object')
		referenceObj = referenceSource.id;
   else
		referenceObj = referenceSource;
	
	if (mode == 'over') {
		// Back up the old colour
		if (multiple >= 2) {
			for (var i = 1; i <= multiple; i++) {
				// First pull out our style
				tableColour = new GetObject(referenceObj +'_'+ i);
				
				// Although we could just swap the entire class, the responsiveness is crap, so we just set one property
				if (tableColour.style) {
					tableClassOld = tableColour.object.className;
					rulesObject = findStyleRule('.'+ tableClassOld +'over');
					
					if (rulesObject !== false) {
						tableColour.style.backgroundColor = rulesObject.css.style.backgroundColor;
					}
				}
			}
		} else {
			tableColour = new GetObject(referenceObj);
	
			// Although we could just swap the entire class, the responsiveness is crap, so we just set one property
			if (tableColour.style) {
				tableClassOld = tableColour.object.className;
				rulesObject = findStyleRule('.'+ tableClassOld +'over');
				
				if (rulesObject !== false) {
					tableColour.style.backgroundColor = rulesObject.css.style.backgroundColor;
				}
			}
		}
		
	} else if (mode == 'out') {
		// Restore
		if (tableColour.style) {
			rulesObject = findStyleRule('.'+ tableClassOld);
			
			if (rulesObject !== false) {
				if (multiple >= 2) {
					for (var i = 1; i <= multiple; i++) {
						tableColour = new GetObject(referenceObj +'_'+ i);
						tableColour.style.backgroundColor = rulesObject.css.style.backgroundColor;
					}
				} else {
					tableColour.style.backgroundColor = rulesObject.css.style.backgroundColor;
				}
			} else {
				tableColour.style.backgroundColor = '';
			}
		}
	}
}

function openWindow (url, windowName, width, height, xpos, ypos, resizable, scrollbars, locationbar, toolbar, menubar, statusbar)
{
	width = (!width) ? 512 : width;
	height =  (!height) ? 384 : height;
	
	xpos = (xpos == 'center') ? xpos = (screen.width / 2) - (width / 2) : xpos;
	ypos = (ypos == 'center') ? (screen.height / 2) - (height / 2) : ypos;
	
	// Options for the popup window
	resizable = (resizable) ? 'yes' : 'no';
	scrollbars = (scrollbars) ? 'yes' : 'no';
	locationbar = (locationbar) ? 'yes' : 'no';
	toolbar = (toolbar) ? 'yes' : 'no';
	menubar = (menubar) ? 'yes' : 'no';
	statusbar = (statusbar) ? 'yes' : 'no';
		
	return window.open(url, windowName, 'width='+ width +', height='+ height +', left='+ xpos +', top='+ ypos +', resizable='+ resizable +', scrollbars='+ scrollbars +', location='+ locationbar +', toolbar='+ toolbar +', status='+ statusbar +', menubar='+ menubar);
}

function launchXeUCC (currencyAmount, currencyFrom)
{
	CurrencyWindow = window.open ('', 'CurrencyWindow', 'toolbar=0,location=0,directories=0,status=0,menubar=0,scrollbars=0,resizable=1,height=150,width=575');
	CurrencyWindow.focus();
	CurrencyWindow.location.href = 'http://www.xe.com/pca/input.cgi?AmountSet='+ currencyAmount +'&From='+ currencyFrom +'&ToSelect=USD'
}

/*
------------========================------------
FEEDBACK FUNCTIONS
------------========================------------
*/

// Requires a setHit to be executed first
function disableObject (formName, objectID, newText) {
	// If we're doing a form post, check the appropriate variables
	if (document.forms[formName][formName +"[action_hit]"]) {
		document.forms[formName][formName +"[action_hit]"].value = objectID.substring(formName.length + 1, objectID.length - 1);
	}

	// Pull out an object reference
	theObject = new GetObject(objectID);

	if (theObject.object) {
		theObject.object.disabled = true;
		
		if (newText) {
			theObject.object.value = newText;
		}
	}
}

// ------------========================------------
// Sets up a hit object, to be used by disableObject on form submit
function setHit (objectID, hitMessage) {
	formHit = new Array();
	formHit['id'] = objectID;
	formHit['message'] = hitMessage;
}

/*
------------------------------------------------------------------------------------------
*/

function collapseSect (sectID, polarity, imgName, imgState)
{
	var changeImg = getObjectRef(imgName);
	var changeImgTo;
	
	switch (polarity)
	{
		case 'show':
			swapClass(sectID +'_off', 'hideitem', 'showitem');
			swapClass(sectID +'_on', 'showitem', 'hideitem');
			(changeImg !== false) ? changeImgTo = imgState[0] : null;
			break;
		case 'hide':
			swapClass(sectID +'_off', 'showitem', 'hideitem');
			swapClass(sectID +'_on', 'hideitem', 'showitem');
			(changeImg !== false) ? changeImgTo = imgState[1] : null;
			break;
		case 'toggle':
			var sectRef = getObjectRef(sectID);
			
			if (sectRef.className == 'hideitem')
			{
				swapClass(sectRef, 'showitem', 'hideitem');
				(changeImg !== false) ? changeImgTo = imgState[0] : null;
			}
			else
			{
				swapClass(sectRef, 'hideitem', 'showitem');
				(changeImg !== false) ? changeImgTo = imgState[1] : null;
			}
			
			break;
		case 'on':
			swapClass(sectID, 'showitem', 'hideitem');
			(changeImg !== false) ? changeImgTo = imgState[0] : null;
			break;
		case 'off':
			swapClass(sectID, 'hideitem', 'showitem');
			(changeImg !== false) ? changeImgTo = imgState[1] : null;
			break;
		default:
			return;
	}
	
	if (changeImg !== false)
	{
		var imgSplit = extractImgState(changeImg.src);
		changeImg.src = imgSplit['leading'] + changeImgTo + imgSplit['trailing'];
	}
}

/*
------------------------------------------------------------------------------------------
*/

function collapseSectGroup (selectedValue, elementSuffix, elementPrefix)
{
	var count = (typeof elementSuffix == 'number') ? elementSuffix : elementSuffix.length - 1;
	var elementPrefix = (typeof elementPrefix == 'undefined') ? '' : elementPrefix;

	for (var i = 0; i <= count; i++)
	{
		var currentValue = (typeof elementSuffix == 'number') ? i : elementSuffix[i];
		
		if (selectedValue !== '' && selectedValue == currentValue)
		{
			collapseSect(elementPrefix + currentValue, 'on');
		}
		else
		{
			collapseSect(elementPrefix + currentValue, 'off');
		}
	}
}

function collapseSectGroupCheck (selectedValue, divName)
{
	if(selectedValue.checked == true) 
	{
		collapseSect(divName, 'on');
	}
	else
	{
		collapseSect(divName, 'off');
	}
}

/*
-------------------------------------------------------------------------------------------
*/

function swapClass (objectName, newClass, oldClass)
{
	// Get the object based on whether we've passed an ID name or an explicit reference
	var object = getObjectRef(objectName);
	var classPrepend = '', classAppend = '', classPosition, finalClass;
	
	// Swap the class
	if (object.className)
	{
		// Rip out the old class if necessary
		if (typeof oldClass == 'string' && oldClass != '')
		{
			var classPosition = object.className.indexOf(oldClass);
			
			if (classPosition >= 0)
			{
				// We've found an old class that needs to be replaced with the new
				classPrepend = object.className.substring(0, classPosition);
				classAppend = object.className.substr(classPosition + oldClass.length);
				finalClass = classPrepend + newClass + classAppend;
				
				if (object.className != finalClass)
				{
					// Replace old with new and return
					object.className = finalClass;
					return object.className;
				}
			}
			else
			{
				// No change necessary
				return object.className;
			}
		}
		else
		{
			// Replace the class outright
			object.className = newClass;
			return object.className;
		}
	}
	else
	{
		return false;
	}
}

/*
 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
 * Digest Algorithm, as defined in RFC 1321.
 * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for more info.
 */

/*
 * Configurable variables. You may need to tweak these to be compatible with
 * the server-side, but the defaults work in most cases.
 */
var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */

/*
 * These are the functions you'll usually want to call
 * They take string arguments and return either hex or base-64 encoded strings
 */
function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }

/*
 * Perform a simple self-test to see if the VM is working
 */
function md5_vm_test()
{
  return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
}

/*
 * Calculate the MD5 of an array of little-endian words, and a bit length
 */
function core_md5(x, len)
{
  /* append padding */
  x[len >> 5] |= 0x80 << ((len) % 32);
  x[(((len + 64) >>> 9) << 4) + 14] = len;

  var a =  1732584193;
  var b = -271733879;
  var c = -1732584194;
  var d =  271733878;

  for(var i = 0; i < x.length; i += 16)
  {
    var olda = a;
    var oldb = b;
    var oldc = c;
    var oldd = d;

    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);

    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);

    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);

    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);

    a = safe_add(a, olda);
    b = safe_add(b, oldb);
    c = safe_add(c, oldc);
    d = safe_add(d, oldd);
  }
  return Array(a, b, c, d);

}

/*
 * These functions implement the four basic operations the algorithm uses.
 */
function md5_cmn(q, a, b, x, s, t)
{
  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
}
function md5_ff(a, b, c, d, x, s, t)
{
  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function md5_gg(a, b, c, d, x, s, t)
{
  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function md5_hh(a, b, c, d, x, s, t)
{
  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5_ii(a, b, c, d, x, s, t)
{
  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
}

/*
 * Calculate the HMAC-MD5, of a key and some data
 */
function core_hmac_md5(key, data)
{
  var bkey = str2binl(key);
  if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);

  var ipad = Array(16), opad = Array(16);
  for(var i = 0; i < 16; i++)
  {
    ipad[i] = bkey[i] ^ 0x36363636;
    opad[i] = bkey[i] ^ 0x5C5C5C5C;
  }

  var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
  return core_md5(opad.concat(hash), 512 + 128);
}

/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
 * to work around bugs in some JS interpreters.
 */
function safe_add(x, y)
{
  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  return (msw << 16) | (lsw & 0xFFFF);
}

/*
 * Bitwise rotate a 32-bit number to the left.
 */
function bit_rol(num, cnt)
{
  return (num << cnt) | (num >>> (32 - cnt));
}

/*
 * Convert a string to an array of little-endian words
 * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
 */
function str2binl(str)
{
  var bin = Array();
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < str.length * chrsz; i += chrsz)
    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
  return bin;
}

/*
 * Convert an array of little-endian words to a string
 */
function binl2str(bin)
{
  var str = "";
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < bin.length * 32; i += chrsz)
    str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
  return str;
}

/*
 * Convert an array of little-endian words to a hex string.
 */
function binl2hex(binarray)
{
  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
  var str = "";
  for(var i = 0; i < binarray.length * 4; i++)
  {
    str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
           hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
  }
  return str;
}

/*
 * Convert an array of little-endian words to a base-64 string
 */
function binl2b64(binarray)
{
  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  var str = "";
  for(var i = 0; i < binarray.length * 4; i += 3)
  {
    var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
                | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
                |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
    for(var j = 0; j < 4; j++)
    {
      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
    }
  }
  return str;
}

function existsInArray(searchArray, searchString)
{
	var md5String = hex_md5(searchString.toUpperCase());

	for(var x = 0; x < searchArray.length; x++) 
	{
		if(md5String == searchArray[x]) {
			return true;
		}
	}
	
	return false;
}

/*
	======================================================================
	ANIMATION FUNCTIONS
	======================================================================
*/

// Slide show function for stepping through slides

function SlideSection(targ, slideArea, offset)
{
	// Store the last slide, update the current on
	if (currentSlide == targ) {
		return;
	}
	lastSlide = currentSlide;
	currentSlide = targ;
    
	// Get the element we want to move, get it's position for moving to	
	theSlide = document.getElementById(slideArea);
	pos = findElementPos(document.getElementById(targ));
	
	// Get the position of the offset div - the one furthest to the left	
	if (offset != "") {
		offsetPos = findElementPos(document.getElementById(offset));
		pos[0] = pos[0] - offsetPos[0];
	}
	
	slideStart(theSlide, theSlide.scrollLeft, pos[0], "x");

}

// Set up a button to move the slideArea

function SlideButton(direction, slideParent, slideArea, offset) {

	slideElement = document.getElementById(slideParent);
	slideArray = new Array();    
    
	if (slideElement.hasChildNodes())
	{
		var children = slideElement.childNodes;
		for (var i = 0; i < children.length; i++) 
		{
			if (slideElement.childNodes[i].className == "diagram") {
				slideArray.push(slideElement.childNodes[i].id.split("-")[0]);
			}
		}
	}

	// Iterate through the number of slides
	for (var i = 0; i < slideArray.length; i++) {
		if (slideArray[i] == currentSlide.split("-")[0]) {
			if (direction == "left") {
				if (i - 1 < 0) {
					gotoSlide = slideArray[slideArray.length - 1];
				} else {
					gotoSlide = slideArray[i - 1];
				}
			} else {
				if ((i + 1) > (slideArray.length - 1)) {
					gotoSlide = slideArray[0];
				} else {
					gotoSlide = slideArray[i + 1];
				}
			}
		}
	}
	
	SlideSection(gotoSlide+slideRef, slideArea, offset);

}

// Animation functions

var slideanim = {time:0, begin:0, change:0.0, duration:0.0, element:null, timer:null};

function slideStart(elem, start, end, direction)
{
	if (slideanim.timer != null) {
		clearInterval(slideanim.timer);
		slideanim.timer = null;
	}
	slideanim.time = 0;
	slideanim.begin = start;
	slideanim.change = end - start;
	slideanim.duration = 50;
	slideanim.element = elem;
	
	if (direction == "x") {
		slideanim.timer = setInterval("slideX();", 15);
	}
	else {
		slideanim.timer = setInterval("slideY();", 15);
	}
}
function slideY()
{
	if (slideanim.time > slideanim.duration) {
		clearInterval(slideanim.timer);
		slideanim.timer = null;
	}
	else {
		move = linear(slideanim.time, slideanim.begin, slideanim.change, slideanim.duration);
		slideanim.element.scrollTop = move; 
		slideanim.time++;
	}
}

function slideX()
{
	if (slideanim.time > slideanim.duration) {
		clearInterval(slideanim.timer);
		slideanim.timer = null;
	}
	else {
		move = cubicInOut(slideanim.time, slideanim.begin, slideanim.change, slideanim.duration);
		slideanim.element.scrollLeft = move;
		slideanim.time++;
	}
}
function findElementPos(elemFind)
{
	var elemX = 0;
	var elemY = 0;
	do {
		elemX += elemFind.offsetLeft;
		elemY += elemFind.offsetTop;
	} while ( elemFind = elemFind.offsetParent )

	return Array(elemX, elemY);
}

// Motion functions

function linear(t, b, c, d)
{
	return c*t/d + b;
}

function sineInOut(t, b, c, d)
{
	return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
}

function cubicIn(t, b, c, d) {
	return c*(t/=d)*t*t + b;
}

function cubicOut(t, b, c, d) {
	return c*((t=t/d-1)*t*t + 1) + b;
}

function cubicInOut(t, b, c, d)
{
	if ((t/=d/2) < 1) return c/2*t*t*t + b;
	return c/2*((t-=2)*t*t + 2) + b;
}

function bounceOut(t, b, c, d)
{
	if ((t/=d) < (1/2.75)){
		return c*(7.5625*t*t) + b;
	} else if (t < (2/2.75)){
		return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
	} else if (t < (2.5/2.75)){
		return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
	} else {
		return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
	}
}