<INPUT TYPE=SUBMIT>
no se ha especificado hacia dónde serán enviados los datos que han sido recabados en el formulario. Pero más intrigante aún es en qué formato serán enviados los datos del usuario, y sobre todo cómo serán manejados en el servidor. Es aquí cuando caemos en la cuenta de que hay dos tipos de páginas esencialmente diferentes: (1) páginas Web que serán enviadas al usuario desde nuestro sitio Web, en las cuales no esperamos que el usuario nos envíe información alguna, páginas en las cuales simplemente anunciamos la existencia de un negocio o de unas oficinas o en donde se proporciona algún tipo de información, y (2) páginas Web en las cuales el usuario tendrá la opción de poder enviarnos información después de haber llenado algún formulario o formularios, y a un nivel más sofisticado, el usuario podrá hacer compras en línea de lo que le sea mostrado en un catálogo en línea, permitiéndole acumular artículos y poner la orden efectuando el pago del pedido allí mismo (esta es la nueva manera de hacer negocios, por Internet, la cual está desplazando las ventas tradicionales de muchos artículos que anteriormente sólo estaban visibles en los aparadores y los cuales se tenían que recoger personalmente en la tienda). En el primer tipo de página, podemos hacer mucho con JavaScript para darle interactividad a la página, no hay formas que llenar, si mucho cookies. Pero en el segundo tipo de página, en donde vamos a tener que recabar y acomodar los datos enviados, almacenándolos en una base de datos confiable y llevando a cabo verificaciones de seguridad, estamos hablando ya de tener que crear otro programa diferente, un programa que sea capaz de interactuar con el usuario y que pueda recibir datos para su manejo posterior, algo para lo cual JavaScript simple y sencillamente no basta. Esto implica que el administrador-programador de un sitio Web, mejor conocido como el webmaster, frecuentemente no sólo tiene como tarea elaborar páginas HTML quizá incorporando código JavaScript y las cuales serán descargadas por los usuarios via Internet a través de algún protocolo como http. También tiene que elaborar programas especializados con algún lenguaje de scripting como CGI que sean capaces de recibir información personalizada que sea enviada por los usuarios, de forma tal que tenemos una situación algo más compleja:
Desafortunadamente, la elaboración de scripts CGI que funcionen correctamente la primera vez como queremos que funcionen es notoriamente problemática y hasta programadores expertos batallan para lograr que sus scripts CGI funcionen como se espera en la primera ocasión.
En los ejemplos dados arriba hemos limitado la acción de la forma para que quede circunscrita dentro de JavaScript. Pero si vamos a enviar los datos de una forma al servidor, esto se lleva a cabo con dos instrucciones JavaScript: el manejador de eventos onSubmit, o el método submit. En la mayoría de los casos, queremos usar una de las dos opciones, pero no ambas.
En la primera opción, ponemos el manejador de eventos onSubmit en la etiqueta <FORM>, lo cual le dice a JavaScript lo que debe de hacer cuando el usuario hace clic en el botón Submit (definido como TYPE="SUBMIT"). En la segunda opción ponemos la instrucción submit en cualquier parte de JavaScript, y puede ser activada mediante cualquier acción, tal como hacer clic en un botón de la forma que ha sido definido con el manejador de eventos onClick.
A continuación tenemos un ejemplo de cómo usar el manejador de eventos para enviar correo electrónico. El manejador de eventos onSubmit le dice a JavaScript qué es lo que hay que hacer cuando el usuario hace clic en el botón Submit (o Submit Query, dependiendo del navegador), en este caso poner en ejecución la función contacto(), en donde campos adicionales de correo (de copia visible CC y copia invisible BCC) son anexados a un URL (enlace Web) mailto: para envío de correo. El navegador automáticamente abrirá una nueva ventana de correo con los campos llenos esperando que el usuario elabore su correo. Escribimos el cuerpo del mensaje en la ventana de correo, y se envía el correo al recipiente (en realidad, el último paso no se completa porque el domicilio de correo fue inventado para propósitos didácticos):
<HTML>En el siguiente ejemplo se usará el método submit para lograr lo mismo. Obsérvese que el manejador de eventos onSubmit es removido, y se agrega en su lugar para el botón renombrado de la forma un manejador onClick. El método submit() substituye el enunciado de return true en el ejemplo anterior:
<HEAD>
<TITLE>Prueba de onSubmit</TITLE>
<SCRIPT LANGUAGE="JavaScript">
function contacto(forma){
Subject=document.forma_de_prueba.caja_de_entrada1.value;
CC= document.forma_de_prueba.caja_de_entrada2.value;
BCC= document.forma_de_prueba.caja_de_entrada3.value;
location = "mailto:armando@ciberneticamexicana.com?subject="+Subject+"&Bcc="+
BCC+"&cc="+CC;
return true;
}
</SCRIPT>
</HEAD>
<BODY>
<FORM NAME="forma_de_prueba" onSubmit="return contacto(this.form)" >Asunto del mensaje: <BR>
<INPUT TYPE="text" NAME="caja_de_entrada1" VALUE="¡Esta es una gran forma!" SIZE=50>
<P>Enviar cc a: <BR>
<INPUT TYPE="text" NAME="caja_de_entrada2" VALUE="" SIZE=50><P>
Enviar CC ciega a: <BR>
<INPUT TYPE="text" NAME="caja_de_entrada3" VALUE="" SIZE=50><P>
<INPUT TYPE="SUBMIT"><BR>
</FORM>
</BODY>
</HTML>
<HTML>Como se destacó en la figura dada arriba, en su expresión más genérica una página Web consiste de dos partes: la forma en sí que es desplegada por el navegador en la máquina del usuario después de que es enviada por el servidor tras haber sido pedida por el usuario mediante el protocolo http, y un script CGI localizado en el servidor. En los esfuerzos para crear grandes páginas Web, el principal obstáculo es la elaboración del programa CGI, lo cual puede ser difícil de implementar y depurar de errores. Una función primaria del programa CGI es validar que el usuario ha proporcionado datos correctos, lo cual puede requerir de varias páginas de código CGI. Es aquí en donde JavaScript demuestra su utilidad, encargándose de validar los datos y enviando la información ya validada al servidor en vez de que el servidor se encarge de hacerlo todo. Una validación típica consiste en determinar que una caja de entrada en la que se espera un número del usuario contenga únicamente dígitos numéricos. Una función que podemos definir para tal propósito es la función isNumberString, que se mostrará a continuación y la cual regresa el valor 1 si la hilera contiene únicamente números, y regresa 0 si contiene algún caracter no-numérico. El valor regresado por la función nos dice si el dato es un dato válido. El siguiente script nos muestra la forma en la cual esto se lleva a cabo:
<HEAD>
<SCRIPT LANGUAGE="JavaScript">
function contacto(form){
Subject=document.forma_de_prueba.caja_de_entrada1.value
CC= document.forma_de_prueba.caja_de_entrada2.value
BCC= document.forma_de_prueba.caja_de_entrada3.value
location = "mailto:armando@ciberneticamexicana.com?subject="+Subject+"&Bcc="+
BCC+"&cc="+CC;
document.forma_de_prueba.submit();
}
</SCRIPT>
</HEAD>
<BODY>
<FORM NAME="forma_de_prueba">
Asunto del mensaje: <BR>
<INPUT TYPE="text" NAME="caja_de_entrada1" VALUE="¡Esta es una gran forma!" SIZE=50><P>
Enviar CC a: <BR>
<INPUT TYPE="text" NAME="caja_de_entrada2" VALUE="" SIZE=50><P>
Enviar CC ciega a: <BR>
<INPUT TYPE="text" NAME="caja_de_entrada3" VALUE="" SIZE=50><P>
<INPUT TYPE="button" VALUE="Send Mail" onClick="contacto()"><BR>
</FORM>
</BODY>
</HTML>
<HTML>Tal vez el lector sienta alguna curiosidad sobre cómo se lleva a cabo una interacción entre la máquina del usuario que está desplegando una página HTML que contiene código JavaScript y un programa de scripting CGI en el servidor. Veremos aquí los rudimentos sobre cómo se lleva a cabo esto para obtener una idea del mecanismo de intercambio.
<HEAD>
<TITLE>Validación de Entrada de Prueba</TITLE>
<SCRIPT LANGUAGE="JavaScript">
function resultadosPrueba (forma) {
VarPrueba = isNumberString (forma.caja_de_entrada.value)
if (VarPrueba == 1)
alert ("¡Felicidades! Metiste solo números");
else
alert ("¡Fuchi! Metiste una hilera con caracteres no numéricos");
}
function isNumberString (InString) {
if(InString.length==0) return (false);
var RefString="1234567890";
for (Count=0; Count < InString.length; Count++) {
TempChar= InString.substring (Count, Count+1);
if (RefString.indexOf (TempChar, 0)==-1)
return (false);
}
return (true);
}
</SCRIPT>
</HEAD>
<BODY>
<FORM NAME="miForma">
Mete una hilera que contenga números:
<INPUT TYPE="text" NAME="caja_de_entrada" VALUE="">
<INPUT TYPE="button" NAME="button" VALUE="Clic" onClick="resultadosPrueba(this.form)" >
</FORM>
</BODY>
</HTML>
Con JavaScript ejecutándose en la página Web en la máquina del usuario, se puede enviar información a un programa CGI en el servidor, y el programa CGI a su vez puede enviar de regreso más código JavaScript (que el navegador del usuario puede procesar) conteniendo información adicional. El programa CGI puede escribir código JavaScript en la máquina del usuario que asigne información a variables JavaScript en la máquina del usuario antes de enviar más código de regreso a la página Web. Usaremos como ejemplo la asignación del número de archivos que hay en un directorio (se sobreentiende que se trata de un directorio en el servidor, ya que JavaScript no permite ningún tipo de acceso al disco duro de la máquina del usuario) a una variable JavaScript que aquí llamaremos “NumberOfFiles”. Las instrucciones consistirán de los siguientes cuatro pasos (usando el ejemplo como guía, se pueden escribir muchas interacciones JavaScript-CGI):
1. Crear una variable JavaScript para contener el número.
2. Invocar el programa CGI en el servidor con cierta información.
3. Poner en marcha el programa CGI para crear y regresar cierto código JavaScript.
4. Imprimir el número en la página Web del usuario usando JavaScript.
En el primer paso, hay que crear una variable JavaScript para contener el número de archivos que hay en el directorio, lo cual se puede hacer del siguiente modo:
<SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript">En el segundo paso, el siguiente bloque de JavaScript invoca el programa CGI, diciéndole al programa CGI el nombre de la variable que será usada para contener el número de archivos y la localización del directorio en el que estamos interesados.
<!-- NumberOfFiles = 0;
//-->
</SCRIPT>
La ubicación del directorio tiene que ser ya sea la ruta completa del servidor hacia el directorio o la ruta relativa a la ubicación del programa CGI. Supondremos que estamos interesados en un directorio llamado “datadir”, que es un subdirectorio en donde está instalado el script. He aquí la manera en la que el JavaScript en la máquina del usuario se comunica con el programa CGI:
<SCRIPTEn el tercer paso, lanzamos el programa CGI en el servidor para crear y regresar cierto código JavaScript. Cuando el programa CGI es lanzado:
LANGUAGE="JavaScript"
SRC="/cgi-bin/script.cgi?NumberOfFiles&datadir"
TYPE="text/javascript">
</SCRIPT>
1. Leerá la información que fue enviada por el JavaScript en la máquina del usuario.
2. Tomará un listado de archivos del directorio.
3. Contará el número de archivos.
4. Creará el código JavaScript que le asigna dicho número a la variable JavaScript.
5. Se envía el código al JavaScript que está en la página Web descargada por el usuario.
Suponiendo que el servidor es una máquina operando bajo un sistema operativo UNIX/LINUX, el siguiente programa CGI elaborado usando el lenguaje de programación Perl puede hacer el trabajo pedido (el script CGI puede ser elaborado usando uno de varios lenguajes, no entraremos aquí en los detalles de cada línea mostrada):
#!/usr/bin/perlEn el cuarto y último paso, se imprime con JavaScript el número de archivos hacia la página Web:
use strict;
my %In = ();
my $NumFiles = 0;
my $JScode = '';
# 1. Leer lo que fue enviado por JavaScript.
($In{var},$In{loc}) = split /&/,$ENV{QUERY_STRING};
# 2. Tomar el listado de archivos del directorio
# 3. y contar el número de archivos.
if(opendir D,$In{loc})
{
my @d = grep ! /^\.\.?$/,readdir D;
closedir D;
$NumFiles = @d;
}
# 4. Crear el código JavaScript para enviarlo de regreso.
$JScode = "$In{var}=$NumFiles;";
# 5. Enviar código de la máquina cliente al JavaScript en la página Web.
print "Content-type: text/javascript\n\n";
print $JScode;
### fin del script ###
<SCRIPT LANGUAGE="JavaScript" TYPE=""text/javascript">De este modo tenemos un programa CGI en el servidor y tres bloques de código JavaScript, los cuales pueden estar en partes diferentes de la página Web siempre y cuando el primer bloque esté en el código fuente en algún lugar arriba del segundo bloque y el segundo bloque se encuentre en algún lugar arriba del tercer bloque, aunque podemos poner los tres bloques en un solo lugar del modo siguiente:
<!--
document.write('El número de archivos es: ' + NumberOfFiles);
//-->
</SCRIPT>
<SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript">En resumen, el primer bloque de JavaScript crea la variable NumberOfFiles, el segundo invoca el programa CGI para asignar el conteo de archivos a esta variable, y el tercer bloque imprime el valor de la variable, esto es, el número de archivos.
<!--
NumberOfFiles = 0;
//-->
</SCRIPT>
<SCRIPT
LANGUAGE="JavaScript"
SRC="/cgi-bin/script.cgi?NumberOfFiles&datadir"
TYPE="text/javascript">
</SCRIPT>
<SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript">
<!--
document.write('El número de archivos es: ' + NumberOfFiles);
//-->
</SCRIPT>
Usando este procedimiento básico para lograr que JavaScript y CGI sostengan una conversación entre ambos, se pueden crear otras interacciones adicionales JavaScript-CGI.
El lenguaje Perl no es la única alternativa para lidiar con los problemas que se presentan con la creación de scripts CGI. Hay otras alternativas, tales como PHP, Ruby, Bash, y Phyton, aclarándose que además de estos lenguajes hay también muchos otros lenguajes para lograr el mismo objetivo, aunque estos que se mencionan aquí son los más populares.
Desde que JavaScript hizo su aparición por vez primera como JavaScript 1.0, dicho lenguaje de programación ha estado siendo mejorado constantemente, y a su repertorio de objetos construídos internamente se le han ido agregando nuevos objetos que le han ido aumentando sus capacidades, objetos que no fueron mencionados en nuestra primera introducción a JavaScript con objeto de no enredar más el tema. Uno de tales objetos que veremos a continuación fue agregado precisamente para poder implementar nativamente en JavaScript la interfaz de la máquina cliente (la máquina del usuario) con la máquina que hace las funciones de servidor. Antes de entrar en pormenores sobre dicho objeto, intentaremos definir algo que hoy se conoce como Scripting Remoto (Remote Scripting). El Scripting Remoto es un conjunto de herramientas también conocido como AJAX que le dan al navegador la capacidad para poder trabajar con archivos o programas ubicados en un servidor Web. AJAX (abreviatura de Asynchronous JavaScript and XML, término usado por vez primera en un artículo elaborado por Jesse James Garrett y publicado el 18 de febrero de 2005 en Adaptive Path) es una referencia a la capacidad de JavaScript para usar un objeto relativamente nuevo llamado XMLHttpRequest usado para poder comunicarse con un servidor Web sin la necesidad de tener que enviar un formulario o de tener que cargar una página completa. El objeto Javascript XMLHttpRequest fue diseñado originalmente por Microsoft y terminó siendo adoptado por Mozilla, Apple y Google, para entrar a estandarización en el World Wide Web Consortium (W3C) que se encarga (entre muchas otras cosas) de fijar las convenciones que detallan las características propias de los objetos JavaScript. Es lo que se encarga de establecer una interacción dinámica entre el usuario, el navegador, y el servidor Web:
La manera en la cual trabaja la metodología AJAX es la siguiente:
1. El código JavaScript crea un ejemplar del objeto XMLHttpRequest y lo envía al servidor Web. El script JavaScript en la máquina cliente puede continuar ejecutándose después de haber sido enviada la requisición al servidor Web.
2. El servidor Web responde ya sea enviando los contenidos de un archivo (ubicado en un directorio o en un subdirectorio en donde a la máquina cliente se le haya dado permiso para acceder a dicho directorio o subdirectorio) o la salida de un programa que se ejecuta en el servidor Web.
3. Cuando se recibe una respuesta del servidor, se pone en marcha en la máquina cliente una función JavaScript para actuar sobre la información recibida.
4. El script al ejecutarse le muestra al usuario la información enviada desde el servidor, usando la convención del modelo DOM (Document Object Model) basada en el objeto document de JavaScript, eliminando con ello la necesidad de que la página Web tenga que ser cargada de nuevo en la máquina del usuario.
Al llegar aquí, se agregará que la “X” en AJAX codifica el acrónimo XML (Extensible Markup Language, o lenguaje de marcado extensible). XML es una convención que especifica el formato de los datos transferidos entre el servidor y el usuario, mientras que XMLHttpRequest es lo que se utiliza para que el usuario pueda recabar datos del servidor. Un archivo o programa puede enviar datos desde el servidor en formato XML, y JavaScript a su vez puede actuar sobre los datos recibidos usando sus métodos para trabajar con XML, los cuales son semejantes a los métodos DOM de JavaScript. XML es sólo una manera de enviar datos, y no siempre es la más sencilla. El servidor podría simplemente enviar texto llano sin ningún adorno, texto que el script en la máquina cliente puede presentarle al usuario, o puede enviar HTML en vez de XML. Incluso algunos programadores regresan datos al usuario en formato JavaScript, lo cual puede ser ejecutado fácilmente en la máquina del usuario usando la función eval.
Lo más importante en todo esto es que no es necesario que toda la página Web esté siendo cargada de nuevo cada vez que hay una actualización dinámica que se está llevando a cabo, sólo una porción de la página Web es “refrescada” mientras que lo demás queda intacto, y esto es válido para cualquier computadora o dispositivo que esté llevando a cabo una transacción con un servidor Web:
Para comunicarse con el servidor usando la metodología AJAX, el primer paso como ya se dijo arriba consiste en crear un ejemplar del objeto XMLHttpRequest con la ayuda de new, haciendo algo como lo siguiente (le daremos aquí al ejemplar creado el nombre xhttp porque da a entender muy bien la intención para la cual el objeto fué creado, aunque también podríamos haberle dado al ejemplar el nombre de requisicionAjax o más brevemente reqAjax, el programador es quien debe dar a los ejemplares de objetos los nombres que mejor le parezcan)::
xhttp = new XMLHttpRequest();
Una vez creado en la máquina cliente un ejemplar del objeto XMLHttpRequest, se recurre al método open() de dicho objeto, especificando el nombre del archivo que será abierto así como la forma en la cual los datos serán enviados al servidor: GET o POST. En una requisición GET los datos son codificados en el domicilio Web URL que carga el programa, mientras que en una requisición POST los datos son enviados por separado y pueden contener más datos. Estos son los mismos dos métodos que se usan en la elaboración de formularios con JavaScript. La sintaxis es:
xhttp.open("GET", "nombre del archivo");
A modo de ejemplo, el siguiente comando abre en el servidor el programa búsqueda.php (escrito obviamente en lenguaje PHP) y envía al servidor Web el valor “Armando” como parámetro de indagación (query):
xhttp.open("GET", "search.php?query=Armando");
Para enviar la requisición al servidor, usamos el método send() del objeto XMLHttpRequest. Los datos a ser enviados son el argumento suministrado a send(). Para una requisición GET, podemos usar el valor null.
Después de que se ha enviado la requisición, el script en la máquina del usuario continúa ejecutándose sin detenerse a esperar un resultado. Puesto que la respuesta puede llegar en cualquier momento, dicha respuesta puede ser detectada mediante un manejador de eventos. El objeto XMLHttpRequest tiene un manejador de eventos precisamente para tal propósito. Se llama onreadystatechange. Podemos crear una función para manejar la respuesta y fijarla como manejador para el evento usando la sintaxis propia de los manejadores de eventos en JavaScript:
xhttp.onreadystatechange = miFuncion;
siendo miFuncion() la manera en la cual manejaremos la respuesta enviada por el servidor. Para miFuncion() más abajo definiremos una función que llamaremos, apropiadamente, xhttpRespuesta(). La siguiente ilustración pretende dar una idea generalizada sobre cómo va evolucionando la transacción entre la máquina del usuario y el servidor Web al ir avanzando el tiempo (de arriba hacia abajo) bajo este esquema:
El objeto XMLHttpRequest tiene una propiedad, “readyState”, que indica el estatus del objeto, y el evento indicado es disparado cada vez que la propiedad “readyState” cambia. Los cinco valores posibles de “readyState” van de cero para una requisición nueva hasta 4 para una requisición ya completa, de modo tal que la función para el manejo del evento sólo tiene que estar alerta para un valor de 4. Aunque el proceso de requisición haya sido completado, ello no significa que haya sido exitoso. La propiedad “status” es puesta a 200 si la requisición tuvo éxito, o a un código de error si fracasó. La propiedad “statusText” almacena una explicación textual del error, o un “OK” en caso de éxito.
Como es común en los manejadores de eventos, hay que asegurarse de que el nombre de la función haya sido especificado sin los paréntesis, porque con los paréntesis se está referenciando al resultado de la función, sin los paréntesis nos estamos refiriendo a la función en sí.
Cuando la propiedad “readyState” alcanza el valor de 4 y la requisición está completa, los datos regresados por el servidor están disponibles al script a través de dos propiedades:
1) responseText.- Esta es la respuesta enviada en texto llano por el servidor, sin ningún tipo de adorno.
2) responseXML.- Esta es la respuesta enviada como un objeto XML.
En la filosofía AJAX, la intención original del objeto XMLHttpRequest es que dicho objeto lleve a cabo una transacción con el servidor obteniendo una respuesta en un documento con formato XML. Sin embargo, esto no tiene que ser necesariamente así, y aún si el documento en el servidor no es un documento XML y lo que se envía es texto llano, la transacción se puede seguir procesando usando responseText en lugar de responseXML.
Los métodos DOM (Document Object Model) de JavaScript fueron concebidos para funcionar con XML, y usualmente se recurre a la propiedad responseXML que forma parte del DOM XML. Posteriormente, usamos el método:
getElementByTagName()
para extraer los datos del documento XML enviado por el servidor.
Como puede apreciarse por lo que hemos visto, las transacciones llevadas a cabo con la metodología AJAX pueden ser algo complicadas, razón por la cual para aclarar las cosas veremos una aplicación de lo anterior mediante un ejemplo en el cual construiremos un modelo de examen de conocimientos a través de preguntas y respuestas que un usuario puede tomar por cuenta propia vía Internet. Podemos simplificar un poco el asunto creando una utilería de funciones comúnmente conocida como “librería” (al estilo de las librerías especializadas de funciones matemáticas que son invocadas en los lenguajes de alto nivel como BASIC o FORTRAN según se requiera dentro de algún programa), definiendo dentro de la librería dos funciones JavaScript que llamaremos xhttpRequisicion() y xhttpRespuesta() y guardando la librería bajo el nombre de archivo “libreria.js”:
********* Archivo libreria.js
// Declaracion de las variables globales xhttp (la cual en forma
// predeterminada es puesta a "falso") y xhttpCallback para
// mantenerle la pista a la requisición hecha al servidor.
// Por conveniencia (y no por requerimiento alguno del lenguaje
// JavaScript) se usará la palabra "Callback" que es convencional
// en la literatura técnica para referirse a la "devolución
// de llamada" hecha por el servidor Web a la requisición de
// servicio, algo así como "devolverle la llamada telefónica" a
// alguien que ha dejado mensaje en una contestadora automática
//
var xhttp=false, xhttpCallback;
//
// Esta es la definición de la primera función, xhttpRequisicion(),
// encargada del montaje de una requisición al servidor:
//
function xhttpRequisicion(nombrearchivo) {
if(window.XMLHttpRequest) { // codigo para IE7+, Firefox,
// Safari y otros navegadores
xhttp = new XMLHttpRequest();
}
else { // codigo para IE5, IE6
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xhttp.open("GET", nombrearchivo);
xhttp.onreadystatechange = xhttpRespuesta;
xhttp.send(null);
}
//
// Esta es la definición de la segunda función, xhttpRespuesta(),
// la cual está a la espera de una respuesta para invocar otra
// función
//
function xhttpRespuesta() {
if (xhttp.readyState != 4) return;
if (xhttp.status == 200) {
// En caso de que la requisición haya tenido éxito:
if (xhttpCallback) xhttpCallback();
} else alert("La requisición fracasó:" + xhttp.statusText);
return xhttp.ResponseXML;
}
*********
Es frecuente encontrar una forma alterna de programar lo anterior:
********* Archivo libreria.js
// Declaracion de variables globales para mantenerle la pista
// a la requisición hecha al servidor
//
var xhttp=false, xhttpCallback;
//
// Esta es la definición de la primera función, xhttpRequisicion(),
// encargada del montaje de una requisición al servidor
//
function xhttpRequisicion(nombrearchivo) {
try {
//código para IE7+, Firefox, Safari y otros navegadores
xhttp = new XMLHttpRequest();
} catch(error) {
try {
// código para IE5 e IE 6
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch(error) {
return false;
}
}
xhttp.open("GET", filename);
xhttp.onreadystatechange = xhttpRespuesta;
xhttp.send(null);
}
//
// Esta es la definición de la segunda función, xhttpRespuesta,
// la cual está a la espera de una respuesta para invocar otra
// función
//
function xhttpRespuesta() {
if (xhttp.readyState != 4) return;
if (xhttp.status == 200) {
// En caso de que la requisición haya tenido éxito:
if (xhttpCallback) xhttpCallback();
} else alert("La requisición fracasó:" + reqAjax.statusText);
return true;
}
*********
Se amerita explicar en mayor detalle el código anterior de la segunda versión que se ha dado a la librería.
La primera función xhttpRequisicion() se encarga de todos los pasos necesarios para crear y enviar una requisición XMLHttpRequest. Todo comienza, desde luego, con la creación de un objeto XMLHttpRequest en JavaScript usando new. Desafortunadamente, en virtud de la competencia intensa que hubo entre Netscape y Microsoft a raíz de la cual Microsoft respondió a la innovación del lenguaje de scripts JavaScript de Netscape inventando su propia plataforma para tales propósitos bajo el nombre ActiveX y creando con ello una incompatibilidad entre los navegadores Internet Explorer y Netscape Navigator, mientras que sólo las versiones 7.0 y superiores del navegador Internet Explorer son capaces de poder manejar objetos creados como objetos XMLHttpRequest, versiones más bajas como 5.0 y 6.0 no reconocen tales scripts y solo aceptan objetos ActiveX, de modo tal que para poder comunicarse con muchos navegadores en la Web que no han sido actualizados se tiene que crear un código para navegadores que trabajan con objetos ActiveX y otro código algo diferente para navegadores que manejan JavaScript. En la segunda versión de la librería “libreria.js” estas incompatibilidades las manejamos no usando el mecanismo de control if..else sino el mecanismo basado en:
try{} catch{} finally
que fue tomado por JavaScript del lenguaje Java para lo que se conoce como el manejo de excepciones, y que nos permite planificar por adelantado en caso de que haya problemas potenciales como navegadores que usan distintos mecanismos de interacción con las páginas Web, respondiendo en forma adecuada cuando haya otra alternativa (o alternativas) cuando puedan presentar dificultades. La sintaxis para el uso de este mecanismo es la siguiente (“try” se traduce como “probar” mientras que “catch” se traduce como “capturar”):
try {De este modo, try y catch (y en ocasiones finally, todo depende de los gustos del programador) son usados para crear la requisición. En ambas librerías primero se usa el método convencional intentando crear un objeto XMLHttpRequest (para navegadores tales como Firefox, Safari, y versiones de Internet Explorer superiores a las versiones 5 y 6), y si se produce un error entonces se recurre al método ActiveX (para navegadores Internet Explorer 5 y 6). Si la alternativa falla también y se produce un error, la variable xhttp es puesta a false para indicar que el navegador no da soporte al conjunto de herramientas AJAX.
// Ejecutar uno o varios enunciados
}
catch (algún error de excepción # 1) {
// Manejar la excepción con otro tipo de código
}
catch (algún error de excepción # 2) {
// Manejar la excepción con otro tipo de código
}
finally {
// Ejecutar operaciones de limpieza
}
En lo que corresponde a la segunda función xhttpRespuesta(), esta función se usa con el manejador de eventos onreadystatechange. Después de checar primero si la propiedad readyState del objeto reqAjax es 4, en caso de que lo sea la función checa entonces si la propiedad status del objeto xhttp tiene un valor 200 que indicará que la requisición fué exitosa. En caso positivo, se ejecutará la función xhttpCallback() almacenada en la variable del mismo nombre. En caso contrario, se dará al usuario un mensaje de error mediante una caja de alerta.
Para usar la pequeña “librería” de funciones (en cualquiera de las dos versiones dadas arriba): (1) guardamos el código como un archivo JavaScript con un nombre como “libreria.js” en la misma carpeta en la cual se encuentra la página HTML principal que invocará dicho archivo, (2) se incluye este código de script al principio del documento principal HTML con una etiqueta como:
<script language="JavaScript" type="text/javascript" src="...">
que en nuestro ejemplo sería:
<script language="JavaScript" type="text/javascript" src="libreria.js">
y que debe ser puesta en la página principal HTML antes de cualesquier otro script que utilice sus funciones, (3) en el script que dé uso al código contenido en la “librería” JavaScript y la página principal HTML, es necesario crear una función que sea llamada cuando la requisición esté completada, poniéndose la variable xhttpCallback igual a dicha función, (4) se hace un llamado a la función xhttpRequisicion() tomando en cuenta que su parámetro (nombrearchivo) es el nombre del archivo o programa ubicado en el lado del servidor (puesto que la librería da soporte únicamente a requisiciones GET, no es necesario especificar método alguno), (5) la función especificada en xhttpCallback será llamada cuando la requisición haya completado exitosamente, y la variable global xhttp almacenará los datos en sus propiedades responseText y responseXML.
A continuación, dándole buen uso a esta pequeña (y reutilizable) librería, como ejemplo de una aplicación JavaScript que usa ventajosamente el Scripting Remoto, crearemos la página HTML que proporcionará al usuario un listado de preguntas, ofreciendo en la misma página las respuestas a cada pregunta. El archivo HTML en la máquina del usuario define un formulario sencillo (usando las etiquetas <form> del mismo HTML, y no un formulario creado con JavaScript o con VBScript) con un campo en donde el usuario suministra su respuesta y la somete con un botón de “Submit”. El código fuente del archivo HTML que llamaremos “cuestionario.htm” es el siguiente:
********* Archivo cuestionario.html
<html>
<head>
<title>Ejemplo de la interacción cliente-servidor</title>
<script language="JavaScript" type="text/javascript"
src="libreria.js">
</script>
</head>
<body>
<b>Examen de conocimientos</b>
<form>
<p>
<b>Pregunta:</b>
<span id="pregunta">...</span>
</p>
<p>
<b>Respuesta:</b>
</p>
<input type="text" name="respuesta" id="respuesta">
<input type="button" value="Submit" id="submit">
</p>
<input type="button" value="Comenzar el examen" id="empezar">
</form>
<script language="JavaScript" type="text/javascript"
src="examen.js">
</script>
</body>
</html>
*********
Aunque podemos poner el listado de preguntas y respuestas dentro de un archivo JavaScript complementario que llamaremos “examen.js” y en el cual pondremos las funciones que manejaran la base de datos en la cual se encuentran las preguntas y respuestas, pondremos por separado el listado de preguntas y respuestas en un archivo XML que será almacenado en el servidor bajo el nombre “preguntas.xml”. Después de todo, el propósito de XML es facilitar el transporte de datos entre el servidor Web y el usuario. En el caso de unas cuantas preguntas y respuestas, la ventaja de poner las preguntas y respuestas en un archivo XML separado en lugar de incluírlas dentro de un script no es aparente; sin embargo para una base de datos mucho más grande (por ejemplo, 300 preguntas y respuestas) la ventaja de recurrir a un archivo separado XML se vuelve obvia. Recuérdese que en XML no hay etiquetas predefinidas, nosotros mismos inventamos las etiquetas y les damos los nombres que queramos a nuestro antojo (de preferencia nombres que se puedan asociar al objetivo que está siendo manejado).
********* Archivo preguntas.xml
<?xml version="1.0" ?>
<preguntas>
<q>¿En qué año descubrió Colón el continente americano?</q>
<r>En 1492/r>
<q>¿Cómo se llama la convención formal que define a JavaScript?</q>
<r>ECMAScript</r>
<q>¿Quién descubrió la penicilina?</q>
<r>El Doctor Alexander Fleming</r>
<q>¿Qué género literario cultivó Jules Verne?</q>
<r>Ciencia-ficción</r>
</preguntas>
*********
En el archivo XML, la etiqueta <preguntas> encierra el archivo entero, y cada pregunta y su respectiva respuesta están encerradas en las etiquetas <q> y <r>. Para usar este documento XML, lo podemos guardar como “preguntas.xml” en la misma carpeta en la cual se encuentran el archivo principal HTML y la librería de funciones que hemos diseñado.
Con un examen de conocimientos tan pequeño conteniendo solo cuatro preguntas y respuestas, podríamos haber simplificado las cosas almacenando las preguntas y respuestas en una lista enlazada (array) JavaScript. Pero con un examen mucho mayor conteniendo miles de preguntas y respuestas, o un programa ubicado en el lado del servidor que “jale” preguntas de una base de datos que constantemente está siendo actualizada y renovada, o inclusive cientos de archivos distintos con diferentes conjuntos de exámenes para escoger entre ellos, podemos apreciar el beneficio de usar un archivo XML separado.
Falta crear un cuarto archivo con el código JavaScript necesario para poder manejar la base de datos de preguntas y respuestas almacenadas en el archivo XML. Para ello, recurriremos a la manipulación de nodos usando las etiquetas en el archivo XML como referente para ir recorriendo la lista enlazada de nodos empezando con el primer nodo accesible con firstChild. Puesto que ya hemos creado una librería para manejar las complejidades de formular requisiciones de servicio y obtener respuestas del servidor, el cuarto archivo que llamaremos “examen.js” no requerirá entrar en esos detalles. Definiremos en este archivo las siguientes tres funciones: obtenerPreguntas(), siguientePregunta() y checarRespuesta().:
********* Archivo examen.js
// Definición de la variable global qn que representa a
// la pregunta actual, inicializada a la primera pregunta
// de la lista de preguntas
//
qn = 0;
//
// La siguiente función va cargando las preguntas del
// archivo XML
//
function obtenerPreguntas() {
obj = document.getElementById("pregunta");
obj.firstChild.nodeValue = "por favor espere";
xhttpCallback = siguientePregunta;
xhttpRequisicion("preguntas.xml");
}
//
// Exhibir la siguiente pregunta
//
function siguientePregunta() {
preguntas = xhttp.responseXML.getElementsByTagName("q");
obj = document.getElementById("pregunta");
if (qn < preguntas.length) {
q = preguntas[qn].firstChild.nodeValue;
obj.firstChild.nodeValue = q;
}
else {
obj.firstChild.nodeValue = ("no hay más preguntas");
}
}
//
// Checar la respuesta a la pregunta proporcionada
//
function checarRespuesta() {
respuestas = xhttp.responseXML.getElementsByTagName("r");
campo_respuesta = document.getElementById("respuesta");
if (r == campo_respuesta.value) {
alert("¡Respuesta correcta!");
}
else {
alert("Incorrecto. La respuesta correcta es: " + r)
}
//
// Pasar a la siguiente pregunta
//
qn = qn + 1;
campo_respuesta.value = "";
siguientePregunta();
}
//
// Montaje de los manejadores de eventos para los botones
//
obj = document.getElementsById("empezar");
obj.onClick = obtenerPreguntas;
respuesta = document.getElementById("submit");
respuesta = checarRespuesta;
*********
En este archivo, la primera línea de código es una variable global, qn, encargada de mantenerle la pista a la pregunta actual que está siendo proporcionada al usuario, y es puesta inicialmente a cero para empezar con la primera pregunta. La función obtenerPreguntas() es invocada cuando el usuario hace clic en el botón de “Comenzar el examen”. Esta función recurre a la librería puesta bajo “libreria.js” para hacer una requisición a los contenidos del archivo “preguntas.xml”, fijando la variable xhttpCallback a la función siguientePregunta(). La función siguientePregunta() es invocada cuando la requisición hecha al servidor ha sido completada. Y la función checarRespuesta() es invocada cuando el usuario escribe su respuesta y la somete a consideración.
Para que lo anterior pueda funcionar del modo deseado, se requiere poner en el servidor Web en un mismo directorio (una misma carpeta) los cuatro archivos “cuestionario.htm”, “examen.js”, “preguntas.xml” y “libreria.js”. Sin embargo, antes de hacer la prueba, es importante tener presente lo siguiente:
Se requiere de una computadora que esté habilitada como servidor Web, por lo general no es posible llevar a cabo transacciones con el objeto XMLHttpRequest en una computadora de usuario en virtud de las restricciones en seguridad impuestas por los navegadores instalados en las máquinas de los usuarios sobre scripting remoto.Esta última advertencia no debe desalentar al lector, porque no es cosa del otro mundo habilitar cualquier computadora de escritorio como un servidor Web (al fin y al cabo, todo servidor Web es en su hardware una computadora como cualquier otra, quizá más veloz y con mayores respaldos de datos y archivos en caso de fallos a través de hardware opcional como los arreglos de discos duros RAID, pero sigue siendo una computadora como cualquier otra).
Una cosa que hemos dejado ausente en esta entrada son las medidas de seguridad que deben ser implementadas en las transacciones de la interfaz cliente-servidor, las cuales se deben apoyar en una validación constante de los datos enviados y recibidos. Dependiendo de las transacciones que se lleven a cabo, esto puede ser un asunto de la más alta prioridad, ya que si un servidor Web es utilizado para el manejo de datos confidenciales de los visitantes (domicilios, números telefónicos, contraseñas, etc.) así como transacciones en las cuales haya intercambio de divisas y transferencias de fondos, la tentación para someter a un servidor Web a un ataque es casi irresistible dependiendo de los montos de dinero que se estén manejando o de la utilidad de los datos confidenciales que caigan en manos de intrusos no autorizados. Las restricciones sobre lo que puede o no hacer un usuario (o mejor dicho, un intruso) sobre un servidor Web son reforzadas tanto a nivel de software con programas de detección de malware (virus, troyanos, gusanos, etc.) así como con tarjetas de expansión que son agregadas al hardware del servidor las cuales permiten una vigilancia más cercana sobre el tráfico de datos que se está realizando.
Usaremos en nuestra implementación de la “búsqueda en vivo” el archivo “librería.js” que fue desarrollado en el ejemplo anterior, demostrando el valor de la <i>reusabilidad</i> de este tipo de librerías. El archivo HTML define un formulario sencillo usando las etiquetas HTML <form>, con un campo de texto en el que se escribe el dato a buscar y un botón de “Submit” junto con algunos cuantos “ganchos” (hooks) para el script:
********* Archivo busqueda.html
<html>
<head>
<title>Ejemplo de una Búsqueda en Vivo</title>
<script language="JavaScript" type="text/javascript"
src="libreria.js">
</script>
</head>
<body>
<b>Ejemplo de Búsqueda en Vivo</b>
<form>
<p>
<b>Buscar:</b>
<input type="text" size="50" id="busquedaviva">
</p>
<div id="resultados">
<ul id="lista">
<li>Los resultados serán mostrados aquí</i>
</ul>
</div>
</form>
<script language="JavaScript" type="text/javascript"
src="busqueda.js">
</script>
</body>
</html>
*********
Como puede verse, al principio en la cabecera <head> del archivo la etiqueta <script> incluye la librería JavaScript “libreria.js” construída en el ejemplo anterior. Posteriormente, en la etiqueta <script> puesta dento del cuerpo <body> del archivo HTML, se invoca el archivo JavaScript “busqueda.js” que será construído más abajo. El elemento de entrada <input> con el valor id “busquedaviva” produce el campo de entrada (la caja de texto) en donde escribiremos lo que queremos buscar. El elemento <div> con el valor id “resultados” actuará como un contenedor para los resultados dinámicamente buscados, y se crea una lista con la etiqueta HTML <ul> que será remeplazada en forma dinámica e interactiva conforme se vaya escribiendo algo de texto en el campo de texto.
A continuación, se presenta un programa elaborado en el lenguaje de programación PHP que será utilizado para producir resultados de búsqueda, en formato XML. Este programa incluye una lista de nombres almacenados bajo un arreglo de lista “array”, y responderá a una petición JavaScript con los nombres de lo que el usuario vaya poniendo en el campo de texto. A modo de ejemplo, se muestra a continuación la salida producida por el programa PHP cuando se está buscando bajo el criterio de búsqueda “Guzman”:
<nombres>Aunque la lista de nombres está siendo almacenada dentro del programa PHP por cuestiones de simplicidad, en una aplicación real la lista sería almacenada en una base de datos como MySQL, y el script podría ser fácilmente adaptado para trabajar con una base de datos que incluya miles de nombres. El programa PHP es el siguiente (el archivo se puede guardar bajo un nombre como “busqueda.php”; la búsqueda es llevada a cabo por el programa PHP actuando dentro del mismo servidor Web, y una vez concluídos los resultados de la búsqueda estos son enviados de regreso a la máquina del usuario):
<nombre>Enrique Guzman</nombre>
<nombre>Alejandra Guzman</nombre>
</nombres>
********* Archivo busqueda.php
<?php
header("Content-type: text/xml");
$nombres = array(
"Joaquin Cordero", "Martha Andrade", "Karla Alvarez",
"Armando Martinez", "Regina Murguia", "Agustin Tellez",
"Shanik Aspe", "Sebastian Rulli", "Enrique Guzman",
"Abel Salazar", "Ariadne Welter", "Ofelia Tellez",
"Ingrid Barrera", "Eulalio Gonzalez", "Thelma Madrigal",
"Angelique Boyer", "Adalberto Martinez", "Carlos Loret",
"Manny Pacquiao", "Susana Gonzalez", "Manuel Clouthier",
"Alejandra Guzman", "Mario Moreno", "Andrea Legarreta",
"Emiliano Zapata", "Daniela Valero", "Lazaro Cardenas",
"Alejandra Duran", "Odalys Ramirez", "German Valdez" );
if (!$query) $query = $_GET['query'];
echo "<?xml version=\"1.0\" ?>\n";
echo "</nombres>\n";
while (list($k,$v)=each($nombres)) {
if (stristr($v,$query))
echo "<nombre>$v</nombre>\n";
}
echo "</nombres>\n";
?>
*********
Aunque ésta no es la ocasión para entrar en mucho detalle acerca del lenguaje de programación PHP, de cualquier modo intentaremos dar una explicación sobre las partes principales del código PHP que se acaba de dar, esto con la finalidad de que no queden muchas dudas sobre lo que está ocurriendo en el trasfondo.
La primera línea del programa abre con “<?php” indicando que se trata no de un programa HTML o de un programa XML o de una porción de código JavaScript sino de un programa PHP, y la última línea “?>” encierra todo el código como tal.
El enunciado header envía una cabecera indicando que la salida será un texto dado en formato XML. Esto se requiere para que XMLHttpRequest pueda usar correctamente la propiedad responseXML (en lugar de la propiedad responseText).
Las variables en PHP cuando son declaradas son identificadores precedidos con el signo de dinero (“$”), por ejemplo $hilera, $fecha, $nombres, $k, $v, etc., y pueden almacenar un valor de cualquier tipo (número o hilera de texto), inclusive un arreglo de lista (array) sin necesidad de tener que especificar el tipo de dato:
$dato = "Armando";
$dato = 2014;
$dato = array('Armando','2014','Regina');
En PHP se declaran arreglos de lista (arrays) asignándole a una “variable de array” una identificación como $nombres y encerrando los elementos del arreglo entre paréntesis con cada elemento del arreglo puesto entre comillas dobles. El constructor array() crea un arreglo de lista, y cada elemento de la lista puede ser accesado por su posición usando para ello un índice (un entero positivo, siendo cero la primera posición):
$dato[0] = "Armando";
$dato[1] = "2014";
$dato[2] = "Regina";
De este modo, el enunciado:
$nombres = array( ... )
crea una lista de nombres, con cada nombre puesto entre comillas dobles y separados mediante una coma, lo cual constituye aquí el contenedor de una base de datos.
En este tipo de programación se hace uso del hecho de que se puede accesar a los valores de un formulario mediante las variables de arreglo de lista $_POST y $_GET. Aquí el programa busca una variable GET que hemos llamado “query” (siguiendo la costumbre usada en el uso de lenguajes de bases de datos SQL que siguen la filosofía de lo que se conoce como un “Structured Query Language”), y se recurre a un bucle del tipo “while” para producir como salida todas las variables que vayan coincidiendo con los resultados de la búsqueda.
Cada arreglo de lista PHP mantiene la pista del elemento actual con el que estamos trabajando con un puntero (apuntador) hacia el elemento actual conocido como el el iterador. La función each() regresa la “llave” (key) y el valor del elemento actual como parte de un arreglo, moviendo al iterador hacia el siguiente elemento del arreglo de lista. Por su parte, el comando PHP list asigna a un conjunto de variables los elementos de un arreglo de lista:
list($primero, $segundo) = array(1,2);
// $primero = 1, $segundo = 2
La función stristr() es la variación insensible a la capitalización (insensible a que cualquiera de los caracteres del texto de una hilera proporcionada como argumento sea puesto en letra mayúscula o letra minúscula) de la función strstr() para el manejo de hileras que busca y encuentra la aparición de una sub-hilera de caracteres dentro de una hilera más grande, regresando el resto de la hilera a partir de esa sub-hilera. Por ejemplo:
$hilera = "Armando,Martinez,2014,Regina";
$resto = strstr($hilera, ",");
// el resto es ",Martinez,2014,Regina"
La línea:
echo "<nombre>$v</nombre>\n";
es la que se encarga de enviar (como un “eco”) del servidor a la máquina del usuario cada dato individual (cada nombre que va puesto entre un par de comillas dobles) en formato XML, enviando todo el “documento XML” conformándose entre la porción de código:
echo "<?xml version=\"1.0\" ?>\n";De este modo, no hay un archivo XML propiamente dicho, el texto XML se va generando paso a paso enviándose dinámicamente del servidor a la máquina del usuario según se requiera.
echo "</nombres>\n";
while (list($k,$v)=each($nombres)) {
if (stristr($v,$query))
echo "<nombre>$v</nombre>\n";
}
echo "</nombres>\n";
Falta por definir el código JavaScript que será enviado a la máquina del usuario para el manejo de los datos XML que estarán siendo enviados por el servidor. En este código definiremos tres funciones: EmpezarBusqueda(), BusquedaEnVivo(), y ExhibirResultados(). Obsérvese que a partir de la primera función empezamos haciendo uso del objeto window, un objeto JavaScript de alto nivel que tiene como propiedades suyas al objeto document, al objeto frame y al objeto location, aunque en realidad lo que usamos son sus métodos clearTimeout() y setTimeout(). El código mostrado puede ser guardado bajo algún nombre descriptivo como “busqueda.js”:
********* Archivo busqueda.js
// Se declara una variable global para administrar el
// lapso de tiempo predeterminado para cada opresión
// de tecla
//
var t;
//
// Empieza la búsqueda con una declaración de la
// primera función que fijará un retardo de tiempo
// de 200 milisegundos
//
function EmpezarBusqueda() {
if (t) window.clearTimeout(t);
t = window.setTimeout("BusquedaEnVivo()",200);
}
//
// Empieza la búsqueda en vivo
//
function BusquedaEnVivo() {
query = document.getElementById("busquedaviva").value;
// Ensamble del nombre del archivo PHP
nombrearchivo = "search.php?query=" + query;
xhttpCallback = ExhibirResultados;
//
// Se recurre a la libreria de funciones para hacer
// el envío de una requisición al servidor
//
xhttpRequisicion(nombrearchivo);
}
//
// Mostrar los resultados de la búsqueda
//
function ExhibirResultados() {
//
// Remover la lista previa:
//
ul = document.getElementById("lista");
div = document.getElementById("resultados");
//
// Manipulación de nodo: remoción
//
div.removeChild(ul);
//
// Construír una nueva lista
//
ul = document.createElement("UL");
//
// Manipulación de nodo: creación
//
ul.id = "lista";
//
nombres = xhttp.responseXML.getElementsByTagName("nombre");
for (i=0; i < nombres.length; i++) {
nombre = nombres[i].firstChild.nodeValue;
// Manipulación de nodo: creación de nodo de texto
texto = document.createTextNode(nombre);
li.appendChildNode(texto);
ul.appendChild(li);
}
if (nombres.length == 0) {
li = document.createElement("LI");
li.appendChild(document.createTextNode("Cero resultados"));
ul.appendChild(li);
}
//
// Mostrar al usuario el texto nuevo
//
div.appendChild(ul);
}
//
// Montar un manejador de eventos
//
obj = document.getElementById("busquedaviva");
obj.onkeydown = EmpezarBusqueda;
*********
El script anterior incluye varios componentes. Primero se define una variable global t, la cual almacena un puntero (apuntador) hacia un lapso programado de tiempo que es usado posteriormente. La función EmpezarBusqueda() es invocada cuando el usuario oprime una tecla (esto es llevado mediante el manejador de eventos puesto al final del script). Esta función utiliza el método setTimeout() del objeto JavaScript window para llamar la función BusquedaEnVivo() después de un retardo de 200 milisegundos. El retardo de tiempo es necesario para que la tecla que el usuario oprima tenga tiempo suficiente para aparecer en el campo de búsqueda (la caja de texto disponible al usuario). La función BusquedaEnVivo() ensambla un nombre de archivo que combina busqueda.php con la petición de búsqueda “query” en el campo de búsqueda, y pone en movimiento la transacción entre el cliente y el servidor usando la función xhttpRequisicion() del archivo “librería.js”. La función ExhibirResultados() es invocada cuando la transacción XMLHttpRequest entre el cliente y el servidor ha sido completada, y se encarga de borrar la lista de la sección <div id="resultados">, ensamblando tras esto una nueva lista usando los resultados previos. En caso de no haber resultados, se pone en la lista un mensaje que dice “Cero resultados”. Las últimas dos líneas del script se encargan de montar a la función EmpezarBusqueda() como el manejador de eventos para el evento onkeydown del campo de búsqueda.
Para poner a prueba lo anterior, se requiere poner en el servidor Web en un mismo directorio (una misma carpeta) los cuatro archivos “busqueda.html”, “busqueda.js”, “busqueda.php” y “libreria.js”. Sin embargo, antes de hacer la prueba, se vuelve a repetir la advertencia que ya se había dado arriba:
Se requiere de una computadora que esté habilitada como servidor Web, por lo general no es posible llevar a cabo transacciones con el objeto XMLHttpRequest en una computadora de usuario en virtud de las restricciones en seguridad impuestas por los navegadores instalados en las máquinas de los usuarios sobre scripting remoto.No solo no es posible experimentar con lo anterior en una computadora ordinaria conectada a Internet que no sea un servidor Web, ni siquiera es posible verificar la integridad del programa PHP que se ha dado aquí (o cualquier otro programa PHP, para ese caso) porque aunque la mayoría de los navegadores tienen la capacidad para poder ejecutar código JavaScript o para poder usar código de lenguaje Java (esto último requiere que se instale el paquete completo del lenguaje Java en una máquina), carecen de la capacidad para poder manejar por cuenta propia un programa escrito en lenguaje PHP.
Las advertencias que se acaban de dar no deben ser motivo para desalentar a los experimentadores más osados que quieran penetrar más a fondo en el estudio de lo que tiene que ver con el montaje de los servidores Web. Contrariamente a lo que algunos pudieran pensar, no se requiere de una computadora poderosa con un costo de varias decenas o inclusive de varios miles de dólares para poder ser propietario de un servidor Web. Es muy posible que la máquina que está usando el lector en estos momentos pueda ser habilitada sin mayores problemas para poder funcionar como un servidor Web. Incluso es posible que el lector tenga alguna computadora vieja arrumbada en casa que pueda ser habilitada como servidor Web siempre y cuando no esté descompuesta. Y lo más atractivo es que todo el proceso sea gratuito, sin costo alguno, ya que todo lo que se requiere está disponible sin costo alguno en Internet (excepción hecha de productos comerciales como el paquete ASP.NET de Microsoft que puede costar bastante caro).
Para poder habilitar una computadora cualquiera ya sea nueva o usada como servidor Web, lo primero que se tiene que hacer es instalarle un sistema operativo que esté a la altura de lo que se requiere para poder usarla como un servidor Web, y ello requiere instalar un sistema operativo propio no de una computadora de escritorio sino de un servidor Web. Un buen sistema operativo para tales efectos, obtenible sin costo alguno descargándolo de la Web, es el Linux Ubuntu; solo hay que tener precaución de descargar el sistema operativo no para computadoras de escritorio ordinarias sino para computadoras que estarán dedicadas como servidores Web (puesto que el proceso de descarga de un sistema operativo como el Linux Ubuntu a un archivo de imagen ISO es algo tardado, hay varias empresas que venden a bajo costo los CD de instalación).
Una vez que se ha instalado en una máquina un sistema operativo como el Linux Ubuntu para servidores Web (sacando fuera de la máquina el sistema operativo previo que haya tenido limpiando los contenidos del disco y formateando el disco), el lector se preguntará: ¿Y cómo obtendré un domicilio IP para poder activar mi servidor Web? Si el usuario está conectado desde su casa a Internet a través de un modem de alta velocidad, el lector ya tiene ese domicilio IP, es el domicilio de su máquina, y ese mismo domicilio lo puede usar para dar de alta su servidor Web.
Con un sistema operativo propio de un servidor Web puesto en la máquina, se vuelve necesario instalarle algo que la convertirá por completo en un servidor Web. Una alternativa atractiva es el servidor Web Apache, considerado como el estándard de la industria, y el cual se puede obtener también gratuitamente de la Web. Además del servidor Web Apache, se requiere obtener e instalar en la máquina un programa propio de un servidor de base de datos, y la mejor alternativa es sin duda alguna MySQL, también obtenible sin costo alguno a través de Internet. Y se requiere instalar en la máquina un lenguaje para ser usado en el lado del servidor, y una buena alternativa para ello es el programa PHP (lo cual le permitiría al lector sujetar a prueba los programas que se han dado arriba). El conjunto Linux-Apache-MySQL-PHP es conocido como LAMP (en alusión a una lámpara que guía el camino), con lo cual se puede tener un servidor Web completo en casa (o inclusive en un negocio). Con el tiempo a esto se le pueden agregar otros accesorios como el conjunto de herramientas Dojo Toolkit. No se requiere pues de un grado avanzado en ciencias de la computación para poder construír y tener en casa algo que tres o cuatro decadas atrás requería de un postgrado, lo que se requiere más bien es interés, tiempo, y paciencia, porque en el camino irán surgiendo problemas de instalación que se tendrán que ir resolviendo. Afortunadamente, hay grandes comunidades en áreas como PHP, MYSQL y Apache que están dispuestas a compartir experiencias y a dar consejos en caso de entrar en un atolladero.
Lo que hemos manejado hasta este punto es una interacción en cierto modo supeditada, en la cual hay dos computadoras que no están situadas al mismo nivel, una de ellas es una computadora que actúa como servidor, y la otra es una máquina cliente (la computadora del usuario). Están posicionadas en dos jerarquías diferentes la una con respecto a la otra, y la conexión entre ambas máquinas (las cuales pueden estar ubicadas en partes distintas del mundo) se lleva a cabo a través de un módem.
Sin embargo, y en principio, no hay nada que impida el poder darle a la máquina cliente las mismas capacidades que posee el servidor, esto es, agregar un programa CGI a la computadora del usuario para que la máquina cliente pueda actuar también como un servidor, e instalar un navegador browser en el servidor para que un operador que esté al frente del servidor pueda hacer las veces de cliente. Esto de hecho no implica mucho trabajo, porque se trata simplemente de instalar en una de las máquinas todos los programas que están instalados en la otra, y viceversa. Esto sitúa ambas máquinas al mismo nivel, con ambas máquinas pudiendo accesar las bases de datos que hay en la otra máquina, con ambas máquinas pudiendo compartir archivos, en fin, con ambas máquinas pudiendo hacer lo que hace cualquier red de computadoras. Y de hecho esto es lo que se tiene, una red de computadoras, con dos máquinas conectadas entre sí. Y si a todo esto se cuenta con la ventaja física de que ambas computadoras estén ubicadas físicamente en el mismo edificio, o en el mismo negocio, se tiene esencialmente lo que se conoce como una red interna de computadoras, una Intranet, una red de computadoras usando las mismas herramientas y programas que fueron desarrollados para la Web, con la diferencia de que se trata de máquinas ubicadas en la misma localidad geográfica y situadas jerárquicamente al mismo nivel. Esto lo podemos esquematizar de la siguiente manera:
Antes del advenimiento de Internet con su rica variedad de interfaces gráficas y navegadores equipados con recursos como JavaScript, cada sistema operativo promocionaba sus capacidades para poder interconectar máquinas en una red de computadoras, con la promesa de que la red podía funcionar sin problemas siempre y cuando cada máquina tuviese instalado el mismo sistema operativo. En el caso de sistemas operativos comerciales no-gratuitos, esto desde luego funciona ventajosamente para aquella empresa cuya fortuna esté basada precisamente en la venta de sus sistemas operativos, porque en una red de computadoras hay que pagar el costo de la licencia para poder instalar un mismo sistema operativo en cada máquina, y si se trata de un negocio que tenga instaladas veinte máquinas interconectadas mediante una red intranet, pues hay que pagar no solo veinte tantos de lo que se pagaría por el sistema operativo instalado en una sola máquina, sino el costo de las actualizaciones requeridas cada vez que el sistema operativo cambie de un modelo a otro, un costo extra que acumulado con el tiempo puede consistir en una propuesta onerosa. Sin embargo, con una red intranet, no es necesario que todas las máquinas de la red tengan instalado el mismo sistema operativo, es más, ni siquiera es necesario que sean máquinas iguales o parecidas en el hardware. Al igual que como ocurre con la red Internet, es posible tener una red intranet que unifique dos máquinas con un sistema operativo Linux instalado en ellas, un sistema operativo Mac OS X instalado en otras tres, y un sistema operativo Windows de Microsoft instalado en una de ellas. Las seis máquinas se intercomunicarán entre sí sin problema alguno, tal y como ocurre con la red mundial Internet. Más aún, en el caso del sistema operativo Linux, en muchas de sus versiones este sistema operativo es gratuito, hay muchas herramientas también gratuitas que han sido desarrolladas para Linux, de modo tal que el costo de instalación de una intranet puede terminar reducido únicamente al costo de las máquinas en sí (sin ningún sistema operativo instalado en ellas al momento de compra) sin necesidad de tener que pagar el costo de una licencia para cada máquina y mucho menos los costos de actualizaciones al ir pasando de una versión del sistema operativo a otra versión más reciente. Esta independencia absoluta de la plataforma vino como una mala noticia para la empresa Microsoft cuyos sistemas operativos mantienen un monopolio en el mercado de las computadoras domésticas, y es la razón por la cual Microsoft de varias maneras ha intentado introducir supuestas mejoras (tales como los controles ActiveX, los cuales funcionan sin problemas únicamente en máquinas que tengan un sistema operativo Windows instalado en ellas) con la esperanza de convertir a Internet en una extensión de Windows manteniendo de este modo un virtual monopolio, aunque el enorme esfuerzo colaborativo mundial de muchos programadores que sin cobrar honorarios ni regalías han estado haciendo contribuciones importantes al sistema operativo Linux (el cual está basado a su vez en el sistema operativo UNIX) ha impedido que el reducido (comparativamente hablando) equipo de programadores de Microsoft haya podido avasallar a Internet como lo hizo con el mercado de las computadoras domésticas. Por otro lado, y a diferencia de lo que ocurre con los sistemas operativos UNIX y Linux que son de código abierto, esto es, las instrucciones en que se basan tales sistemas operativos están disponibles públicamente y sujetas a inspección, los sistemas operativos de Microsoft son de código cerrado, nadie fuera de Microsoft sabe a ciencia cierta cómo están elaborados los programas en que se basan dichos sistemas operativos. Y a muchos administradores de sitios Web les desagrada la idea de tener que trabajar con “cajas negras” gigantescas que ocultan mucho de lo que hacen, razón por la cual en grandes empresas se prescinde del uso de los sistemas operativos de Microsoft para hacer funcionar las computadoras corporativas.
Aún con la adición de un objeto tan potente como XMLHttpRequest, es posible que se requieran recursos adicionales que solo un lenguaje como Java puede proporcionar. Hay cosas que no se pueden hacer con JavaScript, para las cuales Java es una opción casi ideal. Aunque es posible reemplazar cualquier script con applets (subprogramas ejecutables) de Java, ello no significa que tengamos que abandonar lo que ya se haya hecho en JavaScript para reprogramarlo todo en Java. A partir de JavaScript 1.1 se introdujo algo conocido como “LiveConnect” (una especie de software que actúa como interfaz de códigos) que nos permite, empleando código JavaScript, meternos en el corazón de un applet Java. Al hacerlo, podemos obtener acceso a las variables, métodos y clases del applet Java. Es algo así como una especie de “matrimonio” entre JavaScript y Java. LiveConnect, para nuestros fines, nos permite invocar métodos públicos contenidos en un applet Java, lo cual es muy parecido a invocar una función elaborada en el código JavaScript excepto que la “función” reside dentro de un applet Java y se denomina método en lugar de función. Los métodos públicos disponibles en un applet de Java dependen de lo que el autor del applet haya decidido crear, y si vamos a usar applets elaborados por otros programadores hay que tener presente que a veces puede ser difícil entender los programas de otras personas, y la conexión mediante LiveConnect no siempre es sencilla. Sin embargo, si uno mismo programa sus propios applets de Java, se pueden crear y entender los métodos públicos que puedan funcionar en armonía con JavaScript.