/*
 * @(#)validador.js
 */

/**
 * Validador que permite llamadas en cadena. Sólo obtiene el primer error encontrado
 * Por ej. validador.requerido().numerico().obtenerMensaje();
 *
 *
 * @author alejandro.gonzalez
 * @version 0.1
 */
var Validador = Class.create();
Validador.prototype = {
    //--------------------------------------------------------------------------
	// Constructor
    //--------------------------------------------------------------------------
    
	/**
	 * Constructor
	 *
	 * @param campo El campo
	 * @parama nombre El nombre de lcampo a mostrar en los mensaje de error
	 */
	initialize : function(campo, nombre) {
		this.campo = $(campo);
		this.nombre = nombre;
		this.mensaje = '';
	},
	
	//--------------------------------------------------------------------------
	// Métodos
    //--------------------------------------------------------------------------
    
    correo : function() {
    	 if (this.mensaje.empty() && !this.campo.value.empty()) {
	    		var error = Validacion.Campo.correo(this.campo, this.nombre);
	    		
	    		if (!error.empty()) {
	    			this.mensaje = '- ' + error + '\n';
	    		}
			}
		
		return this;
    },
    
    
    alfanumerico : function() {
	    if (this.mensaje.empty() && !this.campo.value.empty()) {
	    		var error = Validacion.Campo.alfanumerico(this.campo, this.nombre);
	    		
	    		if (!error.empty()) {
	    			this.mensaje = '- ' + error + '\n';
	    		}
			}
		
		return this;
    },
    
    /**
	 * Valida campo cuyo valor debe estar compuesto sólo por dígitos
	 *
	 * @return El validador, para llamadas encadenadas
	 */
   digitos : function() {
    	if (this.mensaje.empty() && !this.campo.value.empty()) {
    		var error = Validacion.Campo.digitos(this.campo, this.nombre);
    		
    		if (!error.empty()) {
    			this.mensaje = '- ' + error + '\n';
    		}
		}
		
		return this;
    },
    
    /**
	 * Valida campo cuyo valor debe estar entre 2 valores
	 *
	 * @param minimo El valor mínimo del intervalo
	 * @param maximo El valor máximo del intervalo
	 * @param cerrado Indica si el intervalo es cerrado
	 * @return El validador, para llamadas encadenadas
	 */
    entre : function(minimo, maximo, cerrado) {
    	if (this.mensaje.empty() && !this.campo.value.empty()) {    		   	
    		var error = Validacion.Campo.entre(this.campo, this.nombre, minimo, maximo, cerrado);
    		
    		if (!error.empty()) {
    			this.mensaje = '- ' + error + '\n';
    		}
		}
		
		return this;
    },
    
    /**
	 * Valida campo cuyo valor debe ser mayor a un cierto valor
	 *
	 * @param valor El valor
	 * @return El validador, para llamadas encadenadas
	 */
    mayor : function(valor) {
    	if (this.mensaje.empty() && !this.campo.value.empty()) {
			var error = Validacion.Campo.mayor(this.campo, this.nombre, valor);
			
			if (!error.empty()) {
    			this.mensaje = '- ' + error + '\n';
    		}
		}
		
		return this;
    },
    
    /**
	 * Valida campo cuyo valor debe ser mayor o igual a un cierto valor
	 *
	 * @param valor El valor
	 * @return El validador, para llamadas encadenadas
	 */
    mayorIgual : function(valor) {
    	if (this.mensaje.empty() && !this.campo.value.empty()) {
			var error = Validacion.Campo.mayorIgual(this.campo, this.nombre, valor);
			
			if (!error.empty()) {
    			this.mensaje = '- ' + error + '\n';
    		}
		}
		
		return this;
    },
    
    /**
	 * Valida campo cuyo valor debe ser menor a un cierto valor
	 *
	 * @param valor El valor
	 * @return El validador, para llamadas encadenadas
	 */
    menor : function(valor) {
    	if (this.mensaje.empty() && !this.campo.value.empty()) {
			var error = Validacion.Campo.menor(this.campo, this.nombre, valor);
			
			if (!error.empty()) {
    			this.mensaje = '- ' + error + '\n';
    		}
		}
		
		return this;
    },
    
    /**
	 * Valida campo cuyo valor debe ser menor o igual a un cierto valor
	 *
	 * @param valor El valor
	 * @return El validador, para llamadas encadenadas
	 */
    menorIgual : function(valor) {
    	if (this.mensaje.empty() && !this.campo.value.empty()) {
			var error = Validacion.Campo.menorIgual(this.campo, this.nombre, valor);
			
			if (!error.empty()) {
    			this.mensaje = '- ' + error + '\n';
    		}
		}
		
		return this;
    },

    igual : function(valor) {
    	if (this.mensaje.empty() && !this.campo.value.empty()) {
			var error = Validacion.Campo.igual(this.campo, this.nombre, valor);
			
			if (!error.empty()) {
    			this.mensaje = '- ' + error + '\n';
    		}
		}
		
		return this;
    },
    
    /**
	 * Ejecuta un método de validación, y le entrega como parámetros el campo
	 * y el nombre del campo para mensajes de error.
	 * Espera que el método retorne un string con el error ocurrido
	 *
	 * @param valor El método
	 * @return El validador, para llamadas encadenadas
	 */
    ejecutar : function(metodo) {
    	if (this.mensaje.empty() && !this.campo.value.empty()) {
			var error = metodo(this.campo, this.nombre);
			
			if (error && !error.empty()) {
    			this.mensaje = '- ' + error + '\n';
    		}
		}
		
		return this;
    },
    
    /**
	 * Valida campo cuyo valor debe ser numérico
	 *
	 * @return El validador, para llamadas encadenadas
	 */
   numerico : function() {
    	if (this.mensaje.empty() && !this.campo.value.empty()) {
			var error = Validacion.Campo.numerico(this.campo, this.nombre);
			
			if (!error.empty()) {
    			this.mensaje = '- ' + error + '\n';
    		}
		}
		
		return this;
    },
	
	/**
	 * Valida campo requerido
	 *
	 * @return El validador, para llamadas encadenadas
	 */
	requerido : function() {
		if (this.mensaje.empty()) {
			var error = Validacion.Campo.requerido(this.campo, this.nombre);
			
			if (!error.empty()) {
    			this.mensaje = '- ' + error + '\n';
    		}
		}
		
		return this;
	},
	
	/**
	 * Valida campo cuyo valor debe ser un rut válido
	 *
	 * @param valor El valor
	 * @return El validador, para llamadas encadenadas
	 */
    rut : function() {
    	if (this.mensaje.empty() && !this.campo.value.empty()) {
			var error = Validacion.Campo.rut(this.campo, this.nombre);
			
			if (!error.empty()) {
    			this.mensaje = '- ' + error + '\n';
    		}
		}
		
		return this;
    },
    
    	
	/**
	 * Valida campo cuyo valor debe ser un run válido
	 *
	 * @param valor El valor
	 * @return El validador, para llamadas encadenadas
	 */
    run : function() {
    	if (this.mensaje.empty() && !this.campo.value.empty()) {
			var error = Validacion.Campo.rut(this.campo, this.nombre, true);
			
			if (!error.empty()) {
    			this.mensaje = '- ' + error + '\n';
    		}
		}
		
		return this;
    },
    
    /**
	 * Valida campo cuyo valor debe ser de tipo fecha
	 *
	 * @param valor El valor
	 * @return El validador, para llamadas encadenadas
	 */
    fecha : function() {
    	if (this.mensaje.empty() && !this.campo.value.empty()) {
			var error = Validacion.Campo.fecha(this.campo, this.nombre);
			
			if (!error.empty()) {
    			this.mensaje = '- ' + error + '\n';
    		}
		}
		
		return this;
    },
	
	/**
	 * Obtiene el mensaje de error del validador
	 *
	 * @return El mensaje de error
	 */
	obtenerMensaje : function() {
		var mensaje = this.mensaje;
		if (mensaje.empty()) {
			this.campo.className = "ac_campos";
		} else {
			this.campo.className = "ac_campos_req";
		}
		return mensaje;
	}
};

/**
 * Métodos para extender la clase Element de prototype
 */
var ValidadorUtils = {
	crearValidador : function(campo, nombre) {
		return new Validador(campo, nombre);
	}
};

// Se extiende la clase Element
Element.addMethods(ValidadorUtils);

/**
 * Crea un validador para un campo
 *
 * @param campo El campo
 * @parama nombre El nombre usado en los mensaje de error
 */
function $V(campo, nombre) {
	return new Validador(campo, nombre);
}

/**
 * Namespace para métodos de validación
 *
 * @author alejandro.gonzalez
 * @version 0.1
 */
var Validacion = {

	/**
	 * Expresión regular para correos electronicos
	 */
	CORREO_REGEXP : /^[a-z0-9,!#\$%&\*\+/=\?\^_`\{\|}~-]+(\.[a-z0-9,!#\$%&\*\+/=\?\^_`\{\|}~-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*\.([a-z]{2,})$/ , 

	/**
	 * Expresión regular para alfanuméricos
	 */
	ALFANUMERICOS_REGEXP : /^[\w|\s]{0,}$/,
	
	/**
	 * Expresión regular para dígitos
	 */
	DIGITOS_REGEXP : /^[0-9]+$/,
	
	/**
	 * Expresión regular para números en formato 9.9 ó 9
	 */
	NUMERO_REGEXP : /^-{0,1}[0-9]+\.{0,1}[0-9]*$/,	
	
	/**
	 * Indica si un string está compuesto sólo por dígitos
	 *
	 * @param str El string
	 * @return true si sólo tiene dígitos y false en caso contrario
	 */
	esDigitos : function(str) {
		return Validacion.DIGITOS_REGEXP.test(str.strip());
	},
	
	esAlfanumerico : function(str) {
		return Validacion.ALFANUMERICOS_REGEXP.test(str.strip());
	},
	
	esCorreo : function(str) {
		return Validacion.CORREO_REGEXP.test(str.strip());
	},
	
	/**
	 * Indica si un string que representa un número es mayor a un cierto valor
	 *
	 * @param str El string
	 * @param valor El valor 
	 * @return true si es mayor y false en caso contrario
	 */
	esMayor : function(str, valor) {
		return Number(str.strip()) > Number(valor);
	},
	
	/**
	 * Indica si un string que representa un número es mayor o igual a un cierto valor
	 *
	 * @param str El string
	 * @param valor El valor 
	 * @return true si es mayor o igual y false en caso contrario
	 */
	esMayorIgual : function(str, valor) {
		return Number(str.strip()) >= Number(valor);
	},
	
	/**
	 * Indica si un string que representa un número es menor a un cierto valor
	 *
	 * @param str El string
	 * @param valor El valor 
	 * @return true si es menor y false en caso contrario
	 */
	esMenor : function(str, valor) {
		return Number(str.strip()) < Number(valor);
	},
	
	/**
	 * Indica si un string que representa un número es menor o igual a un cierto valor
	 *
	 * @param str El string
	 * @param valor El valor 
	 * @return true si es menor o igual y false en caso contrario
	 */
	esMenorIgual : function(str, valor) {
		return Number(str.strip()) <= Number(valor);
	},

	esIgual : function(str, valor) {
		return str == valor;
	},
	
	/**
	 * Indica si un string no esta vacío ni en blanco
	 *
	 * @param str El string
	 * @return true si es vacío y false en caso contrario
	 */
	esNoBlanco : function(str) {
		return !str.strip().empty();
	},
	
	/**
	 * Indica si un string es numérico (su valor es del tipo 9.9 ó 9)
	 *
	 * @param str El string
	 * @return true si es numérico y false en caso contrario
	 */
	esNumerico : function(str) {
		return Validacion.NUMERO_REGEXP.test(str.strip());
	},
	
	/**
	 * Indica si un string que representa un número está dentro de un cierto rango
	 *
	 * @param str El string
	 * @param minimo El valor mínimo del intervalo
	 * @param maximo El valor máximo del intervalo
	 * @param cerrado Indica si el intervalo es cerrado
	 * @return true si es menor o igual y false en caso contrario
	 */
	estaEntre : function(str, minimo, maximo, cerrado) {
		if (cerrado) {
			return Validacion.esMayorIgual(str, minimo) && Validacion.esMenorIgual(str, maximo);
		} else {
			return Validacion.esMayor(str, minimo) && Validacion.esMenor(str, maximo);
		}
	},
	
	/**
	 * Indica si un string es Rut
	 *
	 * @param str El string
	 * @return true si es numérico y false en caso contrario
	 */
	esRut : function(str) {
		return ValidacionRut.formatearValidar(str);
	},

	/**
	 * Indica si un string es de tipo fecha
	 *
	 * @param str El string
	 * @return true si es numérico y false en caso contrario
	 */
	esFecha : function(str) {
		//La función validaStringFecha se encuentra en comu.js, este debe ser incluido en la página.
		var patron=/^[0-9][0-9]\/[0-9][0-9]\/[0-9][0-9][0-9][0-9]$/

		return (patron.test(str)) && (validaStringFecha(str));
	}
};

/**
 * Namespace para métodos de validación de campos
 *
 * @author alejandro.gonzalez
 * @version 0.1
 */
Validacion.Campo = {

	correo : function(campo, nombre) {
		return Validacion.esCorreo(campo.value) ? '' : Validacion.Mensaje.CORREO.evaluate({ nombre : nombre });
	},
	
	alfanumerico : function(campo, nombre) {
		return Validacion.esAlfanumerico(campo.value) ? '' : Validacion.Mensaje.ALFANUMERICO.evaluate({ nombre : nombre });
	},
	/**
	 * Valida un campo de dígitos
	 *
	 * @param campo El campo
	 * @param nombre El nombre del campo, para utilizarlo en mensajes de error
	 * @return El texto del error o un string vacío si no hay error
	 */
	digitos : function(campo, nombre) {
		return Validacion.esDigitos(campo.value) ? '' : Validacion.Mensaje.DIGITOS.evaluate({ nombre : nombre });
	},

	/**
	 * Valida que un campo sea mayor a un valor
	 *
	 * @param campo El campo
	 * @param nombre El nombre del campo, para utilizarlo en mensajes de error
	 * @param minimo El valor mínimo del intervalo
	 * @param maximo El valor máximo del intervalo
	 * @param cerrado Indica si el intervalo es cerrado
	 * @return El texto del error o un string vacío si no hay error
	 */
	entre : function(campo, nombre, minimo, maximo, cerrado) {
		if (cerrado) {
			return Validacion.estaEntre(campo.value, minimo, maximo, cerrado) ? '' : Validacion.Mensaje.ENTRE_CERRADO.evaluate({ nombre : nombre, minimo : minimo, maximo : maximo });
		} else {
			return Validacion.estaEntre(campo.value, minimo, maximo, cerrado) ? '' : Validacion.Mensaje.ENTRE_ABIERTO.evaluate({ nombre : nombre, minimo : minimo, maximo : maximo });
		}
	},
	
	/**
	 * Valida que un campo sea mayor a un valor
	 *
	 * @param campo El campo
	 * @param nombre El nombre del campo, para utilizarlo en mensajes de error
	 * @param valor El valor
	 * @return El texto del error o un string vacío si no hay error
	 */
	mayor : function(campo, nombre, valor) {
		return Validacion.esMayor(campo.value, valor) ? '' : Validacion.Mensaje.MAYOR.evaluate({ nombre : nombre, valor : valor });
	},
	
	/**
	 * Valida que un campo sea mayor o igual a un valor
	 *
	 * @param campo El campo
	 * @param nombre El nombre del campo, para utilizarlo en mensajes de error
	 * @param valor El valor
	 * @return El texto del error o un string vacío si no hay error
	 */
	mayorIgual : function(campo, nombre, valor) {
		return Validacion.esMayorIgual(campo.value, valor) ? '' : Validacion.Mensaje.MAYOR_IGUAL.evaluate({ nombre : nombre, valor : valor });
	},
	
	/**
	 * Valida que un campo sea menor a un valor
	 *
	 * @param campo El campo
	 * @param nombre El nombre del campo, para utilizarlo en mensajes de error
	 * @param valor El valor
	 * @return El texto del error o un string vacío si no hay error
	 */
	menor : function(campo, nombre, valor) {
		return Validacion.esMenor(campo.value, valor) ? '' : Validacion.Mensaje.MENOR.evaluate({ nombre : nombre, valor : valor });
	},
	
	/**
	 * Valida que un campo sea menor o igual a un valor
	 *
	 * @param campo El campo
	 * @param nombre El nombre del campo, para utilizarlo en mensajes de error
	 * @param valor El valor
	 * @return El texto del error o un string vacío si no hay error
	 */
	menorIgual : function(campo, nombre, valor) {
		return Validacion.esMenorIgual(campo.value, valor) ? '' : Validacion.Mensaje.MENOR_IGUAL.evaluate({ nombre : nombre, valor : valor });
	},

	igual : function(campo, nombre, valor) {
		return Validacion.esIgual(campo.value, valor) ? '' : Validacion.Mensaje.IGUAL.evaluate({ nombre : nombre, valor : valor });
	},
	
	/**
	 * Valida un campo numérico
	 *
	 * @param campo El campo
	 * @param nombre El nombre del campo, para utilizarlo en mensajes de error
	 * @return El texto del error o un string vacío si no hay error
	 */
	numerico : function(campo, nombre) {
		return Validacion.esNumerico(campo.value) ? '' : Validacion.Mensaje.NUMERICO.evaluate({ nombre : nombre });
	},
	
	/**
	 * Valida un campo requerido
	 *
	 * @param campo El campo
	 * @param nombre El nombre del campo, para utilizarlo en mensajes de error
	 * @return El texto del error o un string vacío si no hay error
	 */
	requerido : function(campo, nombre) {
		return Validacion.esNoBlanco(campo.value) ? '' : Validacion.Mensaje.REQUERIDO.evaluate({ nombre : nombre });
	},

	/**
	 * Valida un rut
	 *
	 * @param campo El campo
	 * @param nombre El nombre del campo, para utilizarlo en mensajes de error
	 * @return El texto del error o un string vacío si no hay error
	 */
	rut : function(campo, nombre, esRun) {
		esRun = !!esRun;
		return Validacion.esRut(campo.value) ? '' : Validacion.Mensaje.RUT.evaluate({ nombre : nombre, campo : ((esRun) ? 'run' : 'rut')});
	},
	
	/**
	 * Valida si es fecha
	 *
	 * @param campo El campo
	 * @param nombre El nombre del campo, para utilizarlo en mensajes de error
	 * @return El texto del error o un string vacío si no hay error
	 */
	fecha : function(campo, nombre) {
		return Validacion.esFecha(campo.value) ? '' : Validacion.Mensaje.FECHA.evaluate({ nombre : nombre });
	}
};

/**
 * Namespace para plantillas de mensajes de error de validación
 *
 * @author alejandro.gonzalez
 * @version 0.1
 */
Validacion.Mensaje = {
	// Mensajes usados por el validador
	REQUERIDO 		: new Template('#{nombre} debe ser ingresado'),
	DIGITOS 		: new Template('El campo #{nombre} s' + specialChar('oacute') + 'lo puede contener datos num' + specialChar('eacute') + 'ricos'),
	ENTRE_ABIERTO 	: new Template('El campo #{nombre} debe ser mayor a #{minimo} y menor a #{maximo}'),
	ENTRE_CERRADO 	: new Template('El campo #{nombre} debe ser mayor o igual a #{minimo} y menor o igual a #{maximo}'),
	NUMERICO 		: new Template('El campo #{nombre} debe ser num' + specialChar('eacute') + 'rico'),
	MAYOR 			: new Template('El campo #{nombre} debe ser mayor a #{valor}'),
	MAYOR_IGUAL 	: new Template('El campo #{nombre} debe ser mayor o igual a #{valor}'),
	MENOR 			: new Template('El campo #{nombre} debe ser menor a #{valor}'),
	MENOR_IGUAL 	: new Template('El campo #{nombre} debe ser menor o igual a #{valor}'),
	IGUAL 			: new Template('El campo #{nombre} debe ser igual a #{valor}'),
	RUT				: new Template('El campo #{nombre} debe ser un #{campo} v' + specialChar('aacute') + 'lido (Ej. 173627217-K)'),
	FECHA			: new Template('El campo #{nombre} debe ser una fecha v' + specialChar('aacute') + 'lida'),
	ALFANUMERICO	: new Template('El campo #{nombre} contiene caracteres no permitidos'),
	CORREO			: new Template('El campo #{nombre} no es una dirección de correo valida')
};


/**
 * Métodos de utilidad para validación de rut
 *
 * @author alejandro.gonzalez
 * @version 0.1
 */

var ValidacionRut = {
      /**
       * Agrega los eventos de validación de rut a un input 
       *
       * @param campo El campo donde se ingresa el rut
       */

      agregarEventos : function(campo) { 
            campo = $(campo);
            Event.observe(campo.id, 'blur', ValidacionRut.onBlur);
      },

      /**
       * Agrega los eventos de validación de rut a un input enviadndo el Label a mostrar:
       * puede ser RUN, Rut.
       *
       * @param campo El campo donde se ingresa el rut
       * @param campo Label que se mostrará en el mensaje de error
       */

      agregarEventosRun : function(campo, label) {   
            campo = $(campo);

            var validaRut = function(evento){
                  ValidacionRut.onBlurRun(evento,label);
            };

            Event.observe(campo.id, 'blur', validaRut);
      },
 

      /**
       * Valida un rut
       */
      validar : function (rut) {
            return !ValidacionRut.formatearValidar(rut);
      },

      formatearValidar : function (rut) {
            if (rut == null || rut.length < 2) {
                  return null;
            }
  
            var pos = rut.length;
            var dvOriginal = rut.charAt(--pos);
  
            if (rut.charAt(pos - 1) == '-') {
                  --pos;
            } else{
            	return null;
            }
          
            var numeroDigitos = 0;
            var rutCompleto = '';
            var dv = 0;

			var m = -1;

          while (0 <= --pos) {
              var c = rut.charAt(pos);
              if (c == '.') {
                  continue;
              }
              var esDigito = '0' <= c && c <= '9';

              if (!esDigito) {
                  return null;
              }

              var n = parseInt(c);
              dv += n * (2 + ++m % 6);

              rutCompleto = c + rutCompleto;

              numeroDigitos++;
          }

          rutCompleto += '-' + dvOriginal;

          dv %= 11;

          if (dv == 1) {
              dv = 'K';
          } else if (2 <= dv) {
              dv = 11 - dv;
          }
            
          if (dv == 'K' && (dvOriginal == 'K' || dvOriginal == 'k')) {
              return rutCompleto;
          }

          if (dv == parseInt(dvOriginal)) {
              return rutCompleto;
          }
          return null;
      },

      onBlur : function(evento) {

            var elemento = Event.element(evento);

            if (elemento.value.length > 0) {
                  var rutFormateado = ValidacionRut.formatearValidar(elemento.value);

                  if (rutFormateado) {
                        elemento.value = rutFormateado;
                  } else {
                        alert('Rut no válido');
                  }
            }
      },

      onBlurRun : function(evento, label) {

            var elemento = Event.element(evento);
            
            if (elemento.value.length > 0) {
                  var rutFormateado = ValidacionRut.formatearValidar(elemento.value);
                  if (rutFormateado) {
                        elemento.value = rutFormateado;
                  } else {
                        alert(label + ' no válido');
                  }
            }
      }
};
