/***************************************************************************
 * Copyright (C) 2006 by Korgault Studios
 * http://www.kgstudios.net
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

/**
 * Funciones y variables de sistema
 *
 * @package	phppr0.Shared.js
 * @author Korvus
 * @date 01-01-2007
 * @version 1.0
 */

if( typeof( APP_VARS ) == "undefined" )
 APP_VARS = new Array();

/**
 * Habilita la captura del evento 'onmousemove' para el documento, lo cual nos proporcionará
 * la posibilidad de obtener las coordenadas del ratón en cualquier momento, accediendo a APP_VARS[ "EVENT" ]
 */

if( typeof( document.captureEvents ) == "function" )
 document.captureEvents( Event.MOUSEMOVE ); 

/**
 * Almacena documentos cargados dinámicamente
 * @var $SOCKETS$ ( Array )
 */
 
APP_VARS[ "SOCKETS" ] = new Array();

/**
 * Información sobre el último evento lanzado
 *
 * - type: tipo de evento
 * - clientX: posición 'X' del cursor del ratón
 * - clientY: posición 'Y' del cursor del ratón
 * - scrollX: posición 'X' de la barra de scroll
 * - scrollY: posición 'Y' de la barra de scroll
 *
 * @var $EVENT$ ( Object )
 */

APP_VARS[ "EVENT" ] = { clientX: 0, clientY: 0, scrollX: 0, scrollY: 0 };

/**
 * Boleano que indica si la página actual está en modo depuración
 * @var $DEBUG$ ( Integer )
 */
 
//APP_VARS[ "DEBUG" ] = false;

/**
 * Objetos de uso general
 * @var APP_OBJECTS ( Array )
 */

var APP_OBJECTS = new Array;

/**
 * Crea un nuevo objeto y lo guarda en el array APP_OBJECTS
 *
 * @function setObject
 * @param sObjectId ( String )
 * @param oObject ( Object )
 * @return ( void )
 * @see phppr0.js.getObject
 */

function setObject( sObjectId, oObject )
{
 if( typeof( APP_OBJECTS[ sObjectId ] ) != "object" )
  APP_OBJECTS[ sObjectId ] = oObject; 
}

/**
 * Devuelve un objeto si anteriormente ha sido establecido mediante la funcion 'setObject'
 *
 * @function getObject
 * @param sObjectId ( string )
 * @return ( object )
 * @see phppr0.js.setObject
 */

function getObject( sObjectId )
{ return( APP_OBJECTS[ sObjectId ] ); }

/**
 * Envia un formulario. Esta función tiene en cuenta los siguientes atributos de todos
 * los subelementos del formulario ( oForm.elements ):
 *
 * 'onloaded': Aplicable a cualquier subelemento
 * 'onsubmit': Aplicable a cualquier subelemento
 * 'onbeforesubmit': Aplicable al formulario en si
 * 
 * @function Submit
 * @param oForm ( Object ) El formulario que se desea enviar
 * @param sAction ( String ) Acción que se desea realizar sobre los datos
 * @param [ sQuestion ] ( String ) Mensaje de confirmación antes de enviar
 * @return ( Boolean | void )
 */

function Submit( oForm, sAction, sQuestion )
{
 /**
  * Si se ha definido el 3r parámetro ( sQuestion ), precisaremos de una confirmación por parte 
  * del usuario para continuar procesando el formulario
  */
	
 if( typeof( sQuestion ) != "undefined" && sQuestion != "" && window.confirm( unescape( sQuestion ) ) == false )
  return( false );
  
 for( var i = 0; i < oForm.elements.length; i++ )
 {
  if( typeof( oForm.elements[ i ].getAttribute( "onsubmit" ) ) != "string" )
   continue;
   
  /**
   * Ejecuta el evento 'onsubmit' del elemento actual del formulario
   * Dicho evento puede venir en forma de cadena o función ( dependiendo del navegador )
   */
   
  if( new Function( "action", unescape( oForm.elements[ i ].getAttribute( "onsubmit" ) ) ).call( oForm.elements[ i ], sAction ) == false )
   return( false );
 }

 /**
  * Si se ha definido el evento 'onbeforesubmit' para el formulario actual, lo ejecutamos y comprobamos qué 
  * valor devuelve. Si devuelve 'false', cancelamos el envío de dicho formulario
  */

 if( typeof( oForm.onbeforesubmit ) == "function" && oForm.onbeforesubmit( sAction ) == false )
  return( false );  

 /**
  * Llegados a este punto, el formulario será enviado ( siempre y cuando no se dé un error en el bucle inferior ),
  * por lo que comprobaremos que todas las tablas que se envían al controlador de formularios poseen un campo '[action]', que
  * define la acción que se llevará a cabo con los registros de la tabla a la que pertenece
  *
  * @var oTableList ( Object ) Listado asociativo de tablas encontradas
  */
  
 var oTableList = new Object();
 
 /**
  * Objeto de comparación por expresiones regulares
  * @var oRegularExpression ( RegExp )
  */
  
 var oRegularExpression = new RegExp( /^([a-z][a-z0-9\_]*)\[([a-z0-9\_]+)\]\[values\]\[[0-9]*\]/i );
 
 /**
  * Listado de coincidencias encontradas con el patrón superior
  * @var mTableMatches ( mixed )
  * @see oRegularExpression
  */
  
 var mTableMatches = null;
 
 for( var i = 0; i < oForm.elements.length; i++ )
 {
  /**
   * Ejecuta la expresión regular y almacena los patrones encontrados en 'mTableMatches'
   */

  if( mTableMatches = oRegularExpression.exec( oForm.elements[ i ].getAttribute( "name" ) ) )
   mTableMatches = mTableMatches.toString().split( "," );  

  /**
   * Si el resultado de dicha búsqueda no es 'null', es un array de longitud mayor que 0 y
   * el valor del 2º elemento ( nombre de la tabla sin corchetes ) no existe en la variable
   * array 'oTableList', lo añadimos a dicho array 
   */

  if( mTableMatches != null && typeof( oTableList[ mTableMatches[ 1 ] ] ) == "undefined" )
   oTableList[ mTableMatches[ 2 ] ] = mTableMatches[ 1 ];
 }
 
 /**
  * Una vez almacenados los nombres de las tablas que se van a enviar, procedemos a modificar
  * el valor de todos los elementos '[action]', y a crearlos si éstos no han sido definidos
  */
 
 for( var sTableName in oTableList )
 {
  /**
   * Almacena el nombre del campo de acción actual, que es compuesto por el valor de 'oTableList'
   * para el indice 'sTableName', más el nombre de la tabla y la constante '[action]'
   *
   * @var sActionFieldName ( String )
   */
	 
  var sActionFieldName = oTableList[ sTableName ] + "[" + sTableName + "][action]";
	 
  /**
   * El campo de que define la acción sobre la tabla actual no existe
   * Lo creamos realizando distintas acciones según el navegador
   *
   * NOTA:
   * Según parece, Internet Explorer no permite la modificación de los
   * atributos 'name' o 'id', de modo que crearemos el nodo a partir de una cadena
   *
   * Por su parte, Mozilla no permite carácteres que no sean letras en
   * el método 'createElement', así que inicializaremos los atributos sobre
   * el nodo devuelto por dicho método
   */
   
  if( typeof( oForm.elements[ sActionFieldName ] ) == "undefined" )
  {
   if( navigator.appName.indexOf( "Microsoft" ) == -1 )
   {   
	/**
	 * Nodo correspondiente al campo '[action]', a partir del que se realizarán las copias
	 * @var oActionFieldNode ( DocumentElement )
	 */
	 
	var oActionFieldNode = document.createElement( "input" );
   
    oActionFieldNode.setAttribute( "type", "hidden" );
    oActionFieldNode.setAttribute( "id", sActionFieldName );
	oActionFieldNode.setAttribute( "name", sActionFieldName );
   
    /**
    * Insertamos dicho nodo como un nuevo elemento del formulario
	*/
   
    oForm.appendChild( oActionFieldNode.cloneNode( false ) );
   }
   else
    oForm.appendChild( document.createElement( "<input type=\"hidden\" name=\"" + sActionFieldName + "\" />" ) );
  }
	
  /**
   * Modificamos el valor del campo a la acción enviada en el primer parámetro
   * Los posibles valores para este campo son: 'ins', 'upd', 'del'
   */
	
  oForm.elements[ sActionFieldName ].value = sAction;
 }
 
 /**
  * Realizamos algunas acciones que requieren algunos controles una vez que nos hemos asegurado
  * que el formulario será enviado ( ya que las comprobaciones anteriores pueden evitar dicho envío )
  */

 for( var i = 0; i < oForm.elements.length; i++ )
 {
  if( oForm.elements[ i ].nodeName.toLowerCase() != "input" )
   continue;
	 
  /**
   * Controlador de checkboxes
   * Si el atributo 'force' es '1' marcaremos la caja obligatoriamente y le asignaremos un valor boleano
   */
   
  if( oForm.elements[ i ].getAttribute( "type" ).toLowerCase() == "checkbox" && oForm.elements[ i ].getAttribute( "force" ) == 1 )
  {
   oForm.elements[ i ].setAttribute( "value", oForm.elements[ i ].checked ? 1 : 0 );
   oForm.elements[ i ].checked = true;
  } 
 }

 /**
  * Deshabilita el elemento que envió el formulario
  */

 if( window.event && window.event.srcElement )
  window.event.srcElement[ 'on' + window.event.type ] = function(){ return( false ); };
 
 /**
  * Finalmente, envía el formulario
  */
 
 oForm.submit();
}

/**
 * Restablece un formulario. Esta funcion es similar al método 'reset' del objeto <form>, con la diferencia
 * que una vez restablecido el formulario, se llama al método 'onloaded' de todos los subelementos de éste /
 * Resets a form. This function is similar to the 'reset' method on <form> object, with some differences such as
 * calling 'onloaded' method for each form subelement
 *
 * @function Reset
 * @param oForm ( object )
 * @return ( void )
 */

function Reset( oForm )
{
 oForm.reset();
 
 for( var i = 0; i < oForm.elements.length; i++ )
 {
  if( typeof( oForm.elements[ i ].onloaded ) != "undefined" )
   new Function( oForm.elements[ i ].onloaded ).call( oForm.elements[ i ] );
 }
}

/**
 * Comprueba si el contenido de una cadena coincide con el patrón del tipo de datos especificado en el 2º parámetro /
 * Checks if an string content matches with the datatype pattern specified at second parameter
 *
 * @function checkDataType
 * @param sValue ( string ) Valor que se desea comprobar / Value to be checked
 * @param sDataType ( string ) Tipo de datos a comprobar / Data type to be checked
 *
 * @see phppr0.xml.datatTypes
 * @return ( bool ) Devuelve 'true' si la cadena es correcta para ese tipo de datos / Returns 'true' if string is correct for the selected datatype
 */

function checkDataType( sValue, sDataType )
{
 if( typeof( APP_DATATYPES ) == "object" && typeof( APP_DATATYPES[ sDataType ] ) != "undefined" )
  var oRegExp = new RegExp( APP_DATATYPES[ sDataType ] );
 
 return( typeof( oRegExp ) == "undefined" || oRegExp.test( sValue ) );
}

/**
 * Vacía una caja desplegable / Clears a combo box
 * @function clearComboBox
 * @param oComboBox ( object )
 * @return ( void )
 */

function clearComboBox( oComboBox )
{
 if( typeof( oComboBox ) != "object" )
  throw new Error( "clearComboBox(): First parameter must be a valid combobox object" );
	
 while( oComboBox.selectedIndex != -1 )
  oComboBox.options[ oComboBox.selectedIndex ] = null;	
}

/**
 * Duplica el desplegable tantas veces como valores hayan sido seleccionados
 * para que el script que reciba los datos, los reciba en forma de array
 *
 * @param oComboBox ( object ) Desplegable que se desea formatear
 * @return ( void )
 */

function submitComboBox( oComboBox )
{
 var oElements = document.getElementsByName( oComboBox.getAttribute( "name" ) + "[]" );
 var oHiddenField = null;
 
 /**
  * Mozilla
  */
 
 if( navigator.appName.indexOf( "Microsoft" ) == -1 )
 {
  oHiddenField = document.createElement( "input" );  
  oHiddenField.setAttribute( "type", "hidden" );    
  oHiddenField.setAttribute( "id", oComboBox.getAttribute( "id" ) + "[]" );
  oHiddenField.setAttribute( "name", oComboBox.getAttribute( "name" ) + "[]" );
 }
 else
  oHiddenField = document.createElement( "<input type=\"hidden\" name=\"" + oComboBox.getAttribute( "name" ) + "[]\" />" );
 
 /**
  * Eliminamos aquellos elementos que tengan el mismo nombre que el desplegable
  */
  
 for( var i = oElements.length - 1; i >= 0; i-- )
  oElements[ i ].parentNode.removeChild( oElements[ i ] );
 
 /**
  * Creamos los elementos que serviran para que los datos se envien en forma de array
  */
 for( var j = 0, i = 0; i < oComboBox.options.length; i++ )
 {	 
  if( oComboBox.options[ i ].selected == true && oComboBox.options[ i ].value != "" )
  {
   oHiddenField.setAttribute( "value", oComboBox.options[ i ].value );
   oComboBox.parentNode.appendChild( oHiddenField.cloneNode( true ) );
  }
 }
}

/**
 * Selecciona el elemento de la caja desplegable coincidente con el valor especificado
 *
 * @function selectComboBox
 * @param oComboBox ( Object )
 * @param sValue ( String )
 */

function selectComboBox( oComboBox, sValue )
{
 if( typeof( sValue ) == "undefined" )
  return( false );
	
 for( i = 0; i < oComboBox.options.length; i++ )
 {
  if( oComboBox.options[ i ].value == sValue )
   oComboBox.selectedIndex = i;
 }
}

/**
 * Rellena una caja desplegable a partir de los resultados devueltos por una URL /
 * Fills the combo box from returned URL result
 *
 * @function loadComboBoxFromXML
 * @param oComboBox ( object )
 * @param sUrl ( string )
 * @sValue ( string ) Valor seleccionado / Selected value
 * @return ( void )
 */

function loadComboBoxFromXML( oComboBox, sUrl, sValue )
{
 /**
  * Comprobamos si la URL requerida se trata de un fichero XML, de lo contario, enviaremos
  * el identificativo de la sesión ( para la carga de XML a partir de scripts )
  */
  
 if( new RegExp( /\.xml$/ ).test( sUrl ) == false )
  sUrl += ( sUrl.indexOf( "?" ) != -1 ? "&" : "?" ) + APP_VARS[ "SESSION" ][ 0 ] + "=" + APP_VARS[ "SESSION" ][ 1 ];
	
 var oDocument = loadXMLFromFile( sUrl );
 var oOptions  = oDocument.getElementsByTagName( "option" );
 
 /**
  * Comprueba si el objeto sobre el que se trabajará es correcto
  * @var oComboBox ( object )
  */
 
 if( typeof( oComboBox ) != "object" )
  throw new Error( "loadComboBoxFromXML(): First parameter must be a valid combobox object" );
 
 for( i = 0; i < oOptions.length; i++ )
  oComboBox[ oComboBox.options.length ] = new Option( oOptions[ i ].firstChild.nodeValue, oOptions[ i ].getAttribute( "value" ) );
 
 /**
  * Selecciona el valor deseado una vez ha terminado de rellenar la caja desplegable
  * @see phppr0.Advanced.HTML :: createEmptyComboBox
  */
 
 if( typeof( sValue ) != "undefined" )
  selectComboBox( oComboBox, sValue );
 
 /**
  * Invoca el método 'onchange' de la caja desplegable si éste es una función válida
  */
 
 if( typeof( oComboBox.onchange ) == "function" )
  oComboBox.onchange( sValue );
}

/**
 * Crea un objeto XML a partir de una URL, cuyo contenido es XML estándar 
 * 
 * @function loadXMLFromFile
 * @param sUrl ( string )
 * @param [ bSynchronous ] ( bool ) Define si el documento se debe carga sincronizado o no
 * @param [ bReloadUrl ] ( bool ) Define si se debe forzar la recarga de una URL
 * @return ( XMLDocument )
 */ 

function loadXMLFromFile( sUrl, bSynchronous, bReloadUrl )
{
 /**
  * Crea un nuevo socket si éste todavía no se ha creado para la ruta especificada
  */
	
 if( typeof( APP_VARS[ "SOCKETS" ][ sUrl ] ) == "undefined" || bReloadUrl == true )
 {
  APP_VARS[ "SOCKETS" ][ sUrl ] = ( window.XMLHttpRequest ) ? new XMLHttpRequest() : new ActiveXObject( "Microsoft.XMLHTTP" );
  APP_VARS[ "SOCKETS" ][ sUrl ].open( "GET", sUrl, false, null, null );
  APP_VARS[ "SOCKETS" ][ sUrl ].send( null );
 }

 /**
  * Realiza una llamada recursiva mientras el documento no esté cargado,
  * de manera que una vez cargado, retornará el documento XML creado en 
  * el bloque de código de la sentencia 'else'
  */  

 if( APP_VARS[ "SOCKETS" ][ sUrl ].readyState < 4 )
  return( loadXMLFromFile( sUrl, bSynchronous ) );
 else
 {
  /**
   * Lanza un error fatal si no se ha cargado el documento correctamente
   */
	 
  if( APP_VARS[ "SOCKETS" ][ sUrl ].status != 200 )
   throw new Error( "Error " + APP_VARS[ "SOCKETS" ][ sUrl ].status + " while loading XML document ( " + sUrl + " ): " + APP_VARS[ "SOCKETS" ][ sUrl ].statusText );
	  
  /** 
   * Internet Explorer
   */
	 
  if( navigator.appName.toLowerCase().indexOf( "Microsoft" ) != -1 )
  {  
   APP_VARS[ "SOCKETS" ][ sUrl ].responseXML = new ActiveXObject( "Microsoft.XMLDOM" );  
   APP_VARS[ "SOCKETS" ][ sUrl ].responseXML.async = false;
   APP_VARS[ "SOCKETS" ][ sUrl ].responseXML.loadXML( oSocket.responseText );
  }
  
  return( APP_VARS[ "SOCKETS" ][ sUrl ].responseXML );   
 }
}