/* ------------------------------------------------------------
 * PROJECT        : 
 * FILENAME       : jq.validateForm.js
 * ------------------------------------------------------------
 * LAST UPDATED   : 12 Feb 2010
 * ------------------------------------------------------------
 * AUTHOR(S)      : Kevin Scholl (ksscholl@magellanhealth.com)
 * ------------------------------------------------------------
 * NOTE(S)        : 
 * ------------------------------------------------------------ */

/*

SUMMARY

This plugin is applied to a form, creating a client-side validation step when the form is submitted. The validation verifies that all required fields contain data, that known data types (e.g., date, e-mail, phone, zip, among others) are correctly formatted, and that confirmation fields (i.e., passwords and e-mail addresses) match.

OPTIONAL SETTINGS

validateAt
A string specifying when the validation should occur. Options include "blur" (when a viable field loses focus), "submit" (whent he form is submitted), or "both". Default value is "both".

dFormat
A string denoted the  preferred date format. Default value is "mm/dd/yyyy". Other options are "mm-dd-yyyy", "m/d/y", or "m-d-y".

passMin
An integer specifying the minimum number of characters required for a password entry. Default value is 8.

msg_error
A string specifying the global error message displayed above the form on a submit attempt, if there are errors. Default value is "Please address the errors noted below...".

msg_warning
A string specifying the global warning message displayed above the form on a submit attempt, if there are warnings. Default value is "Please address the warnings noted below...".

USAGE

$("#FRM").jqValidate();
$("#FRM").jqValidate({ options });

where FRM is the ID of the form containing the element(s) to be validated.

EXAMPLE

$(document).ready(function() {
	$("#myForm").jqValidate({
		cols  :  60,
		rows  :  5,
		chars :  500, // enter "none" for no character counter
		instr : "Please provide an explanation below."
		});
	});

MINIMUM BROWSER SUPPORT

Apple Safari 3, Google Chrome 1, Microsoft Internet Explorer 6, Mozilla Firefox 3, Opera 9

*/

var HAS_ERRORS   = false;
var HAS_WARNINGS = false;
var SHOW_CHECKS  = true;
	
function jqResetForm(frm) {
	$("input:text, input:password, textarea, select").css("borderColor","#7F9DB9");
	$("#screenMsgs").find("ul").remove();
	$("span.msgLevelError, span.msgLevelWarn").remove();
	$("label.valid").removeClass("valid");
	$("fieldset li").removeClass("errorRow warningRow");
	HAS_ERRORS   = false;
  HAS_WARNINGS = false;
	}

function displayError(foo, theMsg) {
	removeValid(foo);
	$(foo).parent().addClass("errorRow").append("<span class=\"msgLevelError\">" + theMsg + "</span>");
	$(foo).siblings("input").not(".noValidation").css("borderColor","#C33");
	HAS_ERRORS = true;
	}

function displayWarning(foo, theMsg) {
	removeValid(foo);
	$(foo).parent().addClass("warningRow").append("<span class=\"msgLevelWarn\">" + theMsg + "</span>");
	$(foo).siblings("input").css("borderColor","#F93");
	HAS_WARNINGS = true;	
	}

function displayValid(foo) {
	$(foo).parent().removeClass("errorRow warningRow");
	$(foo).siblings("input").css("borderColor","#7F9DB9");
	$(foo).siblings("span.msgLevelError, span.msgLevelWarn").remove();
	if (SHOW_CHECKS == true)
	  $(foo).addClass("valid");
	}
	
function removeValid(foo) {
	$(foo).parent().removeClass("errorRow warningRow");
	$(foo).siblings("input").css("borderColor","#7F9DB9");
	$(foo).siblings("span.msgLevelError, span.msgLevelWarn").remove();
	$(foo).removeClass("valid");
	}

function validateRadio(foo) {
	rGrpName  = $(foo).attr("name");
	rGrpValue = $("input[name='" + rGrpName + "']:checked").val();
	if (rGrpValue == "" || rGrpValue == null)
		displayError($(foo).siblings("label"),"ERROR: Required field(s).");
	else
		displayValid($(foo).siblings("label"));
	}

function validateRequired(foo) {
	if ($(foo).val() == "" || $(foo).val() == null) {
		removeValid($(foo).siblings("label"));
		displayError($(foo).siblings("label"),"ERROR: Required field(s).");
		}
	else
		displayValid($(foo).siblings("label"));
	}
	
function validateCurrency(lbl) {
	var currValid = /^(\d{1,3},?(\d{3},?)*\d{3}(\.\d{0,2})?|\d{1,3}(\.\d{0,2})?|\.\d{1,2}?)$/;
	var curr      = $(lbl).siblings("input").val();
	if (curr != "")	{ // if currency is entered, check validity and formatting
		if (!currValid.exec(curr)) {
			displayWarning(lbl,"Contains illegal character(s), or is invalid format.");
			}
		else
			displayValid(lbl);
		}
	else if (!$(lbl).hasClass("required"))
		removeValid(lbl);
	}

function validateDate(lbl,dateFormat) {
	var dt = $(lbl).siblings("input").val();
	if (dt != "")	{ // if data is entered, check validity and formatting
		switch (dateFormat) {
			case "mm-dd-yyyy":  // format mm-dd-yyyy, ex. 02-06-2008
				datereg = /[0-9]{2}\-[0-9]{2}\-[0-9]{4}$/;
				break;
			case "m/d/y":  // format m/d/y, ex. 2/6/08
				datereg = /[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{2}$/;
				break;
			case "m-d-y":  // format m-d-y, ex. 2-6-08
				datereg = /[0-9]{1,2}\-[0-9]{1,2}\-[0-9]{2}$/;
				break;
			default:       // format mm/dd/yyyy, ex. 02/06/2008
				datereg = /[0-9]{2}\/[0-9]{2}\/[0-9]{4}$/;
				break;
			}
		if (!datereg.exec(dt)) {
			displayWarning(lbl,"Date must be formatted as " + dateFormat + ".");
			}
		else
			displayValid(lbl);
		}
	else if (!$(lbl).hasClass("required"))
		removeValid(lbl);
	}

function validateEmail(lbl) {
	var emailLegal   = /[\s\(\)\<\>\,\;\:\\\/\"\[\]]/;
	var emailValid   = /^.+@.+\..{2,}$/;
	var curr_eml = $(lbl).siblings("input").val();
	var prev_eml = curr_eml;
	var prev_lbl = $(lbl).parent().prev().find("label").html();
	if (prev_lbl.indexOf("Email") != -1 || prev_lbl.indexOf("E-mail") != -1)
		prev_eml   = $(lbl).parent().prev().find("input").val();	
	if (curr_eml != "")	{ // if data is entered, check validity and formatting
		$(lbl).siblings("span.msgLevelError, span.msgLevelWarn").remove();
		if (emailLegal.exec(curr_eml)) // check for illegal characters
			displayWarning(lbl,"E-mail address contains illegal character(s).");
		else if (!emailValid.exec(curr_eml)) // check for proper formatting
			displayWarning(lbl,"E-mail address must be of valid format.");
		else
			displayValid(lbl);
		}
	else if (!$(lbl).hasClass("required"))
		removeValid(lbl);
	if (   $(lbl).hasClass("valid") 
			&& $(lbl).parent().prev().find("label").hasClass("valid") 
			&& curr_eml != prev_eml) // check that emails match
		displayError(lbl,"ERROR: E-mail entries must match.");
	}

function validatePassword(lbl,passMin) {
	var curr_pw  = $(lbl).siblings("input").val();
	var prev_pw  = curr_pw;
	var prev_lbl = $(lbl).parent().prev().find("label").html();
	if (prev_lbl.indexOf("Password") != -1)
		prev_pw    = $(lbl).parent().prev().find("input").val();	
	if (curr_pw != "")	{ // if data is entered, check validity and formatting
		$(lbl).siblings("span.msgLevelError, span.msgLevelWarn").remove();
		if (curr_pw.length < passMin) // check password length
			displayWarning(lbl,"Password must be at least " + passMin + " characters.");
		else
			displayValid(lbl);
		}
	else if (!$(lbl).hasClass("required"))
		removeValid(lbl);
	if (   $(lbl).hasClass("valid") 
			&& $(lbl).parent().prev().find("label").hasClass("valid") 
			&& curr_pw != prev_pw) // check that passwords match
		displayError(lbl,"ERROR: Password entries must match.");
	}

function validateSSN(lbl) {
	var ssn = "" 
		+ $(lbl).parent().children("input")[0].value 
		+ $(lbl).parent().children("input")[1].value 
		+ $(lbl).parent().children("input")[2].value;
	// populate (optional) hidden field with concatenated data
	if ($(lbl).parent().children("input")[3])
		$(lbl).parent().children("input")[3].value = ssn;
	if (ssn != "") {
		if (ssn.length != 9 || isNaN(ssn))
			displayWarning(lbl,"SSN must contain 9 numbers.");
		else
			displayValid(lbl);
		}
	else if (!$(lbl).hasClass("required"))
		removeValid(lbl);
	}

function validateTelFax(lbl) {
	var num = "" 
		+ $(lbl).parent().children("input")[0].value 
		+ $(lbl).parent().children("input")[1].value 
		+ $(lbl).parent().children("input")[2].value;
	var num2 = num + $(lbl).parent().children("input")[3].value;
	// populate (optional) hidden field with concatenated data
	if ($(lbl).parent().children("input")[4])
		$(lbl).parent().children("input")[4].value = num2;
	if (num != "") {
		if (isNaN(num))
			displayWarning(lbl,"Number cannot contain alpha character(s).");
		else if (num.length != 10)
			displayWarning(lbl,"Number must be at least 10 digits (incl. area code).");
		else
			displayValid(lbl);
		}
	else if (!$(lbl).hasClass("required"))
		removeValid(lbl);
	}

function validateZIP(lbl) {
	var zip = "" 
		+ $(lbl).parent().children("input")[0].value 
		+ $(lbl).parent().children("input")[1].value;
	// populate (optional) hidden field with concatenated data
	if ($(lbl).parent().children("input")[2])
		$(lbl).parent().children("input")[2].value = zip;
	if (zip != "") {
		if (isNaN(zip))
			displayWarning(lbl,"Zip/Postal Code cannot contain alpha character(s).");
		else if (zip.length != 5 && zip.length != 9)
			displayWarning(lbl,"Zip/Postal Code must contain 5 or 9 numbers.");
		else
			displayValid(lbl);
		}
	else if (!$(lbl).hasClass("required"))
		removeValid(lbl);
	}

function validateStrict(lbl,num) {
	var str = $(lbl).parent().children("input").val();
	if (str != "") {
		if (isNaN(str))
			displayWarning(lbl,"Field may contain only numbers.");
		else if (str.length != num)
			displayWarning(lbl,"Field must contain " + num + " numbers.");
		else
			displayValid(lbl);
		}
	else if (!$(lbl).hasClass("required"))
		removeValid(lbl);
	}

// validate fields before submitting, display any errors/warnings
// usage: $("#elementID").jqValidateForm(settings);
jQuery.fn.jqValidate = function(settings) {
	settings = jQuery.extend({
		validateAt   : "both", // "blur" | "submit" | "both"
		dFormat      : "mm/dd/yyyy",
		passMin      :  8,
		msg_error    : "Please address the errors noted below...",
		msg_warning  : "Please address the warnings noted below..."
		}, settings);

	var MSG_ERROR    = "<li class=\"errorMsg\">" + settings.msg_error + "</li>";
	var MSG_WARNING  = "<li class=\"warningMsg\">" + settings.msg_warning + "</li>";
	
	$("input:file, input:password, input:text, textarea").blur(function(){
		$(this).val($.trim($(this).val()));
		});

	// CHECK FIELDS DURING DATA ENTRY
	
	if (settings.validateAt == "blur" || settings.validateAt == "both") {

		// verify that required fields have data
		$("label.required").each(function() {
			$(this).siblings("input:radio").blur(function(){
				validateRadio(this);
				});
			$(this).siblings("input:file, input:password, input:text, select, textarea").not(".noValidation").blur(function(){
				validateRequired(this);
				});
			});
		
		// verify strict number of characters
		$("label[rel$='strict']").each(function() {
			var num = parseInt($(this).attr("rel"));
			$(this).siblings("input").blur(function() {
				validateStrict($(this).siblings("label"),num);
				});
			});
		// verify formatting
		$("label:contains('Date'), label:contains('DOB')").each(function() {
			$(this).siblings("input").blur(function() {
				validateDate($(this).siblings("label"),settings.dFormat);
				});
			});
		$("label:contains('Email'), label:contains('E-mail')").each(function() {
			$(this).siblings("input").blur(function() {
				validateEmail($(this).siblings("label"));
				});
			});
		$("label:contains('Password')").each(function() {
			$(this).siblings("input").blur(function() {
				validatePassword($(this).siblings("label"),settings.passMin);
				});
			});
		$("label:contains('Social Security'), label:contains('SSN')").each(function() {
			$(this).siblings("input").blur(function() {
				validateSSN($(this).siblings("label"));
				});
			});
		$("label:contains('Telephone'), label:contains('Phone'), label:contains('Facsimile'), label:contains('Fax')").each(function() {
			$(this).siblings("input").blur(function() {
				validateTelFax($(this).siblings("label"));
				});
			});
		$("label:contains('Zip'), label:contains('ZIP'), label:contains('Postal')").each(function() {
			$(this).siblings("input").blur(function() {
				validateZIP($(this).siblings("label"));
				});
			});
		$("span.dollarsign").each(function() {
			$(this).siblings("input").blur(function() {
				validateCurrency($(this).siblings("label"));
				});
			});

		}

	// CHECK FIELDS ON FORM SUBMIT

	if (settings.validateAt == "submit" || settings.validateAt == "both") {

		this.submit(function() {
	
			// trim spaces from beginning and end of free-entry field values
			$(this).find("input:file, input:password, input:text, textarea").each(function(){	
				$(this).val($.trim($(this).val()));
				});
	
			// reset all error messages and indications
			jqResetForm($(this).attr("id"));
	
			// verify that required fields have data
			$("label.required").each(function() {
				$(this).siblings("input:radio").each(function(){
					validateRadio(this);
					});
				$(this).siblings("input:file, input:password, input:text, select, textarea").not(".noValidation").each(function(){
					validateRequired(this);
					});
				});
	
			// verify strict number of characters
			$("label[rel$='strict']").each(function() {
				var num = parseInt($(this).attr("rel"));
				if ( $(this).siblings().is("input") ) validateStrict(this,num);
				});
			// verify formatting
			$("label:contains('Date'), label:contains('DOB')").each(function() {
				if ( $(this).siblings().is("input") ) validateDate(this,settings.dFormat);
				});
			$("label:contains('Email'), label:contains('E-mail')").each(function() {
				if ( $(this).siblings().is("input") ) validateEmail(this);
				});
			$("label:contains('Password')").each(function() {
				if ( $(this).siblings().is("input") ) validatePassword(this,settings.passMin);
				});
			$("label:contains('Social Security'), label:contains('SSN')").each(function() {
				if ( $(this).siblings().is("input") ) validateSSN(this);
				});
			$("label:contains('Telephone'), label:contains('Phone'), label:contains('Facsimile'), label:contains('Fax')").each(function() {
				if ( $(this).siblings().is("input") ) validateTelFax(this);
				});
			$("label:contains('Zip'), label:contains('ZIP'), label:contains('Postal')").each(function() {
				if ( $(this).siblings().is("input") ) validateZIP(this);
				});
			$("span.dollarsign").each(function() {
				if ( $(this).siblings().is("input") ) validateCurrency($(this).siblings("label"));
				});
	
			// submit form or display errors
			if (!HAS_ERRORS && !HAS_WARNINGS) {
				// alert("Validation PASSED!"); return false;
				return true;
				}
			else {
				// $("ul#mainNav").parent().before("<div class=\"grid12\" id=\"screenMsgs\"></div>")
				$("#screenMsgs").append("<ul class=\"screenMsgs\"></ul>")
				if (HAS_ERRORS)
					$("#screenMsgs ul.screenMsgs").append(MSG_ERROR);
				if (HAS_WARNINGS)
					$("#screenMsgs ul.screenMsgs").append(MSG_WARNING);
				$("li.errorMsg, li.warningMsg")
				  .cornerz({background:"#F8F8F8", radius:8})
					.css("padding","6px 6px 6px 28px")
					.prepend("<span class=\"msgIcon\"></span>")
					;
				window.scrollTo(0,0);
				// alert("Validation FAILED!");
				return false;
				}
				
			});
		}
  };