26 noviembre, 2009

Empezando con las extensiones de Chrome

En breve aparecerá la primera versión de Chrome/Chromium con soporte de extensiones. Es el mecanismo ofrecido por el navegador para modificar y mejorar la funcionalidad, personalizándola al gusto del usuario. En el caso de Chromium, bastará con tener conocimientos de HTML, CSS y JavaScript para poder escribirlas. Por ejemplo:

  1. Mostrar iconos en la barra de direcciones al visitar ciertas páginas:



  2. Añadir botones a la barra de herramientas (con opción a mostrar un desplegable con más acciones):



  3. Modificar la apariencia y comportamiento de las páginas web mostradas

Tutorial: Hola mundo!

Nota: traducido de aquí. Vamos a crear un nuevo botón en Chrome que al ser pulsado mostrará una página generada automáticamente. Algo así:



Para empezar, necesitarás la versión de Chrome del canal de desarrolladores, ya que aún no está disponible (al menos a fecha de hoy 26/nov/09) en los canales estable o beta. Los pasos para crear la extensión son los siguientes:
  1. Crea una carpeta llamada "hola".

  2. Crea un archivo llamado manifest.json con este contenido:

    {
      "name": "Mi Primera Extension",
      "version": "1.0",
      "description": "Esta es la primera extension.",
      "browser_action": {
        "default_icon": "icon.png"
      },
      "permissions": [
        "http://api.flickr.com/"
      ]
    }
    
    
  3. Descarga el siguiente icono a la misma carpeta.


  4. Carga la extensión:


    • Abre la página de gestión de extensiones pulsando el menú y seleccionando Extensiones...

    • Si el modo desarrollador tiene un símbolo +, púlsalo para añadir información sobre el desarrollador. El símbolo + cambia a - y aparecerán más botones e información.

    • Pulsa el botón Carga extensión... y aparecerá un diálogo de selección de archivo.

    • En el diálogo selecciona la carpeta "hola" y pulsa OK.

    Si la extensión es válida, aparecerá el icono junto a la barra de direcciones y la información sobre la extensión en la página de gestión, tal y como se ve en la siguiente imagen:




  5. Añade código a la extensión. Edita el archivo manifest.json y agrega la siguiente línea:


    ...
    "browser_action": {
      "default_icon": "icon.png",
      "popup": "popup.html"
    },
    ...
    


  6. Dentro de la carpeta "hola", crea un archivo de texto llamado popup.html y añade el siguiente código: Sigue este enlace para ver el código

  7. Vuelve a la página de gestión de las extensiones y recárgala para ver la nueva versión de la extensión.

  8. Pulsa el icono de la nueva extensión que aparece en la barra de herramientas. Debería aparecer una ventana emergente con el contenido de popup.html:

Si no ves la ventana, sigue las instrucciones de nuevo prestando atención. No intentes cargar un HTML que no esté dentro de la carpeta de la extensión porque no funcionará.

Esta ha sido una introducción básica al sistema de extensiones de Chrome/Chromium. Puedes continuar leyendo sobre las opciones disponibles y sobre cómo realizar tareas más concretas en la Developer's Guide. Intentaré publicar un tutorial más completo enfocando más en el código JavaScript y la API ofrecida para acceder a las opciones del navegador.

21 noviembre, 2009

Persevere, Node.JS y HelmaNG

Se trata de tres servidores de aplicaciones basados en JavaScript. Cada uno de ellos tiene sus propias características que lo hacen apto para un tipo de solución particular:

Persevere 1.0


  • Proyecto perteneciente a la Dojo Foundation, autores del framework cliente de JavaScript
  • Usa una interfície basada en estándares: HTTP/REST, JSON-RPC, JSONPath, y canales REST
  • Está basado en el motor Rhino (Java) al igual que Helma
  • Ofrece almacenamiento persistente de datos JSON dinámicos (objetos, arrays y funciones)
  • Operaciones de creación, lectura, actualización y borrado a través de una interfície web HTTP/REST con JSON como formato
  • Ejecución remota de funciones JavaScript en el servidor usando JSON-RPC
  • Capacidad de consultas indexadas flexible y rápida mediante JSONQuery/JSONPath
  • Capacidades de monitorización basadas en Comet a través de canales HTTP con transporte Bayeux
  • Seguridad a nivel de objeto basada en reglas de datos y con gestión de usuarios
  • Soporte de Json Referencing y JSON Schema (Validación e integridad de datos)
  • Jerarquía de datos basada en clases para facilitar consultas y herencia
  • Arquitectura de fuentes de datos conectables: servicios web remotos, tablas SQL y archivos XML pueden ser usados como almacenes de datos
  • Versiones de objetos con histórico transaccional de estados de los registros

En definitiva, la idea de Persevere es ofrecer la mayor cantidad de opciones en el lado del servidor para almacenar los datos y ofrecerlos óptimamente a los clientes web, de forma que se pueda desplazar el peso de la aplicación web al cliente manteniendo en el servidor la seguridad y la persistencia de los datos. Ver documentación.

La siguiente versión estará basada en Pintura, que es una nueva abstracción del núcleo JavaScript de Persevere, de forma que respeta los estándares de JavaScript en el servidor (CommonJS y JSGI) para permitir el soporte de motores compatibles (V8, JSCore, y Spidermonkey).


Node.JS


Node.js es un framework de entrada/salida basado en eventos y construido sobre el motor de JavaScript de Google V8. Facilita la creación de aplicaciones escalables y altamente eficientes sin necesidad de saber sobre hilos, procesos, semáforos, depuración concurrente, ... gracias a que está basado en el mismo modelo de programación basada en eventos que Twisted de Python o EventMachine de Ruby.
La idea principal detrás de Node.js es la simplicidad. Aprovechando las funciones típicas callback de JavaScript (lanzadas cuando ocurre un evento) con las que cualquier programador está familiarizado, se ha implementado una tecnología revolucionaria basada en el potente motor V8. Las llamadas a la API son, generalmente, asíncronas, por lo que nunca bloquean la ejecución. Resulta fácil para principiantes generar aplicaciones complejas sin preocuparse de las complejidades típicas de aplicaciones concurrentes.

Por ejemplo, para implementar un servidor HTTP totalmente funcional sobre el puerto 8000 que responde a las peticiones con un documento "Hello World", harían falta ocho líneas de código perfectamente comprensible:

var sys = require("sys"),
   http = require("http");
http.createServer(function (request, response) {
  response.sendHeader(200, {"Content-Type": "text/plain"});
  response.sendBody("Hello World\n");
  response.finish();
}).listen(8000);
sys.puts("Server running at http://127.0.0.1:8000/");

En la llamada a la función http.createServer se pasa como parámetro la función callback que será llamada cuando se reciba una petición de conexión. Más simple imposible. De hecho Simon Willison ha cambiado en el último momento el asunto de su charla en Full Frontal '09 para hablar de los resultados que está obteniendo con esta revolucionaria herramienta.



HelmaNG


Es la última versión del más veterano de los servidores de aplicación basados en JavaScript. NG quiere decir Next Generation y es una versión de Helma empezada de cero, con un código Java mucho más pequeño y ligero (principalmente el código en tiempo de ejecución, la consola y el cargador de módulos y recursos). Sigue el estándar de interoperabilidad CommonJS.

Consta de un entorno de ejecución JavaScript basado en Rhino, un sistema de carga de módulos compatible con CommonJS, una consola interactiva, y una biblioteca implementada JavaScript que cubre funcionalidades básicas como extensiones a objetos predefinidos, entrada/salida de archivos, persistencia, tests unitarios, soporte HTTP de cliente y servidor y una framework web construida sobre los elementos anteriores y con código JavaScript 100%.
Helma sigue una filosofía de programación un tanto peculiar basada en objetos y similar al paradigma MVC, pero que puede resultar extremadamente eficaz una vez pasado el proceso de aprendizaje inicial. Su estructura modular permite mantener la aplicación dividida desde el principio según el modelo de objetos y las acciones que se permiten sobre éstos, lo que facilita mucho su crecimiento posterior. El equipo de programadores es principalmente centro-europeo, donde cuenta con una importante base de usuarios.

13 noviembre, 2009

spdy://

Los responsables del navegador Chromium proponen la adopción de un nuevo protocolo llamado spdy (por SPeeDY, veloz) para sustituir al omnipresente HTTP y optimizado para una carga rápida y óptima de contenidos. En sus experimentos han logrado mejoras de hasta un 64% en el tiempo de carga. HTTP es un protocolo a nivel de aplicación que funciona sobre el protocolo a nivel de red TCP. Con 10 años de historia, mejorar la latencia no era precisamente una de las preocupaciones de sus diseñadores. Entre sus limitaciones:

  • Una sola petición por conexión, suplido en parte por el HTTP pipelining o conexiones múltiples en el navegador
  • Peticiones iniciadas exclusivamente por el cliente, aunque el servidor tenga información que deba ser recibida por el cliente, no puede avisar a éste
  • Las cabeceras de petición y respuesta no se comprimen a pesar de alcanzar los 2KB fácilmente por las cookies, lo que afecta sobre todo a las líneas lentas
  • Cabeceras redundantes se repiten en cada petición debido a que para el servidor el cliente siempre es "nuevo"
  • La compresión de datos es opcional y no obligatoria
Otros enfoques han intentado resolver esas carencias, por ejemplo, intentando optimizar el protocolo TCP, pese a los problemas que podría generar en la infraestructura existente. spdy, por su parte, persigue los siguientes objetivos:
  • Reducir el 50% en los tiempos de carga
  • Aprovechar el protocolo TCP existente para evitar requerir una infraestructura de red distinta a la actual
  • Evitar modificaciones en el contenido existente (HTML)
  • Conseguir varias peticiones concurrentes sobre una misma sesión TCP
  • Comprimir las cabeceras y reducir campos innecesarios
  • Simplificar el protocolo para facilitar su implementación y a la vez optimizarlo
  • Mantener SSL como protocolo subyacente para aumentar la seguridad a pesar del incremento en latencia
  • Permitir que el servidor envíe información al cliente cuando lo desee

Características

spdy permite crear una conexión TCP con el servidor sobre la que una serie de flujos concurrentes y entrelazados permiten una comunicación bidireccional (desde el cliente o desde el servidor).
  • Flujos multiplexados sobre una sola conexión TCP. Al no necesitar múltiples conexiones TCP, con lo que ello implica, y al estar mejor aprovechada ésta con mayor densidad de información, la eficiencia es mucho mayor.
  • La priorización de peticiones permite que el cliente identifique la información más importante y evitar que contenido de baja prioridad bloquee al resto.
  • Compresión de las cabeceras HTTP
  • El servidor puede enviar información al cliente mediante una cabecera X-Associated-Content que informa al cliente de nueva información disponible antes de que el éste la solicite.
  • El servidor usa la cabecera X-Subresources para sugerir al cliente que solicite ciertos recursos aunque no se los envíe directamente al cliente.
Más información en el borrador de la especificación, en este artículo de Alex Russell (Dojo Toolkit) y en el artículo introductorio. En él se explica la implementación de un servidor y un cliente Chrome modificado para realizar las pruebas del protocolo con los resultados indicados anteriormente, y que son expuestos con detalle.

10 noviembre, 2009

NodeJS



“Evented I/O for V8 JavaScript”— es un entorno JavaScript construido sobre el rapidísimo motor V8 que provee funcionalidad de entrada y salida basada en eventos para construir servidores TCP y HTTP altamente concurrentes. El diseño de la API es soberbio — todo se consigue usando eventos y llamadas callback de JavaScript (incluso el acceso típico a archivos) y la pequeña librería estándar ya contiene soporte completo de HTTP y DNS.

En general es muy similar a Twisted (Python) y otras por el estilo, pero la sintaxis de funciones anónimas de JavaScript parece más natural que su equivalente en Python.

El protocolario "Hola mundo":

var sys = require('sys'), 
   http = require('http');
http.createServer(function (req, res) {
  setTimeout(function () {
    res.sendHeader(200, {'Content-Type': 'text/plain'});
    res.sendBody('Hello World');
    res.finish();
  }, 2000);
}).listen(8000);
sys.puts('Server running at http://127.0.0.1:8000/');

O un servidor que escucha en el puerto 7000 y devuelve lo mismo que recibe (echo):

var tcp = require('tcp');
var server = tcp.createServer(function (socket) {
  socket.setEncoding("utf8");
  socket.addListener("connect", function () {
    socket.send("hello\r\n");
  });
  socket.addListener("receive", function (data) {
    socket.send(data);
  });
  socket.addListener("eof", function () {
    socket.send("goodbye\r\n");
    socket.close();
  });
});
server.listen(7000, "localhost");

El objetivo de Node es proporcionar una manera fácil de crear programas escalables para redes. En el ejemplo anterior, los dos segundos de retraso no impiden que el servidor gestione solicitudes nuevas. Node indica al sistema operativo (a través de epoll, kqueue, /dev/poll, o select) que debe ser notificado cuando los 2 segundos de arriba pasen o si se hace una nueva conexión, y entonces entra en suspenso. Si alguien nuevo se conecta, entonces se ejecuta la función callback, si el tiempo de espera se termina, se ejecuta la llamada callback interna. Cada conexión consume sólo una pequeña parte del heap.

Esto contrasta con el modelo de concurrencia más común hoy en día en el que se usan hilos del sistema operativo. La gestión de redes basada en hilos es relativamente ineficiente y muy difícil de usar. Node mostrará una mejor eficiencia del uso de la memoria bajo altas cargas que los sistemas que asignan pilas de hilos de 2mb para cada conexión. Además, los usuarios de node no deben preocuparse de bloquear el proceso - no hay locks. Casi ninguna función en node usa directamente la entrada/salida, por lo que el proceso nunca se bloquea. Debido a que nada bloquea, los programadores menos expertos pueden desarrollar sistemas rápidos.

Node es similar en diseño a sistemas como la Event Machine de Ruby o Twisted de Python, y está influido por ellos. Node lleva el modelo de eventos un poco más lejos - presenta el bucle de eventos como una construcción del lenguaje en lugar de como una biblioteca. En otros sistemas, siempre hay una llamada bloqueante para iniciar el bucle de eventos. Normalmente se define el comportamiento a través de llamadas callback al comienzo de un script, y al final se inicia un servidor a través de una llamada bloqueante como EventMachine::run(). En node no existe esa llamada bucle-de-inicio-del-evento. node simplemente entra en el bucle de eventos después de ejecutar el script de entrada. node sale del bucle de eventos cuando no hay más llamadas callback a realizar. Este comportamiento es como el del Javascript del navegador — el bucle de eventos está oculto para el usuario.

HTTP es un protocolo de primera clase en node. La Biblioteca HTTP de node ha crecido a partir de las experiencias del autor desarrollando y trabajando con servidores web. Por ejemplo, enviar datos por streaming a través de la mayoría de frameworks web es imposible. node intenta corregir estos problemas con su parser HTTP y su API. La infraestructura puramente basada en eventos de node constituye una buena base para crear bibliotecas web o frameworks.

Pero ¿qué pasa con la concurrencia de múltiples procesadores? Los procesos son necesarios para escalar a ordenadores multi-núcleo, sin hilos con memoria compartida. Los fundamentos de los sistemas escalables son conexiones de red rápidas y diseño no bloqueante - el resto es intercambio de mensajes. En futuras versiones, node podrá generar nuevos procesos (usando la API de Web Workers), pero eso es algo que ya encaja muy bien en el diseño actual.

Via Simon Willison.

09 noviembre, 2009

Herramientas de Google Closure

He traducido el artículo de presentación y otros de la documentación sobre las nuevas herramientas presentadas por Google hace unos días:

Millones de usuarios de Google en todo el mundo usan aplicaciones JavaScript intensivas, como Gmail, Google Docs y Google Maps. Al igual que los desarrolladores de todo el mundo, los Googlers (empleados de Google) queremos grandes aplicaciones web que sean más fáciles de crear, por lo que hemos construido unas cuantas herramientas para ayudarnos a desarrollar estas aplicaciones (y muchas otras). Estamos contentos de anunciar la publicación en código abierto de estas herramientas, y estamos orgullosos de ponerlos a disposición de la comunidad de desarrollo web.

El compilador de Closure


El compilador de Closure es un optimizador de JavaScript que compila aplicaciones web en código JavaScript compacto y de alto rendimiento. El compilador elimina el código muerto, a continuación, reescribe y minimiza lo que queda para que se ejecute rápido en los motores de JavaScript de los navegadores. El compilador también comprueba la sintaxis, las referencias a variables, y los tipos, y advierte sobre otros problemas comunes de JavaScript. Estos controles y optimizaciones ayudan a escribir aplicaciones más depuradas y fáciles de mantener. Puedes utilizar el compilador con el inspector de Closure, una extensión de Firebug que hace que la depuración del código ofuscado casi tan fácil como la depuración de código legible por humanos.

Los beneficios de usar el compilador son:
  • Eficiencia. El compilador reduce el tamaño de los archivos JavaScript y al mismo tiempo los hace más eficientes, ayudando a que las aplicaciones carguen más rápido y necesiten menos ancho de banda.
  • Comprobación de código. El compilador genera avisos sobre JavaScript ilega y sobre operaciones potencialmente peligrosas, ayudándote a producir JavaScript más depurado y fácil de mantener.

Debido a que los desarrolladores de JavaScript son un grupo diverso, hemos creado una serie de formas distintas de ejecutar el compilador de Clausure. Hemos publicado a fuente abierta una herramienta de línea de comandos. Hemos creado una aplicación web que acepta código para compilación a través de un cuadro de texto o de una API REST. También estamos ofreciendo una extensión de Firefox que puede utilizar con Page Speed para ver convenientemente los beneficios en rendimiento de las páginas web.

Para probar el compilador:
  1. Descarga el paquete del compilador de Closure.

    Crea un directorio llamado closure-compiler.

    Descarga el compilador del archivo compiler.jar y guárdalo en el directorio closure-compiler.

  2. Crea un archivo JavaScript

    Crea un archivo llamado hello.js que contenga el siguiente código:


    // Una función sencilla.
    function hello(longName) {
      alert('Hola, ' + longName);
    }
    hello('Nuevo usuario');

    Guárdalo en el directorio closure-compiler.


  3. Compila el archivo JavaScript.

    Ejecuta el siguiente comando desde el directorio closure-compiler:

    java -jar compiler.jar --js hello.js --js_output_file hello-compiled.js

    Este comando crea un nuevo archivo llamado hello-compiled.js, que contiene el siguiente JavaScript:


    function hello(a){alert("Hola, "+a)}hello("Nuevo usuario");
    

    El compilador ha eliminado los comentarios, el espacio en blanco y un punto y coma innecesario. El compilador ha reemplazado el nombre de parámetro longName por el más corto a. Esto genera un archivo mucho más pequeño que el original.

    Para confirmar que el código JavaScript compilado aún funciona, incluye hello-compiled.js en un archivo HTML como este:


    <html>
      <head>
        <script src="closure-library-read-only/closure/goog/base.js"></script>
        <script src="hello.js"></script>
      </head>
      <body onload="sayHi()">
      </body>
    </html>
    

    Ahora carga el HTML en el navegador y deberías ver una bienvenida amigable. Para aprender más visita este documento sobre compilación avanzada y con archivos externos.


El inspector de Closure


El compilador de Closure modifica el código JavaScript original y genera código más pequeño y más eficiente, pero más difícil de leer y depurar. El inspector ayuda ofreciendo un mapeo del código fuente que identifica las líneas del código original y el compilado. De esa forma es posible ver el código original en tu editor preferido indicado en la variable de entorno $EDITOR.

Sigue estos pasos para habilitar el mapeo de código fuente al usar el inspector:
  1. Instala Firebug y el inspector.
     
  2. Compila tu código JavaScript con el compilador de Closure y añade el parámetro --create_source_map. Por ejemplo, si tu archivo JavaScript se llama example.js, ejecuta este comando desde el directorio closure-compiler para crear un mapa de código fuente en el fichero example-map:


    $ java -jar compiler.jar --js example.js --create_source_map ./example-map --js_output_file example-compiled.js


  3. Para depurar tu código en Firebug, abre una página web en Firefox que incluya example.js. Abre una ventana de Firebug y carga el archivo JavaScript compilado example-compiled.js en el panel izquierdo. Selecciona la pestaña Source Mapping del panel derecho y especifica la localización del archivo de mapa de código fuente example-map introduciendo la URL o haciendo clic en Open Local File y seleccionándolo.


    Screenshot of Closure Inspector source map dialog.


  4. Tras cargar el mapa de código fuente, puedes encontrar el código original de cualquier código en los fuentes compilados. Para hacer esto, pulsa el botón derecho del ratón sobre cualquier código compilado y selecciona Show Original Source, tal y como se muestra a continuación:


    Screenshot of Closure Inspector: show original source.

    Si has indicado la variable de entorno $EDITOR al editor de tu elección, el archivo original se abre en ese editor en la línea que corresponde a la que has hecho el click. Si no lo has indicado, verás el nombre de fichero y el número de línea mostrados en un pop-up, así:


    Screenshot of Closure Inspector pop-up with original source file 
information.

Traza mejorada de pila

Firebug incluye una representación de la traza de pila que puedes ver pulsando  Stack en la parte derecha de la ventana.
El inspector de Closure mejora la representación de la traza de pila mediante:
  • Mostrando los nombres de función originales si se ha cargado el mapa del código fuente.
  • Mostrando el archivo que contiene cada función.
  • Facilitando un botón  Copy Stack de forma que se pueda copiar el texto de la traza de pila.
A continuación hay un ejemplo de traza mejorada de pila del inspector Closure:

Screenshot of Closure Inspector's enchanced stack trace.

Integración de Tests Unitarios

El inspector de Closure soporta la integración con el Framework de test de Closure. Para habilitar esta característica, pulsa la flecha en el menú Script de Firebug y habilita Handle JSUnit Failures. Si una aserción falla en el Framework de testeo, el Inspector de Closure establece un punto de ruptura sobre la aserción y pausa la ejecución.

Aquí hay un ejemplo de punto de ruptura sobre una aserción:

Screenshot of Closure Inspector assertion breakpoint.

La Biblioteca de Closure


La biblioteca de Closure es una amplia colección de código JavaScript modular, bien probada y multi-navegador. Los desarrolladores Web pueden coger solamente lo que necesiten de un amplio conjunto de widgets reutilizables y controles de interfície de usuario, así como de utilidades de bajo nivel para el DOM, comunicación con el servidor, animación, estructuras de datos, pruebas unitarias, edición de texto rico, y mucho, mucho más. (De verdad. Echa un vistazo a la documentación.)

JavaScript carece de una biblioteca de clases estándar como STL o JDK. En Google, la biblioteca de Closure hace el papel de "nuestra biblioteca estándar de JavaScript" para la creación de grandes aplicaciones web complejas. Es intencionadamente agnóstica en cuanto a servidor y destinada para ser usada con el compilador de Closure. Puedes hacer un proyecto grande y complejo (con espacios de nombres y comprobación de tipos), y aún así, pequeño y rápido a través del cable (con compilación). La Biblioteca de Closure proporciona utilidades para tareas comunes de manera que pases el tiempo escribiendo la aplicación en lugar de escribir utilidades y abstracciones de navegador.

Si estás desarrollando una aplicación grande o creciente, podrías beneficiarte de la amplitud de la biblioteca. Una biblioteca bien probada puede aislarte de los problemas de compatibilidad entre navegadores y ahorrar tiempo de programación en el lado del cliente, permitiendo que te dediques a la parte divertida.


Empezando con la biblioteca de Closure

Este ejercicio de "Hola Mundo" te introduce en el proceso de usar la biblioteca en una página web. Para hacer este ejercicio necesitas estar familiarizado con JavaScript, y con un cliente de Subversion. Podrías tener ya uno. Lo sabrás intentando ejecutar el comando del paso 1.

Hola Closure

Para empezar con la librería Closure, utiliza funciones JavaScript de Closure en una página web simple siguiendo estos pasos:

Paso 1: Descargar la biblioteca

Descarga la biblioteca de Closure desde el repositorio de Subversion ejecutando siguiente comando desde consola:

svn checkout http://closure-library.googlecode.com/svn/trunk/ closure-library-read-only

Podrías necesitar un cliente de Subversion para ejecutar el comando, aunque podrías tener ya uno. Prueba el comando, y si no funciona, descarga e instala un  cliente de Subversion.
Tras ejecutar el comando deberías tener un directorio llamado closure-library-read-only que contenga el código de la librería.

Paso 2: Crear un archivo JavaScript que use la biblioteca de Closure

Guarda el siguiente JavaScript en un archivo llamado hello.js. Pon este archivo junto al directorio  closure-library-read-only.

goog.require('goog.dom');

function sayHi() {
  var newDiv = goog.dom.createDom('h1', {'style': 'background-color:#EEE'},
    'Hola Mundo!');
  goog.dom.appendChild(document.body, newDiv);
}

Paso 3: Crea un archivo HTML

Guarda el siguiente HTML en un archivo llamado hello.html. Pone este archivo junto al directorio closure-library-read-only y el archivo hello.js.

<html>
  <head>
    <script src="></script>
    <script src="hello.js"></script>
  </head>
  <body onload="sayHi()">
  </body>
</html>

Paso 4: Dí Hola a la biblioteca

Abre el archivo HTML en el navegador. Deberías de ver las palabras "Hello world!":



¿Cómo funciona el ejemplo?

El JavaScript de hello.js usa dos funciones que no define: goog.dom.createDom() y goog.dom.appendChild(). ¿De dónde vienen esas funciones?

Esas funciones están definidas en la biblioteca de Closure que descargaste en el paso 1, dentro del archivo  closure-library-read-only/dom/dom.js.
Para hacer uso de esas funciones, el ejemplo hace dos cosas:
  • Incluye la orden goog.require('goog.dom') al principio del JavaScript del paso 2.
  • Incluye el archivo de arranque de la biblioteca  base.js en el HTML del paso 3.
El archivo base.js define la función  goog.require(). La llamada de función goog.require('goog.dom') carga el archivo JavaScript que define las funciones del espacio de nombres goog.dom, junto con otros archivos de la biblioteca de Closure que esas funciones necesitan.
La biblioteca de Closure carga esos archivos agregando dinámicamente una etiqueta script al documento por cada archivo necesitado de la biblioteca. Así, por ejemplo, la orden  goog.require('goog.dom') hace que la siguiente etiqueta sea agregada al document, donde path-to-closure es el camino desde el archivo HTML hasta el directorio que contiene base.js:
Normalmente una sola orden goog.require() cargará sólo una fracción de la base de código de la biblioteca.
Incluir base.js no es la única forma de incluir el código de la librería, pero es la forma más fácil de empezar. En cualquier caso, no importa como cargues el código de la librería, siempre usarás goog.require() para declarar las partes de la librería que necesitas.

Construir una aplicación con la biblioteca de Closure

Este otro tutorial te introduce en la experiencia de usar la biblioteca dando un paseo por la construcción de una aplicación simple.
Usa estos enlaces para descargar los dos archivos de código fuente usados en el tutorial:
Este tutorial explica las diferentes partes de estos archivos paso a paso. Fíjate que el archivo notepad.html no funcionará hasta que no descargues y enlaces tu copia de la biblioteca, tal y como se describe a continuación.

La aplicación de Notas

Este tutorial ilustra el proceso de construir una simple aplicación para mostrar notas. El ejemplo:
  • crea un espacio de nombres para la aplicación,
  • usa la función de la biblioteca goog.dom.createDom() para crear la estructura de Document Object Model (DOM) para la lista, y
  • usa una clase de la biblioteca en la lista de notas para permitir al usuario abrir y cerrar elementos de la lista.

Creando un espacio de nombres con goog.provide()

Cuando usas bibliotecas JavaScript de fuentes distintas, siempre existe la posibilidad de que algún archivo Javascript redefina una variable global o un nombre de función que ya uses en tu código. Para minimizar el riesgo de este tipo de colisión de nombres, usa la función goog.provide() de la librería para crear un espacio de nombres para tu código.


Por ejemplo, la aplicación de Notas usa objetos Note creados por una función constructor Note(). Si cualquier otro archivo JavaScript define una función global o una variable Note, sobreescribiría al constructor. Por tanto, el ejemplo crea un espacio de nombres para este constructor con la siguiente llamada a goog.provide:



goog.provide('tutorial.notepad.Note');


La función goog.provide() se asegura de la existencia de la estructura de objeto JavaScript indicada por su argumento. Comprueba si existe cada propiedad de objeto en la ruta de la expresión y si no existe la inicializa. La función anterior es equivalente a:

tutorial = tutorial || {};
tutorial.notepad = tutorial.notepad || {};
tutorial.notepad.Note = tutorial.notepad.Note || {};


Puesto que goog.provide() sólo inicializa propiedades si no existen previamente, nunca sobreescribirá una propiedad.
Fíjate que las órdenes goog.provide() tiene la ventaja añadida de que el script de resolución de dependencias calcdeps.py puede usarlas. Echa un vistazo a Usar el script de cálculo de dependencias para aprender cómo usar calcdeps.py

Una vez que la estructura de objeto tutorial.notepad.Note exista, el ejemplo asigna la función constructor a la propiedad Note.


tutorial.notepad.Note = function(title, content, noteContainer) {
  this.title = title;
  this.content = content;
  this.parent = noteContainer;
};

El constructor Note está ahora en el espacio de nombre tutorial.notepad creado con goog.provide().


Creando una estructura DOM con goog.dom.createDom()

Para mostrar una Note en el documento HTML, el ejemplo facilita a la clase Note el siguiente método:



tutorial.notepad.Note.prototype.makeNoteDom = function() {
  // Crea la estructura DOM que representa la nota.
  this.headerElement = goog.dom.createDom('div', 
      {'style': 'background-color:#EEE'}, this.title);
  this.contentElement = goog.dom.createDom('div', null, this.content);
  var newNote = goog.dom.createDom('div', null,
      this.headerElement, this.contentElement);

  // Agrega la estructura DOM de la nota al documento.
  goog.dom.appendChild(this.parent, newNote);
};


Este método usa la función de la biblioteca goog.dom.createDom(). La siguiente orden goog.require() incluye el código para esta función:



goog.require('goog.dom');


Para incluir una función como goog.dom.createDom() que no sea un constructor, pasa el espacio de nombres que contenga la función a goog.require (en este caso solamente goog.dom). No necesitas incluir el nombre de la función en la orden goog.require() a menos que requieras de una clase. Usando una clase de la biblioteca ilustra una órden goog.require() para este caso.


La función goog.dom.createDom() crea un nuevo elemento DOM. Por ejemplo, la siguiente órden de makeNoteDom() crea un nuevo elemento  div.



this.headerElement = goog.dom.createDom('div', 
      {'style': 'background-color:#EEE'}, this.title);


El segundo argumento en esta llamada a createDom() especifica los atributos a agregar al elemento, y el tercer argumento especifica un hijo que añadir al elemento (una cadena en este caso). Ambos, el segundo y el tercer parámetro, son opcionales. Mira la referencia de la API para más información sobre createDom().


El método makeNoteDom() simplemente crea un argumento de una sola Note. Para crear una lista de notas, el ejemplo incluye una función makeNotes() que toma un array de  de datos de notas e instancia un objeto Note por cada uno, llamando al método makeNoteDom() de Note.



tutorial.notepad.makeNotes = function(data, noteContainer) {
  var notes = [];
  for (var i = 0; i < data.length; i++) {
    var note = 
      new tutorial.notepad.Note(data[i].title, data[i].content, noteContainer);
    notes.push(note);
    note.makeNoteDom();
  }
  return notes;
};

Usando una clase de la biblioteca

Con sólo dos líneas de código, el ejemplo hace de cada nota un Zippy. Un Zippy es un elemento que puede ser plegado y desplegado para ocultar o mostrar contenido. Primero el ejemplo agrega una nueva orden require() para la clase Zippy:
goog.require('goog.ui.Zippy');
Entonces añade una línea al final del método  makeNoteDom :
tutorial.notepad.Note.prototype.makeNoteDom = function() {
  // Crea la estructura DOM para representar la nota.
  this.headerElement = goog.dom.createDom('div', 
      {'style': 'background-color:#EEE'}, this.title);
  this.contentElement = goog.dom.createDom('div', null, this.content);
  var newNote = goog.dom.createDom('div', null,
      this.headerElement, this.contentElement);

  // Agrega la estructura DOM de la nota al documento.
  goog.dom.appendChild(this.parent, newNote);

  // NUEVA LINEA:
  return new goog.ui.Zippy(this.headerElement, this.contentElement);
};
La llamada al constructor new goog.ui.Zippy(this.headerElement, this.contentElement) asigna un comportamiento al elemento nota que conmutará la visibilidad de  contentElement cuando el usuario haga clic sobre  headerElement. Para más información sobre la clase Zippy, echa un vistazo a la documentación de la API de Zippy.

Usando el Notepad en un documento HTML

A continuación se puede ver el código JavaScript completo para esta aplicación de ejemplo:
goog.provide('tutorial.notepad');
goog.provide('tutorial.notepad.Note');

goog.require('goog.dom');
goog.require('goog.ui.Zippy');

/**
 * Itera sobre una lista de objetos de datos de nota, crea una instancia de Nota
 * para cada uno, y le dice a la instancia que construya su estructura DOM.
 */
tutorial.notepad.makeNotes = function(data, noteContainer) {
  var notes = [];
  for (var i = 0; i < data.length; i++) {
    var note = 
      new tutorial.notepad.Note(data[i].title, data[i].content, noteContainer);
    notes.push(note);
    note.makeNoteDom();
  }
  return notes;
};

/**
 * Gestiona los datos e interfície de un solo nodo
 */
tutorial.notepad.Note = function(title, content, noteContainer) {
  this.title = title;
  this.content = content;
  this.parent = noteContainer;
};

/**
 * Crea la estructura DOM para la nota y la agrega al documento.
 */
tutorial.notepad.Note.prototype.makeNoteDom = function() {
  // Create DOM structure to represent the note.
  this.headerElement = goog.dom.createDom('div',
      {'style': 'background-color:#EEE'}, this.title);
  this.contentElement = goog.dom.createDom('div', null, this.content);
  var newNote = goog.dom.createDom('div', null,
      this.headerElement, this.contentElement);

  // Agrega la estructura DOM de la nota al documento
  goog.dom.appendChild(this.parent, newNote);
  return new goog.ui.Zippy(this.headerElement, this.contentElement);
};
Puedes tener este código descargando el archivo notepad.js. El siguiente HTML usa este código de bloc de notas para mostrar una lista de notas en una página web:
<html>
<head>
<title>Notepad</title>
<script src="closure-library/base.js"></script>
<script src="notepad.js"></script>
</head>
<body>

<div id="notes">
</div>

<script>
function main() {
  var noteData = [
    {'title': 'Note 1', 'content': 'Content of Note 1'},
    {'title': 'Note 2', 'content': 'Content of Note 2'}];

  var noteListElement = document.getElementById('notes');
  var notes = tutorial.notepad.makeNotes(noteData, noteListElement);
}
main();
</script>
</body>
</html>
Puedes tener esta página descargando el archivo notepad.html, Esta página:
  • incluye una etiqueta script para el archivo base.js de la biblioteca. El atributo src de esta etiqueta es el camino desde el archivo HTML hasta el archivo  base.js de la biblioteca.
  • incluye una etiqueta  script para el archivo que contiene el código para el bloc de notas.
  • incluye un script en la página que inicializa la lista de notas con una llamada a makeNotes(). La función makeNotes() toma dos argumentos: un array de Objetos, cada uno conteniendo los datos de una nota, y el elemento DOM bajo el que construir la estructura DOM de la lista de notas

Plantillas de Closure

Las plantillas de Closure surgieron de un deseo de tener plantillas web que estén precompiladas para ser ejecutadas de forma eficiente por JavaScript. Las plantillas de Closure tienen una sintaxis simple y natural para programadores. A diferencia de los tradicionales sistemas de plantillas, puedes pensar en las plantillas de Closure como pequeños componentes que se unen para formar una interfície de usuario, en lugar de tener que crear una gran plantilla por cada página. Las plantillas de Closure se aplican para JavaScript y para Java, así que puedes usar las mismas plantillas en el servidor y en el cliente. El compilador, la biblioteca, las plantillas y el inspector de Closure se iniciaron como proyectos de 20% del tiempo de Googlers y cientos de ellos han contribuido con miles de parches. Hoy en día, cada herramienta de Closure se ha convertido en una parte clave de la infraestructura de JavaScript detrás de las aplicaciones web en Google. Por eso estamos particularmente entusiasmados de abrir su código para fomentar y apoyar el desarrollo web fuera de Google. Queremos saber lo que piensas, pero más importante aún, queremos ver que podéis hacer con ellas. Así que bájatelo y diviértete!



Addendum: Parece que Andrea Giammarchi no está muy de acuerdo con las ventajas ofrecidas por el nuevo proyecto de Google. Ha encontrado fácilmente un ejemplo en el que el compilador hace que el código deje de funcionar (aunque hay que decir que hace mal uso de las variables globales que un buen estilo de programación debería evitar). Por otro lado analiza el código de la librería y encuentra distintos problemas de rendimiento que una librería orientada a la velocidad no debería tener. Quizás tenga razón y todo se deba a que detrás de esas herramientas hay programadores expertos en Java y no en JavaScript...

03 noviembre, 2009

Incrustación automática de URIs de datos en archivos CSS

Traduzco la continuación del artículo de Nicholas Zakas en el que presenta dos herramientas para facilitar la adopción de esta tecnología:

La semana pasada posteé sobre URIs de datos y por qué son una característica interesante de los navegadores web. En conclusión, mencioné que el mejor uso de URIs de datos es incrustarlos en archivos CSS para que tuviesen la mejor oportunidad de ser cacheados por el navegador. Recibí un montón de respuestas en este blog y por otros medios que demostraban que la gente estaba muy entusiasmado con las URIs de datos y estaban listos para dar el salto y usarlos en CSS. This left me with me believing that an interesting time is upon us. Esto me hizo pensar que estamos viviendo en un momento interesante.

El problema


Aunque Internet Explorer anterior a la versión 8 no es compatible con los URIs de datos, la mayoría de la gente parece dispuesta a servir contenido alternativo para los navegadores menos capaces y servir el contenido más óptimo para aquellos que pueden manejarlo. El problema es que millones de sitios web están utilizando actualmente archivos CSS que referencian archivos de imagen a la manera tradicional, y todos ellos que tendrían que ser convertidos para usar URIs de datos. Nadie quiere mantener varias versiones de los archivos CSS, por lo que sería mejor si este proceso de conversión pudiera ser automático.

Presentación de CSSEmbed


Pasé los últimos dos días programando y estoy orgulloso de introducir CSSEmbed, una herramienta para integrar automáticamente imágenes dentro de archivos CSS como URIs de datos. Esta es una sencilla herramienta muy pequeña que lee un fichero CSS, identifica las imágenes referenciadas en su interior, las convierte a URIs de datos, y genera una hoja de estilos resultante. La hoja de estilos recién creada es una copia exacta del original, con comentarios y sangrías intactos, la única diferencia es que todas las referencias a archivos de imagen han sido sustituidos por URIs de datos. Because it preserves the original formatting of the style sheet, it can be used both on nicely-formatted source code and crunched source code without worry. Debido a que preserva el formato original de la hoja de estilo, puede ser utilizado tanto con código fuente bien formateado como con código compactado sin problemas.

CSSEmbed requiere Java 1.5+ para funcionar. Para empezar, descarga el archivo JAR. Todas las dependencias están incluidos en el archivo JAR, así que no hay necesidad de ocuparse de la creación de classpaths o de descargar otras dependencias. El uso básico es el siguiente:

java -jar cssembed-xyzjar -o <output filename> <input filename>

Por ejemplo:

java -jar cssembed-xyzjar -o styles_new.css styles.css

When the -o flag is omitted, the output ends up on stdout , thus you can direct the output to a file directly: Cuando se omite el parámetro -o el código se genera sobre la salida estándar, por lo que se puede dirigir a un archivo directamente:

java -jar cssembed-xyzjar styles.css > styles_new.css

Las instrucciones de uso completas están disponibles mediante el parámetro -h


Usage: java -jar cssembed-x.y.z.jar [options] [input file]

Global Options
   -h, --help           Muestra esta información
   --charset            Codificación de carácteres del archivo de entrada
   -v, --verbose        Muestra mensajes de informacion y avisos
   -root                Antepone a todas las URLs relativas
   -o                   Genera la salida en . Por defecto a stdout.

CSSEmbed es inteligente en la forma en que identifica las imágenes. Si la ubicación de la imagen comienza con "http://", entonces la herramienta descarga automáticamente el archivo y lo convierte a una URI. Si la ubicación de la imagen es una ruta relativa (por ejemplo, contiene "../"), CSSEmbed busca el archivo local en relación a la ubicación del archivo de hoja de estilos. Si los archivos indican una ruta absoluta, sin especificar "http://", como "/images/image.png", tendrás que proporcionar una raíz a través de la opción --root. Cuando se especifica, la raíz se antepone a todas las ubicaciones de imágenes que no comiencen por "http://".

Para obtener más información acerca de CSSEmbed, echa un vistazo a la documentación. También puede descargar el código fuente (Licencia MIT) del proyecto GitHub. CSSEmbed se encuentra en etapas muy tempranas, por lo que se agradecerán opiniones. Si encuentras errores, por favor usa el seguimiento de incidencias para informar sobre ellos.

...y presentamos DataURI


When I was researching data URIs, I didn't come across any simple data URIs generators. Cuando estaba investigando URIs de datos, no encontré ningún generador simple de URIs de datos. Está la cocina de URIs de datos de Hixie, pero sólo está disponible online. Lo que realmente buscaba era algo para ser ejecutado en la línea de comandos para poder crear CSSEmbed. Por eso he creado DataURI, que es una sencilla herramienta que lee un archivo y muestra su representación como una URI de datos. DataURI es el motor subyacente utilizado por CSSEmbed para crear sus URIs de datos.

DataURI is also written in Java and requires Java 1.5+ to run. DataURI también está escrito en Java y requiere Java 1.5 o superior para funcionar. Al igual que con CSSEmbed, hay un archivo JAR que contiene todas las dependencias. Las mismas opciones generales de línea de comandos están disponibles, por lo que el uso básico es muy similar a CSSEmbed:

java -jar datauri-xyzjar -o <output filename> <input filename>

Por ejemplo:

java -jar datauri-xyzjar -o output.txt image.png

Los archivos a codificar no tienen por qué ser locales, se pueden incluir URLs en la línea de comandos y se descargan y codifican:

ava -jar datauri-xyzjar -o output.txt http://www.your.domain.com/image.png

Cuando se omite la opción -o, la salida termina en stdout con lo que se puede redirigir a un archivo directamente:

java -jar datauri-xyzjar styles.css > styles_new.css

Las instrucciones de uso completas están disponibles mediante -h:

Usage: java -jar datauri-x.y.z.jar [options] [input file]

Global Options
   -h, --help            Displays this information.
   --charset    Character set of the input file.
   -v, --verbose         Display informational messages and warnings.
   -m, --mime      Mime type to encode into the data URI.
   -o              Place the output into . Defaults to stdout. 

DataURI es capaz de crear URIs de datos de URIs de las imágenes y de algunos archivos de texto (para la lista completa, consulta la documentación). El código fuente está disponible (Licencia MIT) del proyecto GitHub y puedes informar de errores igualmente.

Agradecimientos


I was inspired to create these tools after reading, Me inspiré para crear estas herramientas después de leer Not Just a Pretty Face: Performance and the New Yahoo! de Ryan Grove, Stoyan Stefanov, y Venkateswaran Udayasankar del equipo de búsquedas de Yahoo!. La sección sobre las URIs de datos me llevó a investigar más y, finalmente, a la creación de CSSEmbed y de DataURI.

Estoy lejos de ser tan bueno en Java como lo soy con JavaScript, así que tener un gran ejemplo funcionando ha hecho este desarrollo más fácil. Debo agradecer a Julien Lecomte porque he utilizado el código fuente del YUI Compressor como una guía sobre cómo organizar mi código. El código de Julien es tan limpio que era fácil configurar mis proyectos de la misma manera. También utilicé su archivo Ant como base para el mío.

Seguimiento


Las URIs de datos son definitivamente excitantes como medio para mejorar el rendimiento de sitios web. Cada gran impulso hacia adelante en las técnica de desarrollo necesita de un buen conjunto de herramientas para aumentar su adopción. Espero sinceramente que CSSEmbed y DataURI puede ayudar a la gente a empezar con la creación y el uso de URIs de datos.

Últimos links en indiza.com