/* 
   
Author: Paul Meisel (pmeisel@cybersquad.net)

If you wish to use this code, please copy the code to your server first.
Also leave in this comment to give me credit for my work. 

Latest version: http://javascript.cybersquad.net/for-validation.js

Created on: 2007-05-30

*************************************

How to use:

example: http://javascript.cybersquad.net/form-validation-test.html
On the form element simply add onsubmit="return validate_form(this);"

On any element you want validated add the attribute validate and give the attribute the value you want to appear in the alert.  
The other option is to add rq to the end of the elements id (to turn this feature off add NotByID = true to the form tag.


Also you need to define a style called .Invalid ... usually something like this:

.Invalid {
background-color: red;
}


Note: Firefox doesn't let you change the background-color of a checkbox or radio button.  



What will happen:

When a user submits the form the browsers will test all the fields to be validated and alert will pop up telling the user what fields still need to be filled out.  The fields will get a new class (Invalid), and the first unfinished field will be selected.  
When the user selects a field the class will revert back to the original state.

Added Bonus:
By default users will not be able to double submit the form.  
An element can be set as a confirm for another element (IE Password, and Password Confirm can be made to need the same values)



**************************************

New form attributes:
NotByID
    values: true/false
    default: false
    purpose: if false inputs with rq at the end will be tested

doubleSubmitType
    values: alert/confirm/submit
    default: alert
    purpose: if someone double submits the form depending on the value different things can happen
             alert: a pop up tells them to wait for the form to finish submitting
	     confirm: a message pops up asking if they want to resubmit
	     submit: the form is resubmitted
	     
doubleSubmitMessage
     values: any
     default: depends on the doubleSubmitType
     purpose: acts as the message for doubleSubmitType


New Element attributes:
validate
     purpose: This tells the form validator that the element can not be null and what the error message should be

confirm
    value: the name of a different form element
    purpose: this tells the validator that this element and the other element should have the same value

confirm_error
    purpose: when the elements don't match values the value of this parameter will be add to the alert
     

************************************

Tested on:
   IE 7
   Firefox 2

*/


function validate_form(form) {
	 var elements = form.elements;
	 var element;

	 
	 if (form.getAttribute('NotByID') == true && form.getAttribute('NotByID') != "false") {
	     var byID_p = false;
	 } else {
	     var byID_p = true;
	 }

	 var doubleSubmitType = "alert";
	 var doubleSubmitMessage = "";
	 
	 if (form.getAttribute('doubleSubmitType') != undefined) {
	     doubleSubmitType = form.getAttribute('doubleSubmitType');
	 }

	 
	 // This could be an else for the next if statement, but I need to test to make sure doubleSubmitType is one of the 3 choices.  So I decided to leave it this way
	 switch (doubleSubmitType) {
	 case "alert":
	     doubleSubmitMessage = "This form has already been submitted, please wait.";
	     break;
	 case "confirm":
	     doubleSubmitMessage = "This form has already been submitted, do you wish to resubmit it?";
	     break;
	 case "submit":
	     break;
	 default:
	     alert(doubleSubmitType + " is an unknown doubleSubmitType");
	     return false;
	 }
	 
	 if (form.getAttribute('doubleSubmitMessage') != undefined) { 
	     doubleSubmitMessage = form.getAttribute('doubleSubmitMessage');
	 }


	 if (form.submitted == true) {
	     switch (doubleSubmitType) {
	     case "alert":
		 alert(doubleSubmitMessage);
		 return false;
		 break;
	     case "confirm":
		 if (!confirm(doubleSubmitMessage)) {
		     return false;
		 }
		 break;
	     }
	 }

	 var errors = 0;
	 var blank_errors = "";
	 var general_errors = "";
	 var validate_name;
	 var element_hasAttribute_p;
	 var firstElement;

	 var warnings = "";
	 function invalid (element, type) {
	     // Marks an item as invalid, adds it to the error message, also changes it's style.
	     errors++;
	     if (errors == 1) {
		 firstElement = element;
	     }
	     switch (type) {
	     case "blank":
		 blank_errors += "   " + validate_name + "\n";                   
		 break;
	     case "confirm":
		 general_errors += element.getAttribute("confirm_error") + "\n\n";                   
		 break;
	     }
	     if (element.preValidationClassName == undefined) {
		 element.preValidationClassName = element.className;
	     }
		 element.className += " Invalid";
	     if (element.preValidationOnFocus == undefined) {
		 if (element.onfocus == undefined) {
		     element.preValidationOnFocus = "";
		 } else {
		     element.preValidationOnFocus = element.onfocus;
		 }
	     }
	     element.onfocus=function onfocus(event) {
		 this.className = this.preValidationClassName;
		 this.onfocus = this.preValidationOnFocus;
		 if (this.preValidationOnFocus != "") {
		     this.preValidationOnFocus(event);
		 }
	     };
	 }


	 var radio_test = new Array();
	 for (var i = 0; i < elements.length; i++) {
	     element = elements[i];
	     if (element.hasAttribute) {
		 element_hasAttribute_p = element.hasAttribute('validate');		 
	     } else {
		 
		 element_hasAttribute_p = (element.getAttribute('validate') != null);		 
	     }


	     if (element_hasAttribute_p || (byID_p && element.id && element.id.substring(form[i].id.length - 2) == "rq")) {

		 if (!element_hasAttribute_p) {
		     validate_name = element.id.substring(0, form[i].id.length - 2);
		     
		     element.setAttribute('validate', validate_name);
		 } else {
		     validate_name = element.getAttribute('validate');
		 }
		 // alert(element.type);
		 switch (element.type.toLowerCase()) {
		 case "checkbox":

		     if (!element.checked) {
			 invalid(element, 'blank'); 
		     }
		     break;
		 case "radio":
		     /*
		       Radio buttons are different elements, but have the same name.
		       We want to return an error, and only one error, only if none of the radio buttons, with a given name, are selected
		     */
		     if (radio_test[element.name]) {
			 break;
		     } else {
			 radio_test[element.name] = true;
		     }
		     var radios = form[element.name];
		     if (radios.length) {
			 var found_value_p = false;
			 for (var j = 0; j < radios.length; j++) {
			     var radio = radios[j];
			     if (radio.checked) {
				 found_value_p = true;
			     }
			 }
			 if (!found_value_p) {
			     invalid(element, 'blank');
			     for (var j = 0; j < radios.length; j++) {
				 var radio = radios[j];
				 if (radio.preValidationClassName == undefined) {
				     radio.preValidationClassName = radio.className;
				 }
				 radio.className += " Invalid";
				 if (radio.preValidationOnFocus == undefined) {
				     if (radio.onfocus == undefined) {
					 radio.preValidationOnFocus = "";
				     } else {
					 radio.preValidationOnFocus = radio.onfocus;
				     }
				 }
				 radio.onfocus=function onfocus(event) {
				     var radios = this.form[this.name];
				     for (var k = 0; k < radios.length; k++) {
					 var radio = radios[k];
					 radio.className = radio.preValidationClassName;   
					 radio.onfocus = this.preValidationOnFocus;
				     }
				     if (this.preValidationOnFocus != "") {
					 this.preValidationOnFocus(event);
				     }
				     
				 }
			     }
			     
			 }
		     } else {
			 // This means there is only 1 radio button, and treat it as a checkbox
			 if (!element.checked) {
			     invalid(element, 'blank');
			 }
		     }

		     break;
		 case "select-one":
		     if (element[element.selectedIndex].value == "") {
			 invalid(element, 'blank');
		     }
		     break;
		 case "text":
		 case "textarea":
		 case "password":
		 case "file":
		     if (element.value.replace(/\s*/g, '') == "") {
			 invalid(element, 'blank');
		     }
		     break;
		 default: 
		     if (element.value == "") {
                         invalid(element, 'blank');
                     }
		     warnings += "Got element " + validate_name + " with type \\\"" + element.type + "\\\" and do not know what to do with it... Treated as a textfield";
		 }
	     } 
	     if (element.getAttribute('confirm')) {
		 switch (element.type.toLowerCase()) {    
		 case "text":
                 case "textarea":
                 case "password":
                 case "file":
                     if (form[element.getAttribute('confirm')] && element.value != form[element.getAttribute('confirm')].value) {
                         invalid(element, 'confirm');
                     }
                     break;
		 }
	     }
	 }
	 if (errors > 0) {
	     if (blank_errors) {
		 general_errors += "The Following Fields need to be filled out: \n\n"  + blank_errors;
	     }
	 alert(general_errors);
	     if (firstElement) {
		 firstElement.focus();
	     }
	     if (warnings != "") {
		 setTimeout("throw Error('" + warnings + "')", 0);
	     }
	     return false;
	 }
	     if (warnings != "") {
		 setTimeout("throw Error('" + warnings + "')", 0);
	     }
	     form.submitted = true;
	 return true;
} 
