function DyFormValidator(options) {
	
	if (!SimpleTextManager) {
		throw "DY.FORM.VALIDATOR | Constructor | SimpleTextManager is required ";
	}
	if (!DyRegExp) {
		throw "DY.FORM.VALIDATOR | Constructor | dyRegExp is required ";
	}
	
// ini: propiedades privadas
    // json con la estructura del formulario			
    var _formStructure = options.formStructure || {};
    // json conla configuración ajax
    var _ajaxConfig = options.ajaxConfig || null;
    // boolean para ver si hay que enviar automaticamente el formulario una vez validado. Se pone a false si hay que hacer validaciones concretas que no se puedan automatizar antes del envio
    var _autoSend = (options.autoSend == null)? true : options.autoSend;
	// function para cuando el envio es correcto. Se usa tanto en envios por ajax como en los envios normales
    var _success = options.success || null;
	// funcion que se llama cuando empieza el envio
	var _startSend = options.startSend || null;
	// funcion que se llama cuando ha acabado de validar, se produce justo antes del startSend, peo se lanza independiemntemente de si esta el autoSend activo, mientras que con el startSend no pasa
	var _onValidated = options.onValidated || null;
	// function para cuando se produce un error de validaci�n. Se usa como callback para poder a�adir programaci�n aparte de la predefinida por el propio validator
    var _onValidateError = options.onValidateError || null;
    // variable de almacenamiento de errores
    var _error = '';
    // atributo id del formulario
    var _formID = options.formID || '';
    // prefijo para los nombres de los campos
    var _prefixName = options.prefixName || '';
    // objeto jquery del formulario
    var _form = $('form#' + options.formID) || null;
    // objeto jquery del elemento donde se mostrarán los errores
    var _infoLabel = $(options.infoLabel);
	var _auxInfoLabel = null;
    // a true si queremos escapar los datos en el envio (ESTA OPCIÓN NO FUNCIONA BIEN)
    var _escape = options.escape || false;
    // referencia privada a this
    var _that = this;
	// if true, nested all validation errors
	var _nestedErrors = options.nestedErrors || false;
// end: private properties
    
// ini: public properties
    // options.langManager es el json de configuracion de SimpleTextManager
	var _LM = new SimpleTextManager(options.langManager.textos, options.langManager.idioma, options.langManager.form);
    //_LM.setupTextManager(options.langManager.textos, options.langManager.idioma, options.langManager.form);
    // clase css general para asignar a los elementos que no son validos
    this.errorClass = options.errorClass || '';
    // mensage de 'enviando datos..'
    this.sendingMsg = options.sendingMsg || '';
    // mensage de 'envio ok'
    this.okMsg = options.okMsg || '';
    // TODO: mensage de 'error en el envio'
// end: public properties
    
// ini: private methods
    // Setea el error si y asigna clase css de error general o especifica del elemento
    function _setError(field_name, el, error, errorClass) {
		el = el[0] ? el : _form.find('[name=' + field_name + ']');
		if (!_nestedErrors) {
			if (_error === '') {
				_error = _LM.getLabel(field_name, error);
				if (_formStructure[field_name].infoLabel) {
					_auxInfoLabel = $(_formStructure[field_name].infoLabel);
				}
	        }	
		}
		else {
			if ( _formStructure[field_name].infoLabel ) {
				if ($(_formStructure[field_name].infoLabel).text() === "") {
					$(_formStructure[field_name].infoLabel).html(_LM.getLabel(field_name, error));
				}
			}
			else {
				_error += _LM.getLabel(field_name, error) + "<br />&nbsp<br />";
			}
		}
        
        if (el[0].tagName.toLowerCase() == "option") {
            el = el.parents("select:first");
        }
        el.addClass(errorClass || _that.errorClass);
		if(_onValidateError) _onValidateError(field_name, error);
    }
    // Resetea el formulario asignando valores por defecto de inicio.
    function _resetForm() {
		$.each(_formStructure, function(i, v) {
			var e =  _form.find('[name=' + i.toString() + ']');
			if (e && e[0] && e[0].type) {
				v.type = e[0].type.toLowerCase() || "text";	
			}
			if (v.type != 'password' && v.type != 'hidden' && v.type != 'radio') {
                e
					.removeClass(v.errorClass || _that.errorClass)
					.attr({
						value: _LM.getLabel(i.toString(),'_default'),
	                    title: _LM.getLabel(i.toString(),'_default')
	                });
            }
			if (v.type != 'password' || v.type != 'text') {
                e.attr( { maxlength: v.maxlength || "" } );
            }
			
        });
        _initSubmit();
    }
    // Configuración del submit del formulario
    function _initSubmit(){
        _form.submit(function(){
            _error = "";
			_infoLabel.html('');
			_auxInfoLabel = null;
            $.each(_formStructure, function(rootIndex, rootValue) {
				if (rootValue.infoLabel) {
					$(rootValue.infoLabel).html('');
				}
                $.each(rootValue.validators, function(validatorIndex, validatorValue) {
                    var el = _form.find('[name=' + rootIndex.toString() + ']');
                    el.removeClass(rootValue.errorClass || _that.errorClass);
					if (rootValue.type == 'select') {
                        el = el.find(':selected');
                    }
                    else if (rootValue.type == 'checkbox' || rootValue.type == 'radio') {
                        el = el.filter(':checked');
                    }
                    _initValidate(rootIndex, el, rootValue, validatorValue);
                });
            });
            return _submit(this);
        });
    }
    // Valida un campo
    function _initValidate(field_name, el, rootValue, validatorValue) {
		if (el && el[0] && el[0].value) {
			el[0].value = $.trim(el[0].value);
		}
		if (!validatorValue.regEx) {
            validatorValue.regEx = /[^\s]/;
        }
		if (el[0] && _form.find('[name=' + field_name + ']').attr('type') == "checkbox") {
            return;
        }        
		else {
			if (validatorValue.regEx.constructor == String) {
				var originalRegExp = validatorValue.regEx;
				var valid = false;
				$.each(validatorValue.regEx.split(","), function(i, v){ // Si tiene mas de una opcion de validacion...
					validatorValue.regEx = _that.getFilter(v);
					if (_validate(field_name, el, rootValue, validatorValue, true)) {
						valid = true;
						return;
					}
				});
				if (!valid) {
					_validate(field_name, el, rootValue, validatorValue);
				}
				validatorValue.regEx = originalRegExp;
			}
			else
			{
				_validate(field_name, el, rootValue, validatorValue);
			}
		}
		
    }
	function _validate(field_name, el, rootValue, validatorValue, pre) {
		var valid = true;
		var value = $.trim(el.val()) || "";
		if (rootValue.free) {
            if ( ( value !== "" && value != el.attr('title') ) && !validatorValue.regEx.test(value) ) {
					valid = false;
            }
        }
        else {
            if (!validatorValue.regEx.test(value) || value == el.attr('title')) {
				valid = false;
            }
            else if ( validatorValue.equal && _form.find('[name=' + validatorValue.equal + ']').val() != value ) {
					valid = false;
            }
        }
		if (!valid) {
			if (pre) {
				return false;
			}
			else {
				_setError(field_name, el, validatorValue.errorMsg, rootValue.errorClass);
			}
		}
		return true;
    }

    // Envio del formulario si no hay errores
    function _submit(){
		if (_error !== '') {
			var infoLbl = _auxInfoLabel !== null ? _auxInfoLabel : _infoLabel;
			infoLbl.html(_error);
			//_onValidateError(_error);
            return false;
        }
        else {
			_infoLabel.html(_that.sendingMsg);
			if (_ajaxConfig) {
				if(_onValidated) _onValidated();
				if (_autoSend) doSend();
				
				/*var datapost = _serialize(_form);
				var ajax = _ajaxConfig;
				$.ajax({
					url: ajax.url,
					type: ajax.type,
					dataType: ajax.dataType,
					data: 'nocache=' + Math.random() + '&' + datapost,
					success: _success,
					error: function(a, b, c){
						//alert(a);
					}
				});
				_infoLabel.html(_that.okMsg);*/
				return false;
			}
			else {
				_infoLabel.html(_that.sendingMsg);
				if (_success) {
					_success();
				}
				
				return true;
			}
			
        }
    }
	
	
	function doSend(){
		var datapost = _serialize(_form);
		var ajax = _ajaxConfig;
		if(_startSend) _startSend();
		$.ajax({
			url: ajax.url,
			type: ajax.type,
			dataType: ajax.dataType,
			data: 'nocache=' + Math.random() + '&' + datapost,
			success: _success,
			error: function(a, b, c){
				//alert(a);
			}
		});
		_infoLabel.html(_that.okMsg);
	}
	
	
    // Genera string de envio
    function _serialize(){
        var datapost = "";
        $(_form).find('[name!=""]').each(function(i){
            if (this.type && (this.type.toLowerCase() == "radio" || this.type.toLowerCase() == "checkbox") && !this.checked) {
                // Los radios button y los checkbox no se envian si no estan seleccionados
                return;
            }
            datapost = _escapeData(datapost, this);
        });
        return datapost;
    }
    // Metodo que escapa el valor de un elemento
    function _escapeData(datapost, el) {
        if (_escape) {
            datapost += _prefixName + el.name + '=' + escape(unescape(el.value)) + '&';
        }
        else {
            datapost += _prefixName + el.name + '=' + el.value + '&';
        }
		return datapost;
    }
    // end: private methods
    
    // ini: public methods
    this.resetForm = function(){
        _reserForm();
    };
	
	this.send = function(){
        doSend();
    };
    
    this.getError = function(){
        return _error;
    };
    
    this.getInfoLabel = function(){
        return _infoLabel;
    };
    
    this.getAjaxConfig = function(){
        return _ajaxConfig;
    };
    
    this.getSuccess = function(){
        return _success;
    };
// end: public methods
    
    _resetForm();
    
}
DyFormValidator.prototype = new DyRegExp();