/********************************************************************/
/*																	*/
/* This file defines numerous commonly used javascript functions.	*/
/*																	*/
/********************************************************************/

// this function limits the size of a string to a specified length,
//  truncating and appending "..." if the string exceeds this length
function limitString(str, limit) {
	var appendStr = "...";
	
	if (str.length > limit)
		return str.substr(0, limit - appendStr.length) + appendStr;
	else
		return str;
	
}  // limitString

// this function returns the version of the client browser being used
function browserVersion()
{
	if (navigator.appName == 'Netscape')
		return parseFloat(navigator.appVersion);
	
	else if (navigator.appName == 'Microsoft Internet Explorer') {
		
		if (navigator.appVersion.indexOf("MSIE") != -1) {
			var temp = navigator.appVersion.split("MSIE");
			return parseFloat(temp[1]);	// find first float to the right of MSIE
		}
	}
	
	// if all else fails just return the first number in the appVersion
	return parseFloat(navigator.appVersion);
	
}  // browserVersion

function browserIsMSIE() {
	return (navigator.appVersion.indexOf("MSIE") != -1);
}

function browserIsSafari() {
	return (navigator.userAgent.indexOf("Safari") != -1);
}

// this function checks if the client is using an unsupported browser and if so,
//  redirects the user to the appropriate page
function checkBrowser()
{
	if ((navigator.appName == 'Microsoft Internet Explorer') && (browserVersion() < 5.0))
		window.location = "/outdated_browser.asp";
	
}  // checkBrowser

// tests whether the user has cookies enabled by setting a cookie
//  and then trying to retrieve it back.  If a "true" argument is passed
//	then this function attempts to test whether persistant cookies
//	(i.e. non session cookies) are enabled.
function testClientCookies() {
	
	var testPersistant = false;
	
	if (arguments.length > 0) testPersistant = arguments[0];
	
	if (testPersistant) {
		// save a new cookies with an expiration date set to 5 seconds from now
		var now = new Date();
		var cookiedate = new Date(now.valueOf() + 1000*5);
		document.cookie = "cookieTest=persist;expires=" + cookiedate.toGMTString();
	}
	else {
		// save a session cookie (no expiration specified = lives for the
		//	lifetime of the browser) so that we check for any kind of cookies
		document.cookie = "cookieTest=session"
	}
	
	if ((document.cookie).indexOf("cookieTest") == -1) {
		// cookie not found; all cookies must be disabled
		return false;
	} else {
		return true;
	}
}  // testClientCookies

// tests whether the user has ActiveX controls enabled by attempting
//	to call a method on the RemoteScripting applet that loads with
//	the standard page structure.
// reference: http://home.istar.ca/~neutron/detectdeny/
function testClientActiveX()
{
	var startLocation = document.location.pathname;
	
	if (document.applets.length > 0) {
		// the following throws a "method not supported" error if the applet
		//  was not successfully loaded
		try {
			var foo = document.applets[0].getCodeBase();
			return true;
		}
		catch (e) {
			var errNum = e.number & 0xFFFF;	// error code stored in lower word
			
			if (errNum == 438) {	// this is the error we expect
				// before reporting this, check that the page hasn't already changed
				//  while this method was executing
				if (document.location.pathname == startLocation) {
					// still on same page - report Applet loading error
					return false;
				}
				else {
					// new page - error might not apply anymore so return true
					return true;
				}
			}
			else {
				// some other error occurred; yet browser handle it
				throw e;
			}
		}
	}
	else {
		return true;
	}
}  // testClientActiveX

// CSS makes the standard overlib variables hard to use so we usually turn on the
//  overlib parameter "ol_fullhtml" to specify that when we call overlib() we want
//  to provide all of the html to create the contents of the popup window.  These
//  are some functions to provide various commonly used html frameworks for
//  overLib() calls.
function myOverLib(text, xOffset, yOffset) {
	
	// xOffset and yOffset are optional (defaults here are copied from overlib.js)
	if ((typeof xOffset) == 'undefined') xOffset = 10;
	if ((typeof yOffset) == 'undefined') yOffset = 10;
	
	return overlib('<table width=200 style=\'background-color: #CCCCFF; border: 1px solid #155791\'><tr><td style=\'padding: 2px\'>' + text + '</td></tr></table>', OFFSETX, xOffset, OFFSETY, yOffset);
}

function newOverLib(text) {
	return overlib('<table width=200 style=\'background-color: #CCCC00; border: 1px solid #155791\'><tr><td style=\'padding: 2px\'>' + text + '</td></tr></table>');
}

// unfixed window width
function unfixedOverLib(text, xOffset, yOffset) {
	
	// xOffset and yOffset are optional (defaults here are copied from overlib.js)
	if ((typeof xOffset) == 'undefined') xOffset = 10;
	if ((typeof yOffset) == 'undefined') yOffset = 10;
	
	return overlib('<table style=\'background-color: #CCCCFF; border: 1px solid #155791\'><tr><td style=\'padding: 2px\'>' + text + '</td></tr></table>', OFFSETX, xOffset, OFFSETY, yOffset);
}

// This function is used to make menu items that open and close
function ToggleDisplay(oTitle, oList) {
	
	var pathDiv, imgName;
	pathDiv = (oTitle.src).lastIndexOf("/");
	
	if (pathDiv == -1)	{ imgName = oTitle.src; pathdiv = (oTitle.src).length; }
	else				imgName = (oTitle.src).substr(pathDiv + 1);
	
	if ((oList.style.display == "") || (oList.style.display == "none"))	{
		oList.style.display = "list-item";
		
		if (imgName != "empty_box.gif") { // only change box if not an empty box
			oTitle.src = (oTitle.src).substr(0, pathDiv + 1) + "minus_box.gif";
		}
	}	else {
		
		oList.style.display = "none";
		if (imgName != "empty_box.gif") { // only change box if not an empty box
			oTitle.src = (oTitle.src).substr(0, pathDiv + 1) + "plus_box.gif";
		}
	}
	return false;
}  // ToggleDisplay


function ToggleDisplay2(oList) {
	if ((oList.style.display == "") || (oList.style.display == "none"))	{
		oList.style.display = "inline";
	}else {
		oList.style.display = "none";
	}
	return false;
}  // ToggleDisplay



function TreeClick(LiId, e) {
	if (!e) e = window.event;
	
	var targ;
	if (e.target) targ = e.target;
	if (e.srcElement) targ = e.srcElement;
	
	// in Netscape 6.2, text within an element is considered a seperate 'text'-type
	//  element that is a child of the HTML element that was actually clicked.
	//  These text pseudo-nodes can be identified by their undefined tagName property.
	if (typeof(targ.tagName) == 'undefined') {
		targ = targ.parentNode;
	}
	
	if (targ) {
		if (targ.className.toLowerCase() != 'nocollapse') {
			// if the element's classname is 'nocollapse' then do not do anything
			//  with this event, otherwise expand/collapse this tree branch (LI)
			var oLi = document.getElementById(LiId);
			
			if (oLi == null)
				return;
			
			if (oLi.className == null) {
				oLi.className = 'open';
				showChildren(oLi);
			}
			else if (oLi.className.toLowerCase() == 'open') {
				oLi.className = 'closed';
				hideChildren(oLi);
			}
			else if (oLi.className.toLowerCase() == 'closed') {
				oLi.className = 'open';
				showChildren(oLi);
			}
		}
	}
}

function hideChildren(elt) {
	for (var i=0; i < elt.childNodes.length; i++) {
		if (elt.childNodes[i].className != null) {
			// only nodes of class 'list-expansion' are hidden
			if (elt.childNodes[i].className.toLowerCase() == 'list-expansion') {
				elt.childNodes[i].style.display = 'none';
			}
			
			// recursively hide the children of this element
			hideChildren(elt.childNodes[i]);
		}
	}
}

function showChildren(elt) {
	for (var i=0; i < elt.childNodes.length; i++) {
		if (elt.childNodes[i].className != null) {
			// only nodes of class 'list-expansion' need to be shown
			if (elt.childNodes[i].className.toLowerCase() == 'list-expansion') {
				elt.childNodes[i].style.display = 'block';
			}
			
			// recursively show the children of this element
			showChildren(elt.childNodes[i]);
		}
	}
}

// This function is used to make menu items that open and close - no images are used
function ToggleSimpleDisplay(oList) {
	
	if ((oList.style.display == "") || (oList.style.display == "none"))	{
		oList.style.display = "block";
		
	}	else {
		
		oList.style.display = "none";
	}
	return false;
}  // ToggleSimpleDisplay

// this function takes a Date object and returns a string that represents that date & time
//  in the format: 12:04 pm, 1 Jul 02
function simpleDateFormat(dateObj) {
	// date-part
	var date = monthAbbr(dateObj.getMonth())  + '-' + dateObj.getDate() + '-' + String(dateObj.getFullYear());
	
	// time-part
	var time = simpleTimeFormat(dateObj);
	
	return time + ', ' + date;
	
} // simpleDateFormat

function simpleTimeFormat(dateObj) {
	return (((dateObj.getHours() % 12) == 0) ? 12 : (dateObj.getHours() % 12)) + ':' +
		((dateObj.getMinutes() < 10) ? '0' : '') +
		dateObj.getMinutes() + ' ' +
		((dateObj.getHours() > 11) ? 'pm': 'am');
} // simpleTimeFormat

// this function takes a decimal number and returns a string of that number formatted as
//	a monetary amount (fixed precision at 2 cents)
function moneyFormat(price) {
	// round to nearest 2 digits to try to fix precision errors from storing doubles
	price = Math.round(price * 100) / 100;
	
	var negative = (price < 0);
	
	if (negative) price = price * -1;
	
	// split on the decimal point (may not be one at all)
	var parts = (new String(price)).split('.');
	
	var dollars = parts[0];
	if (dollars == '') dollars = '0';
	
	var cents = '0';
	
	if (parts.length > 1) {
		cents = parts[1];
		
		if (cents == '') cents = '0';
	}
	
	cents = cents + '00';
	cents = '.' + cents.substr(0, 2);
	
	// check for a second parameter: shortFormat
	if (arguments.length > 1) {
		if (arguments[1] == true) {
			if (cents == '.00') {
				cents = '';
			}
		}
	}
	
	return (negative ? '- ' : '') + '$' + dollars + cents;
}

// this function acts like the ASP function Server.HTMLEncode; it replaces
//	special characters with their HTML encoding.
//	reference: http://www.w3.org/TR/REC-html40/charset.html
function EncodeToHTML(str)
{
	str = '' + str;	// make sure its a string
	
	// have to do ampersand encoding FIRST so that we don't encode the
	//	ampersand that is part of the other encodings
	str = str.replace(/&/gi, '&amp;');	// encoding for &
	str = str.replace(/"/gi, '&quot;');	// encoding for "
	str = str.replace(/</gi, '&lt;');	// encoding for <
	str = str.replace(/>/gi, '&gt;');	// encoding for >
	
	return(str);
	
} // EncodeToHTML

// this function takes a string and replaces all JavaScript
//	special characters with their escaped equivalent.
function EncodeToJS(str)
{
	str = '' + str;	// make sure its a string
	
	// have to escape backslashes first, or the backslashes
	//	entered as part of the escaping of other characters
	//	will be inadvertantly escaped when this line is
	//	executed
	str = str.replace(/\\/gi, '\\\\');	// encoding for \
	str = str.replace(/"/gi, '\\\"');	// encoding for "
	str = str.replace(/'/gi, '\\\'');	// encoding for '
	
	return(str);
	
} // EncodeToJS

// this function performs all encoding necessary for a string to be
//	displayed in an overlib popup.
function EncodeToOverLib(str)
{
	return(EncodeToJS(str.replace(/&/gi, '&amp;').replace(/\n/gi, '<br>')));
}

// this is an alternate version of EncodeToOverLib that does NOT also
//  before Javascript encoding on the string.  this is probably how
//  EncodeToJS should perform but I am loathe to mess with it now since
//  there is a lot of code that might already be calling it.
function EncodeToOverLibNoJs(str)
{
	return(str.replace(/&/gi, '&amp;').replace(/\n/gi, '<br>'));
}

// displaying a row after it is hidden is a browser-specific operation
function showRow(rowId)
{
	document.getElementById(rowId).style.display =
		browserIsMSIE() ? 'block' : 'table-row';
}

// function to sort the options of a select input
function sortSelectOptions(options, sortOnValue, sortAsNumbers)
{
	// copy all values from select-options into an array
	var tempArray = new Array(options.length);
	
	for (var i = 0; i < options.length; i++)
		tempArray[i] = new Array(options[i].text, options[i].value);
	
	var selected = options[options.selectedIndex].value;
	
	if (sortOnValue && sortAsNumbers)	tempArray.sort(numSortSecondElt);
	if (sortOnValue && !sortAsNumbers)	tempArray.sort(lexSortSecondElt);
	if (!sortOnValue && sortAsNumbers)	tempArray.sort(numSortFirstElt);
	if (!sortOnValue && !sortAsNumbers)	tempArray.sort(lexSortFirstElt);
	
	for (var i = 0; i < tempArray.length; i++) {
		options[i].text = tempArray[i][0];
		options[i].value = tempArray[i][1];
	}
	
	// set the select to point to the module number that was selected originally
	for (var i = 0; i < tempArray.length; i++) {
		if (tempArray[i][1] == selected) {
			options.selectedIndex = i;
			break;
		}
	}
} // sortSelectOptions

// function to lexicographically sort 2 arrays based on the first element
function lexSortFirstElt(a, b) {
	var strA = '' + a[0], strB = '' + b[0];
	
	if (strA < strB)
		return -1;
	
	if (strA > strB)
		return 1;
	
	return 0;
}

// function to lexicographically sort 2 arrays based on the second element
function lexSortSecondElt(a, b) {
	var strA = '' + a[1], strB = '' + b[1];
	
	if (strA < strB)
		return -1;
	
	if (strA > strB)
		return 1;
	
	return 0;
}

// function to numerically sort 2 arrays based on the first element
function numSortFirstElt(a, b) {
	var numA = new Number(a[0]), numB = new Number(b[0]);
	
	if (numA < numB)
		return -1;
	
	if (numA > numB)
		return 1;
	
	return 0;
}

// function to numerically sort 2 arrays based on the second element
function numSortSecondElt(a, b) {
	var numA = new Number(a[1]), numB = new Number(b[1]);
	
	if (numA < numB)
		return -1;
	
	if (numA > numB)
		return 1;
	
	return 0;
}

function genericArraySort(a, b, index, descending)
{
	var val1=a[index];
	var val2=b[index];
	
	if (val1!=null && typeof(val1)=='string')
	{
		val1=val1.toLowerCase();
	}
	if (val2!=null && typeof(val2)=='string')
	{
		val2=val2.toLowerCase();
	}
	if (!descending) {
		// check for null values before comparing
		if (val1 == null) return -1;
		if (val2 == null) return 1;
		
		if (val1 < val2)
			return -1;
			
		if (val1 > val2)
			return 1;
	}
	else {
		// descending
		
		// check for null values before comparing
		if (val1 == null) return 1;
		if (val2 == null) return -1;
		
		if (val1 > val2)
			return -1;
			
		if (val1 < val2)
			return 1;
	}
		
	return 0;
}	// genericArraySort

// function to convert an integer to the word that describes its place in a sequence
// for example, 1 becomes 1st, 2 becomes 2nd, 14 becomes 14th, etc.
function sequential(i)
{
	if (i < 1)						return ("" + i) + "th";
	
	else if ((i > 10) && (i < 20))	return ("" + i) + "th";		// 11th, 12th, 13th, ..., 19th
	
	else if ((i % 10) == 0)			return ("" + i) + "th";		// 10th, 20th, 30th, ...
	
	else if ((i % 10) == 1)			return ("" + i) + "st";		// 1st, 21st, 31st, ...
	
	else if ((i % 10) == 2)			return ("" + i) + "nd";		// 2nd, 22nd, 32nd, ...
	
	else if ((i % 10) == 3)			return ("" + i) + "rd";		// 3rd, 23rd, 33rd, ...
	
	else							return ("" + i) + "th";		// 4th, 5th, 6th, ...
}  // sequential

// function to convert an integer from 0 to 11 to a 3-letter month abbreviation
function monthAbbr(i)
{
	switch (i)
	{
		case 0:
			return "Jan";
			break;
			
		case 1:
			return "Feb";
			break;
			
		case 2:
			return "Mar";
			break;
			
		case 3:
			return "Apr";
			break;
			
		case 4:
			return "May";
			break;
				
		case 5:
			return "Jun";
			break;
			
		case 6:
			return "Jul";
			break;
			
		case 7:
			return "Aug";
			break;
			
		case 8:
			return "Sep";
			break;
			
		case 9:
			return "Oct";
			break;
			
		case 10:
			return "Nov";
			break;
			
		case 11:
			return "Dec";
			break;
	}
}  // monthAbbr

// function to convert an integer from 0 to 11 to the month's full name
function monthName(i)
{
	switch (i)
	{
		case 0:
			return "January";
			break;
			
		case 1:
			return "February";
			break;
			
		case 2:
			return "March";
			break;
			
		case 3:
			return "April";
			break;
			
		case 4:
			return "May";
			break;
				
		case 5:
			return "June";
			break;
			
		case 6:
			return "July";
			break;
			
		case 7:
			return "August";
			break;
			
		case 8:
			return "September";
			break;
			
		case 9:
			return "October";
			break;
			
		case 10:
			return "November";
			break;
			
		case 11:
			return "December";
			break;
	}
}  // monthName

// CapMsgLen
function CapMsgLen(field, maxLen) {
	if (field.value.length > maxLen) {
		alert('I\'m sorry, this field is limited to ' + maxLen + ' characters.');
		field.value = field.value.substring(0, maxLen);
	}
}

// returns the url-source of the Thawte certificate logo script
function getSecureCertificationUrl() {
	//return 'https://siteseal.thawte.com/cgi/server/thawte_seal_generator.exe';
	return '/common/doNothing.js';
}

// returns html to render the BBBOnline logo link
function getBBBOnlineHtml() {
	return '<a href="http://www.bbbonline.org/cks.asp?id=10501288145551104" target="_blank"><img src="/objects/images/ReliabilitySeal3.gif" border="0"></a>';
}

// sets the html select-input to the specified element (if it exists)
function SetSelectValue(selectInput, elementValue) {
	for (var i=0; i<selectInput.options.length; i++) {
		if (selectInput.options[i].value == elementValue) {
			selectInput.selectedIndex = i;
			return;
		}
	}
}

function SetSelectValueByText(selectInput, elementText) {
	for (var i=0; i<selectInput.options.length; i++) {
		if (selectInput.options[i].text == elementText) {
			selectInput.selectedIndex = i;
			return;
		}
	}
}

