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;
    // function para cuando el envio es correcto. Se usa tanto en envios por ajax como en los envios normales
    var _success = options.success || 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);
    // 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;
// 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 (_error === '') {
			_error = _LM.getLabel(field_name, error);
        }
        if (el[0].tagName.toLowerCase() == "option") {
            el = el.parents("select:first");
        }
        el.addClass(errorClass || _that.errorClass);
    }
    // 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 = "";
            $.each(_formStructure, function(rootIndex, rootValue) {
                $.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 (!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 !== '') {
            _infoLabel.html(_error);
            return false;
        }
        else {

           _infoLabel.html(_that.sendingMsg);

            if (_ajaxConfig) {
                var datapost = _serialize(_form);
				var ajax = _ajaxConfig;
                $.ajax({
                    url : ajax.url,
					timeout:30000,
                    type: ajax.type,
                    dataType: ajax.dataType,
                    data: 'nocache=' + Math.random() + '&' + datapost,
                    success: _success,
					error:function(a,b,c)
					{
						alert(a);
					}
                });
				//_infoLabel.html(_that.okMsg); Esto interfiere con los beforeSend
                return false;
            }
            else {
                if (_success) {
                    _success();
                }
                _infoLabel.html(_that.sendingMsg);
                return true;
            }
        }
    }
    // 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.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();
