/**
 * Validates that a text element has a non-null and non-empty value
 *
 * @param testElement - element reference to test .value
 * @param errorMessage - message to display if the field does not validate
 * @param changeClass - true if the class of the element should be changed to Error if it does not validate
 *
 * @return boolean - true if all is OK, false if validation failed
 */
function validateTextElement(testElement, errorMessage, changeClass) {
	if(isNullOrEmpty(testElement)) {
		alert(errorMessage);
		if(changeClass) testElement.className = "Error";
		testElement.focus();
		return false;
	}
	if(changeClass) testElement.className = "";
	return true;
}

/**
 * Validates that a text element has a non-null and non-empty value that is all digits/numbers
 *
 * @param testElement - element reference to test .value
 * @param errorMessage - message to display if the field does not validate
 * @param changeClass - true if the class of the element should be changed to Error if it does not validate
 *
 * @return boolean - true if all is OK, false if validation failed
 */
function validateAllDigits(testElement, errorMessage, changeClass) {
	if(!allDigits(testElement.value)) {
		alert(errorMessage);
		if(changeClass) testElement.className = "Error";
		testElement.focus();
		return false;
	}
	if(changeClass) testElement.className = "";
	
	return true;
}

/**
 * Builds and returns an IMG node with the given src
 */
function buildClearImageNode(imageSource) {
	var theNode = document.createElement("img");
	
	var styleAttr = document.createAttribute("style");	
	styleAttr.nodeValue = "width:1px; height:1px; border:0;";
	theNode.setAttributeNode(styleAttr);
	
	var srcAttr = document.createAttribute("src");
	srcAttr.nodeValue = imageSource;
	theNode.setAttributeNode(srcAttr);
	
	return theNode;
}

/**
 * Convenient do-nothing function that can be useful with AJAX requests
 */
function doNothing() {}

/**
 * This method is invoked by AJAX requests which get an OK response with a message to display.
 */
function ajaxOK(ajaxOkMessage) {
	alert(ajaxOkMessage);
}

/**
 * This method is invoked by AJAX requests which get an ERROR response
 */
function ajaxError(ajaxErrorMessage) {
	alert(ajaxErrorMessage);
}



//array which holds references to objects which should have the ONCHANGE function called when the window loads
var CALL_ONCHANGE_ONLOAD = new Array();

function onchangeOnLoad() {
	if(onchangeOnLoadSaved != null) {
		onchangeOnLoadSaved();
	}
	
	for(var tempI=0 ; tempI<CALL_ONCHANGE_ONLOAD.length ; tempI++) {
		CALL_ONCHANGE_ONLOAD[tempI].onchange();
	}
}
var onchangeOnLoadSaved = window.onload;
window.onload = onchangeOnLoad;


//array which holds references to objects which should have the ONKEYUP function called when the window loads
var CALL_ONKEYUP_ONLOAD = new Array();

function onkeyupOnLoad() {
	if(onkeyupOnLoadSaved != null) {
		onkeyupOnLoadSaved();
	}
	
	for(var tempI=0 ; tempI<CALL_ONKEYUP_ONLOAD.length ; tempI++) {
		CALL_ONKEYUP_ONLOAD[tempI].onkeyup();
	}
}
var onkeyupOnLoadSaved = window.onload;
window.onload = onkeyupOnLoad;

/**
 * Opens a popup window
 * 
 * @param url - the URL of the popup to open
 * @param width - width of the popup window
 * @param height - height of the popup window
 * @param resize - true to allow resize of the popup, false if not
 */
function openPopup(url, width, height, resize) {
	var popupFeatures = "top=10,left=10,width=" + width + ",height=" + height + ",scrollbars=yes,status=no";
	
	//add the "resize" part of the window features
	if(resize) {
		popupFeatures += ",resizable=yes"; //or 1
	} else {
		popupFeatures += ",resizable=no"; //or 0
	}
	
	window.open(url, "_new", popupFeatures);
}

/**
 * Invokes the onclick() function of the given input if the key
 * that caused the given event was an event-triggering key
 *
 * @param theInput - the input on which to invoke onClick() if needed
 */
function invokeOnClick(theInput) {
	var keyPressedCode = (window.event) ? event.keyCode : e.keyCode;
	
	//32=Space
	if(keyPressedCode == 32) {
		return theInput.onclick();
	}
}

/**
 * Determines if the value of the given text field is NULL or EMPTY
 */
function isNullOrEmpty(textField) {
	if(!textField) {
		return true;
	}
	
	var value = textField.value;
	
	if(value == null || trimString(value).length < 1) {
		return true;
	}
	return false;
}

/**
 * Determines if the values of two text fields match
 */
function valuesMatch(textField1, textField2) {
	var tempValue1 = trimString(textField1.value);
	var tempValue2 = trimString(textField2.value);
	
	return tempValue1 == tempValue2;
}

/**
 * Determines if a string value is all digits (assumes string is not null or empty)
 *
 * Will return true if the string is not null and not empty and has all digits
 * Will return false if the string is null, empty or has any non-digits (1-9)
 */
var NUMBERS = "0123456789"; //all characters valid in a number
function allDigits(adstring) {
	var tempTrimmed = trimString(adstring);
	if(tempTrimmed.length < 1) {
		return false;
	}
	
	//iterate over each value and make sure it is 1-9
	for(var tempI=0 ; tempI<tempTrimmed.length ; tempI++) {
		if(NUMBERS.indexOf(tempTrimmed.charAt(tempI)) <0 ){
			//found a non-numeric character in the string
			//alert("character: \"" + tempTrimmed.charAt(tempI) + "\" is not a number");
			return false;
		}
	}

	//if we are here then we are ok as all digits
	return true;
} //END: allDigits()

/**
 * Trims a value and returns it
 */
function trimString(string) {
	if(string == null) {
		return "";
	}

	//start at front and work to the first non-space character
	var firstNonSpace = 0;
	for(var i=0 ; i<string.length ; i++) {
		var cur = string.substr(i,1);
		if(cur == " " || cur == "\t") {
			firstNonSpace = i+1;
		} else {
			break;
		}
	}

	//start at end and work to last non-space character
	var lastNonSpace = string.length;
	for(var i=string.length ; i>=0 ; i--) {
		var cur = string.substr(i-1,1);
		if(cur == " " || cur == "\t") {
			lastNonSpace = i-1;
		} else {
			break;
		}
	}

	if(lastNonSpace <= firstNonSpace) {
		return "";
	} else {
		if(string) {
			return string.substring(firstNonSpace, lastNonSpace);
		} else {
			return "";
		}
	}
} //END: trimString()

/**
 * Does some simple validation of an email address
 */
function isValidEmail(str) {
	return (str.indexOf(".") > 0) && (str.indexOf("@") > 0);
}

/**
 * "Pauses" JavaScript processing for a certain number of milliseconds
 */
function pauseMillis(millis) {
	var pauseDate = new Date();
	var pauseCurDate = null;
	
	do {
		pauseCurDate = new Date();
	} while(pauseCurDate-pauseDate < millis);
}

/**
 * Array of valid number of days per month (1-based...1=January, 2=February, etc.)
 */
var DAYS_IN_MONTH = new Array(0,31,0,31,30,31,30,31,31,30,31,30,31);

/**
 * Determines if a number is a leap year
 */
function isLeapYear(intYear) {
	return ((intYear % 4 == 0) && (intYear % 100 != 0 || intYear % 400 == 0));
}	

/**
 * Determines if the given value is a valid date in MM/DD/YYYY format
 */
function isValidDate(value) {
	if(!value) return false;

	var trimmed = trimString(value);
	if(trimmed.length < 1) return false;
	
	var parts = trimmed.split('/');
	if(parts.length != 3) return false;
	
	var monthVal = parseInt(parts[0], 10);
	var dayVal = parseInt(parts[1], 10);
	var yearVal = parseInt(parts[2], 10);
	
	if(monthVal < 1 || monthVal > 12) return false;
	
	if(monthVal !=2) {
		if(dayVal < 1 || dayVal > 31) return false;
	} else {
		//February
		if(isLeapYear(yearVal)) {
			if(dayVal < 1 || dayVal > 29) return false;
		} else {
			if(dayVal < 1 || dayVal > 28) return false;
		}
	}
	
	if(yearVal < 2000 || yearVal > 3000) return false;
	
	//if we are here then we have a valid date
	return true;
}

/**
 * Compares two dates and returns -1, 0, or 1 depending on the relationship between the dates
 *
 * Date parameter values are in M/D/YYYY format or MM/DD/YYYY or any derivative thereof
 */
function compareDates(d1, d2) {
	//turn each date into an integer in YYYYMMDD format
	var i1 = dateToInt(d1);
	var i2 = dateToInt(d2);
	
	if(i1 == i2) return 0;
	
	if(i1 < i2) return -1;
	
	return 1;
}

/**
 * Turns a date into an integer in YYYYMMDD format
 *
 * Assumes a valid date in MM/DD/YYYY or M/D/YYYY or MM/D/YYYY or M/DD/YYYY format
 */
function dateToInt(theDate) {
	try {
		var trimmed = trimString(theDate);
		if(trimmed.length < 1) return 0;
		
		var parts = trimmed.split('/');
		if(parts.length != 3) return 0;
		
		var monthVal = parseInt(parts[0]);
		var dayVal = parseInt(parts[1]);
		var yearVal = parseInt(parts[2]);
		
		//start with the year
		var tempString = "" + yearVal;
		
		//now the month
		if(monthVal < 10) {
			tempString += "0" + monthVal;
		} else {
			tempString += monthVal;
		}
		
		//now the day
		if(dayVal < 10) {
			tempString += "0" + dayVal;
		} else {
			tempString += dayVal;
		}
		
		//parse into an integer
		return parseInt(tempString);
		
	} catch (ex) {
		return 0;
	}
}

/**
 * Adds a new message to the messages displayed on the page
 */
function addPageMessage(messageValue) {
	var messagesDIV = document.getElementById("MessagesDIV");
	if(!messagesDIV) {
		//create the DIV and the table
		var contentDIV = document.getElementById("Content");
		messagesDIV = document.createElement("div");
		messagesDIV.setAttribute("class", "Messages");
		messagesDIV.setAttribute("id", "MessagesDIV");
		contentDIV.insertBefore(messagesDIV, contentDIV.firstChild);

		var table = document.createElement("table");
		table.setAttribute("class", "Messages");
		messagesDIV.appendChild(table);
	}
	
	//ref to the table
	var table = messagesDIV.firstChild;
	var tbody = document.createElement("tbody");
	table.appendChild(tbody);
	
	//now add a row to the table
	var tr = document.createElement("tr");
	tbody.appendChild(tr);
	var th = document.createElement("th");
	tr.appendChild(th);
	var img = document.createElement("img");
	img.setAttribute("src",INFORMATION_ICON_PATH);
	img.setAttribute("alt","Information Message");
	th.appendChild(img);

	var td = document.createElement("td");
	td.appendChild(document.createTextNode("First and Last Name have been updated successfully"));
	tr.appendChild(td);
}

/**
 * Removes all messages currently displayed on the page
 */
function removePageMessages() {
	var messagesDIV = document.getElementById("MessagesDIV");
	if(!messagesDIV) {
		//nothing to do
		return;
	}
	messagesDIV.parentNode.removeChild(messagesDIV);
}

/**
 * Adds a message to the given element
 */
function addElementMessage(elementRef, message) {
	if(!elementRef) return;

	var container = document.createElement("span");
	elementRef.appendChild(container);
	
	var img = document.createElement("img");
	img.setAttribute("src",INFORMATION_ICON_PATH);
	img.setAttribute("alt","Information Message");
	img.setAttribute("style","padding:2px 5px 0 0; border:0; width:16px; height:16px;");

	var text = document.createTextNode(message);
	
	container.appendChild(img);
	container.appendChild(text);	
}

/**
 * Removes all children from the given element
 */
function removeAllChildren(elementRef) {
	if(!elementRef) return;
	
	try {
		while(elementRef.hasChildNodes) {
			elementRef.removeChild(elementRef.firstChild);
		}
	} catch (ignore){}
}


/**
 * Use this function to submit a form using an AJAX request
 *
 * This method is only useful when the response is expected to be auto-executed JavaScript
 */
function submitFormViaAJAX(theForm) {
	var theURL = theForm.action + "?" + Form.serialize(theForm);
	new Ajax.Request(theURL,
	  {
	    method:'get'
	  });
}


			
/**
 * Builds an integer total which identifies a relative amount of time
 * from three time selectors: HOUR, MINUTE, AM/PM
 */
function getRelativeTimeTotal(hourSelect, minuteSelect, ampmSelect) {
	//boolean for if this time is PM
	var isPM = ampmSelect.options[ampmSelect.selectedIndex].value == "PM";
	
	var hour = parseInt(hourSelect.options[hourSelect.selectedIndex].value);
	
	//special case when hour == 12 because it will either be zero (am) or 12(pm)
	if(hour == 12) hour = 0;
	
	//add 12 to the hour if we are PM to get 24-hour time
	if(isPM) hour += 12;
	
	//multiply by 100 to move the hour up a couple of "slots" to make way for the minute
	hour *= 100;
	
	//now add the minutes to the hour
	var minutes = parseInt(minuteSelect.options[minuteSelect.selectedIndex].value);
	hour += minutes;
	
	return hour;
}


function getWindowDimensions() {
	if (window.innerHeight) {
		myheight = window.innerHeight;
		mywidth = window.innerWidth;
	} else {
		myheight = document.documentElement.clientHeight;
		if(!myheight || myheight == 0) myheight = document.body.clientHeight;
		mywidth = document.body.clientWidth;
	}
	
	return [mywidth,myheight];
}


function getScrollCoords() {
  var scrOfX = 0, scrOfY = 0;
  if( typeof( window.pageYOffset ) == 'number' ) {
    //Netscape compliant
    scrOfY = window.pageYOffset;
    scrOfX = window.pageXOffset;
  } else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
    //DOM compliant
    scrOfY = document.body.scrollTop;
    scrOfX = document.body.scrollLeft;
  } else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
    //IE6 standards compliant mode
    scrOfY = document.documentElement.scrollTop;
    scrOfX = document.documentElement.scrollLeft;
  }
  return [ scrOfX, scrOfY ];
}

function getMouseCoords(theEvent) {
  if( !theEvent ) {
    if( window.event ) {
      //Internet Explorer
      theEvent = window.event;
    } else {
      //total failure, we have no way of referencing the event
      return[0, 0];
    }
  }
  if( typeof( theEvent.pageX ) == 'number' ) {
    //most browsers
    var xcoord = theEvent.pageX;
    var ycoord = theEvent.pageY;
  } else if( typeof( theEvent.clientX ) == 'number' ) {
    //Internet Explorer and older browsers
    //other browsers provide this, but follow the pageX/Y branch
    var xcoord = theEvent.clientX;
    var ycoord = theEvent.clientY;
    var badOldBrowser = ( window.navigator.userAgent.indexOf( 'Opera' ) + 1 ) ||
     ( window.ScriptEngine && ScriptEngine().indexOf( 'InScript' ) + 1 ) ||
     ( navigator.vendor == 'KDE' )
    if( !badOldBrowser ) {
      if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
        //IE 4, 5 & 6 (in non-standards compliant mode)
        xcoord += document.body.scrollLeft;
        ycoord += document.body.scrollTop;
      } else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
        //IE 6 (in standards compliant mode)
        xcoord += document.documentElement.scrollLeft;
        ycoord += document.documentElement.scrollTop;
      }
    }
  } else {
    //total failure, we have no way of obtaining the mouse coordinates
      return[0, 0];
  }
  return [xcoord , ycoord];
}


/**
 * "Dims" the screen by placing a semi transparent image "filter" over the entire screen
 */
function dimScreen() {
	var dimmerDIV = document.getElementById("DimmerDIV");
	dimmerDIV.style.width = window.screen.width + "px";
	dimmerDIV.style.height = window.screen.height + "px";
	dimmerDIV.style.visibility = "visible";
}

/**
 * Un-Dims the screen to return all to normal
 */
function unDimScreen() {
	document.getElementById("DimmerDIV").style.visibility = "hidden";
}

/**
 * Shows a specific tab and hides all other content from other tabs
 *
 * @param tabPrefix - string that is the beginning of the ID of the tab content element "Tab" where the first tab has ID="Tab1"
 * @param tabToShow - tab number to show, 1 to show the first tab
 * @param tabCount - total number of tabs in the tab group
 */
function showTab(tabPrefix, tabToShow, tabCount) {
	//first hide all tab content elements
	for(var i=1 ; i<=tabCount ; i++) {
		try {
			document.getElementById(tabPrefix+i).style.display = "none";
			document.getElementById(tabPrefix+"Link"+i).className="";
		} catch (ignore) {}
	}
	//now show the tab we want
	document.getElementById(tabPrefix+tabToShow).style.display = "block";
	document.getElementById(tabPrefix+"Link"+tabToShow).className="Active";
}



/**
 * Toggles a Category Tree Category
 *
 * @param elementID - ID of the Contents element that should be shown/hidden
 * @param imageID - ID of the image which needs to be toggled too
 */
function toggleCategory(elementID, imageID) {
	var tempContentsElement = document.getElementById(elementID);
	var tempImgElement = document.getElementById(imageID);
	
	//will be:  Contents_Open or Contents_Closed
	var currentClass = tempContentsElement.className;
	if(currentClass == "Contents_Open") {
		tempContentsElement.className = "Contents_Closed";
		//current src=xxxxx/arrow_down.gif"
		tempImgElement.src = tempImgElement.src.substring(0, tempImgElement.src.length - 8) + "right.gif";
	} else {
		tempContentsElement.className = "Contents_Open";
		//current src=xxxxx/arrow_right.gif"
		tempImgElement.src = tempImgElement.src.substring(0, tempImgElement.src.length - 9) + "down.gif";
	}
}

/**
 * Refreshes the current page
 */
function refreshPage() {
//    window.location.href = unescape(window.location.pathname);
	window.location.reload(true);
}

// Radio Button Validation
// copyright Stephen Chapman, 15th Nov 2004,14th Sep 2005
// you may copy this function but please keep the copyright notice with it
function getRadioButtonGroupValue(btn) {
    var cnt = -1;
    for (var i=btn.length-1; i > -1; i--) {
        if (btn[i].checked) {cnt = i; i = -1;}
    }
    if (cnt > -1) return btn[cnt].value;
    else return null;
}



/**
 * Object that is used to limit the number of characters in one or more textareas
 */
var TextareaLimiter = Class.create();
TextareaLimiter.prototype = {
	//constructor
	initialize: function (areaID,charsID,limit) {
		this.areaID = areaID;
		this.charsID = charsID;
		this.limit = limit;

		//start the polling
		new PeriodicalExecuter(this.poll.bind(this), 0.05); //use bind() so that "this.xxx" references are good
	},
	
	//function that polls a textarea
	poll: function() {
		var area = document.getElementById(this.areaID);
		if(!area) return;
		var areaValue = trimString(area.value);
		var length = areaValue.length;
		var remaining = this.limit - length;
		if(remaining < 0) remaining = 0;
		
		if(length > this.limit) {
			area.value = areaValue.substring(0, this.limit);
		}
		
		var charsNode = document.getElementById(this.charsID);
		charsNode.removeChild(charsNode.firstChild);
		charsNode.appendChild(document.createTextNode(remaining));
	}

}//end TextareaLimiter.prototype


/**
 * Object that is used to poll a text field as a type-ahead
 */
var TypeAheadListener = Class.create();
TypeAheadListener.prototype = {
	//constructor
	initialize: function(formName, textElementName, minChars) {
		this.formName = formName; //name of the form which is to be submitted to perform the type ahead query
		this.textElementName = textElementName; //name of the text element that the type ahead query string comes from
		this.minChars = minChars; //minimum number of characters that must be entered before a type ahead query
		
		this.ajaxInProcess = false;
		this.previousSearchValue = null;
		//start the polling
		new PeriodicalExecuter(this.poll.bind(this), 0.5); //use bind() so that "this.xxx" references are good
	},
	
	//function that indicates to this object that the most recent ajax request is done
	queryDone: function() {
		this.ajaxInProcess = false;
	},
	
	//function that performs the ajax submit
	poll: function() {
		if(this.ajaxInProcess == true) return;
		if(isNullOrEmpty(document.forms[this.formName].elements[this.textElementName])) return;
		if(trimString(document.forms[this.formName].elements[this.textElementName].value).length < this.minChars) return;

		//make sure the current value is different than the previous search value
		if(this.previousSearchValue != null) {
			this.previousSearchValue = trimString(this.previousSearchValue);
		}

		var currentSearchValue = trimString(document.forms[this.formName].elements[this.textElementName].value);
		if(this.previousSearchValue == currentSearchValue) {
			return; //current value is the same as the previous search
		} else {
			this.previousSearchValue = currentSearchValue; //new search value
		}
		
		//if we are here then we can submit the ajax request
		this.ajaxInProcess = true;
		submitFormViaAJAX(document.forms[this.formName]);
	}
}//end TypeAheadListener.prototype









