var Validation = {

	timer: 0,

	params: new Array(),

	usernameRequest: "",

	emailRequest: "",

	usernameRequestResult: true,

	emailRequestResult: true,

	ajaxRequestUrl: "users",

	beforeMessage: '',

	afterMessage: '',

	add: function(form, model, field, message, required, allowEmpty, last, rule, params)
	{
		var a=new Array(10);
		a["field"]=field;
		a["model"]=model;
		a["message"]=message;
		a["rule"]=rule;
		a["params"]=params;
		a["required"]=required;
		a["allowEmpty"]=allowEmpty;
		a["last"]=last;
		a["on"]=true;
		a["form"]=form;

		if (!this.params[form])
			this.params[form]=new Array();
		if (!this.params[form]["fields"])
			this.params[form]["fields"]=new Array();
		if (!this.params[form]["fields"][field])
			this.params[form]["fields"][field]=new Array();
		this.params[form]["fields"][field][rule]=a;

		var inputs=$("#"+form).find(":input[name^='data["+model+"]["+field+"]'][type!=hidden]");
		if (inputs.length!=0)
		{
			inputs.addClass('validate');
			inputs.data('validate', {
				"form": form,
				"model": model,
				"field":field
			});

			inputs.blur(function(){
				var v=$(this).data('validate');
				if (v)
					Validation.validateField(v.form, v.field);
				Validation.hideSubmitMessage(v.form);
			});

			inputs.keyup(function(){
				var v=$(this).data('validate');
				if (v)
				{
					if (Validation.timer)
						clearTimeout(Validation.timer);
					Validation.timer=setTimeout(function(){
						Validation.validateField(v.form, v.field);
					},1000);
					Validation.hideSubmitMessage(v.form);
				}
			});

			inputs.mouseup(function(){
				var v=$(this).data('validate');
				if (v)
				{
					if (Validation.timer)
						clearTimeout(Validation.timer);
					Validation.timer=setTimeout(function(){
						Validation.validateField(v.form, v.field);
					},1000);
					Validation.hideSubmitMessage(v.form);
				}
			});
		}
	},

	init: function(form, model, message)
	{
		if (!this.params[form])
			this.params[form]=new Array();
		this.params[form]['model']=model;
		this.params[form]['form']=form;
		this.params[form]['message']=message;

		$("#"+form).find(":input[type=submit]").attr('disabled', false);
		$("#"+form).submit(function(){
			form=$(this).attr('id');
			return Validation.validateForm(form);
		});
	},

	validateForm: function(form, showMessage)
	{
		if (typeof showMessage=='undefined')
			showMessage=true;

		$("#"+form).find(":input[type=submit]").attr('disabled', true);
		var formValidates=true;
		for (var field in Validation.params[form]["fields"])
		{
			if (!Validation.validateField(form, field))
				formValidates=false;
		}
		if (showMessage)
		{
			if (!formValidates)
				Validation.showSubmitMessage(form);
			else
				Validation.hideSubmitMessage(form);
		}
		$("#"+form).find(":input[type=submit]").attr('disabled', false);
		return formValidates;
	},

	validateField: function(form, field, showMessage)
	{
		if (typeof showMessage=='undefined')
			showMessage=true;

		var validates=true;
		var model='';
		var last='';
		var message='';
		var allowEmpty='';
		var params='';
		var val='';

		if (this.params[form]["fields"][field])
		{
			for (var rule in this.params[form]["fields"][field])
			{
				if (this.params[form]["fields"][field][rule]["on"])
				{
					model=this.params[form]["fields"][field][rule]["model"];
					last=this.params[form]["fields"][field][rule]["last"];
					message=this.params[form]["fields"][field][rule]["message"];
					allowEmpty=this.params[form]["fields"][field][rule]["allowEmpty"];
					params=this.params[form]["fields"][field][rule]["params"];

					val=this.getValue(form, model, field);

					if ((typeof val == 'boolean') && (!val))
						return true;
					else
					{
						if ((typeof val == 'string') && (val=='') && (allowEmpty))
							continue;
						if ((typeof val == 'number') && (val==0) && (allowEmpty))
							continue;

						if (!this.checkRule(form, model, field, val, rule, params, message))
						{
							validates = false;
							if (showMessage)
								this.showMessage(form, model, field, message)
							if (last)
								break;
						}
						else
						{
							if (showMessage)
								this.hideMessage(form, model, field)
						}
					}
				}
			}
		}
		return validates;
	},

	validateRule: function(form, field, rule, showMessage)
	{
		if (typeof showMessage=='undefined')
			showMessage=true;

		var validates=true;
		var model='';
		var message='';
		var allowEmpty='';
		var params='';
		var val='';

		if (this.params[form]["fields"][field])
		{
			if (this.params[form]["fields"][field][rule]["on"])
			{
				model=this.params[form]["fields"][field][rule]["model"];
				message=this.params[form]["fields"][field][rule]["message"];
				allowEmpty=this.params[form]["fields"][field][rule]["allowEmpty"];
				params=this.params[form]["fields"][field][rule]["params"];

				val=this.getValue(form, model, field);

				if ((typeof val == 'boolean') && (!val))
					return true;
				else
				{
					if ((typeof val == 'string') && (val=='') && (allowEmpty))
						return true;
					if ((typeof val == 'number') && (val==0) && (allowEmpty))
						return true;

					if (!this.checkRule(form, model, field, val, rule, params, message))
					{
						validates = false;
						if (showMessage)
							this.showMessage(form, model, field, message)
					}
					else
					{
						if (showMessage)
							this.hideMessage(form, model, field)
					}
				}
			}
		}
		return validates;
	},
   
	getValue: function(form, model, field)
	{
		var data=$("#"+form).find(":input[name^='data["+model+"]["+field+"]'][type!=hidden]");
		if (data.length==0)
		{
			return false;
		}
		else
		{
			var val=false;
			if (data.length==1)
			{
				switch(data.attr("type"))
				{
					case "checkbox":
						if (data.attr("checked")==true)
							val=1;
						else
							val=0;
						break;
					case "radio":
						if ($(data).attr('checked'))
							val=$(this).val()
						else
							val='';
						break;
					case "select-one":
					case "select-multiple":
						if (data.val())
							val=data.val();
						else
							val=0;
						break;
					default:
						if (data.val())
							val=data.val();
						else if (data.text())
							val=data.text();
						else
							val='';
						break;
				}
			}
			else
			{
				switch(data.attr("type"))
				{
					case "select-one":
						if (data.length==3)
						{
							if ((data.eq(0).val()) && (data.eq(1).val()) && (data.eq(2).val()))
								val=data.eq(0).val()+"-"+data.eq(1).val()+"-"+data.eq(2).val();
							else
								val='';
						}
						else if (data.length==5)
						{
							if ((data.eq(0).val()) && (data.eq(1).val()) && (data.eq(2).val()) && (data.eq(3).val()) && (data.eq(4).val()))
								val=data.eq(0).val()+"-"+data.eq(1).val()+"-"+data.eq(2).val()+" "+data.eq(3).val()+":"+data.eq(4).val();
							else
								val='';
						}
						break;
					case "radio":
						val='';
						data.each(function(){
							if ($(this).attr('checked'))
								val=$(this).val()
						});
						break;
					case "checkbox":
						val=new Array()
						data.each(function(){
							if ($(this).attr('checked'))
								val.push($(this).val())
						});
						break;
				}
			}
			return val;
		}
	},

	showMessage: function(form, model, field, message)
	{
		this.hideMessage(form, model, field);
		$("#"+form).find(":input[name^='data["+model+"]["+field+"]'][type!=hidden]").eq(0).closest('div.input').append('<div class="error-message">' + this.beforeMessage + message + this.afterMessage + '</div>').show();
	},

	hideMessage: function(form, model, field)
	{
		$("#"+form).find(":input[name^='data["+model+"]["+field+"]'][type!=hidden]").eq(0).closest('div.input').find('.error-message').remove();
	},

	showSubmitMessage: function(form, message)
	{
		this.hideSubmitMessage(form);
		if (typeof message=='undefined')
			message=this.params[form]["message"];
		$("#"+form).find(":input[type=submit]").eq(0).closest('div.submit').append('<div class="error-message">' + this.beforeMessage + message + this.afterMessage + '</div>').show();
	},

	hideSubmitMessage: function(form)
	{
		$("#"+form).find(":input[type=submit]").eq(0).closest('div.submit').find('.error-message').remove();
	},

	checkRule: function(form, model, field, val, rule, params, message)
	{
		if (!val)
		{
			if ((rule=='required'))
				return false;
			else if (rule!='confirmPassword')
				return true;
		}
		switch(rule)
		{
			case 'between':
				return (((val.length)>=eval(params.param1)) && ((val.length)<=eval(params.param2))) ? true:false
				break;
			case 'comparison':
				if (isNaN(val))
					return false;

				switch(params.param1) {
					case 'isgreater':
					case '>':
						return (eval(val)>eval(params.param2))?true:false
						break;
					case 'isless':
					case '<':
						return (eval(val)<eval(params.param2))?true:false
						break;
					case 'greaterorequal':
					case '>=':
						return (eval(val)>=eval(params.param2))?true:false
						break;
					case 'lessorequal':
					case '<=':
						return (eval(val)>=eval(params.param2))?true:false
						break;
					case 'equalto':
					case '==':
						return (eval(val)==eval(params.param2))?true:false
						break;
					case 'notequal':
					case '!=':
						return (eval(val)!=eval(params.param2))?true:false
						break;
				}
				break;
			case 'datetime':
				return val.match(/^(?:(?:(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))(\/|-|\.|\x20)(?:0?2\1(?:29)))|(?:(?:(?:1[6-9]|[2-9]\d)?\d{2})(\/|-|\.|\x20)(?:(?:(?:0?[13578]|1[02])\2(?:31))|(?:(?:0?[1,3-9]|1[0-2])\2(29|30))|(?:(?:0?[1-9])|(?:1[0-2]))\2(?:0?[1-9]|1\d|2[0-8])))) ([01]\d|2[0-3])(:[0-5]\d){0,2}$/);
				break;
			case 'date':
				return val.match(/^(?:(?:(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))(\/|-|\.|\x20)(?:0?2\1(?:29)))|(?:(?:(?:1[6-9]|[2-9]\d)?\d{2})(\/|-|\.|\x20)(?:(?:(?:0?[13578]|1[02])\2(?:31))|(?:(?:0?[1,3-9]|1[0-2])\2(29|30))|(?:(?:0?[1-9])|(?:1[0-2]))\2(?:0?[1-9]|1\d|2[0-8]))))$/);
				break;
			case 'time':
				return val.match(/^([01]\d|2[0-3])(:[0-5]\d){0,2}$/);
				break;
			case 'boolean':
				return val.match(/^0|1$/);
				break;
			case 'decimal':
				return val.match(/^[-+]?[0-9]*\.{1}[0-9]+(?:[eE][-+]?[0-9]+)?$/);
				break;
			case 'email':
				return this.validEmail(val);
				break;
			case 'equalTo':
				return (val==params.param1)?true:false
				break;
			case 'extension':
				return val.match(params.param1);
				break;
			case 'ip':
				return val.match(/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/);
				break;
			case 'minLength':
				return (val.length<params.param1)?false:true
				break;
			case 'maxLength':
				return (val.length>params.param1)?false:true
				break;
			case 'length':
				return (val.length==params.param1);
				break;
			case 'numeric':
				return val.match(/^[-+]?[0-9]*\.{1}[0-9]+(?:[eE][-+]?[0-9]+)?$/);
				break;
			case 'phone':
				return val.match(/^\+[0-9]{11}$/);
				break;
			case 'postal':
				return val.match(/^[0-9]{2}-[0-9]{3}$/);
				break;
			case 'range':
				if (isNaN(val))
					return false;
				return (( eval(val)>eval(params.param1)) && (eval(val)<eval(params.param2))) ? true:false
				break;
			case 'inList':
				return val.match(params.param1);
				break;
			case 'username':
				return val.match(/^[a-zA-Z0-9_]*$/);
				break;
			case 'alpha':
				return val.match(/^[a-zA-Z]*$/);
				break;
			case 'alphanumeric':
				return val.match(/^[a-zA-Z0-9]*$/);
				break;
			case 'int':
				return val.match(/^[0-9]+$/);
				break;
			case 'float':
				return val.match(/[0-9]+\.[0-9]+$|^[0-9]+$/);
				break;
			case 'tags':
				return val.match(/^[ ]*[\w ,]*[ ]*$/);
				break;
			case 'required':
				return val;
				break;
			case 'custom':
			case 'regex':
				return val.match(params.param1);
				break;
			case 'url':
				return val.match(/^(?:(?:https?|ftps?|file|news|gopher):\/\/)?(?:(?:(?:25[0-5]|2[0-4]\d|(?:(?:1\d)?|[1-9]?)\d)\.){3}(?:25[0-5]|2[0-4]\d|(?:(?:1\d)?|[1-9]?)\d)|(?:[0-9a-z]{1}[0-9a-z\-]*\.)*(?:[0-9a-z]{1}[0-9a-z\-]{0,62})\.(?:[a-z]{2,6}|[a-z]{2}\.[a-z]{2,6})(?::[0-9]{1,4})?)(?:\/?|\/[\w\-\.,@?^=%&:;\/~\+#]*[\w\-\@?^=%&\/~\+#])$/i);
				break;
			case 'multiple':
				var v=true;
				if ((params.param1) && (val.length<params.param1))
					v=false;
				if ((params.param2) && (val.length>params.param2))
					v=false;
				if (params.param3)
				{
					$.each(val, function(index, element){
						if (!element.match(params.param3))
							v=false;
					});
				}
				return v;
				break;
			case 'uniqueUsername':
				return this.uniqueUsername(form, model, field, val, rule, params, message);
				break;
			case 'uniqueEmail':
				return this.uniqueEmail(form, model, field, val, rule, params, message);
				break;
			case 'confirmPassword':
				return this.confirmPassword(form, model, field, val, rule, params, message);
				break;
			default:
				try
				{
					eval("this.rule="+rule);
					return this.rule(form, model, field, val, rule, params, message);
				}
				catch(err)
				{
				}
				return true;
				break;
		}
	},
	
	validEmail: function(email)
	{
		if(email.length<=0)
		{
			return true;
		}
		var splitted=email.match("^(.+)@(.+)$");
		if (splitted==null)
			return false;
		if(splitted[1]!=null)
		{
			var regexp_user=/^\"?[\w-_\.]*\"?$/;
			if(splitted[1].match(regexp_user)==null)
				return false;
		}
		if(splitted[2]!=null)
		{
			var regexp_domain=/^[\w-\.]*\.[A-Za-z]{2,4}$/;
			if(splitted[2].match(regexp_domain)==null)
			{
				var regexp_ip=/^\[\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\]$/;
				if(splitted[2].match(regexp_ip)==null)
					return false;
			}
			return true;
		}
		return false;
	},
	
	uniqueUsername: function(form, model, field, val, rule, params, message)
	{
		if ((val) && (val!=this.usernameRequest))
		{
			this.usernameRequest=val;
			$.ajax({
				url: base+this.ajaxRequestUrl+'/check_username/'+val+'/',
				cache: false,
				dataType: 'json',
				success: function(data)
				{
					if ((!data) || (data.response=="err"))
					{
						Validation.showMessage(form, model, field, message)
						Validation.usernameRequestResult=false;
					}
					else
					{
						Validation.hideMessage(form, model, field, message)
						Validation.usernameRequestResult=true;
					}
				}
			});	
		}
		else
			return Validation.usernameRequestResult;	
	},
	
	uniqueEmail: function(form, model, field, val, rule, params, message)
	{
		if ((val) && (val!=this.emailRequest))
		{
			this.emailRequest=val;
			$.ajax({
				url: base+this.ajaxRequestUrl+'/check_email/'+val+'/',
				cache: false,
				dataType: 'json',
				success: function(data)
				{
					if ((!data) || (data.response=="err"))
					{
						Validation.showMessage(form, model, field, message)
						Validation.emailRequestResult=false;
					}
					else
					{
						Validation.hideMessage(form, model, field, message)
						Validation.emailRequestResult=true;
					}
				}
			});
		}
		else
			return Validation.emailRequestResult;
	},
	
	confirmPassword: function(form, model, field, val, rule, params, message)
	{
		if (val!=this.getValue(form, model, 'password'))
			return false;
		return true;
	},
	
	turnOff: function(form, field, rule)
	{
		if (this.params[form]['fields'][field][rule])
			this.params[form]['fields'][field][rule]['on']=false;
	},	
	
	turnOn: function(form, field, rule)
	{
		if (this.params[form]['fields'][field][rule])
			this.params[form]['fields'][field][rule]['on']=true;
	}

};
