isIE = /Microsoft Internet Explorer/.test( navigator.appName );
IE = isIE;
IE6 = /MSIE\s6/.test( navigator.appVersion );
IE7 = /MSIE\s7/.test( navigator.appVersion );
function cout( str ) { if( !IE ) { console.debug( str ); } else alert( str ); }

// **** countLimit for multiple select boxes
function updateCountLimit() {
	selected = 0, maxCount = selectedItems = "";
	limit = /countLimit(\d\d?)/.exec( this.className )[1];
	items = "";
	for( i = 0; i < this.options.length; i++ ) {
		if( this.options[i].selected ) {
			selected++;
			items += ( ( selected < 10 )? "&nbsp;&nbsp;": "" ) + selected + ". " + this.options[i].innerHTML + "<br />";
		}
	}
	
	divs = this.parentNode.getElementsByTagName( "DIV" );
	for( j = 0; j < divs.length; j++ ) {
		if( divs[j].className.indexOf( "maxLength" ) > -1 ) { maxCount = divs[j]; }
		if( divs[j].className.indexOf( "selectedItems" ) > -1 ) { selectedItems = divs[j]; }
	}
	
	setFieldNotice( this, "reset" );
	if( maxCount != "" ) {
		setFieldNotice( maxCount, "reset" );
		maxCount.innerHTML = "Selected " + selected + " out of " + limit + " total.";
	}
	if( selectedItems != "" ) {
		setFieldNotice( selectedItems, "reset" );
		selectedItems.innerHTML = items;
		selectedItems.style.display = "block";
		offset = this.offsetWidth + this.offsetLeft + 10;
		if( IE7 ) { offset -= 20; }
		selectedItems.style.left = offset + "px";
	}
	if( selected > limit ) {
		setFieldNotice( this );
		if( maxCount ) { setFieldNotice( maxCount ); }
		if( selectedItems ) { setFieldNotice( selectedItems ); }
	}
}
// **** countLimit for multiple select boxes

// **** character counter for textareas
function updateTARS() {
	try {
		max = this.className.replace( /^.*m([0-9]{1,5}).*$/, "$1" );
		max = ( isNumeric( max ) )? max: 500;
		p = this.parentNode;
		s = p.getElementsByTagName( "DIV" );
		if( s.length > 0 && s[0].className.indexOf( "maxLength" ) > -1 ){
			s[0].innerHTML = "Max " + max + " characters; " + ( max - this.value.length ) + " remaining.";
			this.style.background = ( max < this.value.length )? "#FFCFDA" : "#FFFFFF";
		}
	}
	catch( e ) {
		err = "";
		if( !e ) var e = window.event;
		err += "Error: " + e.type + "\n";
		
		for( var p in e ) { err += p + "\n"; }

		alert( err );
	}
}
// **** character counter for textareas

// **** set field colors for error checking
function setFieldNotice( node ) {
		node.style.background = ( setFieldNotice.arguments[1] != "reset" )? "#FFCFDA": ( node.nodeName == "FIELDSET" || node.nodeName == "DIV")? "transparent": "#FFFFFF";
		node.style.borderColor = ( setFieldNotice.arguments[1] != "reset" )? "#9F0006": "#000000";
}
// **** set field colors for error checking

// **** labels surrounding checkboxes or radio buttons shall now be clickable in IE6 and below
function clickableLabels() {
	if( navigator.appName.toLowerCase().indexOf( "microsoft" ) > -1  ) { // checking for any microsoft browser
		labels = document.getElementsByTagName( "LABEL" );
		for( i = 0; i < labels.length; i++ ) {
			if( labels[i].className == "clickable" ) {
				labels[i].onclick = function() {
					if( this.childNodes[0].type == "radio" ) { this.childNodes[0].checked = true; }
					else { this.childNodes[0].click(); }
				}
			}
		}
	}
}
// **** clickable lables above

// **** isNumeric function
function isNumeric( question ) {
	regEx = /^-?\d*(\.\d+)?$/;
	return ( regEx.test( question ) )? true: false;
}
// **** isNumeric function

// **** trim whitespace from a string front and back
function trimString( str ) {
	sInString = str.replace( /^\s+/g, "" );// strip leading
	return sInString.replace( /\s+$/g, "" );// strip trailing
}
// **** trimString above

// **** unique returns an array of unique elements contained in the array passed in
function unique( all ) {
	all.sort();
	temp = Array();
	for( f = 0; f < all.length; f++ ) {
		w = f;
		while( all[f] == all[w] ) { w++; }
		f += ( w - f ) - 1;
		temp.push( all[f] );
	}
	return temp;
}
// **** unique above

/* **** form validation for required fields and correct email format
	universal form validator
	
	for any item needing validation add to the class variable of the element "validate-" + the arguments
	to validate against. The arguments are as follows:

		n
		- NULL if the field is required regardless of any rules for the form. This element will 
			throw an error if it is null or only contains whitespace characters.

		e
		- EMAIL if the field is an email address field. The regular expression will check to see
			if the form element's value is a standard email address.

		d#
		- DATE format verification
			1 - mm-dd-yyyy; optional separators are "/" and "-"
			2 - yyyy-mm-dd; optional separators are "/" and "-"
			3 - shortMonthName dd, yyyy
			4 - longMonthName dd, yyyy
			5 - dd shortMonthName yyyy
			6 - dd longMonthName yyyy
			7 - yyyy
			8 - shortMonthName dd
			9 - longMonthName dd
			0 - yyyy-### for AIB book reviews ID number - trailing number mus be at least 2 digits

		t#
		- TIME not implemented currently
			1 - d:dd am or pm	12hr clock
			2 - dd:dd			24hr clock
			3 - 
			4 - 
			5 - 
			6 - 
			7 - 
			8 - 
			9 - 
			0 - 
		
		o
		- humanity check; simply a text box that is hidden from the user so it should never be filled
			unless stylesheets are turned off and they can't read or the form was filled in by a program.
		
		f
		- fieldset to verify that radio/check boxes have a value.
			this is like "validate-n" for the text boxes; if there is not a single checkbox/radio button
			checked in the fieldset the validation will be negative (there will be an error) if nothing 
			is selected.
			
			if this is to be an optional field dependant on another option do not use the "validate-f"
			command, instead use the group #; "validate-g#"
		
		p
		- pairs for matching 2 fields against each other; such as email validation.

		g#
		- GROUP the number indicates what grouping this element should be verified against.
			Set the id property, of an optional element in the form, similar to this example:
				<input id="validate-g0" type="checkbox"...
				or
				<input id="validate-g0" type="radio"...
			
			Then any input elements with this as its class name will be validated according
			to this option.

		m####
		- MAX length (in characters) of an element. The integer following the m will define
			what the max length of the field will be.
		
		s###
		- multiple select boxes

	These arguments are not pattern dependant so they can come in any order in the class vairable of the
	element. (eg. validate-neg1 is the same as validate-g1ne and/or validate-eng1)
	
	Also any element can be a part of a group and can also be part of multiple groups and be validated 
	for each group independantly, except of course if two group rules contradict but I don't know how
	that would happen but I guess we'll see.
*/
function validateForm( formObj ) {
try {
	valid = /\bvalidate-(n|e|d\d|t\d|o|f|p\d|g\d|m\d{1,5}?|s\d{1,3}?)+?\b/;
	notNull = /\bvalidate-[^n,\s]*n[^n,\s]*\b/;
	email = /\bvalidate-[^e,\s]*e[^e,\s]*\b/;
	date = /\bvalidate-[^d,\s]*d\d[^d,\s]*\b/;
	time = /\bvalidate-[^t,\s]*t\d[^t,\s]*\b/;
	human = /\bvalidate-[^o,\s]*o[^o,\s]*\b/;
	field = /\bvalidate-[^f,\s]*f[^f,\s]*\b/;
	pair = /\bvalidate-[^p,\s]*(p[0-9])+?[^p,\s]*\b/;
	group = /\bvalidate-[^g,\s]*(g[0-9])+?[^g,\s]*\b/;
	maxLength = /\bvalidate-[^m,\s]*m[0-9]{1,5}?[^m,\s]*\b/;
	select = /\bvalidate-[^s,\s]*s\d{1,3}[^s,\s]*\b/;
	trim = /\s+$/;
	errorMsg = "";
	borderColor = formObj[0].style.borderColor;
	arrEmail = new Array();
	tempError = false;
	
	function getMaxLength( name ) {
		validArgs = /m\d{1,5}$/.exec( valid.exec( name )[0] )[0].replace( "m", "" );
		return ( isNumeric( validArgs ) )? validArgs: 500;
	}
	function getGroups( formObj ) {
		all = Array();
		for( i = 0; i < formObj.length; i++ ) {
			if( valid.test( formObj[i].id ) && ( formObj[i].type == "checkbox" || formObj[i].type == "radio" ) && formObj[i].checked ) {
				all.push( formObj[i].id );
			}
		}
		return unique( all );
	}
	function fieldsetHasValue( obj ) {
		r = obj.getElementsByTagName( "INPUT" );
		valued = false;
		if( r[0].type == "radio" || r[0].type == "checkbox" ) { for( fieldsetCounter = 0; fieldsetCounter < r.length; fieldsetCounter++ ) { if( r[fieldsetCounter].checked == true ) { valued = true; } } }
		
		return valued;
	}
	function getPairs( formObj ) {
		p = Array();
		for( b = 0; b < formObj.length; b++ ) {
			if( valid.test( formObj[b].className ) && pair.test( formObj[b].className ) ) {
				p.push( formObj[b] );
			}
		}
		if( p.length % 2 != 0 ) { alert( "javascript error - invalid pair count" ); }
		return p;
	}
	function groupName( obj ) {
		v = group.exec( obj.className );
		return v[0];
	}
	function validEmail( question ) {
		var regEx  = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
		return ( regEx.test( question ) )? true: false;
	}
	function validPhone( question ) {
		regEx  = /^(\d{1,4}?-){3,5}?$/;
		return regEx.test( question );
	}
	
	groups = getGroups( formObj ); // array of ids that signify groups
	pairs = getPairs( formObj );
	for( i = 0; i < formObj.length; i++ ) {
		node = formObj[i];
		if( formObj.className.indexOf( "testing" ) > -1 ) {
			//alert( formObj[i].className );
		}
		if( v = valid.exec( node.className ) ) {
			validArgs = v[0];
			setFieldNotice( node, "reset" );
			tempError = false;
			textArea = maxLength.test( node.className ) && trimString( node.value ) != "";
			emailField = email.test( node.className ) && trimString( node.value ) != "";
			dateField = date.test( node.className ) && trimString( node.value ) != "";
			timeField = time.test( node.className );
			humanField = human.test( node.className );
			fieldset = field.test( node.className );
			groupField = group.test( node.className );
			pairsField = pair.test( node.className );
			selectField = select.test( node.className );
			nNullField = notNull.test( node.className ) && trimString( node.value ) == "";
			if( textArea ) {
				characters = getMaxLength( node.className );
				if( node.value.length > characters ) {
					errorMsg += "This Area must be " + characters + " characters or fewer; you have: " + node.value.length + "\n";
					tempError = true;
				}
			}
			if( fieldset && !fieldsetHasValue( node ) ) {
				errorMsg += "A value has not been selected.\n";
				tempError = true;
			}
			if( groupField ) {
				for( x = 0; x < groups.length; x++ ) {
					testControl = document.getElementById( groupName( node ) ).checked;
					if( node.nodeName != "FIELDSET" ) {
						if( testControl && trimString( node.value ) == "" ) {
							errorMsg += "One (or more) required fields are not filled.\n";
							tempError = true;
						}
					}
					else {
						if( testControl && !fieldsetHasValue( node ) ) {
							errorMsg += "A value has not been selected.\n";
							tempError = true;
						}
					}
				}
			}
			if( emailField ) {
				arrEmail.push( node );
				if( !validEmail( node.value ) ) {
					errorMsg += "One (or more) of the email addresses you entered are not correctly formatted.\n";
					tempError = true;
				}
			}
			if( dateField ) {
				flavor = /d\d/.exec( node.className )[0];
				separators = "([\\.\/-])";
				simpleDay = "((?:[0-2]?\\d)|(?:3[0-1]))";
				monthNumbers = "((?:0?\\d)|(?:1[0-2]))";
				shortMonthNames = "((?:Jan)|(?:Feb)|(?:Mar)|(?:Apr)|(?:May)|(?:Jun)|(?:Jul)|(?:Aug)|(?:Sep)|(?:Oct)|(?:Nov)|(?:Dec))";
				longMonthNames = "((?:January)|(?:February)|(?:March)|(?:April)|(?:May)|(?:June)|(?:July)|(?:August)|(?:September)|(?:October)|(?:November)|(?:December))";
				longYear =  "([12]\\d{3})";
				var properDate = new RegExp( "" );
				switch( flavor ) { // **** validate-d#
					case "d2": // **** flavor 2 yyyy-mm-dd ( or yyyy/mm/dd )
						properDate.compile( "^" + longYear + separators + monthNumbers + "(\\2)" + simpleDay + "$" );
					break;
					case "d3": // **** flavor 3 shortMonthName day, yyyy
						properDate.compile( "^" + shortMonthNames + " " + simpleDay + ", " + longYear + "$" );
					break;
					case "d4": // **** flavor 4 longMonthName day, yyyy
						properDate.compile( "^" + longMonthNames + " " + simpleDay + ", " + longYear + "$" );
					break;
					case "d5": // **** flavor 5 day shortMonthName yyyy
						properDate.compile( "^" + simpleDay + " " + shortMonthNames + " " + longYear + "$" );
					break;
					case "d6": // **** flavor 6 day longMonthName yyyy
						properDate.compile( "^" + simpleDay + " " + longMonthNames + " " + longYear + "$" );
					break;
					case "d7": // **** flavor 7 yyyy
						properDate.compile( "^" + longYear + "$" );
					break;
					case "d8": // **** flavor 8 shortMonthName dd
						properDate.compile( "^" + shortMonthNames + " " + simpleDay + "$" );
					break;
					case "d9": // **** flavor 9 longMonthName dd
						properDate.compile( "^" + longMonthNames + " " + simpleDay + "$" );
					break;
					case "d0": // **** flavor 0 yyyy-### for AIB book reviews ID number
						properDate.compile( "^" + longYear + "-" + "\\d\\d+?" + "$" );
					break;
					default: // **** default ( d1 ) date format mm-dd-yyyy ( or mm/dd/yyyy )
						properDate.compile( "^" + monthNumbers + separators + simpleDay + "(\\2)" + longYear + "$" );
					break;
				}
				if( !properDate.test( node.value ) ) {
					errorMsg += "One (or more) of the dates you entered are not correctly formatted.\n";
					tempError = true;
				}
			}
			if( timeField ) {
				flavor = /t\d/.exec( node.className )[0];
				switch( flavor ) { // **** validate-d#
					case "t2": // **** flavor 2 dd:dd 24hr clock
						properTime = /^(((?:[0-1]?\d)|(?:2[0-4])):([0-5]\d))$/;
					break;
					case "t3": // **** flavor 3 
						// 
					break;
					case "t4": // **** flavor 4 
						// 
					break;
					case "t5": // **** flavor 5 
						// 
					break;
					case "t6": // **** flavor 6 
						// 
					break;
					case "t7": // **** flavor 7 
						// 
					break;
					case "t8": // **** flavor 8 
						// 
					break;
					case "t9": // **** flavor 9 
						// 
					break;
					case "t0": // **** flavor 0 
						// 
					break;
					default: // **** default ( d1 ) 12:22 pm
						properTime = /^(((?:0?\d)|(?:1[012])):([0-5]?\d))\s?([ap]m)$/i;
					break;
				}
				if( !properTime.test( node.value ) ) {
					errorMsg += "One (or more) of the times you entered are not correctly formatted.\n";
					tempError = true;
				}
				else {
					hrs = /(\d\d?)/.exec( node.value );
					if( hrs[1] < 10 && hrs[1][0] != 0 ) { node.value = "0" + node.value; }
				}
			}
			if( humanField ) {
				if( node.value != "" ) {
					errorMsg += "If you are attempting to Spam our site, please don't.\n";
					tempError = true;
				}
			}
			if( selectField ) {
				maxNum = /validate-[^s\s]*s(\d{1,3})/.exec( node.className )[1];
				selected = "";
				for( j = 0; j < node.options.length; j++ ) { if( node.options[j].selected ) { selected++; } }
				if( selected > maxNum ) {
					errorMsg += "Only " + maxNum + " items may be selected in this list, please deselect " + parseInt( selected - maxNum ) + ".\n";
					tempError = true;
				}
			}
			if( pairsField ) {
				regEx = /p[0-9]/;
				for( q = 0; q < pairs.length; q++ ) {
					nodeCompare = regEx.exec( node.className );
					pairCompare = regEx.exec( pairs[q].className );
					if( nodeCompare[0] == pairCompare[0] && node.value != pairs[q].value && node != pairs[q] ) {
						errorMsg += "Please make sure these fields match identically.\n"
						tempError = true;
						setFieldNotice( pairs[q] );
					}
				}
			}
			if( nNullField ) {
				errorMsg += "The " + node.name + " field should not be empty.\n"
				tempError = true;
			}
			if( tempError ) { setFieldNotice( node ); }
		} // end if statement checking for validation property
	} // end for loop - looping through form elements
	if( arrEmail.length > 1 && arrEmail[0].value != arrEmail[1].value ) {
		setFieldNotice( arrEmail[0] );
		setFieldNotice( arrEmail[1] );
		errorEmail += ( errorEmail != "" )? "\n": "";
		errorEmail += "The email addresses you entered do not match.";
	}
	if( errorMsg != "" ) {
		errorMsg = errorMsg.replace( /One \(or more\) required fields are not filled\.\n/gi, "" ) +
			"One (or more) required fields are not filled.\n";
		if( /One \(or more\) required fields are not filled\.\n/gi.test( errorMsg ) ) {
			errorMsg += "The missing fields are denoted by a red color in the area of the problem.";
		}
		alert( errorMsg );
		return false;
	}
	return true;
}
catch( e ) {
	if( !e ) var e = window.event;
	
	er = "";
	for( var p in e ) { er += p + " = " + e.eval( p ) + "\n"; }

	alert( "JavaScript Error!\n" + er );
	return false;
}
}
// **** validateForm above





