function FormValidationPopup( obj ) {
	this.obj = obj;

	this.mobj = document.createElement( 'div' );
	this.mobj.className = 'fvError';
	this.mobj.style.display = 'none';
	this.mobj.innerHTML = '<div class="fvErrorBottom"><div class="fvErrorBottomIn">&nbsp;</div></div>';
	this.mobj.onclick = function( ) { this.style.display = 'none'; };
	
	this.mtobj = document.createElement( 'div' );
	this.mtobj.className = 'fvErrorText';
	
	this.obj.parentNode.insertBefore( this.mobj, this.obj );
	this.mobj.insertBefore( this.mtobj, this.mobj.firstChild );
}

FormValidationPopup.prototype.showMessage = function( text ) {
	this.obj_coords = objCoords( this.obj );
	if( text == '' ) {
		this.mobj.style.display = 'none';
	} else {
		this.mtobj.innerHTML = text;
		this.mobj.style.position = 'absolute';
		this.mobj.style.display = '';

		var wSize = windowSize( );
		var l = ( this.obj_coords.x + this.obj_coords.w - 24 );
		if(( l + this.mobj.offsetWidth ) > wSize.w ) l = wSize.w - this.mobj.offsetWidth;

		this.mobj.style.left = l + 'px';
		this.mobj.style.top = ( this.obj_coords.y - this.mobj.offsetHeight ) + 'px';
	}
};

FormValidationPopup.prototype.setClass = function( name ) {
	if( name == '' ) {
		this.mobj.className = 'fvError';
	} else {
		this.mobj.className = 'fvError ' + name;
	}
};

FormValidationPopup.prototype.getTop = function( ) {
	return ( this.obj_coords.y - this.mobj.offsetHeight );
};

var returnFVIPopup = function( obj ) {
	return new FormValidationPopup( obj );
};

function FormValidationInput( obj, rules, fvg ) {
	if( rules == '' ) {
		this.rules = new Array( );
		return ;
	}
	this.obj = obj;
	this.obj.fviObj = this;
	this.popup = returnFVIPopup( obj );
	this.fvg = fvg;

	this.ajaxRule = -1;
	this.isRequired = false;
	this.isOptional = false;
	this.onlySubmit = false;
	this.ifVisible = false;
	this.minLen = -1;
	this.maxLen = -1;
	this.otherRules = new Array( );
	this.ajaxOk = false;
	
	rules = rules.split( /,/ );
	for( i in rules ) {
		if( rules[i] == '' ) continue;
		
		var rule = rules[i].replace( /([^\]]+)\[.*/, "$1");
		var param = '';
		
		if( rules[i].indexOf( '[' ) != -1 ) {
			for( var n = parseInt( i ) + 1, nm = rules.length; ( n < nm ) && ( rules[i].indexOf( ']' ) == -1 ); n ++ ) { 
				rules[i] = rules[i] + ',' + rules[n];
				rules[n] = '';
			}
			param = rules[i].replace( /[^\]]+\[([^\]]+)\]/, "$1");
		}
	
		if( rule == 'optional' ) {
			this.isOptional = true;
		} else if( rule == 'required' ) {
			this.isRequired = true;
		} else if( rule == 'ifvisible' ) {
			this.ifVisible = true;
		} else if( rule == 'onlysubmit' ) {
			this.onlySubmit = true;
		} else if( rule == 'length' ) {
			var lim = param.split( /,/ );
			if( lim.length != 2 ) continue;

			this.minLen = parseInt( lim[0] );
			this.maxLen = parseInt( lim[1] );
		} else {
			// Vlastni pravidlo - bud nazev nebo kombatibilini = parametr , pak ignorujem nazev, ktery zachovavam pro kombatibilit
			if( typeof( FormValidatorRules ) == 'undefined' ) continue;
			
			var rule_info = null;
			var rule_name = rule;
			if( typeof( FormValidatorRules[rule] ) == 'undefined' ) {
				rule_info = FormValidatorRules[param];
				rule_name = param;
			} else {
				rule_info = FormValidatorRules[rule];
			}
			if( typeof( rule_info ) == 'undefined' ) continue;
			
			this.otherRules[this.otherRules.length] = rule_name;				
		}
	}
	
	this.obj.onblur = function( ) { this.fviObj.ajaxOk = false; this.fviObj.validate( false ); };
}

FormValidationInput.prototype.addError = function( error ) {
	if( this.errors != '' ) this.errors = this.errors + '<br />';
	
	this.errors = this.errors + error;
};

FormValidationInput.prototype.validate = function( submit ) {
	if( submit != true ) submit = false;
	if( typeof( this.obj ) == 'undefined' ) return true;
	
	var data = '';
	this.errors = '';
	
	if( !submit ) if( this.onlySubmit ) return true;
	if( this.ifVisible ) {
		var testObj = this.obj;
		while( testObj ) {
			if( !testObj.style ) break;
			if( testObj.style.display == 'none' ) return true;
			testObj = testObj.parentNode;
		}
	}
	
	if( this.obj.type == 'select-one' ) {
		data = this.obj.options[this.obj.selectedIndex].value;
	} else {
		data = this.obj.value;
	}
	
	if( data == '' || data == 0 ) if( this.isOptional ) {
		this.popup.showMessage( '' );
		return true;
	} else if( this.isRequired ) {
		this.addError( FormValidatorLang.required.alertText );
	}
	
	if( this.minLen != -1 ) {
		len = data.length;
		
		if(( len < this.minLen) || ( len > this.maxLen)) this.addError (
				FormValidatorLang.length.alertText + this.minLen +
				FormValidatorLang.length.alertText2 + this.maxLen +
				FormValidatorLang.length.alertText3
		);		
	}
	
	var i;
	for( i in this.otherRules ) {
		var rule = FormValidatorRules[this.otherRules[i]];
		var fn = null;
		
		if( typeof( rule.regex ) != 'undefined' ) {
			if( !data.match( rule.regex )) this.addError( rule.alertText );
			continue;
		} else if( typeof( rule.nname ) != 'undefined' ) {
			fn = window[ rule.nname ];
		} else if( typeof( rule.nname_i ) != 'undefined' ) {
			fn = window[ rule.nname_i ];
		} else if( typeof( rule.nname_p ) != 'undefined' ) {
			fn = window[ rule.nname_p ];
		} else continue;

		if( fn == null ) continue; 
		if( typeof( fn ) == 'undefined' ) continue;
		
		var fn_result = true;
		if( typeof( rule.nname ) != 'undefined' ) {
			fn_result = fn();
		} else if( typeof( rule.nname_i ) != 'undefined' ) {
			fn_result = fn( this.obj );
		} else if( typeof( rule.nname_p ) != 'undefined' ) {
			fn_result = fn( this.obj.id );
		}

		if( fn_result == false ) this.addError( rule.alertText );
	}

	// AJAXi validace - jen kdyz jsme jinak ok a je zmena
	if( this.errors == '' ) {
		if( !this.ajaxOk ) for( i in this.otherRules ) {
			var rule = FormValidatorRules[this.otherRules[i]];
			if( typeof( rule.file ) == 'undefined' ) continue;
			
			if( typeof( rule.alertTextLoad ) != 'undefined' ) {
				this.popup.setClass( 'fvLoading' );
				this.popup.showMessage( rule.alertTextLoad );
			}

			var self = this;
			this.fvg.addAjaxCall( rule.file, 'validateValue=' + data + '&validateId=' + this.obj.id, function( vresult ) { return self.ajaxReturn( vresult, i ); } );
		}
	} else {
		this.popup.setClass( '' );
	}
	
	this.popup.showMessage( this.errors );
	if( this.errors != '' ) if( submit ) {
		var scroll = getScroll( );
		var otop = this.popup.getTop( ) - 10;
		if( scroll.y >= otop ) {
			if( otop < 0 ) otop = 0;
			setScroll( scroll.x, otop );
		}
	}
	return this.errors == '';
};

FormValidationInput.prototype.ajaxReturn = function( vresult, ajaxRule ) {
	var rule = FormValidatorRules[this.otherRules[ajaxRule]];
	if( vresult['res'] == 'true' ) {
		if( typeof ( rule.alertTextOk ) != 'undefined' ) {
			this.popup.setClass( 'fvOk' );
			this.popup.showMessage( rule.alertTextOk );
		}
		return true;
	} else {
		this.popup.setClass( '' );
		this.popup.showMessage( rule.alertText );

		var scroll = getScroll( );
		var otop = this.popup.getTop( ) - 10;
		if( scroll.y >= otop ) {
			if( otop < 0 ) otop = 0;
			setScroll( scroll.x, otop );
		}
		
		return false;
	}
};

FormValidationInput.prototype.drop = function( ) {
	this.fvg.dropRule( this );
};

function FormValidation( obj ) {
	this.ajaxQueque = new Array( );
	this.sendOnAjax = false;
	this.waitAjax = false;
	this.ajaxOk = true;
	this.fvi = new Array( );
	this.obj = obj;
	this.noValidation = false;
	this.ajaxObj = new AJAX( );

	var self = this;

	this.ajaxObj.onLoad = function( ) {
		var vdata = this.getXML( );
		var vresult = new Array( );
		
		if( vdata.lastChild.nodeName == 'validate' ) {
			for( var i = 0, j = vdata.lastChild.childNodes.length; i < j; i ++ ) {
				var cnode = vdata.lastChild.childNodes[i];
				if( cnode.nodeName == '#text' ) continue;
				if( cnode.nodeName == undefined ) continue;
				
				if( cnode.firstChild == null ) {
					vresult[cnode.nodeName] = ''; 
				} else {
					vresult[cnode.nodeName] = cnode.firstChild.nodeValue;
				}
			}			
		} else {
			vresult['res'] = 'false';
		}
		
		self.ajaxOk = this.ret( vresult ) && self.ajaxOk;
		
		if( self.ajaxQueque.length == 0 ) {	// Nemame frontu - shodime nastaveni
			if( self.ajaxOk && self.sendOnAjax ) {
				self.obj.submit( );
			}
	
			self.waitAjax = false;
			self.sendOnAjax = false;
			self.ajaxOk = true;
		} else {
			self.ajaxValidate( );	// Dalsi AJAXi pravidlo
		}
	};
	
	obj.onsubmit = function( ) { return self.onsubmit; };
};

FormValidation.prototype.addSubmit = function ( obj, rules ) {
	var self = this;

	if( rules.indexOf( 'disable' ) != -1 ) {
		obj.onclick = function( ) { self.noValidation = true; };
	} else {
		obj.onclick = function( ) { self.noValidation = false; return self.onSubmit( ); };  
	}
}

FormValidation.prototype.addInput = function ( obj, rules ) {
	var fvi = new FormValidationInput( obj, rules, this );
	
	this.fvi[this.fvi.length] = fvi;
};

FormValidation.prototype.dropRule = function ( obj ) {
	for( var i = 0, j = this.fvi.length; i < j; i ++ ) {
		if( this.fvi[i] != obj ) continue;
		
		this.fvi.splice( i, 1 );
		break;
	}
};

FormValidation.prototype.onSubmit = function( noSubmit ) {
	// Zde probiha validace
	if( this.noValidation ) return true;
	
	var res = true;
	this.sendOnAjax = false;
	
	for( var i = 0, m = this.fvi.length; i < m; i ++ ) 
		if( !this.fvi[i].validate( res )) {
			res = false;
		}

	if( res ) this.sendOnAjax = ( noSubmit ) ? false : true;	
	if( this.waitAjax ) return false;
	return res;
};

FormValidation.prototype.ajaxValidate = function( ) {
	if( this.ajaxObj.loading == 1 ) return ;
	if( this.ajaxQueque.length == 0 ) return ;

	var rec = this.ajaxQueque.splice( 0, 1 );
	if( rec.length != 1 ) return ;
	
	rec = rec[0];
	this.ajaxObj.ret = rec.ret;
	this.ajaxObj.post( rec.addr, rec.post );
};

FormValidation.prototype.addAjaxCall = function( addr, post, ret ) {
	this.waitAjax = true;
	this.ajaxQueque.splice( 0, 0, { addr: addr, post: post, ret: ret });
	this.ajaxValidate( );
};

// Zde jen povesime objekty na inputz
function EnableFormValidator( form ) {
	if( typeof( FormValidatorLang ) == 'undefined' ) return ;
	var forms;
	
	if( form == null ) {
		forms = document.getElementsByTagName( 'form' );
	} else {
		forms = new Array( form );
	}

	for( var i = 0, m = forms.length; i < m; i ++ ) {
		var form = forms[i];
		var fv;
		if( !form.fv ) {
			fv = new FormValidation( form );
			form.fv = fv;	// Pridame zpetny odkaz
		} else {
			fv = form.fv;
		}
		
		for( ii = 0, mi = form.length; ii < mi; ii ++ ) {
			var input = form[ii];
			if( input.fviObj ) continue;
			
			var rules = form[ii].className;
			if( rules.indexOf( 'validate[' ) != -1 ) rules = rules.replace( /^([^ ]+ )*validate\[(([a-z0-9_\-]+(\[[a-z0-9_\-,]*\])?|,)*)\]( [^ ]+)*$/i, "$2" ); else rules = '';
			
			if( input.type == 'submit' ) {
				fv.addSubmit( input, rules );
			} else {
				fv.addInput( input, rules );
			}
		};
	}
}
