/*
 * Inline Form Validation Engine, jQuery plugin
 * 
 * Copyright(c) 2009, Cedric Dugas
 * http://www.position-relative.net
 *	
 * Form validation engine witch allow custom regex rules to be added.
 * Licenced under the MIT Licence
 */

$(document).ready(function() {

	// SUCCESS AJAX CALL, replace "success: false," by:     success : function() { callSuccessFunction() }, 
	$("[class^=validate]").validationEngine({
		success :  false,
		failure : function() {}
	})
});



jQuery.fn.validationEngine = function(settings) {
	if($.validationEngineLanguage){					// IS THERE A LANGUAGE LOCALISATION ?
		allRules = $.validationEngineLanguage.allRules
	}else{
		allRules = {"required":{    			  // Add your regex rules here, you can take telephone as an example
							"regex":"none",
							"alertText":"&#187; This field is required",
							"alertTextCheckboxMultiple":"* Please select an option",
							"alertTextCheckboxe":"* This checkbox is required"},
						"length":{
							"regex":"none",
							"alertText":"&#187;  Between ",
							"alertText2":" and ",
							"alertText3": " characters allowed"},
						"minCheckbox":{
							"regex":"none",
							"alertText":"&#187;  Checks allowed Exceeded"},	
						"confirm":{
							"regex":"none",
							"alertText":"&#187;  Your field is not matching"},		
						"telephone":{
							"regex":"/^[0-9\-\(\)\ ]+$/",
							"alertText":"&#187;  Invalid phone number"},	
						"email":{
							"regex":"/^[a-zA-Z0-9_\.\-]+\@([a-zA-Z0-9\-]+\.)+[a-zA-Z0-9]{2,4}$/",
							"alertText":"&#187;  Invalid email address"},	
						"date":{
                             "regex":"/^[0-9]{4}\-\[0-9]{1,2}\-\[0-9]{1,2}$/",
                             "alertText":"* Invalid date, must be in YYYY-MM-DD format"},
						"onlyNumber":{
							"regex":"/^[0-9\ ]+$/",
							"alertText":"&#187;  Numbers only"},	
						"noSpecialCaracters":{
							"regex":"/^[0-9a-zA-Z]+$/",
							"alertText":"&#187;  No special caracters allowed"},	
						"onlyLetter":{
							"regex":"/^[a-zA-Z\ \']+$/",
							"alertText":"&#187;  Letters only"}
					}	
	}

 	settings = jQuery.extend({
		allrules:allRules,
		success : false,
		failure : function() {}
	}, settings);	


	$("form").bind("submit", function(caller){   // ON FORM SUBMIT, CONTROL AJAX FUNCTION IF SPECIFIED ON DOCUMENT READY
		if(submitValidation(this) == false){
			if (settings.success){
				settings.success && settings.success(); 
				return false;
			}
		}else{
			settings.failure && settings.failure(); 
			return false;
		}
	})
	$(this).not("[type=checkbox]").bind("blur", function(caller){loadValidation(this)})
	$(this+"[type=checkbox]").bind("click", function(caller){loadValidation(this)})
	
	var buildPrompt = function(caller,promptText) {			// ERROR PROMPT CREATION AND DISPLAY WHEN AN ERROR OCCUR
		/*start replacement of standard code */
		function insertAfter(newElement,targetElement) {
			//target is what you want it to go after. Look for this elements parent.
			var parent = targetElement.parentNode;
			
			//if the parents lastchild is the targetElement...
			if(parent.lastchild == targetElement) {
				//add the newElement after the target element.
				parent.appendChild(newElement);
				} else {
				// else the target has siblings, insert the new element between the target and it's next sibling.
				parent.insertBefore(newElement, targetElement.nextSibling);
				}
		}
		
		function objectBefore(p){
			do p = p.previousSibling;
			while (p && p.nodeType != 1);
			return p;
		}
		
		var parentItem = caller.parentNode;
			
		if (parentItem.nodeName == "DD")
		{//inserting into a dynamic list 
			try{
				var parentParent = $(caller).parent().parent();//the dl list
				var parentParentParent = $(parentParent).parent();//the entire div
				
				/* try and get the name of the item - this assumes the name will be a label within
				 * the dt that goes with the caller's dd */
				try{
					//attempt to update the label from 'this item' to the correct name 
					var label = ($(':first-child',parentParent));
					previousText = $(':first-child',label).attr('for');
					promptText = promptText.replace("This checkbox is required","Please agree to our terms and conditions before continuing");
					promptText = promptText.replace("This field",previousText);
					promptText = promptText.replace("Your field",previousText);
					promptText = promptText.replace("Your "," ");
					promptText = promptText.replace("?","");
					promptText = promptText.replace("_"," ");
				}
				catch (e){}//do nothing 	
				
				//set the attributes id and class - gives us an id and class to refer to the error text from other functions
				var divFormError = document.createElement('div');
				$(divFormError).attr('id',"error_" + $(caller).attr("name"));
				$(divFormError).addClass($(caller).attr("name"));
				$(divFormError).addClass("formError");//add style
				promptText = promptText.replace("_"," ");
				$(parentItem).append(divFormError);
				$(divFormError).html(promptText);
			}
			catch(e){}	
			
						
		}
		else 
		{//we're inserting into a normal list
			
			try{/* try and get the name of the item thats created the error - this assumes its a label directly before the input*/
				var previous = objectBefore(caller);
				var previousText = previous.firstChild.nodeValue;
				promptText = promptText.replace("This checkbox is required","Please agree to our terms and conditions before continuing");
				promptText = promptText.replace("This field",previousText);
				promptText = promptText.replace("Your field",previousText);
				promptText = promptText.replace("Your "," ");
				promptText = promptText.replace("?","");
				}
			catch (e){}//do nothing 
				
			
			var divFormError = document.createElement('li');
			$(divFormError).addClass("formError");
			$(divFormError).addClass($(caller).attr("name"));
			
			//set the attributes id - gives us an id to refer to the error text from other functions
			$(divFormError).attr('id',"error_" + $(caller).attr("name"));
			promptText = promptText.replace("_"," ");
			$(parentItem).append(divFormError);
			$(divFormError).html("<div>" + promptText + "</div>");
		}
		
			
	};
	var updatePromptText = function(caller,promptText) {	// UPDATE TEXT ERROR IF AN ERROR IS ALREADY DISPLAYED
		updateThisPrompt =  $(caller).attr("name")
		$("."+updateThisPrompt).find(".error_" + updateThisPrompt).html(promptText)
		
		//var errorText = document.getElementById(".error_" + updateThisPrompt);
		//alert(errorText.attributes["id"].value);
				
		/*callerTopPosition  = $(caller).offset().top;
		inputHeight = $("."+updateThisPrompt).height()
		
		callerTopPosition = callerTopPosition  -inputHeight -10
		$("."+updateThisPrompt).animate({
			top:callerTopPosition
		});*/
	
		/*end replacement of standard code */
	}
	var loadValidation = function(caller) {		// GET VALIDATIONS TO BE EXECUTED
		
		rulesParsing = $(caller).attr('class');
		rulesRegExp = /\[(.*)\]/;
		getRules = rulesRegExp.exec(rulesParsing);
		str = getRules[1]
		pattern = /\W+/;
		result= str.split(pattern);	
		
		var validateCalll = validateCall(caller,result)
		return validateCalll
		
	};
	var validateCall = function(caller,rules) {	// EXECUTE VALIDATION REQUIRED BY THE USER FOR THIS FILED
		var promptText ="";	
		var prompt = $(caller).attr("name");
		var caller = caller;
		isError = false;
		callerType = $(caller).attr("type");
		
		for (i=0; i<rules.length;i++){
			switch (rules[i]){
			case "optional": 
				if(!$(caller).val()){
					closePrompt(caller)
					return isError
				}
			break;
			case "required": 
				_required(caller,rules);
			break;
			case "custom": 
				 _customRegex(caller,rules,i);
			break;
			case "length": 
				 _length(caller,rules,i);
			break;
			case "minCheckbox": 
				 _minCheckbox(caller,rules,i);
			break;
			case "confirm": 
				 _confirm(caller,rules,i);
			break;
			default :;
			};
		};
	
		if (isError == true){
			
			if($("input[name="+prompt+"]").size()> 1 && callerType == "radio") {		// Hack for radio group button, the validation go the first radio
				caller = $("input[name="+prompt+"]:first")
			}
			($("."+prompt).size() ==0) ? buildPrompt(caller,promptText)	: updatePromptText(caller,promptText)
		}else{
			closePrompt(caller)
		}		
		
		/* VALIDATION FUNCTIONS */
		function _required(caller,rules){   // VALIDATE BLANK FIELD
			callerType = $(caller).attr("type")
			
			if (callerType == "text" || callerType == "password" || callerType == "textarea"){
				
				if(!$(caller).val()){
					isError = true
					promptText += settings.allrules[rules[i]].alertText+"<br />"
				}	
			}
			if (callerType == "radio" || callerType == "checkbox" ){
				callerName = $(caller).attr("name")
		
				if($("input[name="+callerName+"]:checked").size() == 0) {
					isError = true
					if($("input[name="+callerName+"]").size() ==1) {
						promptText += settings.allrules[rules[i]].alertTextCheckboxe+"<br />" 
					}else{
						 promptText += settings.allrules[rules[i]].alertTextCheckboxMultiple+"<br />"
					}
					
				}
			}	
			if (callerType == "select-one") { // added by paul@kinetek.net for select boxes, Thank you
					callerName = $(caller).attr("name");
				
				if(!$("select[name="+callerName+"]").val()) {
					isError = true;
					promptText += settings.allrules[rules[i]].alertText+"<br />";
				}
			}
			if (callerType == "select-multiple") { // added by paul@kinetek.net for select boxes, Thank you
					callerName = $(caller).attr("id");
				
				if(!$("#"+callerName).val()) {
					isError = true;
					promptText += settings.allrules[rules[i]].alertText+"<br />";
				}
			}
		}
		function _customRegex(caller,rules,position){		 // VALIDATE REGEX RULES
			customRule = rules[position+1]
			pattern = eval(settings.allrules[customRule].regex)
			
			if(!pattern.test($(caller).attr('value'))){
				isError = true
				promptText += settings.allrules[customRule].alertText+"<br />"
			}
		}
		function _confirm(caller,rules,position){		 // VALIDATE FIELD MATCH
			confirmField = rules[position+1]
			
			if($(caller).attr('value') != $("#"+confirmField).attr('value')){
				isError = true
				promptText += settings.allrules["confirm"].alertText+"<br />"
			}
		}
		function _length(caller,rules,position){    // VALIDATE LENGTH
		
			startLength = eval(rules[position+1])
			endLength = eval(rules[position+2])
			feildLength = $(caller).attr('value').length

			if(feildLength<startLength || feildLength>endLength){
				isError = true
				promptText += settings.allrules["length"].alertText+startLength+settings.allrules["length"].alertText2+endLength+settings.allrules["length"].alertText3+"<br />"
			}
		}
		function _minCheckbox(caller,rules,position){    // VALIDATE CHECKBOX NUMBER
		
			nbCheck = eval(rules[position+1])
			groupname = $(caller).attr("name")
			groupSize = $("input[name="+groupname+"]:checked").size()
			
			if(groupSize > nbCheck){	
				isError = true
				promptText += settings.allrules["minCheckbox"].alertText+"<br />"
			}
		}

		return(isError) ? isError : false;
	};
	var closePrompt = function(caller) {
		// CLOSE PROMPT WHEN ERROR CORRECTED
		closingPrompt = $(caller).attr("name");
		$("#error_" + closingPrompt).remove();
		$("."+closingPrompt).fadeTo("fast",0,function(){
			$("."+closingPrompt).remove()
		});
	};
	var submitValidation = function(caller) {	// FORM SUBMIT VALIDATION LOOPING INLINE VALIDATION
		var stopForm = false
		$(caller).find(".formError").remove()
		var toValidateSize = $(caller).find("[class^=validate]").size()
		
		$(caller).find("[class^=validate]").each(function(){
			var validationPass = loadValidation(this)
			return(validationPass) ? stopForm = true : "";	
		});
		if(stopForm){							// GET IF THERE IS AN ERROR OR NOT FROM THIS VALIDATION FUNCTIONS
			destination = $(".formError:first").offset().top - 100;
			$("html:not(:animated),body:not(:animated)").animate({ scrollTop: destination}, 1100)
			return true;
		}else{
			return false
		}
	};
};
