/*
 * forms.js
 * by Andy Tidball
 * last revised: 05/27/2002
 *
 * This module encapsulates client-side form validation into a simple interface for the developer
 * This module is designed to work with Internet Explorer 5.0+ and Netscape 6.0+
 *
 * Usage:
 *  First, include this file in the HTML page containing the form by putting the following inside the page's <head> tag:
 *  <script langauge="JavaScript1.2" src="path/to/forms.js"></script>
 *
 *  Next, have the form process the validation procedure when it is submitted by modifying the <form> tag to include the "onsubmit" attribute, as follows:
 *  <form method="post|get" action="path/to/page.html" onsubmit="return validate(this)">
 *  Make doubly sure that the word "return" is in there, if it's not the form will validate, but will continue to the next page even if there are errors
 *
 *  There are two ways to call validate.  You must always pass it the form object to validate (by passing "this" as the first argument), but after that you have the option to pass the address of a callback function if you'd like to do some customized validation as well.  For Example:
 *  <form method="post|get" action="path/to/page.html" onsubmit="return validate(this,myValidateFunction)">
 *  Using this variation, the validation procedure will first call "myValidateFunction()" and pass it the form object that is being validated.  You'll need to write that function ("myValidationFunction(form)" in this example) and have it do any custom validation you require.  If your funciton returns true (meaning your validation succeeded), the normal validation procedure will then run as usual.  If your function returns false, the validation procedure will stop immediately and the form will not be submitted.
 *
 *  Lastly, add "rules" to each of the input tags that require validation.  You do this by adding a "valid" attribute to the tag, and specifying a list of rules as its value.  For Example:
 *  <input type="text" name="name" value="value" valid="exists,email">
 *  The two rules specified for the above <input> tag are "exists" and "email", meaning that the user must fill something into this field, and that something must be in the form of an e-mail address.
 *  The order that rules are specified in does not matter.
 *
 *  That's all there is to it!  Valid rules are listed below, any JavaScript developer is welcome to add new rules by simply adding new "else if" statements to the processing portion of the script, I'm sure you can find it.
 *
 * List of Rules:
 *  exists:  If this rule is specified, an error will be reported if the field is left blank
 *  number:  If this rule is specified, an error will be reported if the value entered is not a number
 *  email:   If this rule is specified, an error will be reported if the value entered doesn't conform to standard e-mail address notation
 *  phone:   If this rule is specified, an error will be reported if the value entered doesn't conform to standard phone number representations
 *  date:    If this rule is specified, an error will be reported if the value entered doesn't conform to standard date representations
 *  choose:  This rule is for use with <select> boxes.  When it's specified it won't allow an option with a value of "choose" to be selected
 *  checked: This rule is for use with checkboxes.  When it is specified, the box must be checked in order to submit the form
 *  =x:      This rule lets you specify that what's entered in this field must be equal to what's entered in the field named "x" (useful for making sure that two password fields match, for instance)
 *  s=x,s<x,s>x: These rules allow you to control the number of characters entered into the field (think of "s" standing for "string").  You can specify that the number of characters must be equal to, less than, or greater then "x" (any number)
 *  n=x,n<x,n>x: These rules allow you to control the range of a number entered into the field (think of "n" standing for "number").  You can specify the number entered must be equal to, less than, or greater than "x" (any number)
 *
 * Note: if "exists" is not specified, then none of the other rules will report an error if the field is left blank
 *  i.e. if a field is specified as valid="exists,phone" then the user must enter a phone number
 *       if it's specified as valid="exists" then the user can enter anything, but can't leave it blank
 *       if it's specified as valid="phone" then the user can either leave the field blank or enter a phone number
 *
 * Enjoy!  Please feel free to send suggestions and feedback to andy at blacklightning dot net
 *
 * This module is free to re-distribute as long as this comment header remains intact.  If you make changes, I encourage you to let me know so I can include them as well.
 *
 */

IE = NS4 = NS6 = OT = false;
if (navigator.appName.match('Microsoft')) {
	IE = true;
} else if (document.layers) {
	NS4 = true;
} else if (navigator.appName.match('Netscape')) {
	NS6 = true;
} else {
	OT = true;
}

function validate(frm, func) {
	// if they passed a callback function and it returns false, then just return false now
	if ((func) && (!func(frm))) {
		return false;
	}

	// this will contain the message to alert after validation is complete
	totalmessage = "";

	// run through each element in the form and validate it
	for (i=0; i<frm.elements.length; i++) {
		temp = checkElement(frm,frm.elements[i]);
		// if element had an error, add it to the message
		if (temp != "") {
			totalmessage += temp + "\n";
		}
	}

	if (totalmessage == "") {
		// if total message is empty, everything validated successfully, return true
		return true;
	} else {
		// otherwise there must be at least one error, alert the error and rturn false
		alert(totalmessage);
		return false;
	}
}

function checkElement(frm,el) {
	// find the "valid" attribute of the current form element according to which browser is in use
	if (IE) {
		if (el.valid) {
			valid = el.valid;
		} else {
			return "";
		}
	} else if (NS6) {
		if (el.attributes.getNamedItem("valid") != null) {
			valid = el.attributes.getNamedItem("valid").nodeValue;
		} else {
			return "";
		}
	} else {
		// they're not using IE or NS6+, nothing we can do for them
		return "";
	}

	// create an array of rules for this element
	rules = new Array();
	rules = valid.split(",");

	// this will contain any error messages created by this element
	message = "";

	// run through the array of rules and check to make sure the element passes each of them
	// if an element doesn't pass a rule, tack an error to the end of the message variable
	for (j=0; j<rules.length; j++) {
		if (rules[j] == "exists") {
			if ((el.value == "") || (el.value == null)) {
				message += "\nYou must fill out the " + el.name + " field.";
			}
		} else if ((el.value == "") || (el.value == null)) {
			// do nothing, don't check the other rules if the field has no value
		} else if (rules[j] == "email") {
			if (!(el.value.match(/^[A-Za-z0-9_\-\.]+@[A-Za-z0-9_\-]+\.[A-Za-z0-9_\.\-]+$/))) {
				message += "\n" + capitalize(el.name) + " is not a valid e-mail address.";
			}
		} else if (rules[j] == "phone") {
			if (!(el.value.match(/\(?\d{3}\)?[ -\.]\d{3}[-\.]\d{4}/))) {
				message += "\n" + capitalize(el.name) + " must be a valid phone number of the form  xxx-xxx-xxxx, xxx.xxx.xxxx, or (xxx) xxx-xxxx.";
			}
		} else if (rules[j] == "number") {
			if (!(el.value.match(/^\d+$/))) {
				message += "\nYou must enter a number for the " + el.name + " field.";
			}
		} else if (rules[j] == "choose") {
			if (el.value == "choose") {
				message += "\nYou must make a selection from the list: " + el.name + ".";
			}
		} else if (rules[j] == "checked") {
			if (!(el.checked)) {
				message += "\nYou must check the " + el.name + " box to continue.";
			}
		} else if (rules[j] == "date") {
			if (!(el.value.match(/\d{1,2}[-\/\.]\d{1,2}[-\/\.]\d{4}/))) {
				message += "\n" + capitalize(el.name) + " must be a valid date of the form MM/DD/YYYY, MM.DD.YYYY, or MM-DD-YYYY.";
			}
		} else if (rules[j].charAt(0) == "=") {
			field = rules[j].substr(1);
			if (el.value != frm.elements[field].value) {
				message += "\nThe two fields " + el.name + " and " + frm.elements[field].name + " must be equal.";
			}
		} else if (rules[j].charAt(1) == "<") {
			type = rules[j].charAt(0);
			field = rules[j].substr(2);
			if (type == "n") {
				if ((el.value*1) >= (field*1)) {
					message += "\n" + capitalize(el.name) + " must be less than " + field + ".";
				}
			} else if (type == "s") {
				if (el.value.length >= field) {
					message += "\n" + capitalize(el.name) + " must have no greater than " + field + " characters.";
				}
			}
		} else if (rules[j].charAt(1) == ">") {
			type = rules[j].charAt(0);
			field = rules[j].substr(2);
			if (type == "n") {
				if (el.value <= field) {
					message += "\n" + capitalize(el.name) + " must be greater than " + field + ".";
				}
			} else if (type == "s") {
				if (el.value.length <= field) {
					message += "\n" + capitalize(el.name) + " must have no less than " + field + " characters.";
				}
			}
		} else if (rules[j].charAt(1) == "=") {
			type = rules[j].charAt(0);
			field = rules[j].substr(2);
			if (type == "n") {
				if (el.value != field) {
					message += "\n" + capitalize(el.name) + " must be exactly " + field + ".";
				}
			} else if (type == "s") {
				if (el.value.length != field) {
					message += "\n" + capitalize(el.name) + " must have exactly " + field + " characters.";
				}
			}
		} else {
			// this isn't a rule we know what to do with, sling an error message for the developer
			alert("Invalid Rule: " + rules[j] + "\nElement: " + el.name);
		}
	}
	return message;
}

// this function is used by the form validation procedure
// it just returns the passed in string with the first letter capitalized
function capitalize(str) {
	return ( (str.charAt(0).toUpperCase()) + str.substring(1) );
}

// the rest of the functions have nothing to do with the validation procedure and are safe to delete
function goNext(len,frm,next) {
	which = event.srcElement;
	key   = event.keyCode;
	if ((which.value.length >= len) && checkCode(key)) {
		document.getElementById(frm).elements[next].focus();
		return true;
	} else {
		return false;
	}
}
function goBack(frm,prev) {
	which = event.srcElement;
	key   = event.keyCode;
	if ((which.value.length <= 0) && checkCode(key)) {
		document.getElementById(frm).elements[prev].focus();
		return true;
	} else {
		return false;
	}
}

function checkCode(key) {
	flag = false;
	if ((key >= 48) && (key <= 57)) {
		flag = true;
	}
	if ((key >= 65) && (key <= 90)) {
		flag = true;
	}
	if ((key >= 96) && (key <=105)) {
		flag = true;
	}
	if (key == 8) {
		flag = true;
	}
	return flag;
}