// <script language="JavaScript">
// Copyright (c) 2000 Youbet.com, Inc. All Rights Reserved.
 
//// COMMON INIT CODE

/*
// Allows closing the pop-up forms from both FireFox and IE
function closeMe(oWindow) {
	oWindow.open('','_parent','');
	oWindow.close();
}
*/

// nullFunction    
// Purpose: Placeholder event handler that does nothing.
function nullFunction() {}

// cancelEvent() 
// Purpose:	Used to cancel any generic event
// Usage: document.onselectstart = cancelEvent; // don't allow dragging. anywhere.
function cancelEvent() {
	try {
		event.cancelBubble = true;
		return false;
	} catch (e) {
		handleException(ChainableException.fromGenericException(e,getCallContext()));
	}
}


// check to see if we can instantiate a named ActiveX Object
// return true if yes, false if no
function checkForActiveXObject(sProgId) {
	try {
		var o = new ActiveXObject(sProgId);
		if (o == null) {
			return false;
		} else {
			return true;
		}
	} catch(e) {
		return false;
	}
}

function checkAndInstallYBRequest() {
	// Code added by HS to check if YBRequest control is installed and functional
	// Duplicated in racing/common/ssIncludes/incUtils.js  [:(]
	// Return false if IE browser and YBRequest was unsuccessfully installed.
	
	// Check if IE browser, if not, return TRUE
	// this allows user to enter YBE, where they receive a page telling them Netscape isn't supported.
	var bIsIE = (navigator.appVersion.match(/MSIE/) != null);	
	if (!bIsIE) {
		return true; 
	}
	
	// Try to instantiate YBRequest... if it fails show the install page in a dialog
	var oYBRequest;
	try {
		oYBRequest = new ActiveXObject("YBRequest.YBHttpRequest");
		var i = oYBRequest.getCompatibilityCode();  // check that it's version 2.0.0.11 or better
	} catch (e) {
		// couldn't instantiate, show install dialog
		var oDialogArgs = new Object();
		oDialogArgs.installed = false; // pass in flag to be set if install succeeds
		window.showModalDialog(COMPONENT_INSTALL_URL,oDialogArgs,"dialogHeight: 300px; dialogWidth: 440px;");
		if (!oDialogArgs.installed) {
			return false; // install failed
		}
	}
	
	// YBRequest is installed, test that it is functional
	try {
		if (!oYBRequest) {
			oYBRequest = new ActiveXObject("YBRequest.YBHttpRequest");
		}
		var i = oYBRequest.status; // should return an int. If control is not working, returns 'undefined'
		if (isNaN(parseInt(i))) {
			throw("foo");
		}
	} catch(e) {
		// call to status failed or was not an int, show 'corrupt component' page.
		window.showModalDialog(CORRUPT_COMPONENT_URL,oDialogArgs,"dialogHeight: 300px; dialogWidth: 440px;");
		return false;
	}
	
	// everything OK, return true
	return true;
}


function getCookieValue(sCookieName) {
	var arCookies = document.cookie.split(";");
	for (var i=0; i < arCookies.length; i++) {
		var aCrumb = arCookies[i].split("=");		
		var sName = aCrumb[0].replace(/^[\s]*/,''); // cut leading spaces		
		if (sName == sCookieName) {
			var sValue = aCrumb[1];
			return sValue;
		}			
	}
}


function resizeMainDIV() {
	// resize the main display div to fit.
	return; // do not need resize 640x480 anymore
	if (document.body.offsetWidth <= BODY_OFFSET_WIDTH_AT_640_MAXIMIZED) {
		divMainMenuContainer.style.width = 547;
		divMain.style.width = 547;
		tblMainMenubar.style.width = 537;
		if (document.all['divWagerBtn'] != null) {
			divWagerBtn.style.left = 453;
		}
		
		if (document.all['divTrackAndResultsButtons'] != null) {
			divTrackAndResultsButtons.style.left = 375;
		}
		
		if (document.all['divTrackDataDisplayArea'] != null) {
			divTrackDataDisplayArea.style.width = 545;
		}

		if (document.all['divLateChangesSidebar'] != null) {
			divLateChangesSidebar.style.left = 549;
		}
	}
}

// reload the current page...
function reloadPage() {
	try {
		var sURL = location.href;
		
		// add 'fromReload' to the querystring, 
		// so the FUM can avoid logging the auto-reloading of pages
		if (sURL.match(/fromReload/) == null) {
			if (location.search == "") {
				sURL += "?fromReload=true";
			} else {
				sURL += "&fromReload=true";
			}
		}
		location.href = sURL;
	} catch (e) {
		handleException(ChainableException.fromGenericException(e,getCallContext()));
	}
}

// replace '%1', '%2' etc. in sInput string with arguments 2, 3 etc.
// used for inserting values into the middle error messages.
// will handle as many inputs as you want.
function replaceWildcards(sInput) {
	var iNumReplacements = replaceWildcards.arguments.length - 1;
	if (iNumReplacements < 1) {
		return sInput;
	} else {
		for (var i=1;i<=iNumReplacements;i++) {
			var oRegExp = new RegExp("%" + i);
			oRegExp.global = true;
			sInput = sInput.replace(oRegExp,replaceWildcards.arguments[i]);
		}
		return sInput;
	}	
}

// replace '%1', '%2' etc. in sInput string with arguments 2, 3 etc.
// used for inserting values into the middle error messages.
// will handle as many inputs as you want.
function replaceWildcards2(sInput) {
	var iNumReplacements = replaceWildcards2.arguments.length - 1;
	if (iNumReplacements < 1) {
		return sInput;
	} else {
		for (var i=1;i<=iNumReplacements;i++) {
			var oRegExp = new RegExp("%" + i, "g"); //assigning the global flag because we can't do it like below.
			// oRegExp.global = true; // this prop is readonly and cannot be assigned like this.
			sInput = sInput.replace(oRegExp,replaceWildcards2.arguments[i]);
		}
		return sInput;
	}	
}

/******
sendXMLRequest() -  HS - 6/5/02
Purpose: 
	Used in simple SOAP-like API to post an XML document to the host at a given URL, then
	parse the XML response from the host.
Inputs: 
	sURL - The URL to send the doc to. sXML - The XML string to send.
Outputs/Returns:
	The XML string returned by the host.
Error handling:
	Throws ChainableExceptions.
Depends on:
	ChainableException.
Used by:
	OnlineCatalog for submitPurchase and priceCart functions.
Notes:
Modifications:
*******/
function sendXMLRequest(sURL,sXML) {
		var iLength = sXML.length;
		var oXML = new ActiveXObject("Microsoft.XMLHTTP");
		
		oXML.open("POST",sURL,false);	
		oXML.setRequestHeader("Content-Length",iLength);
		oXML.send(sXML);
		
		// make sure response is COMPLETED
		if (oXML.readyState != 4) {
			throw(new ChainableException(getCallContext(),null,-1,"XML request didn't complete, readyState=" + oXML.readyState));		
		}
		
		// check response code for 200-OK
		if (oXML.status != 200) {
			throw(new ChainableException(getCallContext(),null,-1,"Bad response status: " + oXML.status));
		}
		
		// check that we got an XML response
		if (oXML.getResponseHeader("Content-type") != "text/xml") {
			throw(new ChainableException(getCallContext(),null,-1,"Bad response content type: " + oXML.getResponseHeader("Content-type")));
		}
		
		// check for parse error
		if (oXML.responseXML.parseError.errorCode != 0) {
				throw(ChainableException.fromMSXMLParseError(oXML.responseXML.parseError,getCallContext()));
		}
		
		// check for chainable exception
		var oException = ChainableException.fromMSXMLDOM(oXML.responseXML);		
		if (oException) {
			throw(oException);
		}
		
		return oXML.responseXML.documentElement.xml;
}

var DATE_STYLE_MDYYYY = 0;
var DATE_STYLE_HMMSSAA = 1;
var DATE_STYLE_MDYY = 2;
var DATE_STYLE_MMDDYY = 3;
var DATE_STYLE_MDYY_HMMSSAA = 4;
var DATE_STYLE_YYYYMD = 5;
var DATE_STYLE_YYYYMD_HMMSSAA = 6;
var DATE_STYLE_HMM = 7;
var DATE_STYLE_MDYY_HMM = 8;
var DATE_STYLE_MONTHDYYYY_HMMAA = 9;
var DATE_STYLE_HMMAA = 10;
var DATE_STYLE_MONTHDYYYY = 11;
var DATE_STYLE_HMMSS = 12;
var DATE_STYLE_YYYYMD_HMMSS_UTC = 13;
var DATE_STYLE_YYYYMMDD_HHMMSS_UTC = 14;
var DATE_STYLE_MMDDYYYY = 15;
/*
 * Returns a string representation of the specified Date object.
 * @param oDate The instance of the Date object to be formatted.
 * @param iDateStyle Specifies how to format the returned string.
 *  One occurrence of a field specifier (such as H for hour or D for day)
 *  means use the minimum number of characters in which the field can be expressed.
 *  Two or more occurrences of a field specifier (such as HH for hour or YYYY for year)
 *  means use exactly that number of characters to represent the field.
 */
function getDateString(oDate, iDateStyle) {
	var sDate = "";

	if (null == iDateStyle)
		iDateStyle = DATE_STYLE_MDYYYY;

	switch(iDateStyle) {
		case DATE_STYLE_HMM:
			var sHours = oDate.getHours();
			if (sHours > 12) sHours -= 12;
			if (0 == sHours) sHours = "12";
			var sMinutes = leftPad('0',oDate.getMinutes(),2);
			sDate = sHours + ":" + sMinutes;
			break;
		case DATE_STYLE_HMMAA:
			var sHours = oDate.getHours();
			var sAMPM = (sHours >= 12) ? " PM" : " AM";
			if (sHours > 12) sHours -= 12;
			if (0 == sHours) sHours = "12";
			var sMinutes = leftPad('0',oDate.getMinutes(),2);
			sDate = sHours + ":" + sMinutes + sAMPM;
			break;
		case DATE_STYLE_HMMSS:
			var sHours = oDate.getHours();
			var sMinutes = leftPad('0',oDate.getMinutes(),2);
			var sSeconds = leftPad('0',oDate.getSeconds(),2);
			sDate = sHours + ":" + sMinutes + ":" + sSeconds;
			break;
		case DATE_STYLE_HMMSSAA:
			var sHours = oDate.getHours();
			var sAMPM = (sHours >= 12) ? " PM" : " AM";
			if (sHours > 12) sHours -= 12;
			if (0 == sHours) sHours = "12";
			var sMinutes = leftPad('0',oDate.getMinutes(),2);
			var sSeconds = leftPad('0',oDate.getSeconds(),2);
			sDate = sHours + ":" + sMinutes + ":" + sSeconds + sAMPM;
			break;
		case DATE_STYLE_MDYY:
			var sYear = oDate.getYear().toString();
			sDate = (oDate.getMonth() + 1) + "/" 
				+ oDate.getDate() + "/" + sYear.substr(sYear.length - 2);
			break;
		case DATE_STYLE_MDYY_HMM:
			sDate = getDateString(oDate, DATE_STYLE_MDYY) + " " + getDateString(oDate, DATE_STYLE_HMM);
			break;
		case DATE_STYLE_MDYY_HMMSSAA:
			sDate = getDateString(oDate, DATE_STYLE_MDYY) + " " + getDateString(oDate, DATE_STYLE_HMMSSAA);
			break;
		case DATE_STYLE_MMDDYY:
			var sYear = oDate.getYear().toString();
			sDate = leftPad('0',(oDate.getMonth() + 1), 2) + "/" 
				+ leftPad('0',oDate.getDate(), 2) + "/" + sYear.substr(sYear.length - 2);
			break
		case DATE_STYLE_MONTHDYYYY:
			sDate = getMonthString(oDate.getMonth()) + " " + oDate.getDate() + ", " + oDate.getFullYear();
			break;
		case DATE_STYLE_MONTHDYYYY_HMMAA:
			sDate = getDateString(oDate, DATE_STYLE_MONTHDYYYY) + " " + getDateString(oDate, DATE_STYLE_HMMAA);
			break;
		case DATE_STYLE_YYYYMD:
			sDate = oDate.getFullYear() + "/" 
				+ (oDate.getMonth() + 1) + "/"
				+ (oDate.getDate());
			break;
		case DATE_STYLE_YYYYMD_HMMSSAA:
			sDate = getDateString(oDate, DATE_STYLE_YYYYMD) + " " + getDateString(oDate, DATE_STYLE_HMMSSAA);
			break;
		case DATE_STYLE_YYYYMD_HMMSS_UTC:
			sDate = oDate.getUTCFullYear() + '/' + (oDate.getUTCMonth() + 1) + '/' + oDate.getUTCDate() + ' ' +
					oDate.getUTCHours() + ':' + oDate.getUTCMinutes() + ':' + oDate.getUTCSeconds() + ' UTC';
			break;
		case DATE_STYLE_YYYYMMDD_HHMMSS_UTC:
			sDate = oDate.getUTCFullYear() + 
					'/' + 
					leftPad('0',oDate.getUTCMonth() + 1,2) + 
					'/' + 
					leftPad('0',oDate.getUTCDate(),2) + 
					' ' + 
					leftPad('0',oDate.getUTCHours(),2) + 
					':' + 
					leftPad('0',oDate.getUTCMinutes(),2) + 
					':'	+ 
					leftPad('0',oDate.getUTCSeconds(),2) + 
					' UTC';
			break;
		case DATE_STYLE_MMDDYYYY:
			sDate = ((10 > oDate.getMonth()+1) ? '0'+(oDate.getMonth()+1):(oDate.getMonth()+1)) +
					'/' + 
					((10 > oDate.getDate()) ? '0'+oDate.getDate():oDate.getDate()) +
					'/' + 
					oDate.getFullYear();
			break;
		default: // To maintain backward compatibility, fall through to DATE_STYLE_MDYYYY.
		case DATE_STYLE_MDYYYY:
			sDate = (oDate.getMonth() + 1) + "/" 
				+ oDate.getDate()  + "/" 
				+ oDate.getFullYear();
			break;
	} // switch
	return sDate;
}

function getMonthString(iMonth /* zero-based, to match JavaScript's Date.getMonth() */, iLanguageId) {
	if (null == getMonthString.arrMonthStrings) {
		// Lazy initialization for maximum efficiency
		getMonthString.arrMonthStrings = ["January", "February", "March", "April", "May", "June",
			"July", "August", "September", "October", "November", "December"];
	}
	return getMonthString.arrMonthStrings[iMonth];
}

function currentTime() {
	// This function is a convenience used during debugging.
	return getDateString(new Date(), DATE_STYLE_HMMSS);
}


function leftPad(sPadChar,sStringToPad,iPaddedLength) {
	var sOutput;
	// Pad a string with a given character to fill out its length to the specified length
	// leftPad('0','1',2) == '01'
	// leftPad('0','11',2) == '11'
	if (sPadChar == null) throw("Missing sPadChar argument when calling leftPad()");
	if (sStringToPad == null) throw ("Missing sStringToPad argument when calling leftPad()");
	if (iPaddedLength == null) throw ("Missing iPaddedLength argument when calling leftPad()");
	
	var iInLength = new String(sStringToPad).length;
	if (iInLength >= iPaddedLength) {
		sOutput = sStringToPad
	} else {
		var iAmountToPad = iPaddedLength - iInLength;
		var sPadString = "";
		// HACK... is there a more efficient way to do this in JS?
		for (var i = 0; i < iAmountToPad; i++) {
			sPadString += sPadChar;
		}
		sOutput =  sPadString + sStringToPad;
	}
	//alert("leftPad("+ sPadChar + "," + sStringToPad + "," + iPaddedLength + ") = " + sOutput);
	return sOutput;
}


////
// Updated version of getCurrencyString. Fixes a bug where commas were showing up in the wrong place.
// Uses 'getLocaleString' but is not desinged to be fully I18N compatible.
////
function getCurrencyString2(nAmount,bAlwaysShowCents) {
	return "$" + formatCurrency(nAmount,bAlwaysShowCents);
}

////
// Format a number using getLocaleString. Cents will be truncated if there are none unless bAlwaysShowCents is true.
//
// Examples (assuming en-US locale)
//
// formatCurrency(1234) returns "1,234"
// formatCurrency(1234,true) returns "1,234.00"
// formatCurrency(1234.56) returns "1,234.56"
//
///
function formatCurrency(nAmount,bAlwaysShowCents) {
	var iTheInt = parseInt(nAmount);
	if (isNaN(iTheInt)) {
		throw("Invalid parameter passed to formatNumber [" + nAmount + "]");
	}
	var fTheFloat = parseFloat(nAmount);
	var iDiff = Math.abs(iTheInt - fTheFloat);
	var sFormattedString = fTheFloat.toLocaleString();
	if (iDiff > 0 || bAlwaysShowCents) {
		// has cents already... leave this alone
		
	} else {
		// has no cents
		sFormattedString = sFormattedString.replace(/\..*$/,"");
	}
	return sFormattedString;
}





/* BROKEN AND DEPRECATED. Use getCurrencyString2().
 * 
 * Usage: sAmount = getCurrencyString(1000, "$", ",", ".", 2); // Yields "$1,000.00"
 */
function getCurrencyString(varAmt, sLeadingCurrencySymbol, sThousandsDelimiter,
	sDecimalSymbol, iDecimalPlaces, sTrailingCurrencySymbol) {
	// Check required parameter
	if (null == varAmt) throw("In getCurrencyString(). Missing varAmt parameter.");
	
	// Stick a $, commas, decimal point, and trailing zeroes on a number, as necessary.
	
	if (null == sLeadingCurrencySymbol) sLeadingCurrencySymbol = "$";
	if (null == sThousandsDelimiter) sThousandsDelimiter = ",";
	if (null == sDecimalSymbol) sDecimalSymbol = ".";
	if (null == iDecimalPlaces) iDecimalPlaces = 2;
	//if (null == sTrailingCurrencySymbol) sTrailingCurrencySymbol = "";

	var iDecimalPos;

	var sAmt = "" + varAmt; // Turn it into a String, if it isn't one already.
	if (isNaN(parseInt(varAmt))) {
		return sAmt;
	}
	
	// Add commas, as necessary.
	if ("" != sThousandsDelimiter) {
		iDecimalPos = "" == sDecimalSymbol ? -1 : sAmt.indexOf(sDecimalSymbol);
		if (iDecimalPos == -1)
			iDecimalPos = sAmt.length;

		var iPotentialComma = iDecimalPos - 4;
		while (iPotentialComma >= 0) {
			if (isDigit(sAmt.charAt(iPotentialComma))) {
				sAmt = insertString(sAmt, iPotentialComma + 1, sThousandsDelimiter);
				iPotentialComma -= 3;
			} else {
				iPotentialComma -= 4;
			}
		}
	}

	// If there isn't a decimal point, add one.
	if ("" != sDecimalSymbol) {
		iDecimalPos = sAmt.indexOf(sDecimalSymbol);
		if (iDecimalPos == -1) {
			sAmt += sDecimalSymbol;
			iDecimalPos = sAmt.length - 1;
		}
			
		// Make sure it is followed by iDecimalPlaces zeroes.
		while (sAmt.length - iDecimalPos - 1 < iDecimalPlaces)
			sAmt += "0";

		if ((sAmt.length - iDecimalPos - 1) > iDecimalPlaces) {
			// Make sure there are only iDecimalPlaces places after the decimal.
			// For now, we'll always round down.
			sAmt = sAmt.substring(0, iDecimalPos + 1 + iDecimalPlaces);
		}
	}
	
	if ("" != sLeadingCurrencySymbol)
		if (sAmt.indexOf(sLeadingCurrencySymbol) == -1)
			sAmt = sLeadingCurrencySymbol + sAmt;
/*
	if ("" != sTrailingCurrencySymbol)
		if (sAmt.indexOf(sTrailingCurrencySymbol) == -1)
			sAmt += sTrailingCurrencySymbol;
*/
	return sAmt;
}

function isDigit(c) {
	var iCharCode = c.charCodeAt(0);
	return ((48 <= iCharCode) && (iCharCode <= 57));
}

function toChar0UpperCase(s) {
	s = s.toLowerCase();
	return s.charAt(0).toUpperCase() + s.substr(1);
}

function insertString(sOld, iOffset, sNew) {
	return sOld.substring(0, iOffset)
		+ sNew + sOld.substr(iOffset);
}

// nonAlphaToUScore(s)
// Purpose: convert all non-alphanumeric characters in a string to underscores
function nonAlphaToUScore(s) {
	return s.replace(/[^a-z0-9]/gi,"_");
}

// mouseToChild()
// Purpose: Check if a mouseout event was triggered by entering a child element
// Requires: event Object triggered from an element
// Returns: true or false
// Throws: never
function mouseToChild() {
	var oElm = event.srcElement;
	var oToElm = event.toElement;
	if (oToElm) {
		if (oElm.children[oToElm.id] != null) {
			return true;
		} else {
			return false;
		}
	} else {
		return false;
	}
}

//  mouseInElement(oElm)
//	Purpose: check if the mouse is within the bounds of a given element
//	Requires: Valid element as a parameter
//	Returns: true or false
//	Throws: never 
//	note: sometimes off by one pixel on certain elements. TODO!! fix this.
function mouseInElement(oElm) {
	
	var iParentOffsetTop = 0;
	var iParentOffsetLeft = 0;
	var oOffsetParent = oElm.offsetParent;
	while ((oOffsetParent != null)&& (oOffsetParent.tagName != "BODY")) {
		iParentOffsetTop += oOffsetParent.offsetTop;
		iParentOffsetLeft += oOffsetParent.offsetLeft;
		oOffsetParent = oOffsetParent.offsetParent;
	}
	var iElmTop = oElm.offsetTop + iParentOffsetTop;
	var iElmLeft = oElm.offsetLeft + iParentOffsetLeft;
	var iElmBottom = iElmTop + oElm.offsetHeight;
	var iElmRight = iElmLeft + oElm.offsetWidth;
	var iMouseX = event.clientX + document.body.scrollLeft - 1;
	var iMouseY = event.clientY + document.body.scrollTop - 1;
	
	
	if (iMouseX > iElmLeft && iMouseX < iElmRight && iMouseY > iElmTop && iMouseY < iElmBottom) {
		return true;
	} else {
		return false;
	}	
}

// getQueryStringParameter()
// Purpose: pull a value out of the document's querystring using a regular expression
// Returns: the unescaped string value, or empty string if not found.
// caution... this unescapes only urlencoded space (%20) characters.
function getQueryStringParameter(sParamName) {
	var sQueryString = location.search;
	if (!sQueryString) {
		return '';
	} else {
		var sUnescaped = sQueryString.replace(/%20/g,' ');
		var regx = new RegExp(sParamName + "=([^&]*)","i");
		var arMatch = sUnescaped.match(regx);
		return((arMatch)?arMatch[1]:'');
	} 	
}


// handleException()
// Purpose: General Purpose handler for any Chainable Exception on a page
// Requires: Chainable Exception parameter
// Returns: null
// Throws: never
function handleException(oChainableException,sLineLabel) {	
	// First check if we should bypass the 'friendly' exception handler
	// this is to allow running in a test harness, where we want to see the actual exceptions, not handle them
	var bRethrow = false;
	try {
		bRethrow = RETHROW_IN_HANDLE_EXCEPTION; 
	} catch (e) {
		// // if the constant is not defined, bRethrow remains false
	}
				
	if (bRethrow) {
		throw(oChainableException);
		return;
	}

	// Use the 'friendly' exception handler, with UI and logging.
	var bTopIsHosed = false;
	var bShowExceptionAlert = true;
	var bReportExceptionToHost = true;
	try {
		var sLoc = top.location;
		if (top.location == HTTP_APP_ROOT) {
			bShowExceptionAlert = top.getExceptionAlertsEnabled();
			bReportExceptionToHost =  top.getExceptionReportsEnabled();
			top.setExceptionAlertsEnabled(false);
			top.setExceptionReportsEnabled(false); 
			var s = top.getSession(); // fault on getting session --> top is hosed
		} 
	} catch(e) {
		bTopIsHosed = true;
		// ignore errors calling to 'top'
	}
	
	if 	(bReportExceptionToHost) {
		// format the exception to XML and send it to the host.
		var sXML;
		try {
			var oFormatter = new ExceptionFormatter(oChainableException,sLineLabel);
			var sXML = oFormatter.toXML();	
		} catch(e) {
			// error formatting the exception
			// this should never happen
			// extract the message and send a simple report to the host
			var sMessage = this.getAnyExceptionMessage(e);
			sXML = "<clientExceptionDoc>Error formatting exception. [" + sMessage + "]</clientExceptionDoc>";
		}
		sendXMLExceptionToHost(sXML);
	}
	
	if (bTopIsHosed) {
		alert("Youbet Express has detected a problem with Internet Explorer. Please click OK and login again. If the wager pad, A/V window, or information product viewer windows are open, please close them before logging back in.");
		window.onbeforeunload = null;
		window.onunload = null;
		try {
			top.location = WWW_HTTP_APP_ROOT;
		} catch (e) {
			//
		}
	} else if (bShowExceptionAlert)  {
		try {
			window.showModelessDialog(ERROR_DIALOG_URL,oChainableException,"dialogHeight:400px;dialogWidth:400px;");
		} catch (e) {
			;
		}
	}
}


// reportExceptionToHost()
// Purpose: 
//	Given a ChainableException object
//	bypass the normal UI for exception handling, but silently send an exception report to the host.		
function reportExceptionToHost(oChainableException,sLineLabel) {
	var sXML;
	try {
		var oFormatter = new ExceptionFormatter(oChainableException,sLineLabel);
		var sXML = oFormatter.toXML();	
	} catch(e) {
		// error formatting the exception
		// this should never happen
		// extract the message and send a simple report to the host
		var sMessage = this.getAnyExceptionMessage(e);
		sXML = "<clientExceptionDoc>Error formatting exception. [" + sMessage + "]</clientExceptionDoc>";
	}
	sendXMLExceptionToHost(sXML);
}

// sendXMLExceptionToHost()
// Purpose: POST  XML to error logging ASP on host.
function sendXMLExceptionToHost(sXML) {
	
	try {
		//alert("Reporting the following to host:\n" + sXML);	
		//return;
		var oXMLHTTP = new ActiveXObject("Microsoft.XMLHTTP");
		oXMLHTTP.open("POST",EXCEPTION_REPORTING_URL,true,null,null); // async = true
		oXMLHTTP.setRequestHeader("Content-Type","text/xml");
		oXMLHTTP.setRequestHeader("Content-Length",sXML.length);
		oXMLHTTP.send(sXML);		
	} catch(e) {
	// ignore any exceptions
	}
}

/**
ExceptionFormatter class -  HS - 8/7/02
Purpose: 
	Given a ChainableException, can format that exception into various output formats
Constructors: 
	Constructor takes a ChainableException, 
	and an optional text label for the line where the exception occurred.
Exception Handling:
	If a bad parameter is passed to the constructor, a
	new exception is generated and that exception is used as the exception to format.

	Other exceptions are caught, and rethrown for handleException() (the only caller of this function) to handle.

Used By:
	handleException()
**/
function ExceptionFormatter(oException, sLineLabel) {
	try {
		var oTheException = oException;
		if (!oTheException) oTheException = new ChainableException(getCallContext(),null,-1,"Missing oException parameter.");
		if (!(oTheException instanceof ChainableException)) oTheException = new ChainableException(getCallContext(),null,-1,"oException parameter is not a ChainableException.");
		
		this.exception = oTheException;
		this.lineLabel = sLineLabel;
	} catch(e) {
		// should never happen, rethow and let handleException handle
		throw(e);
	}	
}
ExceptionFormatter.getAnyExceptionMessage = ExceptionFormatter_getAnyExceptionMessage;

ExceptionFormatter.prototype.getCallStackXML = ExceptionFormatter_getCallStackXML;
ExceptionFormatter.prototype.getChainableExceptionXML = ExceptionFormatter_getChainableExceptionXML;
ExceptionFormatter.prototype.getAnyExceptionMessage = ExceptionFormatter_getAnyExceptionMessage;
ExceptionFormatter.prototype.toXML = ExceptionFormatter_toXML;
ExceptionFormatter.prototype.getClientInfoXML = ExceptionFormatter_getClientInfoXML;
ExceptionFormatter.prototype.getExceptionInfoXML = ExceptionFormatter_getExceptionInfoXML;
ExceptionFormatter.prototype.getBrowserVersion = ExceptionFormatter_getBrowserVersion;
ExceptionFormatter.prototype.getWindowsVersion = ExceptionFormatter_getWindowsVersion;
ExceptionFormatter.prototype.getExceptionContextName = ExceptionFormatter_getExceptionContextName;
ExceptionFormatter.prototype.getExceptionContextArgs = ExceptionFormatter_getExceptionContextArgs;
ExceptionFormatter.prototype.isAOLBrowser = ExceptionFormatter_isAOLBrowser;

/**
ExceptionFormatter.toXML() -  HS - 8/7/02
Purpose:
	Gathers exception information, including client info, stack trace, etc, and formats it into an XML doc.	
Input Parameters:
	None.
Outputs/Returns:
	An XML clientExceptionDoc.
IMPORTANT NOTE: 
Due to the structure of the call stack unwinding code, the toXML method MUST only be called from
within handleException() or reportExceptionToHost();	
Exception Handling:
	Doesn't catch, can throw. Thrown exceptions should be handled in caller, handleException().
Used By:
	handleException() and reportExceptionToHost() ONLY.
**/
function ExceptionFormatter_toXML() {
	try {	
		if (!this.skipCallerChecks) {
			var sCaller = ExceptionFormatter_toXML.caller.toString();
			if (sCaller.match(/(function handleException)|(function reportExceptionToHost)/) == null) throw("ExceptionFormatter must only be called from handleException() or reportExceptionToHost(). Caller = " + sCaller);
		}
		var sXML = '<?xml version="1.0" encoding="ISO-8859-1"?>\n<clientExceptionDoc>\n';
		
		sXML += this.getClientInfoXML();
		
		sXML += this.getExceptionInfoXML();
		
		sXML += this.getCallStackXML();
		
		sXML += "<chainableException>\n" + this.getChainableExceptionXML(this.exception) + "</chainableException>\n";
		
		sXML += "</clientExceptionDoc>\n";
		
		return sXML;
	} catch(e) {
		throw(e);
	}
}

/**
ExceptionFormatter_getAnyExceptionMessage -  HS - 8/7/02
Purpose: 
	Given an error of unknown type, try to extract the error message from it. Can handle ChainableExceptions, JS error objects, and anything that supports toString()
Input Parameters:
	An object.
Outputs/Returns:
	A string
Exception Handling:
	Never throws.
**/
function ExceptionFormatter_getAnyExceptionMessage(e) {
	var sMessage = "";
	try {
		if (!e) throw("Null/invalid argument [" + e + "] passed to getAnyExceptionMessage()");
		if (e instanceof ChainableException || (e.getMessage && e.context)) {
			sMessage = e.getMessage();
		} else	if (e.description) {
			sMessage = e.description;
		} else {
			sMessage = e.toString();
		}
		
		if (sMessage == "") {
			sMessage = e; // last resort, just try to stick the object in a string.
		}
	} catch(e) {
		sMessage = "Couldn't parse exception message from object.";
	}
	
	return sMessage;
}

/**
ExceptionFormatter.getChainableExceptionXML -  HS - 8/7/02
Purpose:
	Recursively creates XML representations of a ChainableException and it's nested children.
Input Parameters:
	e - Required. A ChainbleException object.
	iDepth - Optional. Indicates how far we've recursed through the nested Chainable exceptions.	
Outputs/Returns:
	A series of <exception> elements in an XML string.
Exception Handling:
	If an exception occurs, the error message is returned as the contents of the exception node.
**/
function ExceptionFormatter_getChainableExceptionXML(e,iDepth) {	
	var sXML = "";	
	try {
		if (!(e instanceof ChainableException) && !(e.getMessage && e.context)) throw("Parameter e is not a ChainableException");
		
		// keep track of how far we've recursed
		var iCallDepth = iDepth;
		if (!iDepth) {
			iCallDepth = 0;
		} else {
			if (iCallDepth <= -10) {
				// stop the insanity if we're too deep.
				return "<maxTraceDepth/>\n";
			}
		}

		// get the details from the exception
		var sContext = "";
		var sMessage = "";
		var sNumber = "";
		
		if (e.context) {
			sContext = escapeXMLEntities(e.context);
		}
		
		if (e.messages && e.messages[0]) {
			if (e.messages[0].description) {
				sMessage = escapeXMLEntities(e.messages[0].description);
			} 
			if (e.messages[0].number) {
				sNumber = e.messages[0].number;
			} 
		} 
		
		// format the XML output	
		sXML = "<exception";	 
		sXML += ' depth="' + iCallDepth + '"';
		sXML += ' context="' + sContext + '"';
		sXML += ' message="' + sMessage + '"';
		sXML += ' number="' + sNumber + '"';
		sXML += ' />\n';
		
		// recurse through children
		if (e.previousException) {
			sXML += this.getChainableExceptionXML(e.previousException,--iCallDepth);
		}
	} catch(e) {
		var sMessage = escapeXMLEntities(this.getAnyExceptionMessage(e));
		sXML = "<exception>Error in getChainableExceptionXML(): " + sMessage + "</exception>";
	}
	return sXML;
}


/**
ExceptionFormatter_getClientInfoXML -  HS - 8/7/02
Purpose:
	Pulls togeher a bunch of session and configuration information to add to the exception report.
Input Parameters:
	None.
Outputs/Returns:
	An XML <clientInfo> node
Exception Handling:
	Caches any exception and returns the message as the contents of the clientInfo node.
**/
function ExceptionFormatter_getClientInfoXML() {
	var sXML = "";
	var sAttributes = "";
	var sNodeText = ""
	try {
		try {
			var sClientTime = new Date().toUTCString();
			sAttributes += ' clientTime="' + sClientTime + '"';
		} catch (e) {
			sAttributes += ' clientTime=""';
		}
			
		try {
			var sUserAgent = escapeXMLEntities(navigator.userAgent);
			sAttributes += ' userAgent="' + sUserAgent + '"';
		} catch (e) {
			sAttributes += ' userAgent=""';
		}
			
		try {
			var sBrowserVersion = escapeXMLEntities(this.getBrowserVersion());
			sAttributes += ' browserVersion="' + sBrowserVersion + '"';
		} catch (e) {
			sAttributes += ' browserVersion=""';
		}
			
		try {
			var sBrowserMinorVerion = escapeXMLEntities(navigator.appMinorVersion);
			sAttributes += ' browserMinorVersion="' + sBrowserMinorVerion + '"';
		} catch (e) {
			sAttributes += ' browserMinorVersion=""';
		}
			
		try {
			var sWindowsVersion = escapeXMLEntities(this.getWindowsVersion());
			sAttributes += ' windowsVersion="' + sWindowsVersion + '"';
		} catch (e) {
			sAttributes += ' windowsVersion=""';
		}
			
		try {
			var sIsAOLBrowser = this.isAOLBrowser();
			sAttributes += ' isAOL="' + sIsAOLBrowser + '"';
		} catch (e) {
			sAttributes += ' isAOL=""';
		}
		
		var oSession;
		var iUserId;
		var iSessionId;
		
		// HS - Added code to try and get session info from opener.top as well as top
		try {
			if (top.getSession) {
				oSession = top.getSession();
			} else if (opener.top.getSession) {
				oSession = opener.top.getSession();
			} else {
				oSession = null;
			}
		} catch (e)  {
			oSession = null;
		}
		
		if (oSession != null) {
			iUserId = oSession.userId;
			iSessionId = oSession.id;
			sClientIP = oSession.clientIP
		} else {
			iUserId = null;
			iSessionId = null;
			sClientIP = "";
		}
		sAttributes += ' userId="' + iUserId + '"';
		sAttributes += ' sessionId="' + iSessionId + '"';
		sAttributes += ' clientIP="' + sClientIP + '"';	
					
	} catch(e) {
		var sMessage = escapeXMLEntities(this.getAnyExceptionMessage(e));
		sNodeText += "Error in getClientInfoXML(): [" + sMessage + "]";
	}
	sXML = "<clientInfo" + sAttributes + ">" + sNodeText + "</clientInfo>\n";
	return sXML;
}


/**
ExceptionFormatter_getExceptionInfoXML -  HS - 8/7/02
Purpose:
	Parse the ChainableException object stored in this.exception, 
	and return an <exceptionInfo> xml site summarizing it's contents
Input Parameters:
	None.
Outputs/Returns:
	An XML string
Exception Handling:
	Catches any exception and returns an error message in the contents of the exceptionInfo node.
**/
function ExceptionFormatter_getExceptionInfoXML() {
	var sXML = "";
	var sAttributes = "";
	var sNodeText = "";
	try {
		try {
			var sLineLabel = (this.lineLabel) ? escapeXMLEntities(this.lineLabel) : "";
			sAttributes += ' lineLabel="' + sLineLabel + '"';
		} catch(e) {
			sAttributes += ' lineLabel=""';
		}
		
		try {
			var sDocument = escapeXMLEntities(document.location.href.toString());
			sAttributes += ' document="' + sDocument + '"';	
		} catch(e) {
			sAttributes += ' document=""';	
		}
			
		try {
			var sCallContext = escapeXMLEntities(this.getExceptionContextName());
			sAttributes += ' callContext="' + sCallContext + '"';	
		} catch(e) {
			sAttributes += ' callContext=""';	
		}
			
		try {
			var sCallArgs = escapeXMLEntities(this.getExceptionContextArgs());
			sAttributes += ' callArgs="' + sCallArgs + '"';
		} catch(e) {
			sAttributes += ' callArgs=""';	
		}
		
		var sMessage = "";
		var iNumber = -1;	
		var i; 
		var firstOne = true;
		try {
			for (i = 0; i < this.exception.messages.length; ++i) {
				var oMessage = this.exception.messages[i];
				var theMessage = escapeXMLEntities(oMessage.description);
				if (theMessage.length > 0) {
					if (!firstOne) 
						sMessage += "//";
					else
						iNumber = oMessage.number;
					firstOne = false;
					sMessage += theMessage;
				}
			}	
		} catch(e) {
			;
		}
					
		//if (this.exception.messages.length > 0) {
		//	var oMessage = this.exception.messages[0];
		//	sMessage = escapeXMLEntities(oMessage.description);
		//	iNumber = oMessage.number;
		//}
		sAttributes += ' message="' + sMessage + '"';
		sAttributes += ' number="' + iNumber + '"';	

	} catch(e) {
		var sMessage = escapeXMLEntities(this.getAnyExceptionMessage(e));
		sNodeText = "Error in ExceptionFormatter.getExceptionInfoXML(): [" + sMessage + "]";
	}	
	
	sXML = "<exceptionInfo" + sAttributes + ">" + sNodeText + "</exceptionInfo>\n";
	return sXML;	
}

/**
ExceptionFormatter_getExceptionContextName -  HS - 8/7/02
Purpose:
	Find the name of the function that called the function that called 'ToXML'
Input Parameters:
	None.
Outputs/Returns:
	String containing the function name.
Exception Handling:
	Throws ChainableExceptions;
	
IMPORTANT NOTE:
	This function expects to be called from within
	 GetExceptionInfo(), via ToXML(), via handleException or reportExceptionToHost() 
**/
function ExceptionFormatter_getExceptionContextName() {
	try {
		
		var oGetExceptionInfo = ExceptionFormatter_getExceptionContextName.caller;
		var oToXML = oGetExceptionInfo.caller;
		var oToXMLCaller = oToXML.caller;
		if (!this.skipCallerChecks) {
			var sToXMLCaller = oToXMLCaller.toString();
			if (sToXMLCaller.match(/(function handleException)|(function reportExceptionToHost)/) == null) throw("ExceptionFormatter must only be called from handleException() or reportExceptionToHost(). Caller = " + sCaller);
		}
		var oExceptionContext = oToXMLCaller.caller
		
		sCaller = oExceptionContext.toString().match(/function (.*\(.*\))/)[1];	
		
		return sCaller;
	} catch (e) {
		throw(ChainableException.fromGenericException(e,getCallContext()));
	}
}

/**
ExceptionFormatter_getExceptionContextArgs -  HS - 8/7/02
Purpose:
	Find the arguments of the function that called 'handleException'
Input Parameters:
	None.
Outputs/Returns:
	String containing the function name.
IMPORTANT NOTE:
	This function must be called only from getExceptionInfoXML, from ExceptionFormatter_toXML(), from handleException()
Exception Handling:
	Throws ChainableExceptions;
**/
function ExceptionFormatter_getExceptionContextArgs() {
	try {
		var oGetExceptionInfo = ExceptionFormatter_getExceptionContextArgs.caller;
		var oToXML = oGetExceptionInfo.caller;
		var oToXMLCaller = oToXML.caller;
		if (!this.skipCallerChecks) {
			var sToXMLCaller = oToXMLCaller.toString();
			if (sToXMLCaller.match(/(function handleException)|(function reportExceptionToHost)/) == null) throw("ExceptionFormatter must only be called from handleException() or reportExceptionToHost(). Caller = " + sCaller);
		}
		var oExceptionContext = oToXMLCaller.caller
		
		var sArgs;
		try {
			var oCallerArgs = oExceptionContext.arguments;
			sArgs = "";
			for (var i = 0; i < oCallerArgs.length; i++) {
				sArgs += oCallerArgs[i];
				if (i< oCallerArgs.length-1) sArgs += ", "
			}
		} catch(e) {
			sArgs = "Unavailable";
		} 
		
		return sArgs;
	} catch (e) {
		throw(ChainableException.fromGenericException(e,getCallContext()));
	}
}

/**
ExceptionFormatter_getBrowserVersion -  HS - 8/7/02
Purpose: 
	Parse out the browser version part of the appVersion string.
Input Parameters:
	None.
Outputs/Returns:
	String containing the version number, for example "5.01"
Exception Handling:
	Throws ChainableExceptions
**/
function ExceptionFormatter_getBrowserVersion() {
	try {
		var sVersion = "?";
		var sPartInParens = navigator.appVersion.toString().match(/\((.*)\)/)[1];
		var arBits = sPartInParens.split(';');
		for (var i=0; i<arBits.length;i++) {
			if (arBits[i].match(/MSIE/) != null) {
				sVersion = arBits[i].match(/MSIE (.*)/)[1];
			}
		}
		return sVersion;
	} catch (e) {
		throw(ChainableException.fromGenericException(e,getCallContext()));
	}
}

/**
ExceptionFormatter_getWindowsVersion -  HS - 8/7/02
Purpose: 
	Parse out the windows version part of the appVersion string.
Input Parameters:
	None.
Outputs/Returns:
	String containing the version number, for example "NT 5.0"
Exception Handling:
	Throws ChainableExceptions
**/
function ExceptionFormatter_getWindowsVersion() {
	try{
		var sVersion = "?";
		var sPartInParens = navigator.appVersion.toString().match(/\((.*)\)/)[1];
		var arBits = sPartInParens.split(';');
		for (var i=0; i<arBits.length;i++) {
			if (arBits[i].match(/Windows/) != null) {
				sVersion = arBits[i].match(/Windows (.*)/)[1];
			}
		}
		return sVersion;
	} catch (e) {
		throw(ChainableException.fromGenericException(e,getCallContext()));
	}
}

/**
ExceptionFormatter_isAOLBrowser -  HS - 8/12/02
Purpose: 
	Parse userAgent string and see if the string AOL is in it.
Input Parameters:
	None.
Outputs/Returns:
	true or false
Exception Handling:
	Throws ChainableExceptions
**/
function ExceptionFormatter_isAOLBrowser() {
	var match = navigator.userAgent.toString().match(/AOL/);
	if (match != null) {
		return true;
	} else {
		return false;
	}
}

/**
ExceptionFormatter_getCallStackXML -  HS - 8/7/02
Purpose:
	Recurse down the call stack and returns an XML document containing a trace of the call stack from handleException() on down.
Input Parameters:
	oParentCall - optional. Handle to the current call being recursed to.
	iDepth - optional. Counts how far down the stack we've traveled.
Outputs/Returns:
	An XML 'callStack' node, containing 'call' elements for each caller
Exception Handling:
	Handles all exceptions and returns the error message as the contents of the callStack node.
IMPORTANT NOTE:
	Must be called via ExceptionFormatter_toXML, from handleException()
Used By:
Modifications:
**/
function ExceptionFormatter_getCallStackXML(oParentCall,iDepth) {
	var sXML = "";
	try {
		var iCallDepth = iDepth;
		var oCaller = oParentCall;
		if (!oCaller) {
			// no parent passed in, we're at the beginning of the recursion, start the xml node 
			sXML = "<callStack>\n";
			var oToXML = ExceptionFormatter_getCallStackXML.caller;
			var oToXMLCaller = oToXML.caller;
			if (!this.skipCallerChecks) {
				var sToXMLCaller = oToXMLCaller.toString();
				if (sToXMLCaller.match(/(function handleException)|(function reportExceptionToHost)/) == null) throw("ExceptionFormatter must only be called from handleException() or reportExceptionToHost(). Caller = " + sCaller);
			}
			oCaller = oToXMLCaller.caller; 
		} 
		
		// keep track of how far we've recursed
		if (!iDepth) {
			iCallDepth = 0;
		} else {
			if (iCallDepth <= -10) {
				// stop the insanity if we're too deep.
				return "<maxTraceDepth/>\n";
			}
		}
			
		
		sXML += "<call depth='" + iCallDepth + "'";
		// get the caller's function name
		var sCaller = oCaller.toString();
		try {
			sCaller = sCaller.match(/function (.*\(.*\))/)[1];
			if ("anonymous()" == sCaller) {				
				try {
					sCaller = errorTrap.caller.toString();
				} catch(e) {
					sCaller = "anonymous()";
				}
			}
		} catch (e) {
			sCaller = "Unavailable";
		}	
		sXML += ' function="' + escapeXMLEntities(sCaller) + '"';
		
		// Retrieve the caller's argument list	
		var sArgs;
		try {
			var oCallerArgs = oCaller.arguments;
			sArgs = "";
			for (var i = 0; i < oCallerArgs.length; i++) {
				sArgs += oCallerArgs[i];
				if (i< oCallerArgs.length-1) sArgs += ", "
			}
		} catch(e) {
			sArgs = "Unavailable";
		} 
		sXML += ' args="' + escapeXMLEntities(sArgs) + '">';
		
		sXML += '</call>\n'; // close this call
		
		if (oCaller.caller != null && oCaller.caller.toString().match(/anonymous\(\)/) == null) {
			sXML += this.getCallStackXML(oCaller.caller,--iCallDepth); // recurse if not at bottom of stack
		}
		
		
		if (!oParentCall) sXML += "</callStack>\n"; // close doc once we're unwound
	} catch(e) {
		var sMessage = this.getAnyExceptionMessage(e);
		sXML = "<callStack>Error in ExceptionFormatter.getExceptionInfoXML(): " + sMessage + "</callStack>";
	}
	return sXML;		
}


// handle warning
// passed a string, pops the warning dialog as a modal dialog.
function warnUser(sMessage) {
	window.showModalDialog(WARNING_DIALOG_URL,sMessage,"dialogHeight:200px;dialogWidth:400px;help:no;status:no;");
}

function informUser(sMessage) {
	var theResult = false;
	if (sMessage && sMessage != "undefined") {
		theResult = window.showModalDialog(INFO_DIALOG_URL,sMessage,"dialogHeight:220px;dialogWidth:400px;help:no;status:yes;");
		
	}
	return theResult;
}

/**
 * Class AsyncDownloader
 *	To implement AsyncDownloader:
 *		1. Create a new instance. (e.g. "var myAsyncDownloader = new AsyncDownloader();")
 *		2. Create a "download succeeded" function. This can be an anonymous function in the call to myAsyncDownloader.load().
 *		3. Create a "download failed" function. This can be an anonymous function in the call to myAsyncDownloader.load().
 *		4. Call myAsyncDownloader.load().
 */
AsyncDownloader.READYSTATE_COMPLETED = 4;
AsyncDownloader.deleteInstances = AsyncDownloader_deleteInstances;
AsyncDownloader.dispose = AsyncDownloader_dispose;
function AsyncDownloader() {
	var iInstanceId;

	// Class Variables
	if (null == AsyncDownloader.iHighestUsedInstanceId)
		AsyncDownloader.iHighestUsedInstanceId = 1;
	else
		AsyncDownloader.iHighestUsedInstanceId++;
		
	if (null == AsyncDownloader.instances)
		AsyncDownloader.instances = new Object();

	iInstanceId = AsyncDownloader.iHighestUsedInstanceId;
	AsyncDownloader.instances[iInstanceId] = this;

	// Instance Variables
	this.iFailedLoadAttempts = 0;
	this.oXMLDOM = new ActiveXObject("Microsoft.XMLDOM");
	this.iInstanceId = iInstanceId;
	this.bBusy = false;

	// Member Functions
	this.load = AsyncDownloader_load;
	this.retry = AsyncDownloader_retry;
	
	// Initialization
	this.oXMLDOM.async = true;
	this.oXMLDOM.validateOnParse = WR_VALIDATE_XML;
	this.oXMLDOM.resolveExternals = WR_VALIDATE_XML;
	this.handleReadyStateChange = AsyncDownloader_handleReadyStateChange;

	// The "onreadystatechange" callback function needs to call the handleReadyStateChange method on
	// the appropriate instance of AsyncDownloader.
	// The callback function cannot use the "this" keyword to identify the instance with which
	// it is associated, because when the callback function is called by the Microsoft.XMLDOM object,
	// "this" refers to the window object.
	// Therefore, the callback function must find the appropriate instance of AsyncDownloader in the global name space.
	// To do this, we use this anonymous function to refer to the globally available list of AsyncDownloader instances.
	this.oXMLDOM.onreadystatechange = new Function("AsyncDownloader.instances[" + iInstanceId + "].handleReadyStateChange();");
}

function AsyncDownloader_deleteInstances() {
	for (var instance in AsyncDownloader.instances) {
		// If we set AsyncDownloader.instances[instance] = null without also
		// setting oXMLDOM = null, if there is another reference still holding onto
		// the instance of AsyncDownloader, we can get a "memory could not be read" error
		// when exiting IE.
		AsyncDownloader.dispose(instance);		
	}
	AsyncDownloader.instances = null;
}


function AsyncDownloader_dispose(iInstanceId) {
	// clean up an AsyncDownloader instance
	AsyncDownloader.instances[iInstanceId].oXMLDOM.onreadystatechange = nullFunction;
	AsyncDownloader.instances[iInstanceId].oXMLDOM = null;
	AsyncDownloader.instances[iInstanceId] = null;
}


function AsyncDownloader_load(sURL, sStatusMessage,
	oDownloadSucceededFunction, oDownloadFailedFunction)
{
	var sContext = location.pathname + ":AsyncDownloader_load()";
	try {	
		// If we've already started a download with this instance, don't start another download.
		// Starting a second download can cause IE to freeze.
		if (this.bBusy) return -1;
		this.sURL = sURL;
		this.sStatusMessage = sStatusMessage;
		this.oDownloadSucceededFunction = (null != oDownloadSucceededFunction)
			? oDownloadSucceededFunction
			: nullFunction;
		this.oDownloadFailedFunction = (null != oDownloadFailedFunction)
			? oDownloadFailedFunction
			: nullFunction;
		this.iFailedLoadAttempts = 0;

		window.status = this.sStatusMessage;
		this.bBusy = true;
		//top.Debug.print("ID " + this.iInstanceId + ": " + getDateString(new Date(), DATE_STYLE_HMMSSAA) + " " + this.sURL);
		this.oXMLDOM.load(this.sURL);
		return 0;
	} catch(e) {
		// catch and ignore all exceptions... the handleReadyStateChange method also gets and handles these
	}
}

function AsyncDownloader_getInstance(iInstanceId) {
	return AsyncDownloader.instances[iInstanceId];
}

function AsyncDownloader_retry() {
	var sContext = location.pathname + ":AsyncDownloader_retry()";
	try {
		window.status = this.sStatusMessage;
		this.oXMLDOM.load(this.sURL); //HS bug fix. Used to be oInstance.sURL
	} catch(e) {
		// catch and ignore all exceptions... the handleReadyStateChange method also gets and handles these
	}
}

////
//	Name: AsyncDownloader_handleReadyStateChange
//	Author: SJW / HS
//	Purpose: Main work function for AsyncDownloader, called back by XML downloader, routes to fail/success handlers
//	Usage: Automatically called by AsyncDownloader during a download	
//	Inputs: None
//	Outputs: None
//	Throws: Catches all
//	Requires: XMLDOM; AsyncDownloader parent object must be correctly initialized.
//	Used by: Lots of places that download XML documents.
//	Notes:
////
function AsyncDownloader_handleReadyStateChange() {
	var sContext = location.pathname + ":AsyncDownloader_handleReadyStateChange()";	
	try {
		sContext += " : URL=" + this.sURL + " Location=" + location.href; // append the URL to the context if possible
		if (this.oXMLDOM.readyState == AsyncDownloader.READYSTATE_COMPLETED) {
			window.status = "";
			var iErrCode = this.oXMLDOM.parseError.errorCode;
			if (0 != iErrCode) {
				if (-2146697210 == iErrCode) {
					// "File Not Found": This is normal for a file that hasn't been created yet.
					this.bBusy = false;			
					this.oDownloadFailedFunction();
				} else if (-1072896658 == iErrCode) {
					// "Encoding not supported" User needs to updgrade IE with service pack.
					alert("Unable to load racing data. This may be due to an out of date version of Microsoft Internet Explorer.\n\nPlease go to www.microsoft.com and upgrade to the newest version of Internet Explorer.");
					this.bBusy = false;			
					this.oDownloadFailedFunction();					
				} else {
					// Retry recursively. Start delay at one second and increase to three seconds
					if (this.iFailedLoadAttempts < MAX_XMLDOWNLOAD_RETRYS) {
						this.iFailedLoadAttempts++;
						// try and delete any cached copy of this file
						try {
							var oYBRequest = new ActiveXObject("YBRequest.YBHttpRequest");
							oYBRequest.expireCacheEntry(this.sURL);
							// append a number to the URL
							if (this.sURL.match(/\?/) != null) {
								this.sURL += "&" + new Date().getTime();
							}	else {
								this.sURL += "?" + new Date().getTime();
							}
						} catch(e) {
							// ignore any errors. allow clients without YBRequest installed to continue.
						}
						setTimeout("AsyncDownloader_callInstanceRetry(" + this.iInstanceId + ")", 1000 * (this.iFailedLoadAttempts + 1));
					} else {
						// File corrupt: throw error.
						this.bBusy = false;			
						this.oDownloadFailedFunction();		
					}
				}
			} else {
				this.bBusy = false;			
				this.oDownloadSucceededFunction();
			}
		}
	} catch(e) {
		this.bBusy = false;
		//
		// A "Can't execute code from a freed script" may occur
		// This means the thing which initiated the asynch downloader is no longer
		// around. Lets ignore this error.
		//
		if (e.number != -2146823277) { 
			var oException = ChainableException.fromGenericException(e, sContext);
			handleException(oException);
		}
	} finally {
		window.status = "";
	}
}

function AsyncDownloader_callInstanceRetry(iInstanceId) {
	try {
		AsyncDownloader_getInstance(iInstanceId).retry();
	} catch (e) {
		handleException(ChainableException.fromGenericException(e,getCallContext()));
	}
}

function AsyncDownloader_callInstancePoll(iInstanceId) {
	try {
		AsyncDownloader_getInstance(iInstanceId).poll();
	} catch (e) {
		handleException(ChainableException.fromGenericException(e,getCallContext()));
	}
}

/**
 * Class PollingDownloader
 */
function PollingDownloader() {
	//top.Debug.print("PollingDownloader()");
	var oDownloader = new AsyncDownloader();
	oDownloader.refreshTaskId = null;	

	oDownloader.poll = PollingDownloader_poll;
	oDownloader.stopPolling = PollingDownloader_stopPolling;
	oDownloader.onDownloadComplete = PollingDownloader_onDownloadComplete;
	oDownloader.downloadCompleteAction = null;
	oDownloader.getDateLastPollBegan = PollingDownloader_getDateLastPollBegan;

	oDownloader.sURL = "";
	oDownloader.iPollingInterval = 60000;
	
	// If we've already started a load and we call stopPolling() while the data is downloading,
	// this flag will tell the callback function (onDownloadComplete) not to call the
	// downloadCompleteAction or call setTimeout to initiate another poll once the download completes.
	oDownloader.bActive = false;
	oDownloader.dateLastPollBegan = null;

	return oDownloader;
}

function PollingDownloader_getDateLastPollBegan() {
	return this.dateLastPollBegan;
}

function PollingDownloader_poll() {
	//top.Debug.print(currentTime() + ": PollingDownloader_poll()");
	this.bActive = true;
	this.dateLastPollBegan = new Date();
	this.load(this.sURL, this.sStatusMessage, this.onDownloadComplete, this.onDownloadComplete);
}

function PollingDownloader_onDownloadComplete() {
	//top.Debug.print("PollingDownloader_onDownloadComplete()");

	if (this.bActive) {
		if (null != this.downloadCompleteAction) {
			//top.Debug.print("&nbsp;&nbsp;&nbsp;call downloadCompleteAction");
			this.downloadCompleteAction();
		}
		this.refreshTaskId = setTimeout("AsyncDownloader_callInstancePoll(" + this.iInstanceId + ")",
										this.iPollingInterval);
	}
}

function PollingDownloader_stopPolling() {
	//top.Debug.print("PollingDownloader_stopPolling()");
	this.bActive = false;
	if (this.refreshTaskId) {		
		clearTimeout(this.refreshTaskId);
		this.refreshTaskId = null;
	} 
}

// Handles tab rollovers and clicks requires array with tab information:
//	var arrImgTabs = [
//		// img id,   on pic,    off pic,    rollover pic,    last perm state (used to reset after mouseover -- initially set to default pageload state)
//		["imgTabPromo", "images/hompage/tab_promo_on.gif", "images/hompage/tab_promo_off.gif", "images/hompage/tab_promo_ro.gif", "images/hompage/tab_promo_on.gif"],
//		["imgTabEvents", "images/hompage/tab_events_on.gif", "images/hompage/tab_events_off.gif", "images/hompage/tab_events_ro.gif", "images/hompage/tab_events_off.gif"]
//	];
//
// Call sample:
//		<img id="imgTabPromo" src="images/hompage/tab_promo_on.gif" border="0" onmouseover="handleImgTab(this.id, 'mouseover')" onmouseout="handleImgTab(this.id, 'mouseout')" onclick="handleImgTab(this.id, 'click')" />
//
function handleImgTab(sImg, sAction){
	if (!arrImgTabs) { alert("handleImgTab() ERROR: Missing tab information array."); return;}
	try {
	var oImg = document.getElementById(sImg);
	if (sAction == "click"){
		for(var x in arrImgTabs){
			document.getElementById(arrImgTabs[x][0]).src = arrImgTabs[x][2]; //reset all
			if (sImg == arrImgTabs[x][0]){ // turn selected on					
				oImg.src = arrImgTabs[x][1]; // change to 'on' state
				arrImgTabs[x][4] = oImg.src; // save this state
			}
		}
	}else if (sAction == "mouseover"){
		for(var x in arrImgTabs){
			if (sImg == arrImgTabs[x][0]){
				if (String(oImg.src).indexOf(String(arrImgTabs[x][1])) > -1) //if the selected tab is being rolled over ignore it
					break;
				arrImgTabs[x][4] = oImg.src; //save state
				oImg.src = arrImgTabs[x][3]; //change to rollover img
				break;
			}
		}
	}else if (sAction == "mouseout"){
		for(var x in arrImgTabs){
			if (sImg == arrImgTabs[x][0]){
				oImg.src = arrImgTabs[x][4]; //restore to previous state
				break;
			}
		}
	}
	}catch(e){
		alert("handleImgTab() ERROR: " + e);
	}
}
function LinkButton(oImg, sImgUrl_Norm, sImgUrl_Over, sImgUrl_Down) {
	try {
		if (document.images){
			var cacheNorm = new Image();
			cacheNorm.src = sImgUrl_Norm;
			var cacheOver = new Image();
			cacheOver.src = sImgUrl_Over;
			var cacheDown = new Image();
			cacheDown.src = sImgUrl_Down;
		}
		if (oImg.src == "" && (sImgUrl_Norm != null || sImgUrl_Norm != "undefined" || sImgUrl_Norm != ""))
			oImg.src = cacheNorm.src;
	
		oImg.onmouseover = function(){ oImg.src = cacheOver.src };
		oImg.onmouseout = function(){ oImg.src = cacheNorm.src };
		oImg.onmousedown = function(){ oImg.src = cacheDown.src };
		oImg.onmouseup = function(){ oImg.src = cacheNorm.src };
	} catch(e) {
		;
	}
}

////
// ImageButton Object 
////
function ImageButton(oDiv,sNormalImg,sOverImg,sDownImg,sDisabledImg,sNormalClass,sOverClass,sDownClass,sDisabledClass,fpOnClick,fpRefreshDisplay) {
	// Image button can have four states: normal, over, down, and disabled.
	// Button can have different images for each state, as well as different CSS classes for each state
	// Button also has methods: show, hide, enable, and disable, which are the same for all buttons.
	// Each individual button can also define custom handlers for onclick and refreshDisplay.
	
	// Expects a DIV object, with an inner IMG object with an id of 'imgButtonCaption'
	
	// Usage:
	// Define DIV in HTML: <div id='divMyImageButton'><img id='imgButtonCaption' src="imgButtonNormal.gif"></img></div>
	// Create Object in JS: var oMyButton = new ImageButton(divMyImagebutton,'imgButtonNormal.gif', etc etc);
	
	// define images. This creates image objects and pre-downloads the graphic files
	try {
		if (sNormalImg) {
			var oNormalImg;
			oNormalImg = oDiv.all['imgNormal'];
			if (!oNormalImg) {
				oNormalImg = document.createElement('<img id="imgNormal"></img>');
				oDiv.appendChild(oNormalImg);
			}
			oNormalImg.src = sNormalImg;
			oNormalImg.style.display = 'none';
					
			oDiv.normalImgSrc = sNormalImg;
		}
		
		if (sOverImg) {
			var oOverImg;
			oOverImg = oDiv.all['imgOver'];
			if (!oOverImg) {
				oOverImg = document.createElement('<img id="imgOver"></img>');
				oDiv.appendChild(oOverImg);
			}
			oOverImg.src = sOverImg;
			oOverImg.style.display = 'none';		
			oDiv.overImgSrc = sOverImg;
		}
		
		if (sDownImg) {
			var oDownImg;
			oDownImg = oDiv.all['imgDown'];
			if (!oDownImg) {
				oDownImg = document.createElement('<img id="imgDown"></img>');
				oDiv.appendChild(oDownImg);
			}
			oDownImg.src = sDownImg;
			oDownImg.style.display = 'none';				
			oDiv.downImgSrc = sDownImg;
		}
		
		if (sDisabledImg) {
			var oDisabledImg;
			oDisabledImg = oDiv.all['imgDisabled'];
			if (!oDisabledImg) {
				oDisabledImg = document.createElement('<img id="imgDisabled"></img>');
				oDiv.appendChild(oDisabledImg);
			}
			oDisabledImg.src = sDisabledImg;
			oDisabledImg.style.display = 'none';			
			oDiv.disabledImgSrc = sDisabledImg;
		}
		
		// define classes
		if (sNormalClass) {
			oDiv.normalClass = sNormalClass;
		}
		
		if (sOverClass) {
			oDiv.overClass = sOverClass;
		}
		
		if (sDownClass) {
			oDiv.downClass = sDownClass;
		}
		
		if (sDisabledClass) {
			oDiv.disabledClass = sDisabledClass;
		}
		
		// bind event handlers	
		oDiv.onmouseover = ImageButton_onmouseover;
		oDiv.onmouseout = ImageButton_onmouseout;
		oDiv.onmousedown = ImageButton_onmousedown;
		oDiv.onmouseup = ImageButton_onmouseup;
		
		
		oDiv.show = ImageButton_show;
		oDiv.hide = ImageButton_hide;
		oDiv.enable = ImageButton_enable;
		oDiv.disable = ImageButton_disable;
		
		if (fpOnClick) {
			oDiv.onclick = fpOnClick;
			oDiv.origOnClick = fpOnClick; // safe keeping for the onclick handler 
		}
		
		if (fpRefreshDisplay) {
			oDiv.refreshDisplay = fpRefreshDisplay;
		}
		
		return oDiv;
	} catch (e) {
		handleException(ChainableException.fromGenericException(e,getCallContext()));
	}
}

function ImageButton_onmouseover() {
	var oImage = this.all['imgButtonCaption'];
	if (oImage && this.overImgSrc) {
		oImage.src = this.overImgSrc;
	}
	
	if (this.overClass && (this.className != this.overClass)) {
		this.className = this.overClass;
	}
}

function ImageButton_onmouseout() {
	var oImage = this.all['imgButtonCaption'];
	if (oImage && this.normalImgSrc) {
		oImage.src = this.normalImgSrc;
	}
	
	if (this.normalClass && (this.className != this.normalClass)) {
		this.className = this.normalClass;
	}
}

function ImageButton_onmousedown() {
	var oImage = this.all['imgButtonCaption'];
	if (oImage && this.downImgSrc) {
		oImage.src = this.downImgSrc;
	}
	
	if (this.downClass && (this.className != this.downClass)) {
		this.className = this.downClass;
	}
}

function ImageButton_onmouseup() {
	var oImage = this.all['imgButtonCaption'];
	if (oImage && this.overImgSrc) {
		oImage.src = this.overImgSrc;
	}
	
	if (this.overClass && (this.className != this.overClass)) {
		this.className = this.overClass;
	}
}

function ImageButton_show() { this.style.display = 'block';}

function ImageButton_hide() { this.style.display = 'none';}

function ImageButton_enable() {
	var oImage = this.all['imgButtonCaption'];
	if (oImage && this.normalImgSrc) {
		oImage.src = this.normalImgSrc;
	}
	
	if (this.normalClass) {
		this.className = this.normalClass;
	}
	
	// return the onclick hander to its original state
	if (this.origOnClick) {
		this.onclick = this.origOnClick;
	}
	
	// return mouse event handlers
	this.onmouseover = ImageButton_onmouseover;
	this.onmouseout = ImageButton_onmouseout;
	this.onmousedown = ImageButton_onmousedown;
	this.onmouseup = ImageButton_onmouseup;
}

function ImageButton_disable() {
	var oImage = this.all['imgButtonCaption'];
	if (oImage && this.disabledImgSrc) {
		oImage.src = this.disabledImgSrc;
	}
	
	if (this.disabledClass) {
		this.className = this.disabledClass;
	}
	
	// set the onclick handler to null
	this.onclick = nullFunction;
	
	// set mouse event handlers to null
	this.onmouseover = nullFunction;
	this.onmouseout = nullFunction;
	this.onmousedown = nullFunction;
	this.onmouseup = nullFunction;
}
////
// GoldButton Object 
////
function GoldButton(spn) {
	spn.onmouseover = GoldButton_onmouseover;
	spn.onmouseout = GoldButton_onmouseout;
	spn.onmousedown = GoldButton_onmousedown;
	return spn;
}

function GoldButton_onmouseover() {
	this.style.borderColor = 'white'; this.style.borderStyle = 'outset'; 
}

function GoldButton_onmouseout() {
	this.style.borderColor = 'gold'; this.style.borderStyle = 'outset'; 
}

function GoldButton_onmousedown() {
	this.style.borderColor = 'gold'; this.style.borderStyle = 'inset'; 
}

/**
 * function padWithZeroes
 * @param varNumber A variable to pad with zeroes (intended to be a number, but could be a string)
 * @param iNumChars The minimum number of chars the string should contain after being padded
 */
function padWithZeroes(varNumber, iNumChars) {
	// Refactored to call leftPad. 1/6/03 - HS
	return leftPad('0',varNumber,iNumChars)
}



////
//// CODE FOR CLASS OBJECTS
//// Bind properties and methods to DHTML elements using IE dynabinding

////
// TabBar object
////
function TabBar(oDiv) {
	oDiv.currentSelection = null;
	oDiv.selectTab = TabBar_selectTab;
	return oDiv;
}

function TabBar_selectTab(oTab, bDoTabGraphicsOnly) {
	try {
		if (this.currentSelection) this.currentSelection.unselect();
		this.currentSelection = oTab;
		this.currentSelection.select(bDoTabGraphicsOnly);
	} catch (e) {
		handleException(ChainableException.fromGenericException(e,getCallContext() + " called on " + this.id));
	}
}


////
// Tab object
////
function Tab(oDiv,oParentTabBarDiv,sNormalImg, sOverImg, sSelectedImg, sDisabledImg, sNormalClass, sOverClass, sSelectedClass, sDisabledClass, sFumActionCode) {
	oDiv.displayArea = null;
	oDiv.parentTabBar = oParentTabBarDiv;
	
	oDiv.select = Tab_select;
	oDiv.unselect = Tab_unselect;
	oDiv.bindDisplay = Tab_bindDisplay;	
	oDiv.tabDownImgSrc = sSelectedImg;
	oDiv.tabDownClass = sSelectedClass;
	oDiv.tabNormalImgSrc = sNormalImg;
	oDiv.tabNormalClass = sNormalClass;
	oDiv.fumActionCode = sFumActionCode;
	
	new ImageButton(oDiv,sNormalImg,sOverImg,null,sDisabledImg,sNormalClass,sOverClass,null,sDisabledClass);
	
	oDiv.onclick = Tab_onclick;
	oDiv.isSelected = Tab_isSelected;
	oDiv._isSelected = false;
	return oDiv;
}

function Tab_onclick() {
	try {
		if (this.fumActionCode != null) {
			logFumAction(this.fumActionCode);
		}
		this.parentTabBar.selectTab(this);
	} catch (e) {
		handleException(ChainableException.fromGenericException(e,getCallContext()));
	}
}

function Tab_select(bDoTabGraphicsOnly) {	
	var oImage = this.all['imgButtonCaption'];
	// use the down image of the image button for the selected state of the tab
	if (oImage && this.tabDownImgSrc) {
		oImage.src = this.tabDownImgSrc;
	}
	
	if (this.tabDownClass) {
		this.className = this.tabDownClass;
	}
	
	// set the onclick handler to null
	this.onclick = nullFunction;
	
	// set mouse event handlers to null
	this.onmouseover = nullFunction;
	this.onmouseout = nullFunction;
	this.onmousedown = nullFunction;
	this.onmouseup = nullFunction;
	
	this._isSelected = true;
	if (!bDoTabGraphicsOnly ) this.displayArea.show(); 		
}

function Tab_isSelected() {
	return this._isSelected;
}

function Tab_unselect(bDoTabGraphicsOnly) { 
	var oImage = this.all['imgButtonCaption'];
	if (oImage && this.tabNormalImgSrc) {
		oImage.src = this.tabNormalImgSrc;
	}
	
	if (this.tabNormalClass) {
		this.className = this.tabNormalClass;
	}
	
	this.onclick = Tab_onclick;
	
	// return mouse event handlers
	this.onmouseover = ImageButton_onmouseover;
	this.onmouseout = ImageButton_onmouseout;
	this.onmousedown = ImageButton_onmousedown;
	this.onmouseup = ImageButton_onmouseup;
	
	this._isSelected = false;
	if (!bDoTabGraphicsOnly ) this.displayArea.hide(); 
}

function Tab_bindDisplay(oDisplay) {
	this.displayArea = oDisplay;
	oDisplay.ownerTab = this;
}

////
// MenuTab object
////
function MenuTab(oDiv,oParentTabBarDiv,sNormalImg, sOverImg, sSelectedImg, sDisabledImg, sMenuNormalImg, sMenuOverImg, sNormalClass, sOverClass, sSelectedClass, sDisabledClass) {
	oDiv.displayArea = null;
	oDiv.menuBar = null;
	oDiv.parentTabBar = oParentTabBarDiv;
	
	oDiv.select = MenuTab_select;
	oDiv.unselect = MenuTab_unselect;
	oDiv.bindDisplay = MenuTab_bindDisplay;
	oDiv.bindMenuBar = MenuTab_bindMenuBar;
	
	oDiv.tabDownImgSrc = sSelectedImg;
	oDiv.tabDownClass = sSelectedClass;
	oDiv.tabNormalImgSrc = sNormalImg;
	oDiv.tabNormalClass = sNormalClass;
	
	oDiv.menuNormalImgSrc = sMenuNormalImg;
	oDiv.menuOverImgSrc = sMenuOverImg;
	
	new ImageButton(oDiv,sNormalImg,sOverImg,null,sDisabledImg,sNormalClass,sOverClass,null,sDisabledClass);
	
	// set mouse event handlers
	oDiv.onmouseover = MenuTab_onmouseover;
	oDiv.onmouseout = MenuTab_onmouseout;
	oDiv.onmousedown = MenuTab_onmousedown;
	oDiv.onmouseup = MenuTab_onmouseup;

	oDiv.onclick = MenuTab_onclick;
	oDiv.isSelected = MenuTab_isSelected;
	oDiv._isSelected = false;
	oDiv._isMenuOpened = false;
	return oDiv;
}

function MenuTab_onclick() {
	if (top.isBusy && top.isBusy()) return;
	try {
		if (!this._isMenuOpened) {
			this.parentTabBar.selectTab(this);
		} else {
			this._isMenuOpened = false;
		}
	} catch (e) {
		handleException(ChainableException.fromGenericException(e,getCallContext()));
	}
}

function MenuTab_onmouseover() {
	if (!this._isSelected) {
		var oImage = this.all['imgButtonCaption'];
		if (oImage && this.overImgSrc) {
			oImage.src = this.overImgSrc;
		}
	
		if (this.overClass && (this.className != this.overClass)) {
			this.className = this.overClass;
		}
	}

	var oImage = this.all['imgMenuCaption'];
	if (oImage && this.menuOverImgSrc) {
		oImage.src = this.menuOverImgSrc;
	}
}

function MenuTab_onmouseout() {
	if (!this._isSelected) {
		var oImage = this.all['imgButtonCaption'];
		if (oImage && this.normalImgSrc) {
			oImage.src = this.normalImgSrc;
		}
	
		if (this.normalClass && (this.className != this.normalClass)) {
			this.className = this.normalClass;
		}
	}
	
	var oImage = this.all['imgMenuCaption'];
	if (oImage && this.menuNormalImgSrc) {
		oImage.src = this.menuNormalImgSrc;
	}
}

function MenuTab_onmousedown() {
	if (top.isBusy && top.isBusy()) return;
	
	if (null != this.menuBar) {
		if (this.menuBar.currentMenu != null) {
			this.menuBar.selectMenu();
		} else {
			this.menuBar.selectDefaultMenu();
			this._isMenuOpened = true;
		}
	}

	if (!this._isSelected) {
		var oImage = this.all['imgButtonCaption'];
		if (oImage && this.downImgSrc) {
			oImage.src = this.downImgSrc;
		}
	
		if (this.downClass && (this.className != this.downClass)) {
			this.className = this.downClass;
		}
	}
}

function MenuTab_onmouseup() {
	if (!this._isSelected) {
		var oImage = this.all['imgButtonCaption'];
		if (oImage && this.overImgSrc) {
			oImage.src = this.overImgSrc;
		}
	
		if (this.overClass && (this.className != this.overClass)) {
			this.className = this.overClass;
		}
	}
}

function MenuTab_select(bDoTabGraphicsOnly) {	
	var oImage = this.all['imgButtonCaption'];
	// use the down image of the image button for the selected state of the tab
	if (oImage && this.tabDownImgSrc) {
		oImage.src = this.tabDownImgSrc;
	}
	
	if (this.tabDownClass) {
		this.className = this.tabDownClass;
	}
	
	// set the onclick handler to null
	this.onclick = nullFunction;
	
	this._isSelected = true;
	if (!bDoTabGraphicsOnly ) this.displayArea.show(); 		
}

function MenuTab_isSelected() {
	return this._isSelected;
}

function MenuTab_unselect(bDoTabGraphicsOnly) { 
	var oImage = this.all['imgButtonCaption'];
	if (oImage && this.tabNormalImgSrc) {
		oImage.src = this.tabNormalImgSrc;
	}
	
	if (this.tabNormalClass) {
		this.className = this.tabNormalClass;
	}
	
	this.onclick = MenuTab_onclick;
	
	this._isSelected = false;
	if (!bDoTabGraphicsOnly ) this.displayArea.hide(); 
}

function MenuTab_bindDisplay(oDisplay) {
	this.displayArea = oDisplay;
	oDisplay.ownerTab = this;
}

function MenuTab_bindMenuBar(oMenuBar) {
	this.menuBar = oMenuBar;
	oMenuBar.ownerTab = this;
}

// KeyPressHandler
// Class Object to hold class methods
function KeyPressHandler() {
}

// bind class methods
KeyPressHandler.bindKey = KPH_bindKey;
KeyPressHandler.handleKeyPress = KPH_handleKeyPress;
KeyPressHandler.formHashKey = KPH_formHashKey;

// bindKey()
// Binds a keycode to a handler function
function KPH_bindKey(iKeyCode,bIsControlDown,bIsAltDown,fpHandlerFunction) {
	
	if (KeyPressHandler.bindings == null) {
		KeyPressHandler.bindings = new Object();
	}
	
	if (document.onkeypress == null) {
		document.onkeypress = KeyPressHandler.handleKeyPress;
	} else if (document.onkeypress != KeyPressHandler.handleKeyPress) {
		//alert('Existing keypress handler overridden by KeyPressHandler.handleKeyPress');
		document.onkeypress = KeyPressHandler.handleKeyPress;
	}
	var sHashKey = KeyPressHandler.formHashKey(iKeyCode,bIsControlDown,bIsAltDown);
	// associate the handler function with the key code 
	KeyPressHandler.bindings[sHashKey] = fpHandlerFunction;
}

// handleKeyPress()
// checks if a handler function is bound to the pressed key, executes the handler if it exists
function KPH_handleKeyPress() {
	var iKeyCode = event.keyCode;
	var bIsControlDown = event.ctrlKey;
	var bIsAltDown = event.altKey;
	var sHashKey = KeyPressHandler.formHashKey(iKeyCode,bIsControlDown,bIsAltDown);
	if (KeyPressHandler.bindings[sHashKey] != null) {
		KeyPressHandler.bindings[sHashKey]();
	} 
}

// formHashKey()
// converts a keycode and alt/control flags into a string like '101_false_false'
function KPH_formHashKey(iKeyCode,bIsControlDown,bIsAltDown) {
	var sOutput = iKeyCode + "_" 
	sOutput += ((bIsControlDown) ? 'true' : 'false') + "_";
	sOutput += (bIsAltDown) ? 'true' : 'false';
	return sOutput;
}


//FeedbackForm()
//open form in another window and pass along the session key.
function openFeedbackForm(){
	var oSession = top.getSession();
	var sessC = oSession.sessC;
	var dURL = RACING_FEEDBACK_FORM;
		dURL += "?sessC=";
		dURL += sessC;
	var iWidth = 500;
	var iHeight = 400;
	var sWindow = centerPopup(dURL, "Feedback", iWidth, iHeight);
}
//SuggestionForm()
//open form in another window and pass along the session key.
function openSuggestionForm(){
	var oSession = top.getSession();
	var sessC = oSession.sessC;
	var sURL = RACING_SUGGESTION_FORM;
		sURL += "?sessC=";
		sURL += sessC;
	var iWidth = 500;
	var iHeight = 400;
	var sWindow = centerPopup(sURL, "Suggestion", iWidth, iHeight);
}
//Open Survey Form
function openSurveyForm(){
	var oSession = top.getSession();
	var sessC = oSession.sessC;
	var sURL = RACING_SURVEY_FORM;
		//sURL += "?sessC=";
		//sURL += sessC;
	var iWidth = 500;
	var iHeight = 400;
	var sWindow = centerPopup(sURL, "Survey", iWidth, iHeight);
}

function doYBRequestGet(sUrl) {
    var iRetryCount = 0;
    var oYBRequest = new ActiveXObject(YB_REQUEST_PROG_ID);

	// We will retry this a few times in case a network glich occurs
	for (iRetryCount = 1; iRetryCount <= MAX_XMLDOWNLOAD_RETRYS; ++iRetryCount) 
	{
		oYBRequest.open("GET",sUrl,false);
		oYBRequest.send();

		// check if the send operation is finished successfully
		if (oYBRequest.readyState != 4) 
		{
			if (iRetryCount == MAX_XMLDOWNLOAD_RETRYS) 
			{
				throw ("HTTPS Send Error in utils::doYBRequestGet, readyState="+oYBRequest.readyState);
			}
		} else 
		{
			break;
		}
	}

	// Check the response status and response text
	if (oYBRequest.status != 200) {
		// Not an 'OK' response
		if (oYBRequest.statusText != "") {
			throw ("HTTPS Error in utils::doYBRequestGet: Status" + oYBRequest.statusText);
		} else {
		    throw ("HTTPS Error in utils::doYBRequestGet: Status" + oYBRequest.status);
		}
	}
	
	// Check if response text is valid
	if (oYBRequest.responseText == "") {
		// no response text
		throw ("HTTPS Error in utils::doYBRequestGet: invalid response to XML request.");
	}
    return oYBRequest.responseText;
}

// Loads and caches XML documents synchronously via YBRequest
function loadXMLviaHTTPS(sUrl, bCache) {
    var oXMLDocument;
    if (!bCache || null == (oXMLDocument = top.m_xmlDocumentCache.getDocument(sUrl))) {
		oXMLDocument = new ActiveXObject("Microsoft.XMLDOM");
	    oXMLDocument.validateOnParse = false;
	    oXMLDocument.resolveExternals = false;

	    oXMLDocument.loadXML(doYBRequestGet(sUrl));
	    if (oXMLDocument.parseError.errorCode != 0) {
	    	// xml parse errors can happen for a lot of reasons, send unknown back to client.
	    	throw ("XML Parse Error in loadXMLViaHTTPS: " + oXMLDocument.parseError.reason);
	    }
	    top.m_xmlDocumentCache.setDocument(sUrl, oXMLDocument);
	}
	return oXMLDocument;
}

// Convers string representation of 'true' and 'false' into corresponding boolean values
// Author: Armen Jamkotchian
function stringToBoolean(sBooleanString) {
	var bReturnValue
	if (typeof sBooleanString == "string") {
			return (sBooleanString.match(/^true\W*$|^y\W*$|^yes\W*$/i) != null) ? true : false;
	} else {
		throw "Cannot convert non-string to boolean";
	}
}


// Opens the marketing text contents in a pop up.
function OpenContent(theURL,winName,features) {
	window.open(theURL,winName,features);
}



// set the top level error handler
function errorTrap(sDesc,sUrl,sLine) {
   // we should never get here, this is just a last gasp effort
	// Retrieve the caller information
	var sCaller;
	try {
		sCaller = (errorTrap.caller.toString().match(/function (.*\(.*\))/)[1]);
		if ("anonymous()" == sCaller) { 
			sCaller = errorTrap.caller.toString();
		}
	} catch (e) {
		sCaller = "errorTrap() - caller unknown";
	}
	
	// Retrieve the caller's argument list	
	var sArgs;
	try {
		var oCallerArgs = errorTrap.caller.arguments;
		sArgs = "(";
		for (var i = 0; i < oCallerArgs.length; i++) {
			sArgs += oCallerArgs[i];
			if (i< oCallerArgs.length-1) sArgs += ", "
		}
		sArgs += ")";
	} catch(e) {
		sArgs = "(args unavailable)";
	}
		
	// Create our error message	
	var sExceptionString;		
	sExceptionString =  "Error '" + sDesc + "' occured in file " + sUrl + ", line " + sLine ;
	
	// try to create an exception message and report it to the host.
	try {
		var oChainableException = new ChainableException(sCaller + " args:" + sArgs,null,666,sExceptionString,null);
		// format the exception to XML and send it to the host.
		var sXML;
		try {
			var oFormatter = new ExceptionFormatter(oChainableException);
			oFormatter.skipCallerChecks = true;
			var sXML = oFormatter.toXML();	
		} catch(e) {
			// error formatting the exception
			// this should never happen
			// extract the message and send a simple report to the host
			var sMessage = (e.description) ? e.description : e.toString();
			sXML = "<clientExceptionDoc>Error formatting exception. [" + sMessage + "]</clientExceptionDoc>";
		}
		sendXMLExceptionToHost(sXML);
	} catch (e) {
		// couldn't create chainable exception... use last ditch reporting scheme...
		// create an img element and set it's source to the URL for exception reporting.
		if (document.body) {			
			sExceptionString += "Error while reporting exception. Error was"  + e.description;
			var oImg = document.createElement("IMG");
			document.body.appendChild(oImg);
			var sImgUrl = EXCEPTION_REPORTING_URL + "?" + sExceptionString;
			oImg.src = sImgUrl;
		}
	} finally {
		// pop a simple error dialog
		var sErrorMessage = "An error occurred processing this page. This may be due to a temporary connection problem.\n\nPlease check your Internet connection, then reload the page to try again. \n\nIf problems persist, please contact Youbet Member Servcies at 1-888-YOUBET-8 for further assistance.";
		alert(sErrorMessage);  
		return true;
	}
}

function setContextMenuFunction() {
	if (!ENABLE_CONTEXT_MENU) {
		document.oncontextmenu = function() { return false; }
	}
}

// Returns a string containing document.location and the call context;
function getCallContext() {		
	var sCaller;
	try {
		sCaller = getCallContext.caller.toString().match(/function (.*\(.*\))/)[1];
		if ("anonymous()" == sCaller) { 
			sCaller = errorTrap.caller.toString();
		}
	} catch (e) {
		sCaller = "Call context unavailable";
	}
	
	// Retrieve the caller's argument list	
	var sArgs;
	try {
		var oCallerArgs = getCallContext.caller.arguments;
		sArgs = "(";
		for (var i = 0; i < oCallerArgs.length; i++) {
			sArgs += oCallerArgs[i];
			if (i< oCallerArgs.length-1) sArgs += ", "
		}
		sArgs += ")";
	} catch(e) {
		sArgs = "(args unavailable)";
	} 
	
	var sDocname;	
	try {
		sDocname = location.pathname;
	} catch (e) {
		sDocname = "Unknown document";
	}
	
	return sDocname + "::" + sCaller + " args:" + sArgs; 
}


// Returns a string containing document.location and the callers call context;
function getCallerCallContext() {		
	var sCaller;
	try {
		sCaller = getCallerCallContext.caller.caller.toString().match(/function (.*\(.*\))/)[1];
		if ("anonymous()" == sCaller) { 
			sCaller = errorTrap.caller.toString();
		}
	} catch (e) {
		sCaller = "Call context unavailable";
	}
	
	// Retrieve the caller's argument list	
	var sArgs;
	try {
		var oCallerArgs = getCallerCallContext.caller.caller.arguments;
		sArgs = "(";
		for (var i = 0; i < oCallerArgs.length; i++) {
			sArgs += oCallerArgs[i];
			if (i< oCallerArgs.length-1) sArgs += ", "
		}
		sArgs += ")";
	} catch(e) {
		sArgs = "(args unavailable)";
	} 
	
	var sDocname;	
	try {
		sDocname = location.pathname;
	} catch (e) {
		sDocname = "Unknown document";
	}
	
	return (sDocname + "::" + sCaller + " args:" + sArgs); 
}


// URLUtil class - holds utility functions for dealing with URLs
function URLUtil() {}

URLUtil.getAbsolutePath = URLUtil_getAbsolutePath;

function URLUtil_getAbsolutePath(sLocationHref, sRelativePath) {
	if(!sLocationHref || !sRelativePath) throw(new ChainableException(getCallContext(),null,-1,"Missing argument."));
	var sAbsolutePath = sLocationHref.match(/(.*)(\/.*$)/)[1] + sRelativePath;
	return sAbsolutePath;
}

////
/// XMLLoader class - for easy loading of XML documents
////
function XMLLoader() {}

XMLLoader.loadXML = XMLLoader_loadXML;

function XMLLoader_loadXML(sAbsolutePath) {
	var oDOM = new ActiveXObject("Microsoft.XMLDOM");
	oDOM.async = false;
	oDOM.validateOnParse = (WR_VALIDATE_XML) ? true : false;
	oDOM.resolveExternals = (WR_VALIDATE_XML) ? true : false;
	
	oDOM.load(sAbsolutePath);
	if (0 != oDOM.parseError.errorCode) {
		throw(ChainableException.fromMSXMLParseError(oDOM.parseError,getCallContext()));
	} else {
		return oDOM;
	}
}


function parseSessionXMLIntoObject(oXML, oSession) {
	var sSessionId = "";
	var sUserFirstName = "";
	var sAccountId = "";
	var sAccountBalance = "";
	var sAccountBalanceAsOf = "";
	var bLocationBlocked = false;
	var sLocationBlockedMessage = "";
	var sHasWageringAccount = "";
	var sActiveHubName = "";
	var sCanSwitchAccounts = "";
	var sClientIP = "";
	var sUsageClass = "";
	var sStateId = "";
	var sContactStatus = "";
	var sAVPref = "";
	var sAutoSelectTrackPref = "";
	var sAutoLoadMTVPref = "";
	var sInfoViewerAlwaysOnTop = "";
	var sContentType = "";
	var sUserState = "";
	var sHandle = "";
	var sUserId = "";
	var sStaticLists = "";
	var sHasTurfdaySingleDayPlan = "";
	var sCancelWagerActivated = "";
	
	var arFeatures = new Array();
	
	var oNode;
	
	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/userData/@userId');
	if (oNode) {
		sUserId = oNode.text;
	}

	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/userData/@sessId');
	if (oNode) {
		sSessionId = oNode.text;
	}

	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/userData/@handle');
	if (oNode) {
		sHandle = oNode.text;
	}

	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/userData/@fName');
	if (oNode) {
		sUserFirstName = oNode.text;
	}
	
	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/userData/@contentType');
	if (oNode) {
		sContentType = oNode.text;
	}
	
	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/userData/@hasWageringAccount');
	if (oNode) {
		sHasWageringAccount = oNode.text;		
	}
	
	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/userData/@activeHubName');
	if (oNode && oNode.text != 'null') {		
		// guests logins don't set this.
		sActiveHubName = oNode.text;		
	}
	
	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/userData/@canSwitchAccounts');
	if (oNode) {
		sCanSwitchAccounts = oNode.text;		
	}
	
	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/userData/@clientIP');
	if (oNode) {
		sClientIP = oNode.text;
	}
	
	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/userData/@usageClass');
	if (oNode) {
		sUsageClass = oNode.text;
	}
	
	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/userData/@stateId');
	if (oNode) {
		sStateId = oNode.text;
	}

	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/userData/@contactStatus');
	if (oNode) {
		sContactStatus = oNode.text;
	}
	
	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/userData/@hasSingleDayTurfdayPlan');
	if (oNode) {
		sHasTurfdaySingleDayPlan = oNode.text;
	}
 
	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/userData/@cancelWagerActivated');
	if (oNode) {
		sCancelWagerActivated = oNode.text;
	}

	
	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/userData/wageringLocationBlock/@message');
	if (oNode) {
		warnUser(oNode.text);
		bLocationBlocked = true;
		sLocationBlockedMessage = oNode.text
	}
	
	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/accountBalance/@accountId');
	if (oNode) {
		sAccountId = oNode.text;
	}
	
	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/accountBalance/@current');
	if (oNode) {
		sAccountBalance = oNode.text;
	}
		
	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/accountBalance/@asOf');
	if (oNode) {
		sAccountBalanceAsOf = oNode.text;
	}
	
	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/userData/prefs/pref[@id="' + USER_PREF_AV_ID + '"]/@v');
	if (oNode) {
		 sAVPref = oNode.text;
	}
	
	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/userData/prefs/pref[@id="' + USER_PREF_AUTO_SELECT_TRACK_ID + '"]/@v');
	if (oNode) {
		sAutoSelectTrackPref = oNode.text;
	}
	
	oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/userData/prefs/pref[@id="' + USER_PREF_AUTO_LOAD_MTV_ID + '"]/@v');
	if (oNode) {
		sAutoLoadMTVPref = oNode.text;
	} else {
		sAutoLoadMTVPref = USER_PREF_AUTO_LOAD_MTV_DEFAULT;
	}
	
	
	
	if (USER_PREF_INFO_VIEWER_ALWAYS_ON_TOP_ENABLED)
	{
		oNode = oXML.selectSingleNode('/authResponseDoc/authResponse/userData/prefs/pref[@id="' + USER_PREF_INFO_VIEWER_ALWAYS_ON_TOP_ID + '"]/@v');
		if (oNode) {
			sInfoViewerAlwaysOnTop = oNode.text;
		} else {
			sInfoViewerAlwaysOnTop = "false";
		}
	} else {
		sInfoViewerAlwaysOnTop = "false";
	}
	
	// get features
	var oNodeList = oXML.selectNodes('/authResponseDoc/authResponse/userData/features/feature');
	for (var i = 0; i < oNodeList.length; i++) {
		var sName = oNodeList[i].attributes.getNamedItem("name").text;
		var bAccess = ("true" == oNodeList[i].attributes.getNamedItem("access").text);
		
		arFeatures[sName] = bAccess;
	}
	
	// get static lists
	oNode = oXML.selectNodes('/authResponseDoc/authResponse/userData/staticLists/list');
	if (oNode) {
		var iLists = oNode.length;
		var oIdNode;
		for (var i=0; i < iLists; i++) {
			oIdNode = oNode[i].attributes.getNamedItem('id');
			if (oIdNode) {
				sStaticLists += oIdNode.text + ((i == (iLists-1)) ? "":";");
			}
		}
	}
	
	// store the variables in the session object
	oSession.id = sSessionId;
	oSession.accountBalance = sAccountBalance;
	oSession.accountId = sAccountId;		
	oSession.accountBalanceAsOf = sAccountBalanceAsOf;
	oSession.userFirstName = sUserFirstName;
	oSession.clientIP = sClientIP;
	oSession.usageClass = sUsageClass;
	oSession.stateId = sStateId;
	oSession.contactStatus = sContactStatus;
	oSession.launchSignup = 'false';
	oSession.locationBlocked = bLocationBlocked;
	oSession.locationBlockedMsg = sLocationBlockedMessage;
	oSession.AVPref = sAVPref;
	oSession.autoSelectTrackPref = sAutoSelectTrackPref;
	oSession.autoLoadMTVPref = sAutoLoadMTVPref;
	oSession.infoViewerAlwaysOnTopPref = sInfoViewerAlwaysOnTop;
	oSession.contentType = sContentType;
	oSession.hasWageringAccount = stringToBoolean(sHasWageringAccount);
	oSession.activeHubName = sActiveHubName;
	oSession.canSwitchAccounts = stringToBoolean(sCanSwitchAccounts);
	oSession.handle = sHandle
	oSession.userId = sUserId;
	oSession.staticLists = sStaticLists;
	oSession.features = arFeatures;
	oSession.hasTurfdaySingleDayPlan = stringToBoolean(sHasTurfdaySingleDayPlan);
	oSession.cancelWagerActivated = stringToBoolean(sCancelWagerActivated);
	
	return oSession;
}

// Function that loads pop up windows in the center of the screen....
function centerPopup(sURL, sWinName, iWidth, iHeight, bScrollBars) {
	try {
		if (screen.width) {
			var topPosition = parseInt(((screen.height-iHeight)/2),10) - 20;
			var leftPosition = parseInt(((screen.width-iWidth)/2),10) - 10;
		} else {
			var topPosition = 0;
			var leftPosition = 0;
		}
		var sFeatures = 'scrollbars=' + ((false == bScrollBars) ? 'no' : 'yes') + ',resizable=yes,location=no,directories=no,toolbar=no,menubar=no,width='+iWidth +',height='+iHeight+',top='+topPosition+',left='+leftPosition + ',status=no';
		var oWindow = window.open(sURL,sWinName,sFeatures);
		return oWindow;
	} catch (e) {
		handleException(ChainableException.fromGenericException(e,getCallContext()));
		return null;
	}
}

// Function that loads pop up windows in the center of the screen with Toolbars....
function centerPopupToolbars(sURL, sWinName, iWidth, iHeight) {
	try {
		if (screen.width) {
			var topPosition = parseInt(((screen.height-iHeight)/2),10) - 45;
			var leftPosition = parseInt(((screen.width-iWidth)/2),10) - 10;
		} else {
			var topPosition = 0;
			var leftPosition = 0;
		}
		var sFeatures = 'scrollbars=yes,resizable=yes,location=no,directories=no,toolbar=yes,menubar=no,width='+iWidth +',height='+iHeight+',top='+topPosition+',left='+leftPosition + ',status=no';
		var oWindow = window.open(sURL,sWinName,sFeatures);
		return oWindow;
	} catch (e) {
		handleException(ChainableException.fromGenericException(e,getCallContext()));
		return null;
	}
}

/**
completeAppEvent() -  HS - 7/02
Purpose: Log completion of an application-loading event (i.e. LoadRacingHome) back to the server.
Input Parameters:
	iEventId - Int returned from server-side call to logAppEvent()
	iCompletionCode - Optional int for non-zero code of error that means event did not complete normally.
	sCompletionText - Optional text containing details of error that prevented normal event completion
Outputs/Returns:
	None.
Exception Handling:
	Throws ChainableExceptions.
**/
function completeAppEvent(iEventId,iCompletionCode,sCompletionText) {
	try {
		if (isNaN(parseInt(iEventId))) throw("Invalid iEventId parameter [" + iEventId + "] passed to completeAppEvent");
		var sQueryString = "?eid=" + iEventId;		
		if (iCompletionCode) {
			sQueryString += "&completionCode=" + iCompletionCode;
		} 		
		if (sCompletionText) {
			sQueryString += "&completionText=" + sCompletionText;
		}	
		var sURL = LOG_EVENT_COMPLETION_URL + sQueryString;		
				
		var oXML = new ActiveXObject("Microsoft.XMLDOM");
		oXML.async = false;
		oXML.validateOnParse = false;
		oXML.resolveExternals = false;		
		oXML.load(sURL);
	} catch (e) {
		var oException = ChainableException.fromGenericException(e,getCallContext());
		throw(oException);
	}
}	

/**
setOneYearCookie -  HS - 7/02
Purpose: Sets a cookie that expires in one year. Path = APP_ROOT, domain = hostname
Input Parameters: Cookie name, cookie value (will be escaped).
Outputs/Returns: None. Sets document.cookie
Exception Handling: Throws chainable exceptions.
Depends On: ChainableException. DOM document properties.
Used By: Preload.js
**/
function setOneYearCookie(sCookieName, sCookieValue) {
	try {
		if (!isDefString(sCookieName)) throw("Missing or invalid sCookieName parameter [" + sCookieName + "]");
		if (!isDefString(sCookieValue)) throw("Missing or invalid sCookieValue parameter [" + sCookieValue + "]");
		
		var sCookieString = sCookieName + "=" +  escape(sCookieValue);
		var dtExpiresDate = new Date();
		dtExpiresDate.setFullYear(dtExpiresDate.getFullYear() + 1);
		var sExpires = dtExpiresDate.toGMTString();
		sCookieString += "; expires=" + sExpires;
		sCookieString += "; path=" + APP_ROOT;
		document.cookie = sCookieString;
	} catch (e) {
		throw(ChainableException.fromGenericException(e,getCallContext()));
	}
}

/**
setOneYearVersionIndependentCookie -  XLI - 10/22/04
Purpose: Sets a cookie that expires in one year. Path = /, domain = hostname
Input Parameters: Cookie name, cookie value (will be escaped).
Outputs/Returns: None. Sets document.cookie
Exception Handling: Throws chainable exceptions.
Depends On: ChainableException. DOM document properties.
Used By: DEFAULT.ASP, to set an version independent cookie
**/
function setOneYearVersionIndependentCookie(sCookieName, sCookieValue) {
	try {
		if (!isDefString(sCookieName)) throw("Missing or invalid sCookieName parameter [" + sCookieName + "]");
		if (!isDefString(sCookieValue)) throw("Missing or invalid sCookieValue parameter [" + sCookieValue + "]");
		
		var sCookieString = sCookieName + "=" +  escape(sCookieValue);
		var dtExpiresDate = new Date();
		dtExpiresDate.setFullYear(dtExpiresDate.getFullYear() + 1);
		var sExpires = dtExpiresDate.toGMTString();
		sCookieString += "; expires=" + sExpires;
		sCookieString += "; path=/";
		document.cookie = sCookieString;
	} catch (e) {
		throw(ChainableException.fromGenericException(e,getCallContext()));
	}
}

/**
setCookie -  XL - 5/03  -- updated by RJJ 1/08
Purpose: Sets a cookie that expires at the end of the session. Path = APP_ROOT, domain = hostname
Input Parameters: Cookie name, cookie value (will be escaped). [update] Option parameter for expiration date/time.
Outputs/Returns: None. Sets document.cookie
Exception Handling: Throws chainable exceptions.
Depends On: ChainableException. DOM document properties.
Used By: Preload.js
**/
function setCookie(sCookieName, sCookieValue, dtExpiresDate, sPath, sDomain) {
	try {
		if (!isDefString(sCookieName)) throw("Missing or invalid sCookieName parameter [" + sCookieName + "]");
		if (!isDefString(sCookieValue)) throw("Missing or invalid sCookieValue parameter [" + sCookieValue + "]");
		
		var sCookieString = sCookieName + "=" +  escape(sCookieValue);
		
		// EXPIRATION
		if (typeof(dtExpiresDate) != "undefined" && dtExpiresDate != null)
			sCookieString += "; expires=" + dtExpiresDate.toGMTString();
		
		// PATH			
		if (typeof(sPath) != "undefined" && sPath != null)
			sCookieString += "; path=" + sPath;
		else
			sCookieString += "; path=" + APP_ROOT;
		
		// DOMAIN
		if (typeof(sDomain) != "undefined" && sDomain != null)
			sCookieString += "; domain=" + sDomain;
			
		document.cookie = sCookieString;
	} catch (e) {
		throw(ChainableException.fromGenericException(e,getCallContext()));
	}
}

/**
setYouBetDomainCookie -  XL - 05/01/03
Purpose: Sets a cookie that expires when the browser window beging closed. Path = /, domain = youbet.com
Input Parameters: Cookie name, cookie value (will be escaped).
Outputs/Returns: None. Sets document.cookie
Exception Handling: Throws chainable exceptions.
Depends On: ChainableException. DOM document properties.
Used By: Preload.js
**/
function setYouBetDomainCookie(sCookieName, sCookieValue) {
	try {
		if (!isDefString(sCookieName)) {
			throw("Missing or invalid sCookieName parameter [" + sCookieName + "]");
		}
		
		if (!isDefString(sCookieValue)) {
			throw("Missing or invalid sCookieValue parameter [" + sCookieValue + "]");
		}
		
		var sCookieString = sCookieName + "=" +  escape(sCookieValue);
		sCookieString += "; path=/";
		if (new String(location.hostname).match(/\.com/) != null) {
			// if we're not on 'localhost' set the domain
			// can't set a 'domain' to just 'localhost' must contain at least one dot
			// this is a workaround for some IE versions that reject cookies without domain spec
			sCookieString += "; domain=youbet.com";
		}
		document.cookie = sCookieString;
	} catch (e) {
		throw(ChainableException.fromGenericException(e,getCallContext()));
	}
}

/**
expireCookie -  XL - 5/03
Purpose: Expires a cookie by setting it's expiration one year in the past.
Input Parameters: Cookie name
Outputs/Returns: None. Sets document.cookie
Exception Handling: Throws chainable exceptions.
Depends On: ChainableException. DOM document properties.
Used By: Preload.js
**/
function expireCookie(sCookieName) {
	try {
		if (!isDefString(sCookieName)) {
			throw("Missing or invalid sCookieName parameter [" + sCookieName + "]");
		}

		var dtExpiresDate = new Date();
		dtExpiresDate.setFullYear(dtExpiresDate.getFullYear() - 1); // set expiration to one year ago.
		var sExpires = dtExpiresDate.toGMTString();
		
		var sCookieString = sCookieName + "=";
		sCookieString += "; domain=youbet.com";
		sCookieString += "; path=/";
		sCookieString += "; expires=" + sExpires;

		document.cookie = sCookieString;
	} catch (e) {
		throw(ChainableException.fromGenericException(e,getCallContext()));
	}
}

/**
expireYouBetDomainCookie -  XL - 5/03
Purpose: Expires YouBet.com domain cookie by setting it's expiration one year in the past.
Input Parameters: Cookie name
Outputs/Returns: None. Sets document.cookie
Exception Handling: Throws chainable exceptions.
Depends On: ChainableException. DOM document properties.
Used By: Preload.js
**/
function expireYouBetDomainCookie(sCookieName) {
	try {
		if (!isDefString(sCookieName)) {
			throw("Missing or invalid sCookieName parameter [" + sCookieName + "]");
		}

		var dtExpiresDate = new Date();
		dtExpiresDate.setFullYear(dtExpiresDate.getFullYear() - 1); // set expiration to one year ago.
		var sExpires = dtExpiresDate.toGMTString();
		
		var sCookieString = sCookieName + "=";
		sCookieString += "; domain=youbet.com";
		sCookieString += "; path=/";
		sCookieString += "; expires=" + sExpires;

		document.cookie = sCookieString;
	} catch (e) {
		throw(ChainableException.fromGenericException(e,getCallContext()));
	}
}

/**
isDefString -  HS - 7/02
Purpose:
	Tests an input variable to see if it is a defined, non empty string.
Input Parameters:
	s - input variable to test.
Outputs/Returns:
	s is null - false
	s is "" - false
	s is "undefined" - false
	s none of the above - true
Exception Handling:
	Should never throw.
**/
function isDefString(s) {
	if (!s || s == "" || s == "undefined") {
		return false;
	} else {
		return true;
	}
}

/**
getWagerSequenceNumber - XL - 02/03
Purpose:
	Generate an wager sequence number. Used by wager pad.
Input Parameters:
	sSessStartTimeLocal - session start time.
Outputs/Returns:
	wager sequence number
Exception Handling:
	If current time is less then session start time.
**/
function getWagerSequenceNumber(sSessStartTimeLocal) {
	// returns milliseconds since start of session
	var iTicks = new Date().getTime() - new Date(sSessStartTimeLocal).getTime();
	if (iTicks > 2147483646 || iTicks <= 0) {
		// sanity check, iTicks must be positive signed Int32
		throw("Invalid sequence number. Your clock may have changed since you logged in. Please log out and log in again.");
	}
	return iTicks;
}		

/**
openContestDetail - XL - 02/03
Purpose:
	Open a modal dialog to display contest detail information.
	Used by wager pad confirmation dialog and wager queue pad confirmation dialog.
Input Parameters:
	iContestId - contest id.
Outputs/Returns:
	none.
Exception Handling:
	none.
**/
function openContestDetail(iContestId) {
	var sURL = CONTEST_DETAIL_URL + '?contestId=' + iContestId;
	window.showModalDialog(sURL,"","dialogWidth=594px;dialogHeight=580px;scroll=no");
}


/** getXMLStringAttValue - HS - 7/03
	Given a node and an attribute name, return the value of the attribute.
	Return an empty string if the attribute could not be retrieved.
	Optional bThrowIfMissing argument causes the function to throw an error rather than returning
	an empty string.
**/
function getAttStringValue(oNode,sAttName,bThrowIfMissing) {
	var sRetVal = "";
	try {
		sRetVal = oNode.attributes.getNamedItem(sAttName).nodeValue;
	} catch (e) {
		if (bThrowIfMissing) {
			throw("Attribute [" + sAttName + "] not found.");
		} 
	}
	return sRetVal;
}

/** getXMLIntAttValue - HS - 7/03
	Given a node and an attribute name, return the integer value of the attribute.
	Returns null if the attribute could not be retrieved or is NaN.
	Optional bThrowIfMissing argument causes the function to throw an error rather than returning
	an null.
**/
function getAttIntValue(oNode,sAttName,bThrowIfMissing) {
	var iRetVal;
	try {
		iRetVal = parseInt(getAttStringValue(oNode,sAttName,bThrowIfMissing));
		if (isNaN(iRetVal)) {			
			iRetVal = null;
			if (bThrowIfMissing) throw("foo");
		}
	} catch(e) {
		if (bThrowIfMissing) {
			throw("Attribute [" + sAttName + "] not found.");
		}
	}
	return iRetVal;
}

// wait() - loop doing nothing for a given number of seconds
function wait(numSecs) {
	var dtStart = new Date();
	while ( ((new Date()).getTime() - dtStart.getTime()) < numSecs * 1000) {
		// do nothing.
	}
}

// showPopupBlockerWarning() - shows a warning message. Used when window.open returns null.
// Inputs:
//	sWindowDescription - Required string. String like "Wager Pad" that describes the window that failed to open.
function showPopupBlockerWarning(sWindowDescription) {
	var sMessage = replaceWildcards(WR_ALERT_WINDOW_OPEN_FAILED,sWindowDescription);
	alert(sMessage);
}


////
// numLess() - 
// checks that two parameters are valid numbers, throws if either are not.
// then returns true or false depending on whether the first parameter is less than the second
///
function numLess(first, second) {
	var a = parseInt(first);
	var b = parseInt(second);
	if (isNaN(a)) throw("numLess() - Invalid first parameter [" + first + "]");
	if (isNaN(b)) throw("numLess() - Invalid second parameter [" + second + "]");

	return (a < b);
}

////
// numLessOrEqual() - 
// checks that two parameters are valid numbers, throws if either are not.
// then returns true or false depending on whether the first parameter is less than the second
///
function numLessOrEqual(first, second) {
	var a = parseInt(first);
	var b = parseInt(second);
	if (isNaN(a)) throw("numLessOrEqual() - Invalid first parameter [" + first + "]");
	if (isNaN(b)) throw("numLessOrEqual() - Invalid second parameter [" + second + "]");

	return (a <= b);
}

////
// numGreater() - 
// checks that two parameters are valid numbers, throws if either are not.
// then returns true or false depending on whether the first parameter is less than the second
///
function numGreater(first, second) {
	var a = parseInt(first);
	var b = parseInt(second);
	if (isNaN(a)) throw("numGreater() - Invalid first parameter [" + first + "]");
	if (isNaN(b)) throw("numGreater() - Invalid second parameter [" + second + "]");

	return (a > b);
}

////
// numGreaterOrEqual() - 
// checks that two parameters are valid numbers, throws if either are not.
// then returns true or false depending on whether the first parameter is less than the second
///
function numGreaterOrEqual(first, second) {
	var a = parseInt(first);
	var b = parseInt(second);
	if (isNaN(a)) throw("numGreaterOrEqual() - Invalid first parameter [" + first + "]");
	if (isNaN(b)) throw("numGreaterOrEqual() - Invalid second parameter [" + second + "]");

	return (a >= b);
}

///////////////////////////////////////////////////////////////////////////////
// STRING UTILITIES

/**
    String.isDefined - Xiaowei Li - 08/06

    Purpose:            Tests a string to see if it is a defined, non empty string.
                        The common way of using this is:
                            String(var_name).isDefined()
    Inputs:             None
    Outputs/Returns:    true: is defined and is not empty
                        false: is not defined or is empty
                        var_name is null - false
                        var_name is "" - false
                        var_name is "undefined - false
                        var_name is none of above - true
    Dependencies:       
    Error Handling:     Chainable Exception, though it should never throw.
**/
String.prototype.isDefined =
function String_isDefined() {
    try {
        var sString = this.toString()

	    if (!sString || "" == sString || "undefined" == sString) {
		    return false;
	    } else {
		    return true;
	    }
	} catch (e) {
	    ChainableExceptionHandler.chainException(e);
	}
}



function createXMLHttpRequest(){
    if (window.XMLHttpRequest){
        return new XMLHttpRequest();
    }
    return new ActiveXObject("Microsoft.XMLHTTP");
}



///////////////////////////////////////////////////////////////////////////////
// Cross Browser UTILITIES

/***
    CrossBrowserUtils - Robin Niu - 12/19/2006
    
    Purpose:    CrossBrowserUtils class encapsulate cross browser related utilities
	Used By:    
***/
var CrossBrowserUtils = (
    function CrossBrowserUtils() {
        // CONSTRUCTOR
        
        function ContentUtils() {};
        
        //constant variables
        var FIREFOX = "Firefox";
        var IE = "ie";
        // PUBLIC METHODS
        
        /**
            URLUtils.getEventObject - Robin Niu - 12/19/2006

            Purpose:            get Event object.
            Inputs:             e is a event objetect
            Outputs/Returns:    event object has been handled
            Dependencies:       N/A
            Error Handling:     N/A
        **/
        CrossBrowserUtils.getEventObject =
        function CrossBrowserUtils_getEventObject(oEvent) {
            try{
                if(null == oEvent){
                    oEvent = window.event;
                }
                return oEvent;
            } catch (e) {
                ExceptionHandler.handle(e);
            }
        }
        
        
        /**
            CrossBrowserUtils.getKeyCode - Robin Niu - 12/19/2006

            Purpose:            get Event object to support both IE and Firefox.
            Inputs:             e is a event objetect
            Outputs/Returns:    event object has been handled
            Dependencies:       N/A
            Error Handling:     N/A
        **/
        CrossBrowserUtils.getKeyCode =
        function CrossBrowserUtils_getKeyCode(oEvent) {
            try{
                if(null == oEvent){
                    return window.event.keyCode;
                }
                return oEvent.which;
            } catch (e) {
                ExceptionHandler.handle(e);
            }
        }
        
        
        /**
            CrossBrowserUtils.formSubmit - Robin Niu - 12/19/2006

            Purpose:            submit form to server to support both IE and Firefox.
            Inputs:             oForm object
            Outputs/Returns:    N/A
            Dependencies:       N/A
            Error Handling:     ExceptionHandler
        **/
        CrossBrowserUtils.formSubmit =
        function CrossBrowserUtils_formSubmit(oForm) {
            try{
                if(oForm.fireEvent){
                    oForm.fireEvent("onsubmit");
                } else {
                    oForm.submit();
                }
            } catch (e) {
                ExceptionHandler.handle(e);
            }
        }
        
        /**
            CrossBrowserUtils.getScripts - Robin Niu - 12/19/2006

            Purpose:            get Scriptes for support Firefox
            Inputs:             N/A
            Outputs/Returns:    scripts object
            Dependencies:       N/A
            Error Handling:     ExceptionHandler
        **/
        CrossBrowserUtils.getScripts =
        function CrossBrowserUtils_getScripts() {
            try{
                if(document.scripts){
                    return document.scripts;
                }

                return document.getElementsByTagName("script");
            } catch (e) {
                ExceptionHandler.handle(e);
            }
        }
        
        var _init =
        function CrossBrowserUtils_init(){
            if (gstrBrowserName == FIREFOX){
/*
                // enable using evt.srcElement in Mozilla/Firefox
                Event.prototype.__defineGetter__("srcElement", function () {
					var node = this.target;
					while (node.nodeType != 1) node = node.parentNode;
					// test this:
					if (node != this.target) alert("Unexpected event.target!") // it still happens sometime, why ?
					return node;
                });

                // enable using event.returnValue=false in Mozilla/Firefox
                // Hint: event.returnValue=true doesn't work !
                Event.prototype.__defineSetter__("returnValue", function (b) {
                    if (!b) this.preventDefault();
                    this._returnValue = b;
                    return(b);
                });

                // enable querying event.returnValue in Mozilla/Firefox
                Event.prototype.__defineGetter__("returnValue", function () {
                    return (this._returnValue);
                });

                // enable querying HTMLElement.innerText in Mozilla/Firefox
                HTMLElement.prototype.__defineGetter__("innerText", function () { return(this.textContent); });
                HTMLElement.prototype.__defineSetter__("innerText", function (txt) { this.textContent = txt; });
                
                // enable querying HTMLElement.parentElement in Mozilla/Firefox
                HTMLElement.prototype.__defineGetter__("parentElement",function(){
                    if(this.parentNode==this.ownerDocument)return null;
                    return this.parentNode;
                });
*/
                XMLDocument.prototype.selectNodes = function(cXPathString, xNode)
                {
                    if( !xNode ) { xNode = this; } 

                    var oNSResolver = this.createNSResolver(this.documentElement)
                    var aItems = this.evaluate(cXPathString, xNode, oNSResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
                    var aResult = [];
                    for( var i = 0; i < aItems.snapshotLength; i++)
                    {
	                    aResult[i] =  aItems.snapshotItem(i);
                    }
            		
                    return aResult;
                }

                XMLDocument.prototype.selectSingleNode = function(cXPathString, xNode)
                {
                    if( !xNode ) { xNode = this; } 

                    var xItems = this.selectNodes(cXPathString, xNode);
                    if( xItems.length > 0 )
                    {
	                    return xItems[0];
                    }
                    else
                    {
	                    return null;
                    }
                }
                
				XMLDocument.prototype.selectNodes = function(cXPathString, xNode){
					if( !xNode ) {xNode = this;}

					var defaultNS = this.defaultNS;

					var aItems = this.evaluate(cXPathString, xNode, {
						normalResolver: this.createNSResolver(this.documentElement), lookupNamespaceURI : function (prefix) {
							switch (prefix) {
								case "dflt":
									return defaultNS;
								case YOUBET_PREFIX:
								    return YOUBET_NAMESPACE;
								default:
									return this.normalResolver.lookupNamespaceURI(prefix);
							}
						}
					}, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);

						var aResult = [];
						
						for( var i = 0; i < aItems.snapshotLength; i++){
							aResult[i] =  aItems.snapshotItem(i);
						}
						
					return aResult;
				}
				                
                Element.prototype.selectSingleNode = function(cXPathString)
                {
                    if(this.ownerDocument.selectSingleNode)
                    {
	                    return this.ownerDocument.selectSingleNode(cXPathString, this);
                    }
                }
                Element.prototype.selectNodes = function(cXPathString) {
                    if (this.ownerDocument.selectNodes) {
	                    return this.ownerDocument.selectNodes(cXPathString, this);
                    }
                }
                

//                Element.prototype.__defineGetter__("text", function(){return this.textContent;});
            }

        }
        
        _init();
        return CrossBrowserUtils;
    }
)();

// Checks for existence of the reference object and returns the supported item.
var gE = getElemById = function (id) {
	if (document.getElementById) {
		return document.getElementById(id);
	} else if (document.all) {
		return document.all[id];
	} else if (document.layers) {
		return document.layers[id];
    }
}


// THIS LINE MUST BE LAST
var utils_scriptload = true;  // define variable for ScriptChecker check.
