Geek & Roll » javascript

Cargando recursos binarios desde JavaScript

Desde que Google mostró al mundo su port de Quake 2 a JavaScript usando GWT, no me podía sacar una pregunta de la cabeza: ¿Cómo hacen para cargar los recursos (como por ejemplo los modelos de los jugadores, o los niveles) desde el servidor al cliente, para su uso por el motor del juego en JavaScrip? ¿Es posible obtener archivos binarios del servidor, guardarlos en el sistema de archivos local, y ejecutarlos finalmente por una aplicación en JavaScript? La respuesta obviamente era sí, pero no sabía cómo.

Pero una consulta al código fuente del proyecto quake2-gwt aclaró las dudas. El secreto es sobreescribir un tipo MIME, lo que le permite al objeto XMLHttpRequest recibir respuestas binarias en vez de texto, para lo cual es más comúnmente utilizado.

function load_binary_resource(url) {
  var req = new XMLHttpRequest();
  req.open('GET', url, false);
  //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
  req.overrideMimeType('text/plain; charset=x-user-defined');
  req.send(null);
  if (req.status != 200) return '';
  return req.responseText;
}

En GWT necesitamos hacer uso de una funcion JavaScript nativa (JSNI) para lograr lo mismo:

private native void overrideMimeType(XMLHttpRequest req, String mimeType) /*-{
    req.overrideMimeType(mimeType);
}-*/;

Y finalmente crear un objeto XMLHttpRequest, hacer la petición y procesar los datos binarios en el cliente:

XMLHttpRequest req = XMLHttpRequest.create();
overrideMimeType(req, "text/plain; charset=x-user-defined");

req.setOnReadyStateChange(new ReadyStateChangeHandler(){
    //procesar la respuesta binaria
});

req.open("GET", "archivo.binario");
req.send();

Para convertir la respuesta en un InputStream, en este caso utilizando GWT y clases de emulación io y nio que tomé prestadas del proyecto quake2-gwt:

response = xhr.getResponseText();
byte[] responseBytes = new byte[sbb.stringToByteBuffer(response).remaining()];
InputStream inputStream = new ByteArrayInputStream(responseBytes);

En este punto pueden usar el InputStream como lo harían en un programa Java tradicional, por ejemplo si cargaron los bytes de una imagen, pueden aplicarle filtros a-la-photoshop, en tiempo real, en el cliente, sin necesidad de procesamiento en el servidor mas allá de descargar la imagen.

Formas dinámicas con GWT

Cesar March 8th, 2010 javascript, programacion Haz un comentario

Mi relación con GWT es una de amor – odio. Por un lado, tenemos a un toolkit que me permite utilizar mis conocimientos de Java para crear aplicaciones Web dinámicas, aplicando mejores prácticas en la arquitectura del código del cliente, mejorando el tiempo de respuesta gracias al uso de técnicas como agrupar imágenes para usarlas en el cliente con una sola descarga y código segmentado.

Por el otro lado, algunas tareas se vuelven más complicadas. Estoy convencido que las herramientas como GWT son el futuro, más no me queda claro si la actual implementación sea adecuada para toda la programación del lado del cliente. Además la curva de aprendizaje es más inclinada de lo que debería de ser; existen muchos gotchas todavía que hacen que el programador tenga que estar consciente de que esto es Web y no una aplicación regular.

En esta ocasión el requerimiento era el siguiente: tengo una aplicación que hace uso extensivo de formas para captura de datos. Necesito una manera de declarar dichas formas y que la aplicación automáticamente las ordene en diferentes tabs cuando estas crezcan demasiado. Las formas generadas deben poder ser insertadas en cualquier sitio, además de poder aplicarles estilos con CSS.

Como dije anteriormente, lo que me gusta de GWT es que puedo generar la solución a mis requerimientos de la misma manera en la que estoy acostumbrado a hacerlo. En este caso, creo una clase DynaForm que representa mi forma dinámica:

/*Represents the client side DynaForm*/
public class DynaForm {
	private String formSource;
	private ArrayList<String> panelTitles;
	private ArrayList<HTML> panels;
	private Node root;

	public DynaForm(String formSource){
		setFormSource(formSource);
	}

Ahora viene algo interesante. El método setFormSource(). Es aquí donde vamos a parsear el código de la forma para extraer los valores que nos interesan, y descomponerla en sus partes para después volverla a construir pero con las propiedades que nos interesan, en este caso los tabs.

public void setFormSource(String formSource){
	try{
		Document d = XMLParser.parse(formSource);
		root = d.getFirstChild();
		XMLParser.removeWhitespace(root);
		setPanels(root, formSource);
	}catch(Exception e){
		System.out.println(e.getMessage());
		e.printStackTrace();
	}

	this.formSource = formSource;
}

Como vemos, parseamos la fuente y de esa manera nos aseguramos que es código válido. Aquí podríamos aplicar otras reglas, como por ejemplo eliminar cualquier tag o propiedad maliciosa como una llamada a JavaScript. El método setPanels() es el que se encarga de construir los páneles necesarios.

/*
 * This method creates an array of HTML panels. Both panels and panelTitles must
 * contain the same number of elements. In case a <span> element doesn't provide
 * a 'name' attribute, it is replaced by an empty string.
 *
 * The number of HTML panels created is = # of <span> elements. All elements inside <form>
 * without a <span> are ignored.
 * */
private void setPanels(Node root, String formSource){
	panelTitles = new ArrayList<String>();
	panels = new ArrayList<HTML>();
	NamedNodeMap attribs;

	NodeList allElements = root.getChildNodes();

	/*
	 * Start looping all elements, and create new HTML panels as needed
	 * */
	for(int i=0; i<allElements.getLength(); i++){
		Node n = allElements.item(i);
		//if it's a span, create a new empty HTML panel and set its name.
		if(n.getNodeName().equals("span")){
			//Set the panel title entry
			attribs = n.getAttributes();
			panelTitles.add(getAttribValue(attribs, "name"));
			panels.add(new HTML());
		}else{
			try{
				//get the last panel
				HTML panel = panels.get(panels.size()-1);
				//add the node source to the panel HTML
				panel.setHTML(panel.getHTML() + n.toString());
			}catch(Exception e){
				System.out.println(e.getMessage());
			}
		}
	}
}

El método setPanels() toma todos los elementos de la forma y los analiza de la siguiente manera: Si es un elemento span, quiere decir que es el inicio de un nuevo tab. Toma la propiedad name del span y lo convierte en el título de la nueva tab. Si el elemento no es un span, entonces lo agrega a la lista de elementos que forman el tab actual.

Después, en el punto de entrada de la aplicación:

TabPanel tabPanel = new TabPanel();
dynaForm = new DynaForm(formSource);

//All each HTML panel to the tabPanel
for(int i=0; i<dynaForm.getPanels().size(); i++){
	quirksTabPanel.add(dynaForm.getPanels().get(i), dynaForm.getPanelTitles().get(i));
}

RootPanel.get("dynaForm").add(tabPanel);

Utilizando el siguiente código HTML como fuente de la forma:

<form method='POST' action='contactengine.php' name='contact'>
	<span name='Datos' />
	<label for='Nombre'>Nombre:</label>
    	<input type='text' name='Nombre' id='Nombre' />"

    	<label for='Correo'>Correo:</label>
    	<input type='text' name='Correo' id='Correo' />

	<label for='Mensaje'>Mensaje:</label>
	<textarea name='Mensaje' rows='20' cols='20' id='Mensaje'></textarea>

	<span name='Enviar' />
	<input type='submit' name='submit' value='Enviar' class='submit-button' />
</form>

El resultado es:
DynaForm

Claro que se deben usar estilos para hacer que la forma se vea más presentable. También creo que se puede lograr exactamente el mismo resultado utilizando una combinación de jQuery, selectores CSS y jQuery UI.

SimpleCart(js): e-commerce en minutos

Rafyta November 4th, 2008 internet, javascript Haz un comentario

Si tienes algo que vender en línea pero aún no te animas porque crear una sitio web puede ser tedioso o simplemente demasiado para unos cuantos artículos, ¡ánimo! llega al rescate SimpleCart(js) un script que con sólo 11kb te puede ayudar a echar a andar tu negocio en unos minutos 

  • No necesitas una base de datos
  • No necesitas saber programación, sólo HTML básico y ya
  • Si sabes programar, puedes adaptar el script fácilmente
  • Desde luego es GRATIS y viene con ejemplos fáciles de seguir
  • Sólo necesitas una cuenta de PayPal

SimpleCart(js) DEMO

Más que una guerra de navegadores

Cesar September 9th, 2008 Google, aplicaciones, browsers, javascript, web2.0 1 comentario

En OSNews publicaron un artículo exclusivo sobre Chrome y las “verdaderas” razones por las que Google decidió lanzar su propio navegador. Es una lectura interesante. Traduzco del artículo:

Esa plataforma [HTML + Javascript] simplemente no fue hecha para hacerlo [aplicaciones Web] y está claro que no durará mucho. Y no, Google no podrá compilar Javascript a código nativo y hacerlo rapidísimo aún cuando ellos digan que si. Hay razones técnicas para eso y la única manera de hacerlo es cambiarse a un ambiente de tiempo de ejecución, justo como lo hace Flash, Silverlight y Java (y es no es nativo ya que ejecutan máquinas virtuales)

También:

Ellos [Google] probablemente aprendieron la lección mientras que observaban a Steve Jobs batallar por 8 años con su muñeca OS X y aún así depender del iPod para que le salvara el trasero. 8 años y aún tiene el ¿que? ¿2% del mercado?

El artículo completo en OSNews.

Navegador Google

Cesar September 1st, 2008 FLOSS, Google, browsers, internet, javascript, web2.0 1 comentario

Google anuncia su nuevo proyecto: Google Chrome. Un navegador Web. Así es, Google se enfrentará directamente con pesos pesados como Firefox e Internet Explorer por la batalla del porcentaje de uso. Entre sus monerías incluirá Google Gears por default (para acceso offline de los sitios que lo soporten), una nueva máquina virtual de JavaScript, tabs “especiales” que lo único de especial que tienen es que van por encima de la barra de dirección y no por debajo como lo son tradicionalmente, en vez de la “AwesomeBar” de Firefox ellos tienen la “Omnibox”, una página de inicio con algunas características interesantes, entre otras cosas. Pueden ver el anuncio en blogoscoped.

(vía Slashdot)

Pasatiempo de niño

Cesar July 1st, 2008 FLOSS, Google, javascript, web2.0 5 comentarios

¿Y tu que hacías a los 13 años? Definitivamente impresionante y WTF. Me da pena tener el doble de su edad.

El fork de Ext: OpenEXT

Cesar April 27th, 2008 FLOSS, javascript, programacion Haz un comentario

Iba a pasar tarde o temprano: http://sourceforge.net/projects/openext/

La situación legal de Ext de la cual hablamos anteriormente provocó que algunos de los miembros de la comunidad Ext hicieran algo al respecto. En las propias palabras del autor, OpenEXT no es un fork sino que lanzarán parches encima de ExtJS 2.0.2 (la última versión LGPL) en vez de redistribuir versiones completas de Ext modificadas.

Veamos si esto es suficiente para atraer usuarios de Ext inconformes por el cambio de licencia.

(Via Ajaxian. Y lean el artículo original, dice muchas cosas muy interesantes)

Controversia con Ext JS

Cesar April 23rd, 2008 FLOSS, javascript 8 comentarios

Ext JS es una gran libreria para Javascript, una de las mejores y más completas que he utilizado. Sin embargo, recientemente ha visto un poco de controversia debido a su elección de distribuir la librería bajo un esquema de licencia doble:

  1. LGPL para aquellos que planeen usar Ext de manera personal, sin ánimos de lucro o educacional.
  2. Si planeas distribuir a Ext como parte de un producto de desarrollo de software (como un toolkit, librería, lenguaje, etc) entonces no puedes obtener Ext bajo los términos de la LGPL y tendrás que adquirir una licencia comercial.

Hasta aquí todo bien ¿no? Pero la cosa se complica un poquito: la LGPL versión 3 especifica claramente que si tu obtienes un software con licencia LGPLv3 y tiene alguna restricción extra a aquellas impuestas por la propia LGPL, tu tienes el derecho de eliminar dichas restricciones extras.

Dicho de otra manera: cualquier software con licencia LGPLv3 no puede tener más restricciones que las que la LGPL especifica. Por lo anterior, Matthew Garrett, desarrollador de Debian, decidió redistribuir Ext pero únicamente con la licencia LGPL (sin la licencia propia de Ext).

Matthew dice que él va a usar Ext de manera personal, sin ánimo de lucro y por lo tanto es elegible para obtenerla con LGPL. Al momento de obtenerla con LGPL él es libre de distribuírla de cualquier manera, siempre y cuando se apegue a los términos de la LGPL.

Ni tardo ni perezoso Jack Slocum, autor de Ext, le dice que está en violación de su licencia, porque Ext no se distribuye con licencia LGPL sino con una licencia propia de Ext, que te permite usar Ext bajo los términos de la LGPL en caso de que seas elegible para ello. Esta licencia propia de Ext, de acuerdo a Jack, no le permite a Matthew redistribuir porque Matthew está distribuyendo Ext como parte de una herramienta de desarrollo (como una librería) cosa que la licencia propia de Ext explícitamente prohibe:

If you plan to distribute Ext in a product that will be packaged or sold as a software development library, toolkit or plug-in-based framework (LIBRARY), we require that you work with us to establish a specific license that is appropriate. Use of the open source license terms in a LIBRARY is not permitted without explicit permission.

¿Entonces por qué Matthew se sintió con derecho de hacerlo? Porque él obtuvo Ext con licencia LGPL al planear usar Ext de manera personal, sin ánimos de lucro. Y como la LGPL le permite redistribuír, siempre y cuando se apegue a la LGPL (cosa que está haciendo) simplemente lo hace.

¿Entonces por qué Jack dice que no se puede? Porque él dice que si vas a usar Ext de manera personal, sin ánimos de lucro no obtienes Ext con licencia LGPL, sino que la licencia original de Ext te de da derecho de usar Ext bajo los términos de la LGPL.

¿Lo ven? es como si la licencia propia de Ext fuera una envoltura sobre la LGPL. La palabra clave es bajo los términos de la LGPL. ¿Eso que significa? ¿Es diferente otorgar uso bajo los términos de la LGPL a distribuír un software con licencia LGPL? Al parecer lo que intenta hacer la licencia de Ext es:

Si vas a usar Ext de manera personal, sin ánimos de lucro o si eres una organización educativa, puedes obtener Ext y usarla con los mismos derechos que te otorga la LGPL. Pero si decides distribuir Ext como parte de una librería de desarrollo, esos derechos se te eliminan y tienes que contactarnos para llegar a un acuerdo de licencia.

Aún si lo anterior es lo que en efecto quieren hacer, al momento de obtener los derechos otorgados por la LGPL tu puedes eliminar esa restricción que te intenta quitar esos derechos y hacer con Ext cualquier cosa que la LGPL permita.

No soy abogado, y habrá que estar atentos a la resolución de este problema, pero creo que Matthew tiene razón en esta. Si lo que Jack Slocum y los autores de Ext quieren hacer es proporcionar una licencia similar a la LGPL pero con algunas restricciones extras, deberían de tomar las secciones de la LGPL que le acomoden e incluirlas en la licencia de Ext y distribuir únicamente bajo esa licencia, y no otorgar uso bajo los términos de la LGPL, sea lo que sea que eso signifique.

Programando con Ext: Formularios

Cesar January 23rd, 2008 javascript, programacion 5 comentarios

ExtExt 2.0 es una gran librería de propósito general para JavaScript. Nos sirve tanto para efectos, como para GUI y lo que se necesite para construir aplicaciones Web bonitas, consistentes entre navegadores y con llamadas asíncronas al servidor de manera rápida y más sencilla que “a mano”.

Lo que vamos a ver ahora es cómo construir un formulario, validarlo, y enviarlo al servidor. Ya antes vimos como llenar un formulario automáticamente con datos que vienen del servidor, pues lo que vamos a hacer ahora es lo contrario: el usuario llena el formulario y nosotros lo enviamos al servidor para ser procesado y guardado. Es más fácil de lo que parece, veamos: Leer el resto de este post.

Llamadas asíncronas (AJAX) con Ext

Cesar November 28th, 2007 javascript, programacion 2 comentarios

Ext (disponible en la Web Extjs) es una librería Javascript bastante completa con la que podemos hacer desde bonitos efectos para la interfaz gráfica, hasta llamadas asíncronas al servidor (AJAX) de una manera muy sencilla como veremos a continuación. Definitivamente la parte que más me gusta de Ext es la manera en la que se hace una petición de datos al servidor y cómo maneja estos datos una vez que llegan al cliente. Pero basta de palabras, vayamos al grano.

Ext Form Supongamos que tenemos un formulario como el que se muestra en la imagen y queremos llenarlo con datos proveniente de una base de datos pero de manera dinámica (con AJAX, sin hacer refresh). Pueden utilizar sólo Javascript y manejar todas las incompatibilidades de los navegadores por su cuenta, y pueden usar una librería como Prototype que también es muy buena (y de hecho Ext tiene la opción para usarla) o, podemos usar Ext y facilitar tanto el código del lado del cliente como del lado del servidor. En Ext se usa el concepto de Data Stores que son objetos que nos facilitan el acceso a datos con un formato específico, y que después podemos usarlos para llenar por ejemplo una tabla, o un combo, o cualquier cosa. Para crear un Data Store:


var marker_data = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({url: 'marcadores.php?accion=obtMarc'}),
reader: new Ext.data.JsonReader({},[ 'id', 'nombre', 'direccion', 'latitud', 'longitud', 'descRed', 'descAmp', 'numCuartos', 'numBanos', 'tamano', 'colonia', 'precio', 'enlace']),
remoteSort: false
});

Dentro del Data Store estamos creando otros dos objetos: HttpProxy y JsonReader. El primero nos sirve para indicarle la URL que nos regresará los datos que necesitamos, y el segundo es un objeto que, como su nombre nos indica, nos sirve para consumir datos en formato JSON. Esto es muy conveniente ya que, al estar usando JSON, podremos evaluar la respuesta del servidor, convertirla a un objeto Javascript y accesar sus propiedades como lo hacemos con cualquier otro objeto (aunque Ext hace este trabajo por nosotros automágicamente).

Ya que tenemos el Data Store, ahora hay que indicar que pasa una vez que los datos llegan al cliente:


marker_data.on('load',function(){
fillForm(marker_data);
});

Aquí solamente estamos tomando el objeto marker_data que creamos anteriormente (el Data Store) y le asignamos un manejador de evento. El evento es “load”, es decir, cuando se cargan los datos, y le estamos asignando una función anónima que a su vez llama a otra función (fillForm) que no voy a mostrar aquí por motivos de espacio pero que simplemente toma los valores regresados por el servidor y llena el formulario. Por último hay que decirle al Data Store que haga la llamada y pida los datos:


marker_data.load();

Eso es todo. Al recibir todos los datos del servidor se ejecutará la función creada anteriormente. Donde esto brilla es en la parte del servidor, especialmente si antes utilizabas XML para regresar datos. Siguiendo el mismo ejemplo, el código del servidor en PHP quedaría de la siguiente manera:


$query = mysql_query("SELECT * FROM marcadores");
if(!$query){
die('No se pudo conectar a la base de datos:
'.mysql_error());
mysql_close();
}else{
$arr = array();
while($obj = mysql_fetch_object($query))
$arr[] = $obj;
Echo ''.json_encode($arr).'';
}

Bastante sencillo ¿no? aprovechando la función json_encode de PHP enviamos la respuesta con el formato que Ext la espera. Una vez en el cliente podemos utilizar estos datos así:


marker_data.getAt(0).data.colonia

Este objeto una vez que se invoca el método load contiene los datos regresados por el servidor. Utilizando el método getAt y la propiedad totalLength podemos iterar los resultados sin mucho esfuerzo. Y no hay más que decir del tema, Ext simplifica bastante el desarrollo de aplicaciones Web con Javascript y nos permite abstraer cosas como evaluar cadenas JSON o parsear respuestas en formato XML, así como también despreocuparnos por las incompatibilidades entre los diferentes navegadores. Por el lado malo, la documentación es bastante completa pero a mi ver le hacen falta más ejemplos para poder aprender a utilizar la librería de manera más rápida.

Posts anteriores