/**
 * Concurio Validator 0.1
 * 
 * - Validates a form according to given options, adds messages to fields if 
 *   data is invalid.
 * - if the field is not in the validator options it is ignored
 * - validates text-inputs, text-areas and selects
 * 
 * usage:
 *  $("#target_form").cvalidator({
 *       targets: {
 *           'x': {
 *               validators: [
 *               {name:'validateNumber:', params:{message:"test message"}},
 *               {name:'validateNotEmpty', params:{message:"test"}}
 *               ]
 *          },
 *           'name': {
 *               validators: [
 *               {name:'validateNotEmpty', params:{message:"test"}}
 *               ]
 *           }
 *       }
 *   });
 *   
 * options:
 *  -targets = a hashtable containing fields and the validators used to validate them.
 *             and an params object that should contain the message and other
 *             validator parameters
 *  -error_class = error message class name, defaults to 'cval-error-msg'
 *  -input_error_class = class added to the input if there are errors
 *      
 * current validators:
 *  -validateNumber - validates simple numbers, doesn't check empties
 *  -validateNotEmpty - validates that a field is not empty
 *  -validateRegexp - validates that a field matches a regular expression params:{ regexp, message }
 *  -validateNotValue - validates that a field value is not given value, params:{ value, message }
 *   -validateNotValue - validates that a field value is not given value, params:{ value, message }
 */
(function($) {
    $.fn.extend({
        cvalidator: function(options) {
			var defaults = {
                targets: [],
				error_class : "cval-error-msg",
				input_error_class: "cval-error-input",
				effect : "fadeIn"
            };
			var errors = [];
			var successfullValidations = []
			var passSubmit = true;
			
			
			var makeMessage = function(id, error_class, msg){
				cont = '<div id="cvalerror_'+id+'" class="'+error_class+'">'+msg+'</div>';
				errors.push(id);
				return cont;
			};
			
			/**
			 * remove all error messages
			 */
			var removeMessages = function()
			{   
			    var i =0;
				var len=errors.length;
				for (i = 0; i< len;i++){
					removeMessage(errors[i]);
				}
			};
			
			
			/**
			 * remove a single message
			 * @param {string} name - field name
			 */
			var removeMessage = function(name)
            {
                 $("#cvalerror_"+name).fadeOut('300', function() { 
				    $("#cvalerror_"+name).remove(); 
				 });
            };
			
			/**
			 * validate a single field
			 * params:
			 *	field html elem.
			 */
			var validateField = function(field){
					successfullValidations = [];
				   removeMessage($(field).attr('name'));
			   	   $(field).removeClass(options.input_error_class);
			       var validators = [];
				   try {
			           validators = options.targets[field.name].validators;
				   } catch (e) {
	                    return false;
	               }  
	               var value = $(field).val();
//				   var successfullValidations = [];
//                   var validated = "";
				   var i = 0;
				   var len=validators.length;
	               //call the validator from options
				   for (i = 0; i < len ; i++){    
				        var opts = {};
				        try {
	                       opts = validators[i].params;
						} catch (ex) {
						   opts = {};
						}
						var res = methods[validators[i].name].call(field,value,opts);
						  
				   }
			};
			/**
			 * validate all fields using validateField function
			 * params
			 * 	form object
			 */
			var validateAllFields = function(obj){
				passSubmit = true;
				$('input, textarea, select', obj).each(function(){
					try {
	                   var isIn = options.targets[this.name].validators;
					   validateField(this);
					} catch (e) {
					}
				});
			};
			
			var handleValidationResult = function(field,res, message)
			{
				var validated  ="";
				if ( res !== true) {
					validated += message +"<br/>";
					var validators = options.targets[field.name].validators
					if (successfullValidations.length !== validators.length ) {
	                  //prevent submit
	                  passSubmit = false;
					  options.targets[$(field).attr('id')].valid = false;
	                  //set message
					   $(field).after(makeMessage(field.name, options.error_class, validated));
					   $(field).addClass(options.input_error_class);
					   switch(options.effect) {
							case 'show':
							    $('.'+options.error_class).show('slow');
								break;
							default: 
								$('.'+options.error_class).fadeIn('slow');
						}
	                }
				} else {
					successfullValidations.push(res);
					options.targets[$(field).attr('id')].valid = true
				}
	               
			}
			/**
			 * this collection contains all validators
			 * @param {Object} value - value to validate
			 * @param {Object} message - error message for validator
			 * @param {Object} opts - options hashtable
			 */
			var methods = {
				 validateNumber : function(number,  opts)
		            {
						var field = this
		                if (isNaN(number)){
		                    handleValidationResult(field,false, opts.message );
		                }else {
							handleValidationResult( field,true, opts.message );
						}
		            },
				validatePositiveNumber : function(number,  opts)
		            {
						var field = this
						console.log(number > -1)
		                if ( number*1 < -1 ){
		                    handleValidationResult(field,false, opts.message );
		                }else {
							handleValidationResult( field,true, opts.message );
						}
		            },
				validateNotEmpty : function(value,  opts)
                    {
						var field = this
                        if (value.length < 1){
                            handleValidationResult(field,false, opts.message );
                        } else {
							handleValidationResult( field,true, opts.message );
						}
                    },
				validateNotValue : function(value,  opts)
                    {
						var field = this
                        if (value === opts.value){
                           handleValidationResult(field,false, opts.message );
                        } else {
							handleValidationResult( field,true, opts.message );
						}
                    },
				validateRegexp : function(value,  opts)
				{
					var field = this
					if (value.search(opts.regexp)===-1) {
						handleValidationResult(field,false, opts.message );
					} else {
						handleValidationResult( field,true, opts.message );
					}
					   
				},
				validateAtUrl: function(value, opts){
					var field = this
					var handle = function(e, opts,field){
						if (e === "true") {
                        	handleValidationResult(field, false, opts.message );
	                    } 
						else {
	                        handleValidationResult( field,true, opts.message );
                    	}
					}
					var e =  $.ajax({
						url: opts.url,
						async: false,
						method:"post",
						data: opts.data,
						success: function(e){
							handle(e,opts,field)
						}
					}).responseText;
					
				}
			  };
			  
            var options = $.extend(defaults, options);
			return this.each(function() {
		          var obj = $(this);
				  var fields = $('input, textarea, select', obj);
				  //validates a field when it changes 
				  $(fields).change(function() {
				  	     //remove previous messages
					     removeMessage(this.name);
						 passSubmit = true;  
	                     validateField(this);
                  });
				  //validates all fields before submit
				  $(obj).submit(function(e) {
						 validateAllFields(obj);
						 passSubmit  = true;						 
						 $.each(options.targets, function() {
						 	if (this.valid == false){
								passSubmit = false;
							}
						 });
				  	     if ( passSubmit === true ) {
						 	return true;
						 } else {
//						 	e.preventDefault();
						 	return false;
						 }
				  });
		    });
        }
    });
})(jQuery);

 

