(function($){

$.fn.klikvalidate = function(options){
	
	var currencyExtraChars = [190]; //Char code for a period.
	var numberExtraChars = [190, 188, 189]; //Char codes for period, comma and minus.
	
	var options = $.extend({
		enableTooltips: true,
		preValidate: false,
		autoValidate: true,
		enableMasking: true,
		moveToErrorOnSubmit: true,
		autoDatePicker: true,
		dateFormat: "dd/mm/yyyy",
		requiredMessage: "Cannot be empty",
		mustbecheckedMessage: "Must accept to continue",
		numberMessage: "Must be a number. ie. 2,000 or 1234",
		integerMessage: "May only contain numbers",
		postcodeMessage: "Must be a valid UK postcode",
		dateMessage: "Must be in the format ",
		telephoneMessage: "Must be 11 digits long, numbers only",
		emailMessage: "Must be a valid email address",
		currencyMessage: "Only numeric characters and dots. 2 decimal places",
		maxMessage: "This field may not be longer than % characters."	
	}, options);
	
	options.dateMessage += options.dateFormat;
	
	// Binds the form submit button to a function to loop over fields and validate. Will halt submission if any errors.
	$(this).bind('submit', function(){				
		var isValid = true;
		$($(this).selector + " input").each(function(){
			if(!validateField($(this))) 
				isValid = false;
		});
		$($(this).selector + " textarea").each(function(){
			if(!validateField($(this))) 
				isValid = false;
		});
		$($(this).selector + " select").each(function(){
			if(!validateField($(this))) 
				isValid = false;
		});
		if(options.moveToErrorOnSubmit){
			scrollUp($('.error').first());
		}
		return isValid;
	});
	
	// If autodatepicker is on then it will turn the text box into a jqueryUi calender.
	if(options.autoDatePicker){
		$(".date").datepicker({
			dateFormat: "dd/mm/yy",
			onClose: function(dateText, inst) {if(options.autoValidate) validateField($(this));}
		});
	}
	
	// If the option to prevalidate is set then check over all fields and display errors when page is loaded.
	if(options.preValidate) {
		$($(this).selector + " input").each(function(){
			validateField($(this));
		});
		
		$($(this).selector + " textarea").each(function(){
			validateField($(this));
		});
		
		$($(this).selector + " select").each(function(){
			validateField($(this));
		});
	}
	
	if(options.enableMasking){
		// Key and click handlers to deal with specific formatting in real time.
		$('.max').keydown(function(event){
			if(!checkMax($(this)) && !protectedKeys(event)) event.preventDefault();
		});
		
		$('.max').keyup(function(event){
			checkMax($(this));
		});
		
		$('.integer').keydown(function(event){
			filterNumbers(event, $(this));
		});
		
		$('.currency').keydown(function(event){
	        filterNumbers(event, $(this), currencyExtraChars);
		});
		
		$('.number').keydown(function(event){
	        filterNumbers(event, $(this), numberExtraChars);
		});
		
		$('.currency').blur(function(){
	        var dotPos = $(this).val().indexOf(".");
			if(dotPos != -1 && ($(this).val().length - 1 > dotPos )){
				var afterDot = $(this).val().substring(dotPos + 1);
				if(afterDot.length > 2) afterDot = afterDot.substring(0, 2);
					$(this).val($(this).val() + 0);
			}
		});
	}
	
	
		
	//remove notification on keyup or select change
	if (options.autoValidate){
		$($(this).selector + " input").bind('focusout', function(){
			 validateField($(this));
		});
		
		$($(this).selector + " input").bind('keyup', function(event){
			if(event.keyCode != 9){		
				validateField($(this));
			}
		});
		
		$($(this).selector + " input").bind('change', function(){
			 validateField($(this));
		});
		
		$($(this).selector + "select.required").bind('change', function(){
			if ( $(this).val() != "" ) removeMessage( $(this) );
			else appendMessage($(this), options.requiredMessage);
		});
		
		$($(this).selector + " textarea").bind('focusout', function(){
			 validateField($(this));
		});
		
		$($(this).selector + " textarea").bind('keyup', function(){
			 if(event.keyCode != 9){
				 validateField($(this));
				}
		});
		
		$($(this).selector + " input[type=checkbox]").click(function(){
			 validateField($(this));
		});
		
	}
	
	function validateField(element){
		var valid;
		if(element.hasClass('required')) valid = checkRequired(element);
		if(element.next().html() != options.requiredMessage){
			if(element.hasClass('email')) valid = checkEmail(element);
			if(element.hasClass('postcode')) valid = checkPostcode(element);
			if(element.hasClass('date')) valid = checkDate(element);
			if(element.hasClass('integer')) valid = checkInteger(element);
			if(element.hasClass('number')) valid = checkNumber(element);
			if(element.hasClass('currency')) valid = checkCurrency(element);
			if(element.hasClass('max')) valid = checkMax(element);
			return valid;
		}
	}
	
	
	function checkRequired(element){		
		if((element.attr('type') == "checkbox") && (!element.is(':checked'))) {
			return appendMessage(element, options.mustbecheckedMessage);  			
		} else if( element.val() == '') { 
			return appendMessage(element, options.requiredMessage);
		}else if(element.next().hasClass('error')){
			return removeMessage(element);
		}
	}
	
	function checkMax(element){
		var classes = element.attr('class').split(/\s+/);
		var maxLength;
		$.each(classes, function(index, item){
		    if (parseInt(item)) {
	    		maxLength = parseInt(item);	   
		    }
		});
		
		
		if(element.val().length >= maxLength){ 
			var message = options.maxMessage;
			message = message.replace("%", maxLength);
			if(element.val().length > maxLength){
				appendMessage(element, message);
				element.removeClass('highlight');
				return false; 
			}else{
				appendWarning(element, message);
				return false;			
			}
		}else if(element.val() != ''){
			removeMessage(element);
		}
		return true;
	}
	
	function checkEmail(element) {
		var email = element.val();
		var regex = /^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i;
		if(!element.hasClass("required") &&  email == "") return removeMessage(element);
		if (!regex.test(email)){
			return appendMessage(element, options.emailMessage);
		} else {
			return removeMessage(element);
		}
	}
	
	function checkInteger(element){
		var tel = element.val();
		var i;
		var isNum = true;
    	for (i = 0; i < tel.length; i++){   
	        var c = tel.charAt(i);
    	    if (((c < "0") || (c > "9")) && c != " ") isNum = false;
    	}
		if (!isNum){
			return appendMessage(element, options.integerMessage);
		}else{
			return removeMessage(element);
		}
	}
	
	function checkNumber(element){
		var myNum = element.val();		
		var valid = true;
		for( i = 0; i < myNum.length; i++){
			var cc = myNum.charCodeAt(i);
			if(!(cc >= 48 && cc <=57) && (currencyExtraChars.join().indexOf(cc)>=0)){
				valid = false;
				alert(currencyExtraChars.join().indexOf(cc)>=0);
			}		
		}
		if(element.val() == "") valid = false;
		
		if(!valid){
			return appendMessage(element, options.numberMessage);
		} else { 
			return removeMessage(element);
		}


		
	}
	
	function checkPostcode(element){
		var regex = /[A-Z]{1,2}[0-9]{1,2} ?[0-9][A-Z]{2}/i;
		if (!regex.test(element.val())) 
			return appendMessage(element, options.postcodeMessage);
		return removeMessage(element);
	}
	
	function checkCurrency(element){
		var dotPos = element.val().indexOf(".");
		if(dotPos != -1 && (element.val().length - 1 > dotPos )){
			var afterDot = element.val().substring(dotPos + 1);
			if(afterDot.length > 2) afterDot = afterDot.substring(0, 2);
			element.val(element.val().substring(0, dotPos + 1) + afterDot);
		}
		var myNum = element.val();		
		var valid = true;
		for( i = 0; i < myNum.length; i++){
			var cc = myNum.charCodeAt(i);
			if(!(cc >= 48 && cc <=57) && (currencyExtraChars.join().indexOf(cc)>=0)){
				valid = false;
			}		
		}
		if(element.val() == "") valid = false;
		
		if(!valid) appendMessage(element, options.currencyMessage);
		else removeMessage(element);
	}
	
	function checkTelephone(element){
		if(element.val() != parseInt(element.val()) || element.val().length != 11) appendMessage(element, options.telephoneMessage); 
	}
	
	function checkDate(element){
		if(element.val() != ''){
			if(options.dateFormat == ("dd/mm/yyyy" || "mm/dd/yyyy")) { 
				var regex = /^(\d{1,2})[\s\.\/-](\d{1,2})[\s\.\/-](\d{4})$/ 
			}else if(options.dateFormat == "yyyy/mm/dd"){ 
				var regex = /^(\d{4})[\s\.\/-](\d{1,2})[\s\.\/-](\d{1,2})$/ 
			}
		
			if (!regex.test(element.val())){ 
				return appendMessage(element, options.dateMessage);
			}else{
				var result = element.val().match(regex);
				
				var dateformat = options.dateFormat.split("/");
				
				var month;
				var day;
				var year;
				
				for(var i = 0; i < dateformat.length; i++){
					if(dateformat[i] == "dd") day = parseInt(result[i + 1], 10);
					if(dateformat[i] == "mm") month = parseInt(result[i + 1], 10);
					if(dateformat[i] == "yyyy") year = parseInt(result[i + 1]);
				}
				
				if (month < 1 || month > 12) return appendMessage(element, options.dateMessage);
				
				if (month == 2){
					// Handles feb and takes into account leap years.
					var dim = ((year % 4) == 0) ? 29 : 28;
				}else if(month == 4 || month == 6 || month == 9 || month == 11){
					// Decides on 30 or 31 days in the month.s
					var dim = 30;
				}else{
					var dim = 31;
				}
				
				if(!(day >= 1 && day <= dim)) return appendMessage(element, options.dateMessage);
			}
			if(element.hasClass('required')) return removeMessage(element);
		}
		return removeMessage(element);
	}
	
	
	function appendMessage(element, message){
		element.next('.error').remove();
		element.next('.warning').remove();
		element.addClass('highlight');
		element.after('<div class="error">'+ message +'</span>');
		return false;
	}
	
	
	function appendWarning(element, message){

		element.next('.error').remove();
		element.next('.warning').remove();
		element.after('<div class="warning">'+ message +'</span>');
		return false;
	}  
	
	function removeMessage(element){
		
		element.removeClass('highlight');
		element.next().remove('.error');
		element.next().remove('.warning');
		return true;
	}
	
	function filterNumbers(event, element, extrakeys){
		if (extrakeys == null) extrakeys = [];
		
		if (protectedKeys(event) || event.keyCode == 32 ||  (event.keyCode >= 48 && event.keyCode <= 57)) {
                // let it happen, don't do anything as these are 0-9, backspace, delete, left, right and space.
        } else {
            if (extrakeys.join().indexOf(event.keyCode)>=0) {
            	if(event.keyCode==189 || event.keyCode==190 || event.keyCode==188){
            		if(event.keyCode==189 && element.val() != "") event.preventDefault();
            		if((event.keyCode==190 || event.keyCode==188) && element.val() == "") element.val("0");
            	}
            }else{
            	event.preventDefault();
            }     
        }
	}
	
	// Checks for one of the keys which are unrelated to input characters. Such as left, right, delete, backspace
	function protectedKeys(event){
		if(event.keyCode == 46 || event.keyCode == 8 || event.keyCode == 37 || event.keyCode == 39 || event.keyCode == 40 || event.keyCode == 9){
			return true
		}
		return false;
	}

	
	function scrollUp(element){
		var px = element.offset().top - 20;
		//scroll the browser window up 100px before the target element
		if ($.browser.opera){
			$('html').animate({scrollTop: px}, 1000, 'swing', function(){flashErrors();});
		} else {
			$('html,body').animate({scrollTop: px}, 1000, 'swing', function(){flashErrors();});
		}
		
	}
	
	function flashErrors(){
		$('.error').animate({
			opacity: 0
		}, 300, "linear", function(){
			$('.error').animate({
				opacity: 1
		}, 700, "linear");
		});
	}

}

})(jQuery);
