/*
 * 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
 */

var formCloseDenied = new Array();

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":"* This field is required",
					"alertTextCheckboxMultiple":"* Please select an option",
					"alertTextCheckboxe":"* This checkbox is required"},
			    "length":{
					"regex":"none",
					"alertText":"*Between ",
					"alertText2":" and ",
					"alertText3": " characters allowed"},
			    "minCheckbox":{
					"regex":"none",
					"alertText":"* Checks allowed exceeded, maximum ",
					"alertText2": " allowed"},	
			    "maxCheckbox":{
					"regex":"none",
					"alertText":"* Checks allowed not reached, minimum ",
					"alertText2":" needed"},	
			    "confirm":{
					"regex":"none",
					"alertText":"* Your field is not matching"},		
			    "telephone":{
					"regex":"/^[0-9\-\(\)\ ]+$/",
					"alertText":"* Invalid phone number"},	
			    "email":{
					"regex":"/^[a-zA-Z0-9_\.\-]+\@([a-zA-Z0-9\-]+\.)+[a-zA-Z]+$/",
					"alertText":"* Invalid email address"},	
			    "date":{
                                    	"regex":"/^[0-3][0-9][\/][0-1][0-9][\/][0-9]{4}$/",
                            	 	"alertText":"* Invalid date, must be in DD/MM/YYYY format",
                            	 	"alertText2":"* Invalid date",
                            	 	"separator":"/",
                            	 	"format":"dmY"},
			    "datetime":{
                                    	"regex":"/^[0-3][0-9][\/][0-1][0-9][\/][0-9]{4} [0-2][0-9]:[0-5][0-9]:[0-5][0-9]$/",
                            	 	"alertText":"* Invalid date and time, must be in DD/MM/YYYY HH:II format",
                            	 	"alertText2":"* Invalid date and time"},
 			    "integer":{
 			    		"regex":"/^[\-]{0,1}[0-9,]+$/",
 			    		"alertText":"* Invalid integer number",
			    		"alertText2":"* Invalud value! Only values between",
			    		"alertText3":" and ",
			    		"alertText4":" accepted!"},
 			    "float":{
 			    		"regex":"/^[\-]{0,1}[0-9,]+\.[0-9]+$/",
 			    		"alertText":"* Invalid float number",
			    		"alertText2":"* Invalid value! Only values between",
			    		"alertText3":" and ",
			    		"alertText4":" accepted!"},
			    "seoname":{
					"regex":"/^[^\/]+$/",
					"alertText":"* Invalid value! Forward slash not allowed!"},
			    "seourl":{
					"regex":"/^[a-z0-9_\-]+$/",
					"alertText":"* Only URL friendly characters allowed!"},
 			    "onlyNumber":{
					"regex":"/^[\-]{0,1}[0-9\.,]+$/",
					"alertText":"* Numbers only"},	
			    "noSpecialCaracters":{
					"regex":"/^[0-9a-zA-Z]+$/",
					"alertText":"* No special characters allowed"},
			    "onlyLetter":{
					"regex":"/^[a-zA-Z\ \']+$/",
					"alertText":"* Letters only"}
					}	
	}

 	settings = jQuery.extend({
		allrules:allRules,
		success : function() { formSubmitValid = true; },
		failure : function() { formSubmitValid = false; }
	}, settings);	


	$("form.forms").bind("submit", function(caller){   // ON FORM SUBMIT, CONTROL AJAX FUNCTION IF SPECIFIED ON DOCUMENT READY
                if(submitValidation(this) == false){
			if (ajaxFormSubmitDefault && settings.success){
				settings.success && settings.success(); 
                                return false;
			}
		}else{
			settings.failure && settings.failure(); 
			return false;
		}
	})

	$(this).bind("blur", function(caller){loadValidation(this)})
	$(this).bind("keyup", function(caller){loadValidation(this)})
	$(this).bind("change", function(caller){loadValidation(this)})
	
	var buildPrompt = function(caller,promptText) {			// ERROR PROMPT CREATION AND DISPLAY WHEN AN ERROR OCCUR
		var divFormError = document.createElement('div')
		var formErrorContent = document.createElement('div')
		var arrow = document.createElement('div')
		$(caller).addClass("inputfielderror");

		
                $(divFormError).addClass("formError").click(function() {$(this).fadeOut("slow").remove() })
                $(divFormError).addClass("efv"+$(caller).attr("id"))
                $(formErrorContent).addClass("formErrorContent").attr("id", "efvid"+$(caller).attr("id"))
                $(arrow).addClass("formErrorArrow")

                $("body").append(divFormError)
                $(divFormError).append(arrow)
                $(divFormError).append(formErrorContent)
                $(arrow).html('<div class="line10"></div><div class="line9"></div><div class="line8"></div><div class="line7"></div><div class="line6"></div><div class="line5"></div><div class="line4"></div><div class="line3"></div><div class="line2"></div><div class="line1"></div>')

                $(formErrorContent).html(promptText)
	
		callerTopPosition = $(caller).offset().top;
		callerleftPosition = $(caller).offset().left;
		callerWidth =  $(caller).width()
		callerHeight =  $(caller).height()
		inputHeight = $(divFormError).height()

		//callerleftPosition = callerleftPosition + callerWidth -30
		callerleftPosition = callerleftPosition - 25
		callerTopPosition = callerTopPosition  -inputHeight -10
	
		$(divFormError).css({
			top:callerTopPosition,
			left:callerleftPosition,
			opacity:0
		})
		$(divFormError).fadeTo("fast",0.8);
	};
	var updatePromptText = function(caller,promptText) {	// UPDATE TEXT ERROR IF AN ERROR IS ALREADY DISPLAYED
		updateThisPrompt =  "efv"+$(caller).attr("id")
		//$(updateThisPrompt).find(".formErrorContent").html(promptText)
		$("#efvid"+$(caller).attr("id")).html(promptText)
		callerTopPosition  = $(caller).offset().top;
		inputHeight = $("."+updateThisPrompt).height()
		
		callerTopPosition = callerTopPosition  -inputHeight -10
		$("."+updateThisPrompt).animate({
			top:callerTopPosition
		});
	}
	var loadValidation = function(caller) {		// GET VALIDATIONS TO BE EXECUTED
		
		rulesParsing = $(caller).attr('class');
		rulesRegExp = /validate\[(.*)\]/;
		getRules = rulesRegExp.exec(rulesParsing);
		if(getRules==null) {
			return false
		}
		else {
			str = getRules[1]
			pattern = /[^a-zA-Z0-9_\-\.]+/;
			var 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 = "efv"+$(caller).attr("id");
		var caller = caller;
                var callerName = $(caller).attr("name");
                var isError = false;
		var 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 "date":
				 _date(caller,rules,i,false);
			break;
			case "datetime":
				 _date(caller,rules,i,true);
			break;
			case "integer":
				_isnumber(caller,rules,i, false);
			break;
			case "float":
				_isnumber(caller,rules,i, true);
			break;
			case "custom": 
				 _customRegex(caller,rules,i);
			break;
			case "length": 
				 _length(caller,rules,i);
			break;
			case "minCheckbox": 
				 _minCheckbox(caller,rules,i);
			break;
			case "maxCheckbox": 
				 _maxCheckbox(caller,rules,i);
			break;
			case "confirm": 
				 _confirm(caller,rules,i);
			break;
			default :;
			};
		};
		if (isError == true){

			if($("input[name="+callerName+"]").size()> 1 && (callerType == "radio" || callerType == "checkbox")) {		// Hack for radio group button, the validation go the first radio
				caller = $("input[name="+callerName+"]").first();
                                prompt = "efv"+$(caller).attr("id");
			}
			($("."+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
				if(!$(caller).val() || $(caller).val()=='NULL') {
					isError = true;
					promptText += settings.allrules[rules[i]].alertText+"<br />";
				}
			}
			if (callerType == "select-multiple") { // added by paul@kinetek.net for select boxes, Thank you
				if(!$(caller).find("option:selected").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 _date(caller,rules,position,isTime){		 // VALIDATE DATE RULES
			minValue = rules[position+1]
			maxValue = rules[position+2]
                        var mind = new Date(minValue.substr(0,4), minValue.substr(5,2)-1, minValue.substr(8,2))
                        var maxd = new Date(maxValue.substr(0,4), maxValue.substr(5,2)-1, maxValue.substr(8,2))
			if(isTime) dateRule = "datetime"; else dateRule = "date";
			pattern = eval(settings.allrules[dateRule].regex)
			
			if(!pattern.test($(caller).attr('value'))){
				isError = true
				promptText += settings.allrules[dateRule].alertText+"<br />"
			}
			var dayfield=$(caller).attr('value').substr(0,10).split(settings.allrules["date"].separator)[settings.allrules["date"].format.indexOf('d')]
    			var monthfield=$(caller).attr('value').substr(0,10).split(settings.allrules["date"].separator)[settings.allrules["date"].format.indexOf('m')]
    			var yearfield=$(caller).attr('value').substr(0,10).split(settings.allrules["date"].separator)[settings.allrules["date"].format.indexOf('Y')]

                        if(isTime) {
                            var hourfield=$(caller).attr('value').substr(11,2);
                            var minutefield=$(caller).attr('value').substr(14,2);
                            var secondfield=$(caller).attr('value').substr(17,2);
                        }
    			var dayobj = new Date(yearfield, monthfield-1, dayfield)
    			if ((dayobj.getMonth()+1!=monthfield)||(dayobj.getDate()!=dayfield)||(dayobj.getFullYear()!=yearfield))	{
				isError = true
				promptText += settings.allrules[dateRule].alertText2+"<br />"
    			}
                        if(isTime && !isError && (hourfield>23 || minutefield>59 || secondfield>59) ) {
				isError = true
				promptText += settings.allrules[dateRule].alertText2+"<br />"
                        }
                        if(dayobj<mind) {
                                isError = true
                                promptText += settings.allrules[dateRule].alertText3+minValue+" (YYYY-MM-DD)<br />"
                        }
                        if(dayobj>maxd) {
                                isError = true
                                promptText += settings.allrules[dateRule].alertText4+maxValue+" (YYYY-MM-DD)<br />"
                        }
		}
		function _isnumber(caller,rules,position,isfloat){	 // VALIDATE FLOAT RULES
			minValue = parseFloat(rules[position+1])
			maxValue = parseFloat(rules[position+2])
            		curText  = new String($(caller).attr("value"));
         		while (curText.indexOf(thousandsSeparator)>-1)
               			curText = curText.replace(thousandsSeparator,'');
         		if (isfloat && decimalSeparator!='.' && curText.indexOf(decimalSeparator)>-1)
               			curText = curText.replace(decimalSeparator,'.');
			curValue = parseFloat(curText)
			
			if(isfloat) numRule = "float"; else numRule = "integer";
			
			pattern = eval(settings.allrules[numRule].regex)
			
			if(!pattern.test($(caller).attr('value'))){
				isError = true
				promptText += settings.allrules[numRule].alertText+"<br />"
			}
    			if(curValue=='NaN' || curValue < minValue || curValue > maxValue)	{
				isError = true
				promptText += settings.allrules[numRule].alertText2+" "+minValue
				promptText += settings.allrules[numRule].alertText3+" "+maxValue
				promptText += settings.allrules[numRule].alertText4+" <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])
			fieldLength = $(caller).attr('value').length

			if(fieldLength<startLength || fieldLength>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+nbCheck
				promptText += settings.allrules["minCheckbox"].alertText2+"<br />"
			}
		}
		function _maxCheckbox(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["maxCheckbox"].alertText+nbCheck
				promptText += settings.allrules["maxCheckbox"].alertText2+"<br />"
			}
		}

		return(isError) ? isError : false;
	};
	var closePrompt = function(caller) {	// CLOSE PROMPT WHEN ERROR CORRECTED
		callerName = $(caller).attr("name");
                callerType = $(caller).attr("type");
        	if($("input[name="+callerName+"]").size()> 1 && (callerType == "radio" || callerType == "checkbox")) {		// Hack for radio group button, the validation go the first radio
                    caller = $("input[name="+callerName+"]").first();
                }
                closingPrompt = "efv"+$(caller).attr("id")
		$(caller).removeClass("inputfielderror");
		$("."+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;
			$("html:not(:animated),body:not(:animated)").animate({ scrollTop: destination}, 1100)
			return true;
		}else{
			return false
		}
	};
};
