
// from http://www.digital-web.com/articles/seven_javascript_techniques/
var FLAGS = {
    w3: function() {
        return !!(document.getElementById && document.createElement);
    }(),
    ie: function() {
        return !!window.ActiveX;
    }()
};




// extend native Array object with an each method; pass a function object to iterate it with
// if 'each' function exists, don't override it
if (!Array.prototype.each) {
	Array.prototype.each = function(func){
		for(var i=0; ob=this[i]; i++){
			func(ob, i);
		}
	}
}





// from prototype.js
// gets any object by ID (use in place of document.getElementById)
// can be used to filter passed object array
function $() {
  var elements = new Array();

  for (var i = 0; i < arguments.length; i++) {
    var element = arguments[i];
    if (typeof element == 'string')
      element = document.getElementById(element);

    if (arguments.length == 1)
      return element;

    elements.push(element);
  }

  return elements;
}






// dynamically populates a targeted SELECT without page reload
// params: SELECT as object, DataURL as string
// returns: nothing
// needs: JQuery script include, getOpts.asp, setOpts()
// notes: includes basic data compression (~50% less bandwidth)
function fillOpts(p_oSelect, p_strDataURL, p_callback_success){
	$.ajax({
	type: "GET",
	url: p_strDataURL + '&compress=true',
	success: 
		function(r){						
			r = r.replace(/\<o/g, '<option value=').replace(/\<\/o\>/g, '</option>\r\n');	// decompress options
			p_oSelect.disabled = false;
			p_oSelect.options.length=0;

			// restore props
			p_oSelect.title	   = p_oSelect.x_title;
			p_oSelect.style.fontStyle = p_oSelect.x_style_fontStyle;	
			p_oSelect.style.cursor = p_oSelect.x_style_cursor;
			p_oSelect.parentNode.style.cursor = p_oSelect.parentNode.x_style_cursor;

			var htmlDefaultOpt = "<option value='' class='default'>" + p_oSelect.x_opt_text + "</option>\r\n";
			p_oSelect = setOpts(p_oSelect, htmlDefaultOpt + r);

			if(!p_oSelect.disabled){	p_oSelect.focus();	}
			//if(	p_oSelect.onfill!=undefined  ){	p_oSelect.onfill();	}
			if(p_callback_success && p_callback_success!=undefined){	p_callback_success(p_oSelect);	}
		}
	});
	
	p_oSelect.disabled = true;
	p_oSelect.options.length=1;

	// save props
	p_oSelect.x_opt_text = p_oSelect.options[0].text;
	p_oSelect.x_title = p_oSelect.title;
	p_oSelect.x_style_fontStyle = p_oSelect.style.fontStyle;	
	p_oSelect.x_style_cursor = p_oSelect.style.cursor;
	p_oSelect.parentNode.x_style_cursor = p_oSelect.parentNode.style.cursor;

	// temporarily set props
	p_oSelect.options[0].text = ' loading...';
	p_oSelect.title = ' loading...';
	p_oSelect.style.fontStyle = 'italic';	
	p_oSelect.style.cursor = 'progress';
	p_oSelect.parentNode.style.cursor = 'progress';	// for IE (no cursor on SELECT)
}

function setOpts(p_oSelect, p_strOpts){
	p_oSelect.innerHTML = "&nbsp;\r\n" + p_strOpts;
	if(p_oSelect.outerHTML){		//fix IE option insertion
		var strSelectHandle = p_oSelect.id;	// save handle before destroying/re-creating
		p_oSelect.outerHTML = p_oSelect.outerHTML;
		p_oSelect = $("SELECT#" + strSelectHandle)[0];		// restore handle
	}
	return p_oSelect;  // return handle
}






// desc: sets checked property of a checkbox array
// params: array as object -or- array name as string, optional check state to set as boolean (else toggles current)
function setCheckstate(ctls, p_bCheckstate){
	if(typeof(ctls)!='object'){	ctls = document.getElementsByName(ctls);	}
	for(var c=0; c<ctls.length; c++){
		ctls[c].checked = (p_bCheckstate!=undefined ? p_bCheckstate : !ctls[c].checked);
	}
}
// desc: gets checked property of a checkbox array
// params: array as object -or- array name as string
// returns: index of (first) checked elem
// TODO: enable returning multiple indices as array
function getCheckstate(ctls){
	if(typeof(ctls)!='object'){	ctls = document.getElementsByName(ctls);	}
	for(var c=0; c<ctls.length; c++){
		if(ctls[c].checked){return c}
	}
}
// desc: gets value property of the checked item in a radio button or checkbox array
// params: array as object -or- array name as string
// returns: value of (first) checked elem
function getCheckedElemValue(ctls){
	if(typeof(ctls)!='object'){	ctls = document.getElementsByName(ctls);	}
	for(var c=0; c<ctls.length; c++){
		if(ctls[c].checked){return ctls[c].value}
	}
}
// desc: gets index property of the checked item in a radio button or checkbox array
// params: array as object -or- array name as string
// returns: index of (first) checked elem
function getCheckedElemIndex(ctls){
	if(typeof(ctls)!='object'){	ctls = document.getElementsByName(ctls);	}
	for(var c=0; c<ctls.length; c++){
		if(ctls[c].checked){return c}
	}
}
// desc: allows unchecking of already checked radio buttons
function fixRadio(ctl){	// need uniqueID!!
	if(ctl && ctl.tagName=='INPUT' && ctl.type=='radio' && ctl.checked && ctl.uniqueID){
//		setTimeout('document.getElementById('+ctl.uniqueID+').checked=false; document.getElementById('+ctl.uniqueID+').fireEvent("onchange");', 10, 'Javascript'); 
		if(ctl.id || ctl.id==''){ctl.id = ctl.uniqueID};
		setTimeout("document.getElementById('" +ctl.id + "').checked=false;", 10, 'Javascript'); 
	};
}




/*
function getUniqueID(el){
	if(el.uniqueID && el.uniqueID!=''){// already got one
	}else{	// make one
		if(!window.lastUniqueIDindex){window.lastUniqueIDindex=0;}
		window.lastUniqueIDindex++;
		el.uniqueID = 'ss__id' + window.lastUniqueIDindex;
	}
	return el.uniqueID;
}
*/


// desc: sets property on all elements in an element array
// params: array as object -or- array name as string, property name as string, property value
// NOTE: untested
function setProperty(ctls, p_strPropName, p_PropVal){
	if(typeof(ctls)!='object'){	ctls = document.getElementsByName(ctls);	}
	for(var c=0; c<ctls.length; c++){
		ctls[c][p_strPropName] = p_PropVal;
	}
}


// Author: Rob Eberhardt
// desc: can use to make radio buttons de-selectable
function allowDeSelect(srcElem) {
	//var	srcElem = getSrcElem(ev);	//TODO: make work w/o explicitly passing 'this'
	var strHandle = 
		srcElem.uniqueID ? 'document.getElementById("' + srcElem.uniqueID + '")' : 
		srcElem.id ? 'document.getElementById("' + srcElem.id + '")' : 
		srcElem.name ? 'document.getElementById("' + srcElem.name + '")' : 
		null;
	if(strHandle==null){return}
	if(srcElem.checked){
		window.setTimeout(strHandle + '.checked=false', 0);
	}
}
			


// Author: Rob Eberhardt
// cross browser hookup for init procs;  will fire after code loads but before linked resources like images;
// props to Dean Edwards & Matthias Miller
function hookup(p_callback){
	// Mozilla
	if(document.addEventListener) {
		return document.addEventListener("DOMContentLoaded", p_callback, false);
	}

/*
	// IE
	if(document.attachEvent && document.readyState){
		return document.attachEvent("onreadystatechange", function(){
			if(document.readyState=='interactive'){		//<-- problem: IE only fires on REloads, not 1st load
				p_callback();
			}
		});
	}
*/
	// IE
  if (document.createElement && document.getElementsByTagName) {
      var deferScript = document.createElement('SCRIPT');
      deferScript.defer = true;
//      deferScript.src = 'javascript:void 0';	// <-- with HTTPS, causes "This page contains both secure and nonsecure items." warning
//      deferScript.src = 'https:///';
      deferScript.src = '//0';
      deferScript.onreadystatechange = function() {
          if(this.readyState=='loaded' || this.readyState=='complete'){
              p_callback();
          }
      }
      document.getElementsByTagName('HEAD')[0].appendChild(deferScript);
      /* clear reference to prevent leaks in IE */
      deferScript = null;
      return;
  }

	// other browsers
	var onload1 = window.onload;
	window.onload = function(){
		if(onload1){	onload1();	}
		p_callback();
	}

}


// return cross-browser srcElement reference
function getSrcElem(ev){
	return (ev==undefined) ? window.event.srcElement : ev.target;
}





// desc: compatibility wrapper for event object
// example use:	return evt(event).stop();
evt = function(ev){
	if(ev==undefined && window.event!=undefined){	ev = window.event;	}

	this.srcElem = ev.target!=undefined ? ev.target : ev.srcElement;
	this.target = this.srcElem;
	this.srcElement = this.srcElem;

	this.stop = function(){
		if(ev.stopPropagation !=undefined){	ev.stopPropagation();	ev.cancelBubble = true;	 return false;	}
		if(ev.cancelBubble !=undefined){	ev.cancelBubble = true;	return false;	}
	}
	this.stopPropagation = this.stop;
	this.cancelBubble = this.stop;	

/* UNTESTED:	
	this.keyCode = ev.keyCode!=undefined ? ev.keyCode : (ev.which!=undefined ? ev.which: undefined);
	this.which = this.keyCode;
	this.altKey = this.altKey;
	this.shiftKey = ev.shiftKey;
	this.ctrlKey = ev.ctrlKey;

	this.pageX = (ev.pageX ? ev.pageX : ev.clientX + document.documentElement.scrollLeft);
	this.pageY = (ev.pageY ? ev.pageY : ev.clientY + document.documentElement.scrollTop);
	this.type = ev.type;
	this.timeStamp = (ev.timeStamp ? ev.timeStamp : new Date().valueOf);
*/
	
	//TODO:
	//this.relatedElement = 
	//this.fromElement = 
	//this.toElement = 

	return this;
}







/* 
author: Rob Eberhardt
desc: fix MinWidth for IE6 & IE7
params: none
returns: nothing
notes: cannot yet fix childless elements like INPUT or SELECT
history:
	2006-11-20	revised for standards-mode compatibility
2006-11-17	first version
*/
function fixMinWidthForIE(){
	try{
		if(!document.body.currentStyle){return}	//IE only
	}catch(e){return}
	var elems=document.getElementsByTagName("*");
	for(e=0; e<elems.length; e++){
		var eCurStyle = elems[e].currentStyle;
		var l_minWidth = (eCurStyle.minWidth) ? eCurStyle.minWidth : eCurStyle.getAttribute("min-width"); //IE7 : IE6
		if(l_minWidth && l_minWidth != 'auto'){
			var shim = document.createElement("DIV");
			shim.style.cssText = 'margin:0 !important; padding:0 !important; border:0 !important; line-height:0 !important; height:0 !important; BACKGROUND:RED;';
			shim.style.width = l_minWidth;
//			shim.appendChild(document.createElement('&nbsp;'));	// breaks with IEemu.js currentStyle definition
			shim.innerHTML = "&nbsp;";
			if(elems[e].canHaveChildren){
				elems[e].appendChild(shim);
			}else{
				//??
			}
		}
	}
}



// author: Rob Eberhardt
// desc: cross-browser maxlength for textareas
// params: none
// returns: boolean for event approval/suppression
// needs: getSrcElem
function doMaxlength(ev){
	var elem = getSrcElem(ev);
	var maxLength = elem.getAttribute("maxlength") | elem.getAttribute("dn_maxlength");	// asp.net allows maxlength props on multi-line textboxes, but removes specifically strips the maxlength attribute when rendering to HTML;  so, workaround by calling it dn_maxlength;
	if(!maxLength || isNaN(maxLength)){return true};
	//if(!evt.isChar){return true};	// always allow non-char keypresses
//	var bIsEditKey=(',8,46,9,37,38,39,40,'.indexOf(','+evt.keyCode+',')!=-1)
//	if(bIsEditKey){return true};

	// if already too long, truncate
	if(elem.value.length > parseInt(maxLength)){	
		elem.value = elem.value.substring(0, parseInt(maxLength));
	}

	// return full state
	return elem.value.length <= parseInt(maxLength);
	//return parseInt(maxLength) - elem.value.toString().length;
}


	
// author: Rob Eberhardt
// desc: hooks up all TEXTAREAs to doMaxLength
// params: none
// returns: nothing
// needs: doMaxLength
function hookupMaxLengths(evt){
	var colTEXTAREAs = document.getElementsByTagName("TEXTAREA");
	for(var i=0; i<colTEXTAREAs.length; i++){
		var elem = colTEXTAREAs[i];
		elem.maxLength = elem.getAttribute("maxlength");
	
/*
		elem.onkeydown = function(evt){var bIsEditKey=(',8,46,9,37,38,39,40,'.indexOf(','+evt.keyCode+',')!=-1); if(!bIsEditKey){return doMaxlength(evt)}};
		elem.onkeypress= function(evt){if(evt.charCode!=0){return doMaxlength(evt)}};
		elem.onkeyup		= function(evt){var bRetVal=doMaxlength(evt); setCharCountdown(this); return bRetVal;};
		elem.onchange	= function(evt){var bRetVal=doMaxlength(evt); setCharCountdown(this); return bRetVal;};

*/

		//elem.onkeydown = function(evt){if(getEvent(evt).keyCode!=0){return doMaxlength(evt)}};
//		elem.onkeypress= function(evt){if(evt.charCode!=0){return doMaxlength(evt)}};
		elem.onkeyup			= doMaxlength;
		elem.onchange			= doMaxlength;
		elem.onmousemove	= doMaxlength;
		

		// if already too long, truncate
		if(elem.value.length > parseInt(elem.maxLength)){	
			elem.value = elem.value.substring(0, parseInt(elem.maxLength));
		}

	}
}






// author: Rob Eberhardt, www.slingfive.com
// date: sometime in 2002
// params: any object container/collection and property expression(s) to check (strComparison)
// returns: array of all objects in that container matching the criteria
function fnSelectObjectsAsArray(oFromContainer, strComparison, strOrderBy) {
	if(oFromContainer==null){oFromContainer=document.body.all};
	if(!oFromContainer.length && oFromContainer.all) {oFromContainer = oFromContainer.all}; // if collection wasn't specified, assume .all
	var arrReturnCollection = new Array();
	for(i=0; i<oFromContainer.length; i++) {
		var bMatchesCriteria = false;
		//alert("with(oFromContainer[i]){" + strComparison + "}");
		try{bMatchesCriteria = eval("with(oFromContainer[i]){" + strComparison + "}");}catch(e){bMatchesCriteria = false;}
		if(bMatchesCriteria) {
			arrReturnCollection.length++;
			arrReturnCollection[arrReturnCollection.length-1] = oFromContainer[i];
		}
	}
	return arrReturnCollection;
}




// desc: sets form elem visibility and required state
// params: form elem as object, show state as boolean
// NOTE: precursor to subscribeVisToValue
function conditionalField(p_oElem, p_bShow){
	function _isFormElem(p){	return p.tagName && 'INPUT|TEXTAREA|SELECT'.indexOf(p.tagName)!=-1;	}
	var oFormElem = p_oElem;	// if p_oElem is not a require-able form elem, find it 
	if(!_isFormElem(oFormElem) && p_oElem.htmlFor && p_oElem.htmlFor!=''){	// if label, try its FOR
			oFormElem = document.getElementById(oFormElem.htmlFor);
	}
	if(!_isFormElem(oFormElem) && oFormElem.childNodes){	// else scan contained elems
		for(var c=0; c<oFormElem.childNodes.length; c++){
			if(_isFormElem(p_oElem.childNodes[c])){
				oFormElem = p_oElem.childNodes[c];
			}
		}
	}
	if(p_bShow){
		p_oElem.style.visibility = 'visible';
		oFormElem.setAttribute('required', true);
	}else{
		p_oElem.style.visibility = 'hidden';
		oFormElem.removeAttribute('required');
	}
}




// desc: subscribes the visibility of one element to the the value of another
// params: element to show/hide as object, element(s) to monitor as object or collection, value(s) to match as scalar or array
// returns: nothing
// NEEDS: getCheckedElemValue, hookupElemArrayEvtHandler
// NOTE: replacement for conditionalField
function subscribeVisToValue(p_VisElem, p_ValueElem, p_VisValue){
	function ValueElem_onchange(){
		function _isFormElem(p){	return p.tagName && 'INPUT|TEXTAREA|SELECT'.indexOf(p.tagName)!=-1;	}

		if(typeof(p_VisElem)!='object'){	p_VisElem = document.getElementById(p_VisElem);	}
		if(typeof(p_ValueElem)!='object'){	p_ValueElem = document.getElementsByName(p_ValueElem);	}
		if(p_ValueElem.length && p_ValueElem[0].tagName=='SELECT'){p_ValueElem = p_ValueElem[0];	}	// only use 1st SELECT
		if(!p_VisElem || !p_ValueElem){return}

		var isCtlSet= p_ValueElem.length && p_ValueElem[0] && p_ValueElem[0].tagName=='INPUT' && (p_ValueElem[0].type=='checkbox' || p_ValueElem[0].type=='radio');
		
		var curValue = null;
		if(p_ValueElem.tagName=='SELECT'){
			curValue = p_ValueElem.value;
		}else if(p_ValueElem.length && isCtlSet){
			curValue = getCheckedElemValue(p_ValueElem); 
		}else{
			// ??
		}
		
		// determine if VisElem should be shown/hidden
		var bShow = false;
		if(typeof(p_VisValue)=='object' && p_VisValue.length){
			for(var v=0; v<p_VisValue.length; v++){
				if(curValue && curValue.toString()==p_VisValue[v].toString()){bShow = true;}
				// if multiple checkboxes checked?  now: last wins
			}
		}else{
			if(curValue && curValue.toString()==p_VisValue.toString()){bShow = true;}
		}

		// apply show/hide
		p_VisElem.style.display = bShow ? '' : 'none';	

		// now suspend "required" for any contained form elements
		for(var e=0; e<p_VisElem.getElementsByTagName("*").length; e++){
			var el=p_VisElem.getElementsByTagName("*")[e];
			if(_isFormElem(el)){
				if(el.getAttribute('required')){	el.setAttribute('_required', el.getAttribute('required'))	};	// backup original required attribute
				if(bShow){
					if(el.getAttribute('_required')){	el.setAttribute('required', el.getAttribute('_required'))	};// restore original required attribute
				}else{
					el.removeAttribute('required');
				}
			}
		}
		
		if(navigator.userAgent.indexOf('Gecko')>0){	p_VisElem.focus();	}	// workaround FF's redraw problems
	}
	
	ValueElem_onchange();
	if(p_ValueElem.tagName && p_ValueElem.tagName=='SELECT'){
		addEvent(p_ValueElem, 'onchange', ValueElem_onchange);  // cross-browser addEvent function
	}else{	// assume control array
		hookupElemArrayEvtHandler(p_ValueElem, 'onclick', ValueElem_onchange);
	}
}


// desc: hooks-up event-handler proc to each elem in elem array
// params: p_colElems as object collection, p_strEvtName as string of event name, p_oEventHandler as function object reference
// returns: nothing
// note: use with radio button/checkbox arrays,	eg:	 hookupElemArrayEvtHandler(frmReport.source, 'onclick', source_onchange);
// NEEDS: custom addEvent function
function hookupElemArrayEvtHandler(p_colElems, p_strEvtName, p_oEventHandler){
	for(var s=0; s<p_colElems.length; s++){
		addEvent(p_colElems[s], p_strEvtName, p_oEventHandler);  // cross-browser addEvent function
	}
}

// based on: http://www.scottandrew.com/weblog/articles/cbs-events
// note: evType is now 'on' agnostic
function addEvent(obj, evType, fn, useCapture){
	if(evType.substring(0,2)=='on'){evType = evType.substring(2)}	// fix any 'on' prefix
  if (obj.addEventListener){
    obj.addEventListener(evType, fn, useCapture);
    return true;
  } else if (obj.attachEvent){
    var r = obj.attachEvent("on"+evType, fn);
    return r;
  } else {
    alert("Handler could not be attached");
  }
}







// ListManager
// desc: add/remove elements in a container -or- rows in a table;  clears contained form element values
function clsListMgr(){
	this.addRow = function(oCnt){
		if(!oCnt){return}
		var oRow = oCnt.tBodies[0].rows[0].cloneNode(true);
		oCnt.tBodies[0].appendChild(oRow);
		if(!oCnt.itemCount){oCnt.itemCount = 1;}
		oCnt.itemCount++;
		for (var c=0; c<oRow.cells.length; c++){
			for(var e=0; e<oRow.cells[c].childNodes.length; e++){
				var elem = oRow.cells[c].childNodes[e];
				uniquifyID(elem, oCnt.itemCount);
				prepField(elem);
			}
		}
		oRow.cells[1].childNodes[0].focus();
		//oRow.cells[1].childNodes[0].select();
	}
	this.delRow = function(oRow){
		if(!oRow){return}
		while(oRow.tagName !='TR'		){oRow = oRow.parentNode;}
		var oCnt = oRow;
		while(oCnt.tagName !='TABLE'){oCnt = oCnt.parentNode;}
		if (oCnt.tBodies[0].rows.length==1){	return;	}
		oCnt.tBodies[0].removeChild(oRow);
	}


	this.addItem = function(oCnt){
		if(!oCnt){return}
		var oItem = oCnt.childNodes[!oCnt.childNodes[0].tagName ? 1 : 0].cloneNode(true);	// Moz adds text node for preceding whitespace
		oCnt.appendChild(oItem);
		if(!oCnt.itemCount){oCnt.itemCount = 1;}
		oCnt.itemCount++;
		var elems = oItem.getElementsByTagName('*');	// clear form elems & fix dupe IDs
		for(var e=elems.length-1; e>0; e--){
			var elem = elems[e];
			uniquifyID(elem, oCnt.itemCount);
			prepField(elem);
		}
	}
	this.delItem = function(oCnt, oItem){
		if(!oCnt || !oItem){return}
		while(oItem.parentNode != oCnt){
			oItem = oItem.parentNode;
		}	
		if(oCnt.childNodes.length<3){	return;	}
		oCnt.removeChild(oItem);
	}
	var asdf = new Array();

	
	//---- internal ListMgr utils ----
	
	function prepField(elem){
		if(elem.nodeType!=1){return} //skip text nodes	
		var inputType = elem.tagName=='INPUT' ? elem.getAttribute("type") : null;
		var isCtlArray = (elem.tagName=='INPUT' && (inputType=='checkbox' || inputType=='radio'));
		var isKBInput = elem.tagName=='TEXTAREA' || (elem.tagName=='INPUT' && ('text,file,password').indexOf(inputType)!=-1);
		if(isKBInput){	elem.value = '' /*elem.defaultValue*/;	}
		if(isCtlArray){	elem.checked = false /*elem.defaultChecked*/;	}
		if(elem.tagName=='SELECT'){
			//alert(elem.parentNode.innerHTML);	//IE changes the SELECT markup, losing the archetype's selected attribute
			//alert(elem.selectedIndex);
		}
		elem.focus();
		//elem.select();
	}
	function uniquifyID(elem, i){
		if(elem.nodeType!=1){return} //skip text nodes	
		if(elem.tagName=='SELECT' || elem.tagName=='TEXTAREA' || elem.tagName=='INPUT'){
			if(elem.id){
				elem.id = elem.id + ':' + i;	// uniquify IDs
				//	elem.value = elem.id;
			}
		}else if(elem.tagName=='LABEL'){
			if(elem.htmlFor){
				elem.htmlFor = elem.htmlFor + ':' + i;	// uniquify ID refs
				//	elem.innerHTML = elem.htmlFor;
			}
		}
	}
}

/*
Name: formValidator,  http://slingfive.com/pages/code/formValidator/
Desc: automates most basic form validation tasks, via custom attributes
Usage: see formValidation_demo.html
Author: Rob Eberhardt, Slingshot Solutions, http://slingfive.com/
Created: ~2002
Revision History:
	2007-05-18, v1.67:	relaxed email validation to tolerate leading/trailing spaces
	2007-01-26, v1.66:	completed checkRequired to also handle checkboxes/radio buttons
	2006-08-09, v1.65:	encapsulated private interfaces into class; improved isBoolean, isPhone, isTime & cTime;
	2006-05-18, v1.60c:	improved min/maxValue checking (numbers were compared as strings)
	2006-04-27, v1.60b:	firefox compatibility
	2004-03-24, v1.52:	1st public release; added "friendly" public interface procs; fixed new minLength bug
	2004-02-20: fixed minlength check when value is null (value+''.length)
	2004-01-26: fixed istime check, improved to handle shorthand times
	2004-01-22: fixed required & minValue/maxValue bugs

====CHECKS====
data types:
	date/datetime, time, boolean/yesno, number/numeric, float, integer
data formats:
	email, phone, ssn, postal code/zipcode, creditcard

====OTHER OPTIONS====
required, minLength, minValue, maxValue

*/


/*
==========================================
public interfaces
==========================================
*/

function validateForm(p_oForm, p_oOptions){	return oFormValidator.validateForm(p_oForm, p_oOptions);	}
function validateField(p_oField){	return oFormValidator.validateField(p_oField);	}
function showFailedField(p_oField, p_strErrMessage){ return oFormValidator.showErrField(p_oField, p_strErrMessage);	}







/* 
==========================================
private interfaces
========================================== 
*/
function _clsFormValidator(){
	var cls = this;	//grab current scope for safe intra-class communications later 


	// loops through form, checks each field with checkRequired, validateField, checkMinLength & checkRange
	// raises err alerts if needed, returns overall validation status as boolean
	this.validateForm = function(oForm, p_oOptions){
		if(p_oOptions && p_oOptions.batch){
			// TODO: enable batching all validation messages
		}
		for(var e=0; e<oForm.elements.length; e++){
			var elem = oForm.elements[e];
			if(!cls.checkRequired(elem)){
				return false;
			}else if(!cls.validateField(elem)){
				return false;
			}
		}
		return true;
	}
	

	//check a single field
	this.validateField = function(oFld){
		// -- skip required here -- 
		if(!this.checkDatatype(oFld)){return false};
		if(!this.checkFormat(oFld)){return false};
		if(!this.checkLength(oFld)){return false};
		if(!this.checkRange(oFld)){return false};
		return true;
	}




	/* ==== BASIC CHECKS ==== */


	// check if required field has any data;
	// TODO: enable required for radio buttons 
	this.checkRequired = function(oFld){
		// for checkboxes/radio buttons, check required, get all w/same name, then check if ANY is "checked"
		if(oFld.tagName=='INPUT' && (oFld.type=='checkbox' || oFld.type=='radio') && (oFld.getAttribute("required") && !oFld.checked) && oFld.name && oFld.name!=''){
			var ctlSet = document.getElementsByName(oFld.name);
			for(var e=0; e<ctlSet.length; e++){
				if(ctlSet[e].checked){return true;}
			} 
			this.showErrField(oFld, "(This field is required)");
			return false;
		}
		// otherwise, check that EACH is done
		if(oFld.getAttribute('required') && oFld.value == ''){
			this.showErrField(oFld, "(This field is required)");
			return false;
		}
		return true;
	}


	this.checkDatatype = function(oFld){
		var r=true, strMsgDetail;
		var strFldDataType = oFld.getAttribute("datatype");
		if(strFldDataType && strFldDataType !="" && oFld.value && oFld.value !=""){
			switch(strFldDataType.toLowerCase()) {
				case "float":
				case "number":
				case "numeric":	{r = this.isNumeric(oFld.value);
					strMsgDetail = '\r\n' + "(value must be numeric)";
					break;
				}
				case "integer":	{r = this.isInteger(oFld.value);
					strMsgDetail = '\r\n' + "(value must be an integer [whole number])";
					break;
				}
				case "date":
				case "datetime": {r = this.isDate(oFld.value);
					strMsgDetail = '\r\n' + "(value must be a date)";
					if(r && (oFld.type == "text" || oFld.tagName == "TEXTAREA")){oFld.value = this.FormatShortDate2(this.cDate(oFld.value))}
					break;
				}
				case "time":		{r = this.isTime(oFld.value);
					strMsgDetail = '\r\n' + "(value must be a time)";
					if(r && (oFld.type == "text" || oFld.tagName == "TEXTAREA")){oFld.value = this.cTime(oFld.value)}
					break;
				}
				case "yesno":
				case "boolean": {r = this.isBoolean(oFld.value);
					strMsgDetail = '\r\n' + "(value must be boolean [true/false])";
					if(r && (oFld.type == "text" || oFld.tagName == "TEXTAREA")){oFld.value = this.cBool(oFld.value)}
					break;
				}
			}	
		}
		if(!r){	this.showErrField(oFld, strMsgDetail)	};
		return r;
	}
	
	this.checkFormat = function(oFld){
		var r=true, strMsgDetail;
		var strFldDataType = oFld.getAttribute("datatype");
		if(strFldDataType && strFldDataType !="" && oFld.value && oFld.value !=""){
			switch(strFldDataType.toLowerCase()) {
				case "email":		{r = this.isEmail(oFld.value);
					strMsgDetail = '\r\n' + "(must be in valid email format, e.g. 'user@example.com')";
					break;
				}
				case "phonenumber":
				case "phone":		{r = this.isPhone(oFld.value);
					strMsgDetail = '\r\n' + "(these formats will work: (###) ###-####, ###.###.#### ext ###)";
					break;
				}
				case "ssn":			{r = this.isSSN(oFld.value);
					strMsgDetail = '\r\n' + "(format as ###-##-#### or #########)";
					break;
				}
				case "creditcard":	{r = this.isCreditCard(oFld.value);
					strMsgDetail = '\r\n' + "(enter a valid number formatted as ####-####-####-#### or ################)";
					break;
				}
				case "postalcode":
				case "zipcode":		{r = this.isPostalCode(oFld.value); 
					break;
				}
			}
		}
		if(!r){	this.showErrField(oFld, strMsgDetail)	};
		return r;
	}


	// check that field's data meets its minlength;
	this.checkLength = function(oFld){
		var iMinLength = oFld.getAttribute("minlength");
		if(iMinLength!=null && this.isNumeric(iMinLength) && (oFld.value+'').length < this.cNum(iMinLength)){
			this.showErrField(oFld, "\r\n\(Enter at least " + iMinLength + " characters)");
			return false;
		}
		return true;
	}



	// check field's value against max/min values allowed
	// started datatype checking
	this.checkRange = function(oFld){
		if(oFld.value && oFld.value!=''){
			var minValue = oFld.getAttribute("minValue");
			var maxValue = oFld.getAttribute("maxValue");
			var bHasMin = !!minValue;
			var bHasMax = !!maxValue;
			if(!isNaN(minValue)){minValue = parseFloat(minValue)};
			if(!isNaN(maxValue)){maxValue = parseFloat(maxValue)};
			var fldVal = oFld.value;
			if(!isNaN(fldVal)){fldVal = parseFloat(fldVal)};

			if(bHasMin && bHasMax && (fldVal < minValue || fldVal > maxValue)){	// check full range
				this.showErrField(oFld, "\r\n\(Value must be in the range " + minValue + " to " + maxValue + ")");
				return false;
			}else if(bHasMin && (fldVal < minValue)){	// if checking min value
				this.showErrField(oFld, "\r\n\(Value must be " + minValue + " or higher)");
				return false;
			}else if(bHasMax && (fldVal > maxValue)){	// check max value
				this.showErrField(oFld, "\r\n\(Value must be " + maxValue + " or lower)");
				return false;
			}
		}
		return true;
	}



	/* ==== UTILS ==== */

	// raise error && explanation to user, highlight + focus field;
	this.showErrField = function(oFld, strMsgDetail){
		if(!strMsgDetail){strMsgDetail = ''}
		var strMsg, strElemHandle = this.getElemHandle(oFld);
		oFld.origStyle = {backgroundColor : oFld.style.backgroundColor};	// save orig style
		oFld.style.backgroundColor="#ff3030";
		if(oFld.tagName == "SELECT"){
			strMsg = "Please choose a valid option for '" + strElemHandle + "'." + '\r\n';
		}else if(_isChkRad(oFld)){
			strMsg = "Please make a choice for '" + strElemHandle + "'." + '\r\n';			
		}else{
			strMsg = "Please enter valid data for '" + strElemHandle + "'." + '\r\n';
		}
		window.alert(strMsg + strMsgDetail, "Validation Error");
		oFld.style.backgroundColor = oFld.origStyle.backgroundColor;	// restore orig style
		oFld.focus();	// FF chrome freaks on focus: "Permission denied to set property XULElement.selectedIndex'...
	}


	// gets Something to call the field, tries: 1.Title, 2.Name, 3:Id
	this.getElemHandle = function(oFld){
		if(_isChkRad(oFld)){	// if radio/checkbox, try title of LABEL pointing at it
			var strFldHandle = oFld.title ? oFld.title : oFld.innerText ? oFld.innerText : oFld.innerHTML;
			var colLabels = document.getElementsByTagName("LABEL");
			for(var e=0; e<colLabels.length; e++){
				var el=colLabels[e];
				var strLblHandle = el.title ? el.title : el.innerText ? el.innerText : el.innerHTML;
				if(el.htmlFor==oFld.id){
					//alert(strLblHandle);
					if(strLblHandle!=oFld.title){	// exclude small text labels (e.g. "Yes"/"No")
						return strLblHandle;
					}
				}
			}
		}
		var strElemHandle = oFld.title;
		if (strElemHandle == ""){strElemHandle = oFld.name;}
		if (strElemHandle == ""){strElemHandle = oFld.id;}
		return strElemHandle;
	}

	// is it a checkbox or radio button
	_isChkRad = function(oFld){	// as boolean
		return oFld.tagName=='INPUT' && (oFld.type=='checkbox' || oFld.type=='radio');
	}



	/* ==== DATA-TYPE CHECKS ==== */
	
	this.isDate = function(v){
		var dt = new Date(v);
		var dt1 = new Date(100,1,1);
		dt1.setFullYear(0);	// years <=0 are forced/assumed to 4 digit otherwise
		var dt2 = new Date(9999,12,31);
		return  dt1 < dt && dt < dt2;
	}
	this.isDateTime = function(v){
		return this.isDate(v);	// ?
	}
	this.isTime = function(v){
		return _parseTime(v) != null;
	}
	this.isNumeric = function(v){
		return !isNaN(v);
	}
	this.isInteger = function(v){
		return this.isNumeric(v) && v.indexOf(".")==-1;
	}
	this.isFloat = function(v){
		return this.isNumeric(v);
	}
	this.isBoolean = function(v){	// true/false, 'true/false', numeric, y/n, yes/no
		var W = (''+v).toLowerCase();
		return v==true || (W=="true" || W=="false") || this.isNumeric(v) || W=='y' || W=='n' || W=='yes' || W=='no';
	}
	


	/* ==== DATA-FORMAT CHECKS ==== */
	
	this.isEmail = function(v){
		v = _trim(v);
	// regex based on http://regexlib.com/REDetails.aspx?regexp_id=356
		var re = new RegExp(/^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$/);
		return (re.exec(v)!=null);
	}
	this.isSSN = function(v){
	// regex based on http://www.regexlib.com/REDetails.aspx?regexp_id=535
		var re = new RegExp(/^(?!000)([0-6]\d{2}|7([0-6]\d|7[012]))([ -])?(?!00)\d\d\3(?!0000)\d{4}$/);
		return (re.exec(v)!=null);
	}
	this.isPhone = function(v){
	// custom regex, waiting to publish...		// requires area code, allows extension
		var re = new RegExp(/^(\d)?[ ]*[\(\.\-]?(\d{3})?[\)\.\-]?[ ]*(\d{3})[\.\- ]?(\d{4})[ ]*(x|ext\.?)?[ ]*(\d{1,7})?$/);
		return (re.exec(v)!=null);
	}
	this.isPostalCode = function(v){
		//regex based on http://regexlib.com/REDetails.aspx?regexp_id=23
		var re = new RegExp(/^(\d{5}-?\d{4}|\d{5}|[A-Z]\d[A-Z] \d[A-Z]\d)$/);
		return (re.exec(v)!=null);
	}
	this.isCreditCard = function(v){
	// regex based on http://regexlib.com/REDetails.aspx?regexp_id=67 (also considered 455, 49)
	// "the four major credit cards"
		var re = new RegExp(/^((4|5)\d{3}-?\d{4}-?\d{4}-?\d{4}|(4|5)\d{15}|(6011)-?\d{4}-?\d{4}-?\d{4}|(6011)-?\d{12}|(3\d{3})-\d{6}-\d{5}|(3\d{14}))$/);
		return (re.exec(v)!=null);
	}




	/* ==== DATA-TYPE CONVERSIONS ==== */
	
	this.cNum = function(v){
		if(!this.isNumeric(v)){return v}
		return new Number(v);
	}
	this.cInt = function(v){
		if(!this.isInteger(v)){return v}
		return parseInt(v);
	}
	this.cFloat = function(v){
		if(!this.isFloat(v)){return v}
		return parseFloat(v);
	}
	this.cBool = function(v){	// true/false, 'true/false', #s (nonzero/zero), yes/no, y/n
		if(!this.isBoolean(v)){return v}
		var W = (''+v).toLowerCase();
		return v==true || W=='true' || W=='yes' || W=='y' || (this.isNumeric(v) && v!=0);
	}
	this.cDate = function(v){
		return new Date(v);	//.getVarDate()
	}
	this.cTime = function(v){
		var arrParts = _parseTime(v);
		if(!arrParts){return v;}	// !isTime

		var h='00'; var m='00'; var s='00'; var z='';	// parts & defaults
		if(arrParts.length >1){h = parseInt(arrParts[1])};
		if(arrParts.length >2 && arrParts[2] !=undefined){m = arrParts[2].replace(':', '')};
		if(arrParts.length >3 && arrParts[3] !=undefined){s = arrParts[3].replace(':', '')};
		if(arrParts.length >4 && arrParts[4] !=undefined){z = arrParts[4].replace(/ /g, '')};
		
		if(z==''){z = h>11 ? 'PM' : 'AM'};	// no meridian, decide by hour
		h = h%12;	// convert to 12hr format
		if(h==0){h = 12}	// & adjust that for 1-based clock

		return h +':'+ m +':'+ s +' '+ z;
	}




	/* ==== FORMATTING ==== */
	this.FormatShortDate2 = function(v) {
		if(!this.isDate(v)){return v};
		var dt = new Date(v);
	//	var nFullyear = (''+v).substring(0,3);
	//	if(isInteger(nFullyear)){dt.setFullYear(nFullyear)};	// fix 2 digit assumption
		var strRet = '' + (dt.getMonth()+1) + '/' + dt.getDate() + '/' + dt.getFullYear();
		return strRet;
	}
	this.FormatShortDate = function(v) {
		if(!this.isDate(v)){return v};
		var dt = new Date(v);
	//	var nFullyear = (''+v).substring(0,3);
	//	if(isInteger(nFullyear)){dt.setFullYear(nFullyear)};	// fix 2 digit assumption
		var strRet = '' + dt.getFullYear() + '/' + (dt.getMonth()+1) + '/' + dt.getDate();
		return strRet;
	}




	// checks if a value is a time, and passes back dissected parts of it
	function _parseTime(v){
		v = _trim(v);
		var re = new RegExp(/^([0-1]?[0-9]|2[0-3])(:[0-5][0-9])?(:[0-5][0-9])?( *[ap]m?)?$/ig);
		return re.exec(v);
	}
		
	function _trim(v){
		return v.replace(/^\s*([^\s]*)\s*$/, '$1');	// Trim leading & trailing spaces
	}



	/* ==== bootstraps for backwards-compat ==== 
	this.fnValidateForm = this.validateForm;
	this.fnCheckValidField = this.validateField;
	this.fnCheckRequiredField = this.checkRequired;
	this.fnCheckMinLengthField = this.checkLength;
	this.fnCheckValueRange = this.checkRange;
	this.fnGetFieldHandle = this.getElemHandle;
	*/
}




var oFormValidator = new _clsFormValidator;

// DEPRECATED, mapped for compatibility
fnValidateForm = oFormValidator.validateForm;
fnCheckValidField = oFormValidator.validateField;
subShowErrField = oFormValidator.showErrField;

function showSubtopImage(){
	var randomimage = [	
		'/images/content_top_1.jpg', '/images/content_top_2.jpg', '/images/content_top_3.jpg', 
		'/images/content_top_4.jpg', '/images/content_top_5.jpg', '/images/content_top_6.jpg', '/images/content_top_7.jpg' 
		]
	var randomtop = Math.round(Math.random() * (randomimage.length - 1));
	document.write('<img src="' + randomimage[randomtop] + '" border=0 width=378 height=80>');
}




// generic videoplayer opener
// use thus: <a href="/Video/myvid.wmv" onclick="return playmedia(this); void(0);" target="_blank">my title</a>
function playmedia(p_oLink, p_strTitle, p_oFeatures){
	var oFeatures = {width:'320px',height:'308px'};	// defaults if none passed
	var strFilePath ='';
	if(typeof p_oFeatures=='object'){
		oFeatures = p_oFeatures;		
	}else if(typeof p_oFeatures=='string'){	// backwards compat
		strFilePath = p_oFeatures;
	}
	// calculate window dimensions larger than video object
	var oVidSize = {width:oFeatures.width, height:oFeatures.height};
	oFeatures.width = parseInt(parseInt(oFeatures.width)*1.15) + 'px';
	oFeatures.height = parseInt(parseInt(oFeatures.height)*1.35) + 'px';
	
	if(!p_strTitle){p_strTitle = p_oLink.innerHTML};
	if(!strFilePath){strFilePath = p_oLink.href};	//IE resolves to absolute, regardless of .href, .getAttribute('href'), or .attributes('href').value
	return popWin('/PlayMedia.asp?title=' + escape(p_strTitle) + '&FilePath=' + strFilePath + '&Width=' + oVidSize.width + '&Height=' + oVidSize.height, oFeatures);
}


// opens window
// params: URL, features object -- e.g. {width:100,height:100}
// returns: nothing
// notes: 
//	* uses default features, overrides with property bag object
//	* defaults to 75% screen width and 80% screen height (min 780px width unless explicitly overridden)
//	* property extension:  "size:'big'" sizes to 85% x 90% screen size
function popWin(p_strURL, p_oFeatures, p_strHandle){
	var oFeatures = {	// default values
		width:screen.availWidth*0.75, height:screen.availHeight*0.85, left:screen.availWidth*0.05, top:screen.availHeight*0.05,
		channelmode:0,directories:0,fullscreen:0,location:0,menubar:0,resizable:1,scrollbars:1,status:0,titlebar:1,toolbar:0
		}; 
	// override default properties with any passed properties
	if(p_oFeatures){
		if(p_oFeatures.size=='big'){oFeatures.width	= screen.availWidth*0.90;}
		if(p_oFeatures.size=='auto'){oFeatures.width = 'auto'; oFeatures.height = 'auto';}
		if(oFeatures.width < 780){oFeatures.width = 780};	// enforce minimum of 780px width
		for(var f in p_oFeatures){
			oFeatures[f] = p_oFeatures[f];
		}
	}
	if(!p_strHandle || p_strHandle==''){p_strHandle = 'popWin'};
	
	// serialize features object into features string
	var strFeatures = '';	
	for(var f in oFeatures){
		strFeatures += ',' + f +'='+ oFeatures[f];
	}
	if(strFeatures.length && strFeatures.substring(0)==','){strFeatures = strFeatures.substring(1)};	// trim leading comma
	//alert(strFeatures);
	oWin = window.open(p_strURL, p_strHandle, strFeatures);
	if(oWin){oWin.focus()};
	return false;
}



	

function IsInteger(p) {
	return p==parseInt(p);
}
function IsNumeric(p) {
	return p==parseFloat(p);
//	return !isNaN(p) && p==parseFloat(p);		//is this needed?
}
function Trim(str){
	return str.replace(/^\s*|\s*$/g, "");
}
function Left(str, n){
	if (n <= 0){
		return "";
	}else if (n > String(str).length){
		return str;
	}else{
		return String(str).substring(0, n);
	}
}
function Right(str, n){
    if (n <= 0)
       return "";
    else if (n > String(str).length)
       return str;
    else {
       var iLen = String(str).length;
       return String(str).substring(iLen, iLen - n);
    }
}
//bootstrap other cases;
isInteger=IsInteger;
isNumeric=IsNumeric;
trim=Trim;
left=Left;
right=Right;






function checkKey(e, f){
    if (e.keyCode == 13) f.submit();
}

function setCookie(name, value, expires, path, domain, secure){
    document.cookie =  name +"="+ escape(value) 
			+ ((expires)? "; expires="+ expires.toGMTString() : "") 
			+ ((path)		? "; path="		+ path : "")
      + ((domain) ? "; domain=" + domain : "") 
      + ((secure) ? "; secure" : "");
}

function getCookie(name){
    var dc = document.cookie;
    var prefix = name + "=";
    var begin = dc.indexOf("; " + prefix);

    if (begin == - 1){
        begin = dc.indexOf(prefix);

        if (begin != 0) return null;
    }else{
        begin += 2;
    }

    var end = document.cookie.indexOf(";", begin);

    if (end == - 1){
        end = dc.length;
    }

    return unescape(dc.substring(begin + prefix.length, end));
}

function checkLogin(){
	if($==undefined){var $ = function(o){return document.getElementById(o)}}
	if($("username") && $("password") && $("loginauto")){
		if(getCookie("loginauto") == "yes"){
      $("username").value = getCookie("lmusername").replace("+", " ");
      $("password").value = getCookie("lmpassword").replace("+", " ");
      $("loginauto").checked = true;
    }
    $("username").focus();
  }
}

function setCursor(){
}



function setNews(p_idx){
	if(p_idx==undefined){p_idx= getCookie("newsBlockIndex");}	// if no block index, remember last 
		
	// sections
	var colNewsAnchors = document.getElementsByName('news');  // grabs anchors, but we'll use their parent container blocks
	if(!colNewsAnchors.length){return};
	if(p_idx==undefined){p_idx=colNewsAnchors.length-1};	// set default index
	for(var n=0; n<colNewsAnchors.length; n++){ colNewsAnchors[n].parentNode.style.display = 'none'; }	// hide all
	colNewsAnchors[colNewsAnchors.length -1 -p_idx].parentNode.style.display = '';	// show current

	// links
	var colNewsLinks = document.getElementsByName("newstimelink");
	if(!colNewsLinks.length){return};
	for(var n=0; n<colNewsLinks.length; n++){	colNewsLinks[n].style.fontWeight='normal';	}	// unbold all
	colNewsLinks[colNewsLinks.length -1 -p_idx].style.fontWeight='bold';	// bold current

	// save block index for later
	setCookie("newsBlockIndex", p_idx)
	return false;
}



function CheckUserStatus(){
    var loggedin = getCookie("lmloggedin");
    var loginform = document.getElementById("rightcolumn");

    if (loggedin == "yes"){
        loginform.style.visibility = "hidden";
    }else{
        loginform.style.visibility = "visible";
    }
}


//<script type="text/javascript"> '-- jogs intellisense below

function global_init(){
	if(arguments.callee.done){return}
	arguments.callee.done = true;
	
	checkLogin();
	hookupMaxLengths();
	fixMinWidthForIE();
}
hookup(global_init);	// fire when DOM content is loaded (which is before window.onload)
