/* Form validating jQuery plugin
 * 
 * requires jquery, core.js
 * */
(function($){$.extend($.fn,{
	validate:function(settings){				
		if (!this.length){
			return;
			}
		var validator = $.data(this[0], 'validator');
		if ( validator ) {
			return validator;
			}
		validator = new FormValidator( this[0],settings );
		$.data(this[0], 'validator', validator);
		return validator;
		}
	});
})(jQuery);


var FormValidator=function(form,settings){
	this.settings=$.extend( {}, FormValidator.defaultSettings, settings );	
	this.form=$(form);
	this.initialize();
	}
	FormValidator.FIELD_ERROR_CLASS='field-error';
	$.extend(FormValidator,{			
		defaultSettings:{
			fieldErrorClass:FormValidator.FIELD_ERROR_CLASS,
			rules:{}
			},
		FIELD_VALIDATION_STATUS:{
			ok:1,
			error_mask:10,
			error_minlength:11,
			error_maxlength:12,
			error_length:13,
			error_compare:14,
			error_comparenot:15,
			error_minvalue:16,
			error_callback:17,
			error:100
			},
		defaultFieldRule:{},
		rules:{
			mail:{
				mask:/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i
				},
			'date':{
				mask:/^\d{2,4}[\.\/\-]\d{2}[\.\/\-]\d{2}(?:\s\d{2}[\.\/\-\:]\d{2}[\.\/\-\:]\d{2})$/,
				minlength:8,
				oninit:function(field){
					$(field).calend();
					}
				},
			'datefree':{
				oninit:function(field){
					$(field).calend();
					}
				},
			url:{
				mask:/^((https?|ftp):\/\/)?(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i
				},
			login:{
				mask:/^[A-Za-z0-9!@#\$%^&\*\(\)_\-\+\.]+?$/i,
				minlength:3
				},
			password:{
				mask:/^[a-zA-Z0-9\.\,\!\@\#\$\%\^\&\*\(\)_\-\+]+$/,
				minlength:8
				},
			password2:{
				mask:/^[a-zA-Z0-9\.\,\!\@\#\$\%\^\&\*\(\)_\-\+]+$/,
				minlength:8,
				compare:'password'
				},
			givenname:{
				mask:/^[a-zA-Zа-яА-ЯёЁ\-\s]+$/i
				},
			captcha:{
				strictlength:6
				},
			phone:{
				minlength:1,
				mask:/^[0-9\+\-\s\(\)\.\#]+$/
				},
			notnullvalue:{
				minvalue:1
				},
			notemptyvalue:{
				minlength:1
				}
			},
		getErrorStr:function(error){
			for (var i in FormValidator.FIELD_VALIDATION_STATUS){
				if (FormValidator.FIELD_VALIDATION_STATUS[i]==error){
					return i;
					}
				}
			}
		});
	FormValidator.prototype={
		initialize:function(){			
			var $this=this;
			this.updateFields();
			this.putEvents();
			},
		putEvents:function(){
			var $this=this;
			this.form.submit(function(){
				return $this.submitFormHandler();
				});
			var fc=function(){
				$this.fieldErrorFree(this);
				};
			this.fields.click(fc).keydown(fc);
			//this.fields.focus(function(){$this.fieldFocusHandler(this)});
			//this.fields.blur(function(){$this.fieldBlurHandler(this)});			
			},
		updateFields:function(){
			var $this=this;			
			this.fields=$('input',this.form).add($('textarea',this.form)).add($('select',this.form))
				.each(function(){							   
					if ($.data(this,'validator')){
						return;
						}
					var name=this.name;
					var rules=duplicateObject($this.settings.defaultFieldRule);
					
					var sname=searchInClass(this,'field-');
					if (sname && FormValidator.rules[sname]){
						rules=$.extend({},rules,FormValidator.rules[sname])
						}
					
					if ($this.settings.rules[name]){
						rules=$.extend({},rules,$this.settings.rules[name])
						}
					if (rules && rules['oninit']){
						rules['oninit'].call(this,this);
						}
					$.each(
						['focus','blur','change','keydown','keyup'],
						function(){
							var evnt=String(this);
							$(this).bind(evnt,function(){
								var field=this;
								var rules=$.data(field,'rules');
								
								if (method_exists($this,'fieldHandler'+evnt.firstCharUpper())){
									$this['fieldHandler'+evnt.firstCharUpper()](field);
									}						
								if (rules && rules['on'+evnt]){
									rules['on'+evnt].call(field,field);								
									}
								if ($this.settings['onField'+evnt.firstCharUpper()]){
									$this.settings['onField'+evnt.firstCharUpper()].call(field,field);
									}
								
								});
							}
						);
					$.data(this,'validator',$this);
					$.data(this,'rules',rules);					
					});
			},
		validate:function(){
			var $this=this;
			var errors=0;
			var tofocus;
			this.errorFields={};
			$.each(this.fields,function(){
				var field=this;
				var fres=$this.validateField(field);
				var rules=$.data(field,'rules');
				if (fres>FormValidator.FIELD_VALIDATION_STATUS.ok){
					if (!rules.warn){
						errors++;
						if (!tofocus){
							tofocus=field;
							}						
						if (field.name){
							$this.errorFields[field.name]=fres;
							}
						}
					$this.fieldError(field,fres);
					}
				else{
					if (field.name){
						$this.errorFields[field.name]=false;
						}
					}
				});
			$(tofocus).focus();
			return (errors==0);
			},
		validateField:function(field){
			var rules=$.data(field,'rules');
			var val=this.fieldVal(field);			
			if (rules){				
				if (rules.minlength && val.length<rules.minlength){
					return FormValidator.FIELD_VALIDATION_STATUS.error_minlength;
					}
				if (rules.maxlength && val.length>rules.maxlength){
					return FormValidator.FIELD_VALIDATION_STATUS.error_maxlength;
					}
				if ($.isFunction(rules.callback) && !rules.callback(val,field)){
					return FormValidator.FIELD_VALIDATION_STATUS.error_callback;
					}
				/*if (field.__skipValidation){
					return;
					}*/
				if (rules.minvalue && (isNaN(Number(val)) || Number(val)<rules.minvalue ) ){
					return FormValidator.FIELD_VALIDATION_STATUS.error_minvalue;
					}
				if (rules.strictlength && val.length!=rules.strictlength){
					return FormValidator.FIELD_VALIDATION_STATUS.error_length;
					}
				if (rules.mask && !rules.mask.test(val)){
					return FormValidator.FIELD_VALIDATION_STATUS.error_mask;
					}				
				if (rules.compare && (!val || val!=this.fieldVal(this.fieldByName(rules.compare)))){
					return FormValidator.FIELD_VALIDATION_STATUS.error_compare;
					}
				if (rules.comparenot && (!val || val==this.fieldVal(this.fieldByName(rules.comparenot)))){
					return FormValidator.FIELD_VALIDATION_STATUS.error_comparenot;
					}
				}				

			return FormValidator.FIELD_VALIDATION_STATUS.ok;
			},
		fieldVal:function(field){			
			return (field.type=='radio')?getRadioValue(field.name):$(field).val();
			},
		fieldByName: function( name ) {
			// select by name and filter by form for performance over form.find("[name=...]")
			var form = this.form[0];
			return $(document.getElementsByName(name)).map(function(index, element) {
				return element.form == form && element.name == name && element  || null;
				});
			},
		fieldError:function(field,error){
			var rules=$.data(field,'rules');
			if (rules && rules.onerror){
				rules.onerror.call(field,field,FormValidator.getErrorStr(error),this);
				}
			if (this.settings.onFieldError){
				this.settings.onFieldError.call(field,field,FormValidator.getErrorStr(error));
				}
			if (!(rules && rules.warn)){
				$(field).addClass(this.settings.fieldErrorClass);
				}
			},
		fieldErrorFree:function(field){
			if ($(field).hasClass(this.settings.fieldErrorClass)){
				$(field).removeClass(this.settings.fieldErrorClass);
				if (this.settings.onFieldErrorFree){
					this.settings.onFieldErrorFree.call(field,field);
					}
				}
			},
		fieldHandlerBlur:function(field){
			var $this=this;
			var rules=$.data(field,'rules');
			if ((rules && rules.validateOnBlur) || this.settings.validateFieldsOnBlur){
				var fres=$this.validateField(field);
				if (fres>FormValidator.FIELD_VALIDATION_STATUS.ok){
					this.fieldError(field,fres);
					}
				}
			},
		submitFormHandler:function(){
			if (this.settings.onpresubmit){
				this.settings.onpresubmit.call(this.form[0]);
				}			
			if (this.validate()){
				if ($.isFunction(this.settings.onsubmit)){					
					return this.settings.onsubmit(this.getResults(),this.form[0]);
					}
				else{					
					return true;
					}
				}
			else{
				if ($.isFunction(this.settings.onerror)){
					this.settings.onerror(this.errorFields||{});
					}
				return false;
				}
			
			},				
		getResults:function(){
			var $this=this;
			var res={};
			$.each(
				this.fields,
				function(){
					if (this.type=='radio'){
						if (res[this.name]==undefined){
							res[this.name]=$this.fieldVal(this);
							}
						}
					else if (this.type=='checkbox'){
						res[this.name]=Number(this.checked);
						}
					else{
						res[this.name||this.id]=$(this).val();
						}					
					}
				);
			return res;
			}
		};