(function($){
	var defaults = {
		submit_count:0,
		event:'blur',
		error_class:'invalid',
		msg_class:'error',
		rules:{
			'digits':{
				check:function(value){
					if(jQuery.trim(value)==''){
						return 'Обязательно для заполнения';
					} else {
						return /^\d+$/.test(value.replace(/\s+/g,'')) ? true : "Некорректное число";
					}
				}
			},
			'float':{
				check:function(value){
					if(jQuery.trim(value)==''){
						return 'Обязательно для заполнения';
					} else {
						return /^\d+[.,]?\d*$/.test(value.replace(/\s+/g,'')) ? true : "Некорректное число";
					}
				}
			},
			'phone':{
				check:function(value){
					if(jQuery.trim(value)==''){
						return 'Обязательно для заполнения';
					} else {
						return /^\+?\d{11,20}?$/.test(value.replace(/\s+/g,'')) ? true : "Некорректный телефон";
					}
				}
			},
			'email':{
				check:function(value){
					if(jQuery.trim(value)==''){
						return 'Обязательно для заполнения';
					} else {
						return /^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/i.test(_.trim(value)) ? true : "Некорректный e-mail";
					}
				}
			},
			'ru_text':{
				check:function(value){
					if(jQuery.trim(value)==''){
						return 'Обязательно для заполнения';
					} else {
						return /^[абвгдеёжзийклмнопрстуфхцчшщьыъэюя]{5,}$/i.test(value.replace(/\s+/g,'')) ? true : "Некорректное значение";
					}
				}
			},
			'ru_name':{
				check:function(value){
					if(jQuery.trim(value)==''){
						return 'Обязательно для заполнения';
					} else {
						return /^[абвгдеёжзийклмнопрстуфхцчшщьыъэюя]{2,}$/i.test(value.replace(/\s+/g,'')) ? true : "Некорректное имя";
					}
				}
			},
			'captcha':{
				check:function(value){
					if(jQuery.trim(value)==''){
						return 'Обязательно для заполнения';
					} else {
						//ajax
						return true;
					}
				}
			},
			'default':{
				check:function(value){
					if(jQuery.trim(value)==''){
						return 'Обязательно для заполнения';
					} else {
						return true;
					}
				}
			},
		},
		failure:function(){
			alert('Проверьте все поля');
		},
		reset:function(){
		},
		show_error:function(el,msg){
			var params=this;
			var offset=$(el).offset();
			var el_w=$(el).outerWidth();
			var el_h=$(el).outerHeight();
			$(el).addClass(params.error_class);

			if(_.isUndefined($(el).data().validator_msg_el)){
				var msg=$('<div></div>').attr('id',$(el).attr('name')+'_validator_message')
					.addClass(params.msg_class)
					.append('<span>'+msg+'</span>')
					.css({position:'absolute','left':offset.left+el_w,'top':offset.top,'display':'none'}).appendTo($('body'));
					
				var msg_w=$(msg).outerWidth();
				var msg_h=$(msg).outerHeight();
				$(msg).css({left:offset.left+el_w-msg_w,top:offset.top-msg_h}).show();
				$(el).data().validator_msg_el=$(msg);
			} else {
				$($(el).data().validator_msg_el).show().html('<span>'+msg+'</span>');
				var msg=$($(el).data().validator_msg_el);
				var msg_w=$(msg).outerWidth();
				var msg_h=$(msg).outerHeight();
				$(msg).css({left:offset.left+el_w-msg_w,top:offset.top-msg_h});
			}
		}
	};
	
	function validator_get_type(el){
		var prefix='validator_';
		var type='default';
		var classes=_.string.words(_.string.clean(el.attr('class')));
		for(var i=0;i<classes.length;i++){
			if(_.startsWith(classes[i],prefix)){
				type=classes[i].replace(prefix,'');
				break;
			}
		}
		return type;
	}
	
		
	var methods = {
		init:function(options){
			return this.each(function(){
				var form=this;
				$(form).attr('novalidate','novalidate');
				$(form).data('validator',$.extend(true,defaults,options));
				$(form).data().validator.form=$(form);
				var params=$(form).data().validator;
				
				var elements=new Object();
				
				$(this).find('input[type=text][required=required],textarea[required=required]').each(function(){
					if(jQuery.trim($(this).attr('name'))!=''){
						elements[jQuery.trim($(this).attr('name'))]=$(this);
						
						$(this).bind('keyup.validator',function(){
							$(this).removeClass(params.error_class);
							if(!_.isUndefined($(this).data().validator_msg_el)){
								$($(this).data().validator_msg_el).hide();
							}
						});
						$(this).bind(params.event+'.validator',function(){
							$(form).my_validator('validate',$(this));
						});
					}
				});
				/*var elements=new Array();
				$(this).find('input[type=text],textarea').each(function(){
					elements[elements.length]=$(this);
				});*/
				$(this).data().validator.elements=elements;
				
				$(this).bind('submit',function(e){
					e.preventDefault();
					$(this).data().validator.submit_count++;
					$(this).my_validator('validate');
				});
			});			
		},
		validate:function(obj){
			var form=this;
			var source;
			var validate_all=true;
			var validation_result=new Object();
			if(_.isUndefined(obj)){
				source=$(form).data().validator.elements;
			} else if(_.isArray(obj)){
				validate_all=false;
				source=obj;
			} else if(_.isElement(obj[0])){
				validate_all=false;
				source=new Array();
				source[0]=$(obj);
			}
			
			if(_.toArray(source).length>0){
				_.each(source,function(item){
					var check_result=true;
					var field_type=validator_get_type(item);
					if(!_.has($(form).data().validator.rules,field_type)){
						field_type='default';
					}
					check_result=$(form).data().validator.rules[field_type].check(item.val());
					if(check_result!==true){
						validation_result[item.attr('name')]=check_result;
					}
				});
				
				if(validate_all){
					if(_.keys(validation_result).length==0){
						if(!_.isUndefined($(this).data().validator.success)){
							$(this).data().validator.success();
						}
					} else {
						$(this).my_validator('invalidate',validation_result);
						if(!_.isUndefined($(this).data().validator.failure)){
							$(this).data().validator.failure();
						}
					}
				} else {
					$(this).my_validator('invalidate',validation_result);
				}
			}
			return this;
		},
		invalidate:function(obj){
			var form=this;
			var elements=this.data().validator.elements;
			_.each(obj,function(value,key){
				if(_.has(elements,key)){
					$(form).data().validator.show_error(elements[key],value);
				}
			});
			return this;
		},
		get_fields:function(){
			var result=new Object();
			$(this).find('input,textarea').each(function(){
				var el=this;
				var name=jQuery.trim($(this).attr('name'));
				var type=$(this).attr('type');
				if(name!=''){
					switch(type){
						case 'radio':
							if($(el).is(':checked')) result[name]=$(el).val();
							break;
						default:
							result[name]=$(el).val();
					}
				}
			});
			return result;
		},
		reset:function(){
			$(this).find('input[type=text],textarea').each(function(){
				$(this).val('');
			});
			$(this).data().validator.reset();
			return this;
		}
	};

    $.fn.my_validator = function(method){
		if (methods[method]){
			return methods[method].apply( this, Array.prototype.slice.call(arguments,1));
		} else if(typeof method === 'object' || !method){
			return methods.init.apply(this,arguments);
		} else {
			$.error('Method '+ method+' does not exist on jQuery.validator');
		}
    };
	
})(jQuery);
