﻿/**
 * [ USO ]
 *  - Para otorgar la funcionalidad de autovalidación al formulario, basta con emplear el prefijo "frm" en el atributo "name"
 *  - Para otorgar funcionalidad a los elementos del formulario, basta con emplear la nomenclatura "tipodatos_obligatoriedad_persistencia" en el atributo "id"
 *
 * [ EJEMPLO ]
 *   <form method="post" name="frmTest">
 *     <input type="text" name="nombre" id="string_1_0" />
 *     <input type="text" name="email" id="email_1_1" value="test@test.com" />
 *   </form> 
 *
 *  - Crea un formulario con 2 campos:
 *    - Campo "nombre", cuyo tipo de datos es "string" y es obligatorio
 *    - Campo "email", cuyo tipo de datos es "email", es obligatorio, y posee valor por defecto persistente, lo que significa
 *      que si no se introduce ningún valor, carga el valor por defecto ( atributo "value" ) al retirar el foco
 */

/**
 * Etiquetas por idiomas para el tratamiento de errores del formulario
 * Para añadir un nuevo campo validable, basta con agregar un índice en el idioma corresppondiente, con la siguiente estructura:
 *  "field_nombrecampo", donde "field_" indica que se trata de un campo autovalidable, y "nombrecampo" indica el valor del atributo "name" del elemento del formulario asociado a la etiqueta
 *
 * Para añadir un nuevo mensaje de error asociado a la validación incorrecta de un tipo de datos:
 *  "datatype_tipodatos", donde "datatype_" indica que se trata de un mensaje de error asociado a un tipo de datos, y "tipodatos" se corresponde con el tipo de datos deseado
 *
 * @var oValidatorMessageList ( Array )
 */

var oValidatorMessageList = 
{
 es: 
 	{ 
	 mode_mandatory: "El campo <b>%s</b> es obligatorio",
	 datatype_int: "El campo <b>%s</b> debe ser numérico",
	 datatype_string: "El campo <b>%s</b> no es una cadena de texto válida",
	 datatype_phone: "El campo <b>%s</b> no es un número de teléfono válido",
	 datatype_dni: "El campo <b>%s</b> no es un DNI válido",
	 datatype_email: "El campo <b>%s</b> no es un correo electrónico válido",
	 datatype_bool: "Debe marcar el campo <b>%s</b> para proceder al envío",

	 field_nom: "Nombre",
	 field_cognoms: "Apellidos",
	 field_empresa: "Empresa",
	 field_carrec: "Cargo",
	 field_adresa: "Dirección",
	 field_poblacio: "Población",
	 field_provincia: "Provincia",
	 field_pais: "País",
	 field_telefon: "Teléfono",
	 field_cp: "Código postal",
	 field_email: "E-mail",
	 field_assumpte: "Asunto",
	 field_missatge: "Mensaje",
	 field_avis: "Aviso Legal"
	},
 ca: 
 	{ 
	 mode_mandatory: "El camp <b>%s</b> es obligatori",
	 datatype_int: "El camp <b>%s</b> ha de ser numéric",
	 datatype_string: "El camp <b>%s</b> no es una cadena de text vàlid",
	 datatype_phone: "El camp <b>%s</b> no es un telèfon vàlid",
	 datatype_dni: "El camp <b>%s</b> no es un DNI vàlid",
	 datatype_email: "El camp <b>%s</b> no es un email vàlid",
	 datatype_bool: "Ha de marcar el camp <b>%s</b> per a procedir amb l'enviament",

	 field_nom: "Nom",
	 field_cognoms: "Cognoms",
	 field_empresa: "Empresa",
	 field_carrec: "Càrrec",
	 field_adresa: "Adreça",
	 field_poblacio: "Població",
	 field_provincia: "Provincia",
	 field_pais: "País",
	 field_telefon: "Telèfon",
	 field_cp: "Codi postal",
	 field_email: "E-mail",
	 field_assumpte: "Assumpte",
	 field_missatge: "Missatge",
	 field_avis: "Avís Legal"
	},
 pt: 
 	{ 
	 mode_mandatory: "O campo de <b>%s</b> é necessário",
	 datatype_int: "O campo de <b>%s</b> deve ser numérico",
	 datatype_string: "O campo de <b>%s</b> não é um texto válido",
	 datatype_phone: "O campo de <b>%s</b> não é um número de telefone válido",
	 datatype_dni: "O campo de <b>%s</b> não é um DNI válido",
	 datatype_email: "O campo de <b>%s</b> não é um e-mail válido",
	 datatype_bool: "Você deve marcar o <b>%s</b> para continuar",

	 field_nom: "Nome",
	 field_cognoms: "Apelidos",
	 field_empresa: "Companhia",
	 field_carrec: "Cargo",
	 field_adresa: "Direcção",
	 field_poblacio: "Localidade",
	 field_provincia: "Distrito",
	 field_pais: "Paìs",
	 field_telefon: "Telefone",
	 field_cp: "Codi postal",
	 field_email: "E-mail",
	 field_assumpte: "Assunto",
	 field_missatge: "Comentário",
	 field_avis: "Condições legais"
	},	
 en: 
 	{ 
	 mode_mandatory: "The field <b>%s</b> is mandatory",
	 datatype_int: "The field <b>%s</b> must be a number",
	 datatype_string: "The field <b>%s</b> is not a valid string",
	 datatype_phone: "The field <b>%s</b> is not a valid phone number",
	 datatype_dni: "The field <b>%s</b> is not a valid DNI",
	 datatype_email: "The field <b>%s</b> is not a valid email address",
	 datatype_bool: "You must check the field <b>%s</b> in order to submit the form",

	 field_nom: "Name",
	 field_cognoms: "Surname",
	 field_empresa: "Company",
	 field_carrec: "Position",
	 field_adresa: "Address",
	 field_poblacio: "Town",
	 field_provincia: "Province",
	 field_pais: "Country",
	 field_telefon: "Telephone",
	 field_cp: "Zip Code",
	 field_email: "E-mail",
	 field_assumpte: "Title",
	 field_missatge: "Body",
	 field_avis: "Legal Notice"
	},
fr: 
 	{	
  	 /**
 	  * Obligatoriedad
 	  */
 		
	 mode_mandatory: "Le champ <b>%s</b> est obligatoire",
	 
	 /**
	  * Tipos de datos
	  */
	 
	 datatype_int: "Le champ <b>%s</b> doit être numérique",
	 datatype_string: "Le champ <b>%s</b> n'est pas une chaîne valide",
	 datatype_phone: "Le champ <b>%s</b> n'est pas un numéro de téléphone valide",
	 datatype_dni: "Le champ n'est pas valide DNI/CIF",
	 datatype_email: "Le champ <b>%s</b> n'est pas une adresse email valide",
	 datatype_bool: "Doit sélectionner le champ <b>%s</b> pour effectuer l'envoi",
	 datatype_date: "Le champ <b>%s</b> ne contient pas une date valide",

	 /**
	  * Nombres de campos
	  */
	 
	 field_nom: "Prénom",	 
	 field_cognoms: "Nom de famille",
	 field_fechaNacimiento: "Date de naissance",	 	
	 field_pasaporte: "Passeport",	 
	 field_idpais: "Pays",
	 field_pais: "Pays",
	 field_provincia: "Département",
	 field_compania: "Enterprise",
	 field_cargo: "Poste ocupé",
	 field_correo: "Email",
	 field_telefon: "Téléphone",
	 
	 
	 field_cp: "Code postal",
	 field_dni: "CIF/NIF",
	 field_email: "Email",
	 field_assumpte: "Sujet",
	 field_missatge: "Message",
	 field_cv: "Currículum",
	 field_avis: "Conditions légales",		 
	},	
}

/**
 * Función encargada de validar el tipo de datos contenido en el 1r parámetro
 * 
 * @function validateDataType
 * @param sValue ( String )
 * @param [ sDataType ] ( String )
 * @return ( Boolean )
 */

function validateDataType( oFormElement, sDataType )
{
 /**
  * Bandera que indica si la validación es correcta ( por defecto NO )
  * @var bIsValid ( Boolean )
  */ 
 	
 var bIsValid = new Boolean( false );

 /**
  * Carga el tipo de datos si el elemento ha sido precargado
  */
 
 if( !sDataType && oFormElement._validationConfig )
  sDataType = oFormElement._validationConfig[ 0 ];
 
 /**
  * Procede a validar según el tipo de datos
  */
 
 switch( sDataType.toLowerCase() )
 {
  case "bool":

   bIsValid = /(0|1|true|false|si|no)/i.test( oFormElement.value ) == true;
  
   if( oFormElement.getAttribute( "type" ) == "checkbox" && oFormElement._validationConfig[ 1 ] == 1 )
    bIsValid = new Boolean( oFormElement.checked );
   
  break;
    
  case "int": bIsValid = /^[0-9]+$/.test( oFormElement.value ) == true; break;
  case "float": bIsValid = /^[0-9]+(\.[0-9]+)?$/.test( oFormElement.value ) == true; break;
  case "string": bIsValid = /.+/.test( oFormElement.value ) == true; break;
  case "phone": bIsValid = /[0-9]{9,}/.test( oFormElement.value ) == true; break;
  case "dni": bIsValid = /[0-9]{8}[a-z]/i.test( oFormElement.value ) == true; break;
  case "date": bIsValid = /^(\d{1,2}[\/\-]\d{1,2}[\/\-]\d{4})|(\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2})$/.test( oFormElement.value ) == true; break;
  case "email": bIsValid = /^[a-z0-9_\+-]+(\.[a-z0-9_\+-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*\.([a-z]{2,4})$/i.test( oFormElement.value ) == true; break;
  case "captcha": bIsValid = /[a-z0-9]{4}/.test( oFormElement.value ); break;
  default: bIsValid = new Boolean( true ); break;
 } 
 
 return( bIsValid );
}

/**
 * Reemplaza los elementos '%s' por el valor indicado en el 2 parámetro
 * @function sprintf
 * @return ( String )
 */

function sprintf( sString, sReplacement )
{ return( sString.toString().replace( "%s", sReplacement ) ); }

/**
 * Copiamos el mtodo 'onload' antes de sobreescribirlo
 * Este método será invocado después de aplicar los cambios asociados al sistema de validación
 * Este comportamiento evita confrontaciones con jQuery
 */

window._onload = window.onload;

/**
 * Método encargado de precargar el sistema de validación
 * @function window.onload
 */

window.onload = function()
{
 /**
  * Recorre los formularios existentes en la página, y precarga solamente aquellos
  * cuyo nombre comience con el prefijo 'frm'
  */
    
 for( var i = 0; i < document.forms.length; i++ )
 {
  if( String( document.forms[ i ].getAttribute( "name" ) ).indexOf( "frm" ) == 0 )
   this.prepareForm( document.forms[ i ] );
 }
	
 /**
  * Invoca el mtodo 'onload' previamente salvado
  */
	
 if( typeof( this._onload ) == "function" )
  this._onload();	
}


/**
 * Método encargado de precarga el sistema de validación para el formulario especificado
 * en el primer parámetro
 * Devuelve el formulario precargado
 *
 * @function window.prepareForm
 * @param oForm ( HTMLElement )
 * @return ( HTMLElement )
 */

window.prepareForm = function( oForm )
{
 /**
  * Precarga los elementos del formulario
  */
    
 for( var i = 0; i < oForm.elements.length; i++ )
 {
  /** 
   * Ignora el elemento actual si no posee atributo 'id'
   */
	 
  if( !oForm.elements[ i ].getAttribute( "id" ) )
   continue;

  /**
   * Configuracin del elemento del formulario segn la estructura del atributo 'id'
   * Empleamos el separador '_' para obtener los ndices del array resultante, que son los siguientes:
   *  - 0: Tipo de datos del elemento ( string, number, phone, email, dni )
   *  - 1: Los posibles valores para este ndice son:
   *	  0: El campo es opcional
   *      1: El campo es obligatorio
   *  - 2: Los posibles valores para este ndice son:
   *	  0: El campo no mantiene su valor inicial si el usuario no introduce un valor ( por defecto )
   *      1: El campo mantiene su valor inicial si el usuario no introduce un valor
   
   * [ EJEMPLO ] Genera un campo obligatorio de tipo 'dni'
   * <input type="text" name="campo1" id="dni_1_0" value="Campo 1" />
   * 
   * @var oElementConfig ( Array )
   */
	 
  var oElementConfig = oForm.elements[ i ].getAttribute( "id" ).split( "_" );
  oForm.elements[ i ]._validationConfig = oElementConfig;

  /**
   * El elemento tiene un valor asignado, por defecto le aplicamos la funcionalidad de reiniciar
   * automticamente el valor al retirar el foco, si el usuario no ha definido un valor manualmente
   */

  if( !oElementConfig[ 2 ] && oForm.elements[ i ].value )
   oElementConfig[ 2 ] = true;
  
  /**
   * Evento lanzado al enviar el formulario
   * Este mtodo se encarga de comprobar si el campo contiene el tipo de datos correcto o
   * si se trata de un campo obligatorio sin valor definido
   *
   * Devuelve los siguientes valores, en funcin del resultado de la validacin:
   *  - 0: El campo es obligatorio y no se ha definido ningn valor
   *  - 1: El campo no posee el formato deseado
   *  - 2: El campo posee el formato correcto
   */   
  
  oForm.elements[ i ].onsubmit = function()
  {
   /**
    * Define si el elemento se considera vaco
    * @var bIsEmpty ( Boolean )
    */
      
   var bIsEmpty = ( !this.value || this.value == this._default && this._validationConfig[ 2 ] == 1 );
      
   /**
    * Campo obligatorio vaco
    */
   
   if( bIsEmpty == true && this._validationConfig[ 1 ] == true )
    return( 0 );

   /**
    * Formato del campo no vlido
    */

   else if( bIsEmpty == false && validateDataType( this ) == false )
    return( 1 );   

   /**
    * Formato correcto
    */

   else
    return( 2 );
  }
  
  /**
   * Aplica la funcionalidad para autocompletar el valor si el usuario no ha introducido nada
   * Este mtodo aplica los siguientes eventos al elemento actual:
   *  - onclick: encargado de vaciar el valor al hacer click en el control
   *  - onblur: encargado de restaurar, si es preciso, el valor del control al retirar el foco
   */
 
  if( Boolean( oElementConfig[ 2 ] ) == true )
  {
   oForm.elements[ i ]._default = oForm.elements[ i ].value;
   oForm.elements[ i ].onblur = function(){ if( this.value.length == 0 ) this.value = this._default; }
   oForm.elements[ i ].onclick = oForm.elements[ i ].onfocus = function(){ if( this.value == this._default ) this.value = ""; }
  }
 } 	

 /**
  * Evento lanzado antes de enviar el formulario
  * Esta funcin se encarga de validar todos sus subelementos mediante la llamada al mtodo 'onsubmit', que slo
  * existir para aquellos elementos que hayan sido precargados mediante el bucle superior
  */

 oForm.onsubmit = function()
 { 
  /**
   * Comprueba si existe un idioma definido
   */
   
  if( typeof( this.elements[ "idioma" ] ) == "undefined" )
   throw new Error( "Campo de idioma requerido ( <input name=\"idioma\" value=\"...\" /> )" );

  else
  {
   /**
    * @var oErrorLayer ( DOMElement ) Capa sobre la que se volcar el resultado de la validacin, en caso de ser negativo
    * @var oLanguageList ( Array ) Listado de etiquetas asociadas al idioma indicado en el campo oculto 'idioma'
    */
 
   var oErrorLayer = null, oErrorList = new Array();
   var oLanguageList = oValidatorMessageList[ this.elements[ "idioma" ].value ];
	
   for( var i = 0; i < this.elements.length; i++ )
   {
    /**
     * Ignora los elementos NO precargados
 	 */
	  
    if( typeof( this.elements[ i ].onsubmit ) != "function" )
     continue;
	
    switch( this.elements[ i ].onsubmit() )
    {
 	 /**
	  * Campo obligatorio vaco
	  */
	   
	 case 0:	
	  
	   this.elements[ i ].className = "error";
	   oErrorList.push( sprintf( oLanguageList[ "mode_mandatory" ], oLanguageList[ "field_" + this.elements[ i ].getAttribute( "name" ) ] ) ); 
	  
	 break;
	
	 /**
	  * Campo con formato incorrecto
	  */
	
	 case 1: 
	  
	   this.elements[ i ].className = "error";
	   oErrorList.push( sprintf( oLanguageList[ "datatype_" + this.elements[ i ]._validationConfig[ 0 ] ], oLanguageList[ "field_" + this.elements[ i ].getAttribute( "name" ) ] ) );
	  
 	 break;
	
	 /** 
	  * Campo correcto
	  */
	
	 case 2:
	  
	   this.elements[ i ].className = "input";
	 
	 break;
    }
   }
  
   /**
    * Enva el formulario si no se han generado errores
    */
    
   if( oErrorList.length == 0 )
    return( true );
   
   /**
    * Se han producido errores
    * Busca la capa sobre la que volcar el resultado, o la crea si sta no existe
    */
   
   else if( !( oErrorLayer = document.getElementById( "errors" ) ) )
   {
    oErrorLayer = this.appendChild( document.createElement( "div" ) );
    oErrorLayer.setAttribute( "id", "errors" );
    oErrorLayer.style.display = "block";
   }
   
   oErrorLayer.innerHTML = oErrorList.join( "<br />" );
  }
   
  return( false );
 }

 return( oForm ); 
}

