/*

*

* EasyAjax_Form Script

* 

* Copyright 2009 John Stevens.  

*

* Get this script at Themeforest.net : 

* http://themeforest.net/item/easyajax_form/57497

*

* Requires jQuery 1.3+

*

* version 5.5

*

*/



EasyAjax_Form = function () {

	

	/*

	 * Configuration Options

	 */

	 

	var config = {};

	

	config = {

		errorMsgs: {

			required: '*Required',

			email: 'Please enter a valid email address.',

			phone: 'Example: 111-222-3333.',

			lengthInput: '50 or fewer characters for this field.',

			lengthText: '5000 or fewer characters for this field.',

			ruHuman: 'Incorrect single digit number',

			ajaxTimeout: 'An error occurred when saving your request, please try again.' +  

				'If this problem persists, please try again later.'				  

		},

		ruHuman: {

			answer: '4'

		},

		fades: {

			validation: 200, /*The time it takes validation messages to fade in.*/

			ajax: 10 /*The time it takes ajax communicaiton to fade in and out.*/

		},

		ajaxTimers: {

			pause: 10, /*Ajax message sent to server is delayed by this amount.*/

			timeout: 10000 /*Will return the ajax_timeout error message if no server response in this amount of time*/

		},

		valClasses: { /*Customize the class names used in the HTML that will trigger Validation actions in this script.*/

			requiredField: 'REQUIRED',

			emailField: 'EMAIL',

			phoneField: 'PHONE',

			charLengthInput: 'LENGTH_INPUT',  /*Default is 50 characters, useful for regular input fields*/

			charLengthText: 'LENGTH_TEXT',    /*Useful for text areas.  Default is 5000 characters (about 800 words) */

			optionalField: 'OPTIONAL',

			ruHumanField:'RU_HUMAN'

		},

		formClasses: {   /*Customize the class names used in the HTML that will trigger Ajax actions in this script*/

			validateOnly: 'VALIDATE',

			ajaxOnly: 'AJAX',

			disable: 'DISABLE',

			choose: 'CHOOSE'

		},

		cssSelectors: { /*Customize class & ID names used in the CSS file.  Make sure to update the CSS file if you change these*/

			validationPass: 'PASS',  

			validationFail: 'FAIL',

			ajaxLoading: '#FORM_LOAD', /* # needed as it is used as a jQuery selector in this script, this is an ID.*/

			ajaxTimeout: "AJAX_TIMEOUT", /*Reccomend keep double quotes*/

			ajaxResponse: "AJAX_RESPONSE" /*Reccomend keep double quotes*/

		},

		regExps: { /*Contains regular expressions used later in the script*/

			email: /^\w+([\.\-]?\w+)*@\w+([\.\-]?\w+)*(\.\w{2,3})+$/,

			phone: /^\(?(\d{3})\)?[\.\-\/ ]?(\d{3})[\.\-\/ ]?(\d{4})$/

		}, 

		charLimit: {

			input: 50,

			textArea: 5000

		}

	};

	

	/*

	 * I use the jQuery $.each function for looping in this script.  I find it easier to read and work with

	 * and it does all the necessary type checking, etc behind the scenes.  In my mind it is well worth the 

	 * slightly, slightly, slightly slower processing.  In my experience I've seen no perceptible performance 

	 * difference. 

	 *

	 * See the following url for more information on $.each : 

	 * http://docs.jquery.com/Utilities/jQuery.each

	 */

	

	/*

	 * General Helper Functions

	 */

	

	function appendSpan(allFields) {   /*Appends an empty span element after each form field found in the HTML*/

		$.each(allFields, function (i, field) {     

			$('<span></span>').insertAfter(this);		 			 

		});

	}

	function hasClassArray_maker(currentField, formLevel) { /*Populates the HasClass Array for a given form field when needed.*/

		var hasClassArray = [],

			Classes = config.valClasses,

			j = 0; 

		if (formLevel) { /*Makes the second paramenter optional. Is passed true from the init function, null for validation uses*/

			Classes = config.formClasses;

		}

		$.each(Classes, function (k, currentClass) {       

			if ($(currentField).hasClass(currentClass)) {

				hasClassArray[j] = currentClass;

				j = j + 1;

			}	

		});	

		return hasClassArray;

	}

	function fieldProperty_maker(currentField) {

		var valTypes = config.valClasses,  /*The keys of valClasses object will describe all possible field properties*/

			fieldProperties = {}; 

		$.each(valTypes, function (l, currentType) {

			fieldProperties[l] = false;								   

		});

		return fieldProperties;

	}

	function calcFieldProperties(fieldProperties, hasClassArray) { /*Based on classes present, determines what validation is indicated*/

		$.each(hasClassArray, function (m, currentClass) {   	

			switch (currentClass) {            

			case config.valClasses.emailField:    

				fieldProperties.emailField = true;

				break;

			case config.valClasses.requiredField:

				fieldProperties.requiredField = true;

				break;										

			case config.valClasses.phoneField:

				fieldProperties.phoneField = true;

				break;

			case config.valClasses.charLengthInput:

				fieldProperties.charLengthInput = true;

				break;									

			case config.valClasses.charLengthText:

				fieldProperties.charLengthText = true;

				break;

			case config.valClasses.ruHumanField: 

				fieldProperties.ruHumanField = true;

			default:

				break;

		    }	

	    }); 

	    return fieldProperties;	

	}

	function boolObjDecoder(boolObj, retFalseIf) { /*Returns false if any value in the object is equal to the second paramenter (true or false)*/

		var x = 0; 

		$.each(boolObj, function (p, currentBool) {

			var booleanKeepGoing;					  

			if (currentBool === retFalseIf) {

				x = 1;

				booleanKeepGoing = false;

			}

		}); 

		if (x === 1) {

			return false; 

		}

		return true;

	}

	/*

 	 * Ajax Helper Functions

     */	

	 

	function ajaxError(daddy, thisForm) {  /*Is called in the case of a timeout error*/

		$(config.cssSelectors.ajaxLoading).fadeOut(config.fades.ajax / 4, function () {

			$(this).remove();

		});  

		$('<div id="Response">' +

		  	'<p class=' + config.cssSelectors.ajaxTimeout + '>' + config.errorMsgs.ajaxTimeout + '</p>' +

			'</div>').hide().appendTo(daddy).fadeIn(config.fades.ajax);

		$('<div id="Refresh">' +

		  	'<p><a href ="#">Click here</a> to re-enter form information.</p>' +

			'</div>').hide().appendTo(daddy).fadeIn(config.fades.ajax);

		$('#Refresh').click(function () {

			$('#Response, #Refresh').fadeOut(config.fades.ajax / 2, function () {

				$('#Response, #Refresh').remove();										  

				$(thisForm).fadeIn(config.fades.ajax / 2);											  

			});

			return false;

		});

		return true;

	}

	function ajaxSuccess(serverResponse, daddy, thisForm) {

		$(config.cssSelectors.ajaxLoading).fadeOut(config.fades.ajax / 4, function () {

			$(this).remove();

		});		   

		$('<div id="Response">' +

			'<p class=' + config.cssSelectors.ajaxResponse + '>' + serverResponse + '</p>' +

			'</div>').hide().appendTo(daddy).fadeIn(config.fades.ajax);  

		$('#Refresh').click(function () {   

			$('#Response, #Refresh').fadeOut(config.fades.ajax / 2, function () {

				$('#Response, #Refresh').remove();										  

				$(thisForm).fadeIn(config.fades.ajax / 2);	

			});

			return false;

		});	

		return true;

	} 

	

	/*

	 * Validation Helper Functions

	 */

	 

	function requiredValidator(currentField, input) {

		var thisError = config.errorMsgs.required,            

			hasReqErr = false;  

		if (input === '') { 

			$(currentField).next().removeClass(config.cssSelectors.validationPass);

			$(currentField).next().hide().addClass(config.cssSelectors.validationFail).

				text(thisError).fadeIn(config.fades.validation);

			hasReqErr = true;  

		}	

		return hasReqErr;    

	}

	function emailValidator(currentField, input) {

		var thisError = config.errorMsgs.email,            

			hasEmailErr = false,

			reEmail = config.regExps.email; 

		if (!reEmail.test(input)) {                          

			$(currentField).next().removeClass(config.cssSelectors.validationPass);

			$(currentField).next().hide().addClass(config.cssSelectors.validationFail).

				text(thisError).fadeIn(config.fades.validation);

			hasEmailErr = true;  

		}	 

		return hasEmailErr;   		

	}

	function phoneValidator(currentField, input) {

		var thisError = config.errorMsgs.phone,            

			hasPhoneErr = false,

			rePhone = config.regExps.phone; 

		if (!rePhone.test(input)) {                          

			$(currentField).next().removeClass(config.cssSelectors.validationPass);

			$(currentField).next().hide().addClass(config.cssSelectors.validationFail).

				text(thisError).fadeIn(config.fades.validation);

			hasPhoneErr = true;  

		}	 

		return hasPhoneErr;   		

	}

	function inputLengthValidator(currentField, input) {

		var thisError = config.errorMsgs.lengthInput,            

			hasInputLengthErr = false;

		if (input.length > config.charLimit.input) {                          

			$(currentField).next().removeClass(config.cssSelectors.validationPass);

			$(currentField).next().hide().addClass(config.cssSelectors.validationFail).

				text(thisError).fadeIn(config.fades.validation);

			hasInputLengthErr = true;  

		}	 

		return hasInputLengthErr; 		

	}

	function textLengthValidator(currentField, input) {

		var thisError = config.errorMsgs.lengthText,            

			hasTextLengthErr = false;

		if (input.length > config.charLimit.textArea) {                          

			$(currentField).next().removeClass(config.cssSelectors.validationPass);

			$(currentField).next().hide().addClass(config.cssSelectors.validationFail).

				text(thisError).fadeIn(config.fades.validation);

			hasTextLengthErr = true;  

		}	 

		return hasTextLengthErr; 

	}

	function ruHumanValidator (currentField, input){ 

		var thisError = config.errorMsgs.ruHuman,

			hasRuHumanErr = false;

		if(input !== config.ruHuman.answer){

			$(currentField).next().removeClass(config.cssSelectors.validationPass);

			$(currentField).next().hide().addClass(config.cssSelectors.validationFail).

				text(thisError).fadeIn(config.fades.validation);

			hasRuHumanErr = true;	

		}

		return hasRuHumanErr;

	}

	function fieldValidator(fieldProperties, input, currentField) {  /*This function is called by the Main Functions (below) and in turn calls the specific validation types (above)*/

		var errorObject = {}; 

		errorObject = {

			requiredError: false,

			emailError: false, 

			phoneError: false,

			inputLengthError: false,

			textLengthError: false,

			ruHumanError: false

		};

		if (fieldProperties.requiredField) {

			errorObject.requiredError = requiredValidator(currentField, input);

			if (errorObject.requiredError) {

				return errorObject;

			}	

		}  

		if (fieldProperties.emailField) {

			errorObject.emailError = emailValidator(currentField, input);

			if (errorObject.emailError) {

				return errorObject;	

			}

		}

		if (fieldProperties.phoneField) {

			errorObject.phoneError = phoneValidator(currentField, input);

			if (errorObject.phoneError) {

				return errorObject;	

			}

		}

		if (fieldProperties.charLengthInput) {

			errorObject.inputLengthError = inputLengthValidator(currentField, input);

			if (errorObject.inputLengthError) {

				return errorObject;

			}

		}

		if (fieldProperties.charLengthText) {

			errorObject.textLengthError = textLengthValidator(currentField, input);

			if (errorObject.textLengthError) {

				return errorObject;	

			}

		}

		if (fieldProperties.ruHumanField) {

			errorObject.ruHumanError = ruHumanValidator(currentField, input);

			if (errorObject.ruHumanError) {

				return errorObject;	

			}

		}

		return errorObject;	

	}

	function greenChecker(currentField) { /*Adds a green check if validation passes*/

		$(currentField).next().removeClass('FAIL');

		if ($(currentField).next().hasClass("PASS")) {

			return;

		}

		$(currentField).next().hide().addClass("PASS").text('').fadeIn(config.fades.validation);	

	}



	

	/*

	 * Main Functions

	 */

	 

	function validate_onBlur(formID, allInputs) { 

		$(allInputs).blur(function () {

			var allValClasses = {}, 

				currentField = this,     

  				input = $(currentField).val(),		

  				blankOptInput = false,

				hasClassArray = [], 

				fieldProperties = {}, /*An object of booleans describing what validation is needed for this field.*/

				validationTracker = {},

				validationResults = true; /*Initializes the validationResults with a true (as in validation passes)*/	

			allValClasses = config.valClasses;	

			hasClassArray = hasClassArray_maker(currentField); /*Holds which classes from config.valClasses are present in this form element*/

			fieldProperties = fieldProperty_maker(currentField);  /*Initializes all field properties with false*/			

			if (input === '' && $(currentField).hasClass(config.valClasses.optionalField)) { /*Determine if field is blank & optional*/ 

				blankOptInput = true;

			}

			if (hasClassArray.length !== 0) {

				fieldProperties = calcFieldProperties(fieldProperties, hasClassArray); /*Based on classes present, determines what validation is indicated*/

				if (!blankOptInput) {

					validationTracker = fieldValidator(fieldProperties, input, currentField); /*validationTracker will contain a true for one of its values if there are any validation errors.*/

				}

				validationResults = boolObjDecoder(validationTracker, true); /*validationResults will be false if the validationTracker object contains any value of true (if a valdiation error is returned)*/

			}

			if (validationResults) { /*if true, give a green check to this field*/ 

				greenChecker(currentField);

			} 	

		});		 

	}

	function validate_onSubmit(formID, allFields) { /*See the validate_onBlur function for more notes. Only new stuff is noted here.*/

		$("" + formID + "").submit(function () { 							

			var allValClasses = {},	   

		   		allGood = [], /*Will be populated with booleans for each field. If all true, the form passes validation*/

				formValid = false;

			allValClasses = config.valClasses;	

			$.each(allFields, function (r, currentField) { /*For each form field, do the following (run validation)*/				     

				var	input = $(currentField).val(),		

  					blankOptInput = false,

					hasClassArray = [], 

					fieldProperties = {}, 

					validationTracker = {},

					validationResults = true; 

				allGood[r] = false; 

				hasClassArray = hasClassArray_maker(currentField); 

				fieldProperties = fieldProperty_maker(currentField);   

				if (input === '' && $(currentField).hasClass(config.valClasses.optionalField)) {  

					blankOptInput = true;

				}

				if (hasClassArray.length !== 0) {

					fieldProperties = calcFieldProperties(fieldProperties, hasClassArray); 

					if (!blankOptInput) {

						validationTracker = fieldValidator(fieldProperties, input, currentField); 

					}

					validationResults = boolObjDecoder(validationTracker, true); 

				}

				if (validationResults) { /*if true, give a green check to this field*/ 

					allGood[r] = true;

					greenChecker(currentField);

				} 						   

			});

			formValid = boolObjDecoder(allGood, false);

			if (formValid) {

				return true; 

			}

			return false;

		});

		

	}

	function ajaxOnly(formID, allFields) { 

		$("" + formID + "").submit(function () { 						

			var thisForm = this,					  

		  		toFile = this.action,           

		  		daddy = $(this).parent(),  	

		 		dataString = $(this).serialize();  /*jQuery function to make form data as portable as possible*/

		    $(thisForm).fadeOut(config.fades.ajax, function () {   

				$("<div id='FORM_LOAD'/>").hide().appendTo(daddy).show();   	 									  

			});	  							

			$.ajax({

				type: "post",

				url: toFile,

				data: dataString,

				timeout: config.ajaxTimers.timeout,                   

				error: function (XMLHttpRequest, timeout) { /*Run the following in the event of a timeout error*/

					setTimeout(function () {

						ajaxError(daddy, thisForm);	

					}, config.fades.ajax + 10);  /*Timeout here to take the fade into account, otherwise the loading div may not be removed*/

				},

				success: function (serverResponse) {           

					setTimeout(function () { /*Response function delayed in order to show the loading icon for a perceptible amount of time*/

						ajaxSuccess(serverResponse, daddy, thisForm);

					}, config.fades.ajax + config.ajaxTimers.pause);  

				}

			});

			return false; 	

		});

	}

	function validatePlusAjax_onSubmit(formID, allFields) { /*See the above main functions for more notes, only new stuff is noted here.*/

		$("" + formID + "").submit(function () { 							

			var allValClasses = {},	   

		   		allGood = [], 

				formValid = false,

				thisForm = this, /*Start: Ajax Variables*/

				toFile = this.action,           

		  		daddy = $(this).parent(),  	

		 		dataString = $(this).serialize(); /*End: Ajax Variables*/

			allValClasses = config.valClasses;	

			$.each(allFields, function (r, currentField) { 

				var	input = $(currentField).val(),	 /*Start: Validation variables*/   	

  					blankOptInput = false,

					hasClassArray = [], 

					fieldProperties = {}, 

					validationTracker = {},

					validationResults = true; /*End: Validation variables*/					  

				allGood[r] = false; 

				hasClassArray = hasClassArray_maker(currentField); 

				fieldProperties = fieldProperty_maker(currentField);   

				if (input === '' && $(currentField).hasClass(config.valClasses.optionalField)) {  

					blankOptInput = true;

				}

				if (hasClassArray.length !== 0) {

					fieldProperties = calcFieldProperties(fieldProperties, hasClassArray); 

					if (!blankOptInput) {

						validationTracker = fieldValidator(fieldProperties, input, currentField); 

					}

					validationResults = boolObjDecoder(validationTracker, true); 

				}

				if (validationResults) { /*if true, give a green check to this field*/ 

					allGood[r] = true;

					greenChecker(currentField);

				} 						   

			});

			formValid = boolObjDecoder(allGood, false);

			if (formValid) {

			    $(thisForm).fadeOut(config.fades.ajax, function () {   

					$("<div id='FORM_LOAD'/>").hide().appendTo(daddy).show();   	 									  

				});	

				$.ajax({

					type: "post",

					url: toFile,

					data: dataString,

					timeout: config.ajaxTimers.timeout,                   

					error: function (XMLHttpRequest, timeout) { 

						setTimeout(function () {

							ajaxError(daddy, thisForm);	

						}, config.fades.ajax + 10);

					},

					success: function (serverResponse) {           

						setTimeout(function () {

							ajaxSuccess(serverResponse, daddy, thisForm);

						}, config.fades.ajax + config.ajaxTimers.pause);  

					}

				});				 

			}

			return false;

		});		

		

	}

	

	/*

	 * Class Reader Functions: <form> Element

	 *

	 * These read the class attribute of the <form> tag to determine which

	 * main functions above will be utilized.

	 */

	 

	function chooseID(formID) {  /*Called in the case where the user chooses the form by ID*/

		var allFields;

		if (formID) {

			formID = '#' + formID;

		} else {

			formID = "form";

		}

		allFields = $('' + formID + ' :input:not(:submit):not(:button):not(:image)'); 

		if ($(formID).hasClass(config.formClasses.validateOnly)) {	

			validate_onBlur(formID, allFields); 

			validate_onSubmit(formID, allFields);

			return;

		} else if ($(formID).hasClass(config.formClasses.ajaxOnly)) {

			ajaxOnly(formID, allFields);

			return;

		} else {

			validate_onBlur(formID, allFields); 

			validatePlusAjax_onSubmit(formID, allFields);

			return;

		}

	}

	function init() {

		var formID = "form", 

			allFields = $('' + formID + ' :input:not(:submit):not(:button):not(:image)'); 

		appendSpan(allFields); /*If you are using Ajax Only, this line can be deleted.*/

		if ($(formID).hasClass(config.formClasses.disable)) {

			return;	

		} else if ($(formID).hasClass(config.formClasses.choose)) {

			return;		

		} else if ($(formID).hasClass(config.formClasses.validateOnly)) {	

			validate_onBlur(formID, allFields); 

			validate_onSubmit(formID, allFields);

			return;

		} else if ($(formID).hasClass(config.formClasses.ajaxOnly)) {

			ajaxOnly(formID, allFields);

			return;

		} else {

			validate_onBlur(formID, allFields); 

			validatePlusAjax_onSubmit(formID, allFields);

			return;

		}

	}

	

	/*

	 * EA_Form Public Pointers

	 *

	 * These pointers are used for access from the outside. 

	 * Notice the name changes for outside access.

	 */

	

	return {

		CustomMessages: config.errorMsgs,

		CustomFades: config.fades,

		CustomTimers: config.ajaxTimers,

		CustomCharLimits: config.charLimit,

		Go: init, 

		ByID: chooseID

	}; 

}(); /*End EasyAjax_Form Function*/



$(function () {

	EasyAjax_Form.Go();

});


