// JavaScript Document

/**************************************************************************************
COPYRIGHT 2004 - RawData Corporation, www.RawData.net
DEVELOPER:		Richard Soares
VERSION:		2.2 - 08.27.04
FILE NAME:		FormValidationControls.js

FUNCTION:		Performs automated form validation through the use of the browser DOM.
				Checks for validation control attributes added to form elements.
				Executes EMPTY/NULL validation or PATTERN MATCH validation.
				Any found errors produce a composite alert and highlight invalid fields.
				
COMPATIBILITY:	MOST MODERN BROWSERS THAT SUPPORT THE DOM AND THIS SCRIPT.
				Includeing: WIN + MAC -> IE-5+, NS-4.7+, FIREFOX-0.9+, MOZILLA-1.7+
				
INSTRUCTIONS:	Summary - Include this script in your files <head> region. Add the following 
				form element attributes to an existing form element. Then call this script from 
				the onSubmit Event Method in your form tag. All else is automated.

STEP ONE:		Add this script to your HTML <header> tag, like so:
				<script language="JavaScript" type="text/JavaScript" src="/PATH/FormValidationControls.js"></script>
				WHERE 'PATH' IS THE PATH TO THIS FILE.
				
STEP TWO:		Add the onSubmit event to your HTML form tag, like so:
				onSubmit="(return validateFormByDOM('color')"
				WHERE 'COLOR' IS THE BACKGROUND COLOR FOR FIELDS THAT RETURN ERRORS. (OPTIONAL)
				
STEP THREE:		Use the following form control attributes to validate your form.
				NOTE - Only those form elements that contain these controls will be validated.
				If you dont need an element to be validated, simply dont add any validation controls to it.
				
				SPECIAL NOTE: All form elements should contain there required HTML attributes as normal, such as: name, id, type ... etc.
				
				ATTRIBUTE			FUNCTION								NOTES
INPUT FIELDS 	required="true"		Check for Empty							Precidence over 'pattern' matching.
				pattern="RegEx"		Compare fld value against Expression	Used inplace of 'required' attreibute. (see sample patterns below)
				errorMsg="message"	Message returned to user.				Free form value.
				
				EXAMPLE:
				Validate for letters only using pattern.
				<input name="firstname" type="text" id="firstname" pattern="[A-Za-z]+" errorMsg="Invalid characters found in First Name."/>
				Validate not empty using required.
				<input name="firstname" type="text" id="firstname" required="true" errorMsg="Invalid characters found in First Name."/>

INPUT FIELDS WITH A NUMERIC RANGE, ie: 18-36
				min_req="min-num"	Checks for minumim numeric range		Where 'min-num' is the lowest number allowed.
				min_req="max-num"	Checks for maximum numeric range		Where 'max-num' is the highest number allowed.
				errorMsg="message"	Message returned to user.				Free form value.
				*If min_req & max_req attributes are found required is assumed. The 'required' attribute is not needed.
				
TEXTAREA FIELDS	required="true"		Check for Empty							No Pattern Matching offered.
				textsize="n"		Check for maximum char length.			Optional. Extends the 'required' attribute...
																			Also returns appends related error messages.
				errorMsg="message"	Message returned to user.				Free form value.
				
				EXAMPLE:
				<textarea name="comments" required="true" textsize="10" errorMsg="Invalid Comments format."></textarea>


RADIO BUTTONS	required="true"		Checks for 'checked' radio in group.	MUST appear on first radio btn in group.
				radiogroup="n"		Where 'n' is the TOTAL number of 
									buttons n the radio group.				MUST appear on first radio btn in group with 'required'.
				id="unque_name"		Basic HTML id name						MUST, needed to find all radio btns in group.
				errorMsg="message"	Message returned to user.				Free form value.

				EXAMPLE:
				<input name="gender" type="radio" id="btn-1" value="male" required="true" radiogroup="2" errorMsg="You must select a gender." />
				<input name="gender" type="radio" id="btn-2" value="female" />
				
CHECK BOXES		required="true"		Check for 'checked'.					
				errorMsg="message"	Message returned to user.				Free form value.
				
				EXAMPLE:
				<input type="checkbox" name="terms" id="terms" value="accepted" required="true" errorMsg="You must accept the terms." />
				
SELECT			required="true"		Check for 'selected'.					Required first <option> to be blank or null (See example).
				errorMsg="message"	Message returned to user.				Free form value.
				
				EXAMPLE:
				<select name="selectletter" id="selectletter" required="true" errorMsg="Invalid Pick a Letter selection" >
      				<option value="">select letter</option>
	  				<option value="A">A</option>
      				<option value="B">B</option>
    			</select>
				
EXTRA FEATURES:	There is a special function available to check if the users browser supports the DOM and this script. 
				You can call DOMcheck() in this script for a true/false return (true being DOM is supported).
				You can call this before calling validateFormByDOM() to decide if you can use this script or if you
				will rollback to old-school form validation techiques. That code you will have to write. ;-)
				
SAMPLE REGULAR EXPRESSION PATTERNS: Use these patterns in conjuction with the 'pattern' control form attribute for <input> elements.

North American Phone Number: 	Like: (401) 123-1234, (123)123-1234, 123-123-1234
								Pattern: (\(?\d{3}\)?)?(\-| )?\d{3}(\-| )?\d{4}
								
US Zip Codes:					Like: 12345, 12345-1234
								Expression: \d{5}(-\d{4})?
								
Canadian Postal Codes:			Like: M1A 1A1, H9Z 9Z9
								Pattern: [ABCEGHJKLMNPRSTVXY]\d[A-Z] \d[A-Z]\d
								
Social Security Numbers:		Like: 123-45-6789
								Pattern: \d{3}-\d{2}-\d{4}
								
URLs:							Like: http://www.rawdata.net, https://www.rawdata.net
								Pattern: https?://[-\w.]+(:\d+)?(/([\w/_.]*)?)?
								
Email Addresses:				Like: name@example.com, first.last@example.com
								Pattern: (\w+\.)*\w+@(\w+\.)+[A-Za-z]+

CREDIT CARDS					Master Card: 5[1-5]\d{14}
								Visa: 4\d{12}(\d{3})?
								American Express: 3[47]\d{3}
								Discover: 6011\d{14}
								All of the above: 5[1-5]\d{14}|4\d{12}(\d{3})?|3[47]\d{3}|6011\d{14}



**************************************************************************************/

function validateFormByDOM(errorColor) { 
	// Remove all fld error highlights before we mark new errors.
	var elements = document.getElementsByTagName('input');
	for ( var i = 0; i < elements.length; i++ ) {
		elements.item(i).style.background = "white";
	}
	var elements = document.getElementsByTagName('textarea');
	for ( var i = 0; i < elements.length; i++ ) {
		elements.item(i).style.background = "white";
	}
	var elements = document.getElementsByTagName('select');
	for ( var i = 0; i < elements.length; i++ ) {
		elements.item(i).style.background = "white";
	}
	
	
	// DEFINE ERROR VARIABLES & HIGHLIGHT COLOR
	// Validated Elements object - Used to store validated form element ID's and thier values for dependancy checks.
	var valElements = new Object();
	
	// Composite error message returned to user.
	var str = ""; 
	
	// Set default error field color.
	if ( errorColor == null ) {
		var	errorFldColor = "yellow"; // Some browsers will not show colored field backgrounds.
	} else {
		var errorFldColor = errorColor; // Color passed into function.
	}
		
	////////////// VALIDATE <input> FORM ELEMENTS //////////////
	var elements = document.getElementsByTagName('input');
	
	// Loop through form elements
	for ( var i = 0; i < elements.length; i++ ) {
	
		// Get all available <input> validation attributes.
		var required 	= elements.item(i).getAttribute('required');
		var pattern 	= elements.item(i).getAttribute('pattern');
		var min_req 	= elements.item(i).getAttribute('min_req');
		var max_req 	= elements.item(i).getAttribute('max_req');
		var radiogroup	= elements.item(i).getAttribute('radiogroup');
		var dependancy	= elements.item(i).getAttribute('dependancy');
		
		// Get other basic HTML element attributes; we may need them later.
		var id			= elements.item(i).getAttribute('id');
		var type 		= elements.item(i).getAttribute('type');
		var name 		= elements.item(i).getAttribute('name'); 
		
		// Check if element is only REQUIRED, do not validated against a pattern.
		// Check field types.
		if ( required != null ) {
			// Check FIELD's
			if ( type != "radio" || type != "checkbox" ) {
				// Get the elements value.
				var value = elements.item(i).value;
				// Only validate field if NOT EMPTY.
				if ( value == "" || value == null || value == 0 ) {
					// Add up all error messages.
					str += elements.item(i).getAttribute('errorMsg') + "\n" +
							"-> Found the field EMPTY. \n\n";
					// Notify user by changing bg color of field.
					elements.item(i).style.background = errorFldColor;
					// Set dependancy validation state.
					hasValue = false;
				} else {
					// Set dependancy validation state.
					var hasValue = "cat";
				} // end if else
			} // end if
			
			// Check RADIO's
			if ( type == "radio" ) {
				// We "step forward" through the elements using the value from RADIOGROUP.
				radioChecked = false;
				var radioCount = i+(radiogroup-1);
				for ( n = i; n <= radioCount; n++ ) {
					if ( elements.item(n).checked == true ) {
						radioChecked = true;
						/*
						// Append elements ID and VALUE to element validation object for dependancy checks.
						valElements[radID] = true;	
						
						// Append elements ID and VALUE to element validation object for dependancy checks.
						valElements[radID] = false;	
						
						*/
							
					} // end if
				} // end for loop
				if ( radioChecked == false ) {
					// Add up all error messages.
					str += elements.item(i).getAttribute('errorMsg') + "\n" +
							"-> Found no option selected. \n\n";
				} // end if
			} // end if
			
			// Check CHECKBOX's
			if ( type == "checkbox" ) {
				var ckbnChecked = elements.item(i).checked;
				if ( ckbnChecked == false ) {
					// Add up all error messages.
					str += elements.item(i).getAttribute('errorMsg') + "\n" +
							"-> This required checkbox was not checked. \n\n";
					// Set dependancy validation state.
					var hasValue = false;
				} else {
					// Set dependancy validation state.
					var hasValue = true;
				}// end else if
			} // end if			
		} // end if
		
		// Check if the element has a PATTERN to validate against.
		// Do PATTERN check only if REQUIRED DOES NOT EXIST.
		if ( pattern != null && required == null ) {
			// Get the elements value.
			var value = elements.item(i).value;
			// Only validate field if NOT EMPTY.
			if ( value != "" && value != null && value != 0 ) {
				// Extract pattern string from field.
				var strPattern = value.match(pattern);
				// If strPattern = null, then all we have are offencing charaters in the field.
				if ( strPattern != null ) {
					// Check if only the pattern match is in the field; without offending chars.
					if ( strPattern[0].length < value.length ) {
						// Extract the offending chars from field value.
						var offendingChar = value.replace(strPattern[0],"");
						// Add up all error messages.
						str += elements.item(i).getAttribute('errorMsg') + "\n" +
								"-> Found this illegal value: '" + offendingChar + "' \n\n";
						// Notify user by changing bg color of field.
						elements.item(i).style.background = errorFldColor;
						// Set dependancy validation state.
						var hasValue = false;
					} else {
						// Set dependancy validation state.
						var hasValue = true;
					} // end else if
				} else {
					// Add up all error messages.
					str += elements.item(i).getAttribute('errorMsg') + "\n" +
							"-> Found this illegal value: '" + value + "' \n\n";
					// Notify user by changing bg color of field.
					elements.item(i).style.background = errorFldColor;
					// Set dependancy validation state.
					var hasValue = false;
				} // end if else
				
			} else {
			
				// We have a pattern, but no field value to check against; Alert EMPTY FIELD.
				// Add up all error messages.
				str += elements.item(i).getAttribute('errorMsg') + "\n" +
						"-> Found the field EMPTY \n\n";
				// Notify user by changing bg color of field.
				elements.item(i).style.background = errorFldColor;
				// Set dependancy validation state.
				var hasValue = false;
			} // end if else		
		} // end if
		
		// Check if the element has a mandatory range MIN-MAX: ie: 18-36
		if ( min_req != null && max_req != null ) {
			// Get vthe elements value.
			var value = elements.item(i).value;
			// Validate value against range.
			if ( value < min_req || value > max_req ) {
				// Make friendly empty value.
				if ( value == "" ) {
					value = "EMPTY FIELD"
				}
				// Add up all error messages.
				str += elements.item(i).getAttribute('errorMsg') + "\n" +
						"-> Found this illegal value range: '" + value + "' \n\n";
				// Notify user by changing bg color of field.
				elements.item(i).style.background = errorFldColor;
				// Set dependancy validation state.
				var hasValue = false;
			} else {
				// Set dependancy validation state.
				var hasValue = true;
			}// end else if
		} // end if	
		// Append elements ID and VALUE to element validation object for dependancy checks.
		valElements[id] = hasValue;	
	} // end <input> for loop
	
	////////////// VALIDATE <textarea> FORM ELEMENTS //////////////
	var elements = document.getElementsByTagName('textarea');
	
	// Loop through form elements
	for ( var i = 0; i < elements.length; i++ ) {
	
		// Get all available <textarea> validation attributes.
		var required 	= elements.item(i).getAttribute('required');
		var textsize 	= elements.item(i).getAttribute('textsize');
		
		// Get other basic HTML element attributes; we may need them later.
		var id			= elements.item(i).getAttribute('id');
		var type 		= elements.item(i).getAttribute('type');
		var name 		= elements.item(i).getAttribute('name'); 
		
		// Check <textarea> for EMPTY and SIZE if required.
		if ( required != null ) {
			// Get the elements value.
			var value = elements.item(i).value;
			
			// Check for field EMPTY.
			if ( value == "" || value == null || value == 0 ) {
				// Add up all error messages.
				str += elements.item(i).getAttribute('errorMsg') + "\n" +
						"-> Found the text area to be EMPTY. \n\n";
				// Notify user by changing bg color of field.
				elements.item(i).style.background = errorFldColor;
				// Set dependancy validation state.
				var hasValue = false;
			} else {
				// Set dependancy validation state.
				hasValue = true;
			}// end if
			
			// Check for total field CHAR LENGTH.
			if ( textsize != null ) {
				if ( value.length > textsize ) {
					// Add up all error messages.
					str += elements.item(i).getAttribute('errorMsg') + "\n" +
						"-> Textual length can not exceed " + textsize + " charaters. \n\n";
					// Notify user by changing bg color of field.
					elements.item(i).style.background = errorFldColor;
					// Set dependancy validation state.
					var hasValue = false;
				} // end if
			} // end if
		} // end if
		// Append elements ID and VALUE to element validation object for dependancy checks.
		valElements[id] = hasValue;	
	} // end <textarea> for loop
	
	////////////// VALIDATE <select> FORM ELEMENTS ////////////// 
	var elements = document.getElementsByTagName('select');
	
	// Loop through form elements
	for ( var i = 0; i < elements.length; i++ ) {
		// Get all available <select> validation attributes.
		var required 	= elements.item(i).getAttribute('required');
		var id 			= elements.item(i).getAttribute('id');
		
		// Set dependancy validation state.
		var hasValue = true;
		
		// Check <select> for EMPTY and SIZE if required.
		if ( required != null ) {
			// Get the elements value.
			var x = document.getElementById(id);
			var value = x.options[x.selectedIndex].value;
			
			// Check for field EMPTY.
			if ( value == "" || value == null || value == 0 ) {
				// Add up all error messages.
				str += elements.item(i).getAttribute('errorMsg') + "\n" +
						"-> No option selected from menu. \n\n";
				// Notify user by changing bg color of field.
				elements.item(i).style.background = errorFldColor;
				// Set dependancy validation state.
				var hasValue = false;
			} else {
				// Set dependancy validation state.
				hasValue = true;
			}// end if
		} // end if
		// Append elements ID and VALUE to element validation object for dependancy checks.
		valElements[id] = hasValue;	
	} // end <select> for loop
	
	////////////// EVALUATE ELEMENT DEPENDANCIES //////////////
	/*
	var depStr = "";
	for ( anItem in valElements ) {
			depStr += anItem + "=" + valElements[anItem] + "\n";
	} // end for loop
	alert(depStr);
	*/
	
	////////////// ALERT USER TO FORM VALIDATION ERRORS //////////////
	if ( str != "" ) {
		// Do not submit the form.
		alert( "VALIDATION ALERT!!\n" + str );
		return false;
	} else {
		// Form valuse are valid; submit.
		return true;
	} // end if else
}

function DOMcheck() {
	if( !document.getElementsByTagName('html') ) {
		return true; // return true so we know the DOM is supported.
	}
}
// END SCRIPT