// JavaScript Document
// autor Karol Dr±gowski
//versja 1.0b
//Validator JavaScript wymaga jQuery i pluginu jQuery Form 
//niektóre regóły do wyrażeń regularnych zostały znalezione inne napisane przez autora

/*
clone=function(ob){
        var n={};
        for(p in ob){
			n[p]=typeof ob[p]=='object'?clone(ob[p]):ob[p];
        }
        return n;
 }
 */

 clone=function(ob){
        var typ=typeof ob;
		if(typ!='object') return ob;
		var n=ob instanceof Array?[]:{};
        for(p in ob){
			n[p]=clone(ob[p]);
        }
        return n;
 }
 
 count=function(ob){
        var n=0;
        for(p in ob)
			n++;
        return n;
 }

var Validate={
	reguly:{},
	dane:{},
	errors:{},
	statusy:{},
	currentf:null,
	currentfs:null,
	rulesDefFormat:{
		int: 'liczba całkowita',
		nat: 'liczba całkowita dodatnia',
		nat0: 'liczba całkowita nieujemna',
		login: 'przynajmniej 2 znaki',
		haslo: 'przynajmniej 3 znaki',
		email: 'np. login@domena.pl',
		alfPL: 'znaki alfabetu polskiego',
		kodPocz: 'xx-xxx',
		nip: 'xxx-xxx-xx-xx',
		telKomPL: 'xxxxxxxxx',
		telPL: 'xx-xxxxxxx',
		req: 'pole wymagane'
	},

	rulesDefNames:{
		login: 'Login',
		haslo: 'Hasło',
		email: 'Email',
		telPL: 'Telefon stacjonarny',
		telKomPL: 'Telefon komórkowy',
		kodPocz: 'Kod pocztowy',
		nip: 'NIP'
	},
	
	rulesWagi:{
		nip:['657234567',11]	
	},
	
	rulesMatch:{
		req:'.+',
		nat:'[1-9]\\d*',
		nat0: '0|[1-9]\\d*',
		int: '0|-?[1-9]\\d*',
		login: '.{2,}',
		haslo: '.{3,}',
		email: '[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z0-9]+',
		alfPL: '[a-zA-Z±żĽ¶ćęńłóŻ¬Ńˇ¦ĆĘÓŁ ]+',
		telKomPL:  '\\d{9}',
		telPL: '\\d{2}-\\d{7}',
		kodPocz: '\\d{2}-\\d{3}',
		nip:function(s){
			if(/^\d{3}-\d{3}(-\d{2}){2}$/.test(s))
				return Validate.checkSum('nip',s);
			return false;
		},
		when:function(v,pary_war_akcja){
			var thi=Validate;
			$.each(pary_war_akcja, function(i,o){
				if(o.eq==v)
					$.each(o.then,function(i2,o2){
						thi.validateField(i2,o2,o.ending);
					});
			});
			return true;
		},
		cont:function(v,wzorce){
			//czemu¶ nie działa dopracować
			var rules=[];
			var thi=Validate;
			if(thi.rulesDefFormat['cont'])
				delete thi.rulesDefFormat['cont'];
			var stat=true;
			$.each(wzorce, function(i,o){
				var t_s=thi.lightFor(o, v);
				stat=stat&&t_s;
				if(!t_s && thi.rulesDefFormat[o])
					rules.push(thi.rulesDefFormat[o]);
			})
			if(rules.length>0)
				thi.rulesDefFormat['cont']='musi zawierać: ['+rules.join(', ')+']';
			return stat;			
		},
		valIn:function(v,zbior){
			var stat=false;
			$.each(zbior, function(i,o){
				stat=stat||(v==o);
			})
			return stat;
		},
		eqField:function(v,pole){
			return v==Validate.getVal(pole);
		},
		eq:function(v,oczek){
			return v==oczek;
		},
		minLen:function(s,l){return s.length>=parseInt(l)}
		//maxLen:function(s,l){return s.length<=l}

	},
	
	checkSum:function(typ,value){
		var thi=this;
		var value=value.replace(/-| /g,"");
		var dest=parseInt(value.charAt(value.length-1),10);
		value=value.slice(0,-1);
		var wagi=thi.rulesWagi[typ][0];
		var modulo=thi.rulesWagi[typ][1];
		var checksum=0;
		for(var i=0;i<value.length;i++)
			checksum=checksum+(parseInt(value.charAt(i),10)*parseInt(wagi.charAt(i),10));
		checksum=checksum % modulo;
		return checksum==dest;
	},
	
	strictFor:function(sel, val){
		var reg=this.rulesMatch[sel];
		if(!reg) return /.?/;
		eval("reg=/^(?:"+reg+")$/");
		var stat=typeof arguments[2]=='boolean'?arguments[2]:true;
		if(typeof val!='object')
			return stat==reg.test(val);
		else{
			var res=val.length>0;
			$.each(val,function(i,o){
				res=res&&(stat==reg.test(o));
			})
			return res;
		}
	},
	
	lightFor:function(sel, val){
		var reg=this.rulesMatch[sel];
		if(!reg) return /.?/;
		eval("reg=/.*("+reg+").*/");
		var stat=typeof arguments[2]=='boolean'?arguments[2]:true;
		if(typeof val!='object')
			return stat==reg.test(val);
		else{
			var res=val.length>0;
			$.each(val,function(i,o){
				res=res&&(stat==reg.test(o));
			})
			return res;
		}
	},

	fireRuleFunction:function(val,rul){
		var stat=typeof arguments[2]=='boolean'?arguments[2]:true;
		var fuct=this.rulesMatch[rul];
		if(typeof val!='object')
			return stat==fuct(val);
		else{
			var res=val.length>0;
			$.each(val,function(i,o){
				res=res&&(stat==fuct(o));
			})
			return res;
		}
	},
	
	fireRuleFunctionWithParam:function(val,rul,crit){
		var stat=true;
		var fuct=this.rulesMatch[rul];
		if(typeof crit.stat=='boolean'){
			stat=crit.stat;
			delete crit.stat;
			if(count(crit)==1)
				crit=crit['0'];
		}
		if(typeof val!='object')
			return stat==fuct(val,crit);
		else{
			var res=val.length>0;
			$.each(val,function(i,o){
				res=res&&(stat==fuct(o,crit));
			})
			return res;
		}
	},
	
	createFieldSelector:function(i){
		var thi=this;
		var field=$('[@name='+i+']', thi.currForm());
		if(field.size()>0)
			return '[@name='+i+']';
		field=$('[@name^="'+i+'["]', thi.currForm());
		if(field.size()>0)
			return '[@name^="'+i+'["]';
		return false;
	},
	
	setCurrentForm:function(f){
		var sel;
		var thi=this;
		if($(f).attr('id'))
			sel='#'+$(f).attr('id');
		else
			sel='n@'+$(f).attr('name');
		if(!thi.reguly[sel])
			thi.reguly[sel]={};
		thi.currentfs=sel;
		thi.currentf=$(f)[0];
	},
	
	currForm:function(){
		return this.currentf;	
	},
	
	currFormRules:function(){
		return this.reguly[this.currentfs];	
	},
	
	setFormRule:function(val){
		this.reguly[this.currentfs]=val;
	},
	
	getFieldType:function(selector){
		var thi=this;
		return $(selector, thi.currForm())[0].type;
	},
	
	errorPreppend:function (type){
		switch(type){
			case 'select-one':
				return 'Wybierz ';
			case 'select-multiple':
				return 'Wybierz ';
			case 'radio':
				return 'Wybierz ';
			case 'checkbox':
				return 'Zaznacz ';
			default:
				return 'WprowadĽ ';
		}		
	},
	
	eventTypeFor:function(type){
		if(type=='checkbox')
			return 'click';
		return 'keyup';
	},
	
	setFormData:function(d){
		//Validate.dane[Validate.currentfs]=d;
		this.errors[this.currentfs]=[];
		this.statusy[this.currentfs]=[];
	},
	
	appendError:function(err){
		this.errors[this.currentfs].push(err);
	},
	
	countErrors:function(){
		return this.errors[this.currentfs].length;
	},
	
	getVal:function(f){
		var thi=this;
		var sel=thi.createFieldSelector(f);
		var field=$(sel,thi.currentf);
		if(field.size()>0){
			var val=[];
			var type=thi.getFieldType(sel);
			field.each(function(i,o){
				switch(type){
					case 'radio':
						if(o.checked)
							val.push(o.value);
					break;
					case 'checkbox':
						if(o.checked)
							val.push(o.value);
					break;
					case 'select-one':
						if(o.selectedIndex!=-1)
							val.push(o.options[o.selectedIndex].value);
					break;
					case 'select-multiple':
						if(o.selectedIndex!=-1){
							for(var ind=o.selectedIndex; ind<o.options.length; ind++)
								if(o.options[ind].selected)
									val.push(o.options[ind].value);	
						}
					break;
					default:
						val.push(o.value);
				}
			});
			if(val.length==1)
				return val.pop();
			if(val.length>1)
				return clone(val);
		}
		return '';
	},
	
	getStat:function(f){
		var t=this.statusy[this.currentfs][f];
		if(typeof t!='undefined')
			return t;
		else
			return null;		
	},	

	setStat:function(f,st){
		this.statusy[this.currentfs][f]=st;	
	},

	setRulesEvent:function(selector, type, o){
		var ev=this.eventTypeFor(type);
		var f=this.currForm();
		if(o.when){		
			var ev_func;
			switch(type){
				case 'checkbox':
					ev_func=function(){
						var check=$(selector, f).is(':checked');
						o2=o.when[0];
						find_hid=$(o2.jssel, f);
						if(check)
							find_hid.show();
						else					
							find_hid.hide();
					};
				break;
			}
			ev_func();
			$(selector,f).bind(ev,ev_func);
		}
	},
	
	prepareToValidate:function(f){
		if($(f).is('.formPrepared') || !$(f).attr('rel'))
			return true;
		eval("var rules="+$(f).attr('rel'));
		$(f).attr('rel','');
		if(rules){
			this.setCurrentForm(f);
			rules=this.prepareRules(rules)
			this.setFormRule(rules);
			$(f).addClass('formPrepared');
			//printObject(this.currFormRules())
		}
	},
	
	prepareRules:function(r){
		var thi=Validate;
		$.each(r, function(i,o){
			var selector=thi.createFieldSelector(i);
			var type=thi.getFieldType(selector);
			thi.setRulesEvent(selector,type,o);
			if(typeof o=='string')
				r[i]={'type':type, 0:o};
			else if(o.when){
				$.each(o.when, function(i2,o2){
					r[i].when[i2].then=thi.prepareRules(o2.then);
				})
			}else
				r[i]['type']=type;
		});
		return r;
	},

	validateForm:function(data, jqForm){
		f=jqForm;
		var thi=Validate;
		if(!$(f).is('.formValidate'))
			return true;
		if(!$(f).is('.formPrepared'))
			thi.prepareToValidate(f);
		if(!$(f).is('.formPrepared')) return true;
		thi.setCurrentForm(f);
		thi.setFormData(data);
		//printObject(Validate.currFormRules());
		$.each(thi.currFormRules(), function(i,o){
			thi.validateField(i,o)
		})
		if(thi.countErrors()>0){
			$showInfo({typ:'err',hideafter:-1, msg:thi.createErrorMsg()});
			return false;
		}
		return true;
	},
	
	validateField:function(name,rul){
		r=clone(rul);
		var thi=this;
		var textpo=arguments[2]?' '+arguments[2]:'';
		if(thi.getStat(name)!=null) return;
		var locerror=[];
		var kom='';
		var typ;
		if(r.msg){
			kom=r.msg;
			delete r.msg;
			typ=1;
		}else if(r.name){
			kom=thi.errorPreppend(r.type)+r.name+textpo;
			delete r.name;
			typ=2;
		}else if(thi.rulesDefNames[r[0]]){
			typ=3;
			kom=thi.errorPreppend(r.typ)+thi.rulesDefNames[r[0]]+textpo;
		}else{
			typ=4;
			kom=thi.errorPreppend(r.typ)+'<b>nienawane pole</b>'+textpo;
		}
		var stat=true;
		delete r.type;
		if(r.when && kom!='' && typ!=4)
			thi.appendError(kom);
		var dane=thi.getVal(name);
		var req=false;
		var reqs=false;
		var regulc=count(r);
		if(regulc>1 && (typeof r.req=='boolean' || (r['0'] && r['0']=='req'))){
			req=true;
			if(typeof r.req=='boolean'){
				reqs=r.req;
				delete r.req;
			}else{
				reqs=true;
				delete r['0'];
			}
		}
		$.each(r,function(i,o){
			var bad=thi.validateRules(dane,i,o);
			stat=stat&&bad.stat;
			if(!bad.stat && bad.msg!='')
				locerror.push(bad.msg);
		});
		if(req && !stat){
			stat=thi.strictFor('req', dane, reqs);
			if(!stat && thi.rulesDefFormat['req'])
				locerror.push('! '+thi.rulesDefFormat['req']);
		}
			
		if(!stat && kom!='' && !r.when){
			if(typ==1 || locerror.length==0)
				thi.appendError(kom);
			else
				 thi.appendError(kom+' ('+locerror.join(", ")+')');
		}
		thi.setStat(name,stat);		
	},
	
	validateRules:function(val,rul,crit){
		var ret={stat:true, msg:''};
		var thi=this;
		var blad=false;
		if(val==null) val='';
		if(!isNaN(parseInt(rul))){
			rul=crit;
			crit=true;	
		}
		var typvar=typeof rul;
		if(typvar=='string'){
			var typreg=typeof crit;
			var typtest=typeof thi.rulesMatch[rul];
			if(typreg=='boolean'){
		 		if(typtest=='string')
					ret.stat=thi.strictFor(rul,val,crit);
				else if(typtest=='function')
					ret.stat=thi.fireRuleFunction(val,rul,crit);
				else
					blad=true;
		 	}else if(typtest!='undefined'){
		 		ret.stat=thi.fireRuleFunctionWithParam(val,rul,crit);
				
			}else
				blad=true;
		}else{
			ret.msg='Niepoprawny identyfikator reguły';
			ret.stat=false;
			blad=true;
		}
	
		if(blad){
			ret.stat=false;
			ret.msg='Niepoprawna reguła : val-'+val+' rul-'+rul+' crit-'+crit+' typvar-'+typvar+' typreg-'+typreg+' typtest+'+typtest;			
		}
		if(!blad && !ret.stat && thi.rulesDefFormat[rul])
			ret.msg=thi.rulesDefFormat[rul];
			
		return ret;
	},

	createErrorMsg:function(){
		tab=this.errors[this.currentfs];
		var ret='';
		if(tab.length==1)
			ret+=tab[0];
		else{
			ret+='<p>Aby kontynuować popraw warto¶ci poniżej wymienionych pól:</p>';
			$.each(tab, function (i,o){
				ret+='<li>'+o+'</li>';						 
			});
		}
		return ret;
	},

	testAll:function(type, a){
		var ret=true; 
		for(var i=0; i<a.length;i++)
			eval('ret&&=this.'+type+"('"+a[i]+"'");
		return ret; 
	}
}

function int(s){
	return 	Validate.strictFor('int',s);
};

function nat(s){
	return 	Validate.strictFor('nat',s);
};

function nat0(s){
	return 	Validate.strictFor('nat0',s);
};
