(function($){ 
     $.fn.extend({
		
         valid8: function(options) {		
            return this.each(function(){
				
				$(this).data('valid', false);
				
				var defaultOptions = {
					regularExpressions: [],
					ajaxRequests: [],
					jsFunctions: [],
					onValid: defaultOnValid,
					onError: defaultOnError,
					validationEvents: ['keyup','blur'],
					validationFrequency: 500,
					values: null
				};
				
				if(self.type == 'checkbox')
					defaultOptions.regularExpressions = [{expression: /^true$/, errormessage: 'Required'}];
				else
					defaultOptions.regularExpressions = [{expression: /^.+$/, errormessage: 'Required'}];
				
				$(this).data('settings', $.extend(defaultOptions, options));
				
				initialize(this);
				
								
			});
		},
		
		isValid: function(){
			var valid = true;
			this.each(function() {
				validate(this);
				if($(this).data('valid') == false)
					valid = false;
			});
			return valid;
		}
		
	});
	
	function initialize(el) {
		$(el).data('errors', new Array());
		$(el).data('valids', new Array());
		$(el).data('keypressTimer', null);
		
		if($(el).attr('value').length > 0)
			validate(el);

		activate(el);
	};
	
	function activate(el) {
		var events = $(el).data('settings').validationEvents;
		if(typeof events == 'string')
			$(el)[events](function (e){ handleEvent(e, el); });
		else {
			$.each(events, function(i, event){
				$(el)[event](function (e){ handleEvent(e, el); });
			});
		}
	};
	
	function validate(el) {
		// Dispose old errors and valids
		$(el).data('errors', new Array());
		$(el).data('valids', new Array());

		var value;

		// Handle checkbox
		if(el.type == 'checkbox')
			value = el.checked;
		else
			value = el.value;
				
		regexpValidation(value.replace(/^[ \t]+|[ \t]+$/,''),el);
	};
	
	function regexpValidation(value, el){
		$.each($(el).data('settings').regularExpressions, function(i, validator){
			if(!validator.expression.test(value))
				$(el).data('errors')[$(el).data('errors').length] = validator.errormessage;
			else if(validator.validmessage)
				$(el).data('valids')[$(el).data('valids').length] = validator.validmessage;
		});
		
		if($(el).data('errors').length > 0)
			onError(el);
		else if($(el).data('settings').jsFunctions.length > 0) {
			functionValidation(value, el);
		}
		else if($(el).data('settings').ajaxRequests.length > 0){
			fileValidation(value, el);
		}
		else {
			onValid(el);
		}
						
	};
	
	function functionValidation(value, el){
		$.each($(el).data('settings').jsFunctions, function(i, validator){
			
			var v;
			if(validator.values){
				if(typeof validator.values == 'function')
					v = validator.values();
			}
			
			var values = v || value;
			
			if(validator['function'](values).valid)
				$(el).data('valids')[$(el).data('valids').length] = validator['function'](values).message;
			else
				$(el).data('errors')[$(el).data('errors').length] = validator['function'](values).message;	
		});
		
		if($(el).data('errors').length > 0)
			onError(el);
		else if($(el).data('settings').ajaxRequests.length > 0) {
			fileValidation(value, el);
		}
		else {
			onValid(el);
		}
	}
	
	function fileValidation(value, el){
		$.each($(el).data('settings').ajaxRequests, function(i, validator){
			
			var v;
			if(validator.values){
				if(typeof validator.values == 'function')
					v = validator.values();
			}
			
			var values = v || {value: value};
			
			$.post(validator.url, values,
			  function(data, textStatus){
				if(data.valid) {
					$(el).data('valids')[$(el).data('valids').length] = data.message;
				} else {
					$(el).data('errors')[$(el).data('errors').length] = data.message;
				}
					if($(el).data('errors').length > 0)
						onError(el);
					else {
						onValid(el);
					}
			  }, "json");
		});
		
	};
	
	function handleEvent(e, el){
		if(e.keyCode && $(el).attr('value').length > 0) {
			clearTimeout($(el).data('keypressTimer'));
			$(el).data('keypressTimer',setTimeout(function() {
				validate(el);
			}, $(el).data('settings').validationFrequency));
		}
		else if(e.keyCode && $(el).attr('value').length <= 0)
			return false;
		else {
			validate(el);
		}
	};
	
	function defaultOnValid(valids,el) {
		var parent = $(el).parent();
		parent.parent().removeClass('error');
		parent.next().removeClass('loading');
		parent.parent().addClass('valid');

		setMessage(valids,el);
	};

	function defaultOnError(errors,el) {
		var parent = $(el).parent();
		parent.parent().removeClass('valid');
		parent.next().removeClass('loading');
		parent.parent().addClass('error');

		setMessage(errors,el);
	};
	
	function onValid(el) {
		$(el).data('valid', true);
		$(el).data('settings').onValid($(el).data('valids'), el);
	}

	function onError(el) {
		$(el).data('valid', false);
		$(el).data('settings').onError($(el).data('errors'), el);
	}
	
	function setMessage(messages,el) {
		var parent = $(el).parent();
		var elementId = el.id + "ValidationMessage";		
		if($('#'+elementId).length == 0){
			if (parent.next().next().length == 0) // Modif : if there is no next (td), we push the span at the end of the input's td
			{
				parent.append($(document.createElement('span')).attr('id', elementId));
			}
			else // else, we put it in the next td
			{
				parent.next().html("");
				parent.next().append($(document.createElement('span')).attr('id', elementId));
			}
		}

		$('#'+elementId).html("");
		if (typeof(messages[0]) != 'undefined')
			$('#'+elementId).text(messages[0].toString());
	};
})(jQuery);
