31 julio, 2008

OSCON 2008

En esta página de oreilly.com están publicadas las conferencias que tuvieron lugar entre el 21 y el 25 de Julio en la OsCON 2008 (Open Source Convention). Muchas incluyen el material de presentación y son bastante interesantes. Dos ejemplos:

  1. Even faster Websites. Webs aún más rápidas. Presentación (PPT).
  2. How to Build and Launch a Successful Globalized App from Day One (or All the Crap You Forget to Do). Cómo construir y lanzar una exitosa aplicación global desde el primer día (y todas las cosas que se suelen olvidar). Presentación (PDF).

30 julio, 2008

EmailToID

EmailToID es un servicio simple que mapea direcciones de correo electrónico en URLs válidos como identificadores de OpenID, el servicio que pretende terminar con la necesidad de darse de alta en cada página web.

Una de las críticas a la usabilidad de OpenID es que esté basada en direcciones web (URLs) para identificador a los usuarios ya que están mucho más acostumbrados a usar su dirección de correo electrónico (email). EmailToID hace uso de la nueva especificación para convertir emails en URLs y que facilitará la adopción de este estándar web.

Dentro de nada, para autenticarse en muchos sitios, sólo va a ser necesario indicar el email y se hará una redirección al servidor de OpenID que pedirá la contraseña para permitir acceso a la página en la que se pretende entrar.

Aviso: recomiendo usar un e-mail como los de spamgourmet (al estilo de my.openid.usuario@spamgourmet.com) para evitar recibir spam. Que se use un e-mail como identificador no implica que se use la dirección principal, aunque sea la más fácil de recordar. Lo de Spamgourmet vale también para las altas normales en los sitios que lo pidan, es lo más efectivo para evitar recibir spam.

Rastreo de webs en paralelo con PHP y cURL

Via Sentido Web descubrimos curl_multi_init y un conjunto de funciones de cURL para PHP que permiten leer en paralelo múltiples recursos web (páginas) a la vez. Útil para implementar arañas o robots para buscadores.

$mh = curl_multi_init(); 
curl_multi_add_handle($mh,$ch1); 
curl_multi_add_handle($mh,$ch2); 
  
$running=null; 
do { 
curl_multi_exec($mh,$running); 
} while ($running > 0); 
  
$data1 = curl_multi_getcontent($ch1); 
$data2 = curl_multi_getcontent($ch2); 
  
curl_multi_remove_handle($ch1); 
curl_multi_remove_handle($ch2); 
curl_multi_close($mh); 

Las entrañas de Meebo

Interesante artículo (en inglés) sobre el funcionamiento de Meebo (el servicio de mensajería instantánea via web con AOL, Messenger, Yahoo o GTalk). Relacionado con Comet, el protocolo de comunicación persistente entre cliente y servidor web, y las decisiones que han tomado para optimizar las conexiones y facilitar la extensibilidad del servicio. Por ejemplo, descartaron Apache a favor del más ligero Lighttpd, o aprovechan una misma conexión para distribuir los mensajes de varios sistemas de mensajería.



Via Simon Willison.

29 julio, 2008

JSON-Head

Simon Willison acaba de publicar en Google Apps una API que permite obtener los datos de la cabecera de una petición HEAD a un servidor web para una URL. La aplicación, que sigue el estándar REST y devuelve la información en JSON, está disponible en json-head.appspot.com.

Dos ejemplos:

  • http://json-head.appspot.com/?url=http://www.google.com/
    {
        "status_code": 200, 
        "ok": true, 
        "headers": {
            "Content-Length": "0", 
            "Via": "HTTP\/1.1 GWA", 
            "Set-Cookie": "PREF=ID=0a98e4d9becd0aba:TM=1217346285:LM=1217346285:S=Gl6xa2hh9AUbijTY; expires=Thu, 29-Jul-2010 15:44:45 GMT; path=\/; domain=.google.com", 
            "X-Google-Cache-Control": "remote-fetch", 
            "Server": "gws", 
            "Cache-Control": "private, max-age=0", 
            "Date": "Tue, 29 Jul 2008 15:44:45 GMT", 
            "Content-Type": "text\/html; charset=ISO-8859-1", 
            "Expires": "-1"
        }
    }
  • http://json-head.appspot.com/?url=http://www.yahoo.com/&callback=foo
    foo({
        "status_code": 200, 
        "ok": true, 
        "headers": {
            "Via": "HTTP\/1.1 GWA", 
            "Content-Encoding": "gzip", 
            "Accept-Ranges": "bytes", 
            "X-Google-Cache-Control": "remote-fetch", 
            "Vary": "User-Agent", 
            "Last-Modified": "Tue, 29 Jul 2008 15:27:23 GMT", 
            "Connection": "close", 
            "Cache-Control": "private", 
            "Date": "Tue, 29 Jul 2008 15:44:59 GMT", 
            "P3P": "policyref=\"http:\/\/p3p.yahoo.com\/w3c\/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE GOV\"", 
            "Content-Type": "text\/html; charset=utf-8", 
            "X-XRDS-Location": "http:\/\/open.login.yahooapis.com\/openid20\/www.yahoo.com\/xrds"
        }
    })
El segundo ejemplo hace uso de lo que se conoce como JSONP, es decir, obtenerobtener la información como el parámetro de una llamada a una función Javascript cuyo nombre se indica en el parámetro callback.

28 julio, 2008

Firebug Lite 1.2

Me he retrasado un poco publicando esto pero las vacaciones mandan. Con la nueva versión de Firebug Lite (Bookmarklet) los cambios han sido tremendos. Prácticamente se dispone de la totalidad de las herramientas que la famosa extensión de Firefox ofrece, pero ahora compatible con Internet Explorer, Opera, y Safari. O, ¿por qué no?, para quien tenga problemas con las betas de Firebug en Firefox3 como me ha estado pasando a mi durante unos días.

Buscar o navegar

Mucha gente parece sorprenderse al enterarse de que millones de usuarios de internet buscan cada día ebay.com o msn.com o simplemente "ebay" o "msn" incluso aunque pueden teclear eso mismo en la barra de direcciones y obtener exactamente lo mismo.
La gente no entiende los DNS, los nombres de dominio, o la diferencia entre buscar y navegar directamente. Y como todos saben lo que significa buscar en google, eso es exactamente lo que hacen. Puedes aceptarlo o negar la realidad.
La gente normal no sabe la diferencia entre anuncios de estilo AdSense y el resto de enlaces en la mayoría de webs. Y casi el mismo número tampoco sabe qué son los resultados patrocinados. Para ellos no es más que una página de enlaces. Simplemente hacen click en los que creen que les llevará a lo que quieren. Es así de simple.
Jeremy Zawodny en The Truth about Web Navigation.

27 julio, 2008

Redefiniendo document.write

Via Kassens.net, un blog de un programador de MooTools, veo una idea excelente. Alguien quería un servicio de Geolocalización (averiguar la ubicación geográfica de un visitante) sin tener que pagar por ella. Muchos servicios de anuncios modernos muestran dicha información como un reclamo adicional, en especial webs de contenido pornográfico y erótico. Dichos anuncios se insertan mediante la inclusión de un script que utiliza la llamada document.write para mostrar el anuncio.

En este blog han tenido la genial idea de redefinir el funcionamiento de document.write para extraer la información de localización sin que en ningún momento el anuncio se muestre en el contenido de la página:

document.write = function(txt) { alert(txt.match(/<span class="location">(.*)<\/span>/)[1]) };
El mismo anuncio ya se encarga de realizar la llamada.

No pudo ser

Faltan 3 minutos para que terminen las dos horas de la última oportunidad para pasar la ronda 1 de Google Code Jam 08 y no puedo enviar los resultados. En realidad, el código está creado para los problemas B y C (supondría un total de 85 puntos) y funciona correctamente para los ejemplos que incluyen los enunciados que son sencillos y con números muy bajos. Mi empecinamiento en resolverlos mediante el Javascript del navegador no ha sido buena idea debido a que es necesaria mucha más potencia de procesamiento que en los anteriores y el navegador se queda frito con cualquier conjunto de datos descargado. Podría optimizar el código porque sé que Javascript puede con eso, pero no en el tiempo disponible.

Estoy contento por haber sabido resolverlos, pero un poco molesto con mi decisión un tanto absurda, de no haber optado por un entorno que permitiese resolver los problemas con más velocidad ya que es necesaria tras la ronda de clasificación debido a la complejidad de los casos de prueba.

A continuación enlazo con todos los problemas que he intentado resolver y los conjuntos de prueba descargados:

Addendum

Parece que Andrea Giammarchi (WebReflection) ha tropezado con la misma piedra. En su caso ha pasado por Javascript, PHP, Python y C#, cada uno con más potencia que el anterior.

22 julio, 2008

Clausuras para PHP

Simon Willison:

Nunca pensé que conocería el día, pero un parche para añadir clausuras a PHP ha sido propuesto y ¡aceptado! Parece una implementación sólida y la sintaxis es similar a la de Javascript pero hace explícitas las variables que van a ser capturadas. Como con PHP, por defecto los valores se copian en la clausura, pero puedes usar un signo "&" para especificar en su lugar el paso por referencia al estilo de Javascript.

18 julio, 2008

Google Code Jam 08

He superado la primera ronda del Google Code Jam 08 resolviendo dos de los tres problemas planteados utilizando HTML+Javascript. Con el tercero no he podido plantearlo, ni por tanto implementarlo. Aquí presento el código de los dos primeros problemas.

El primero consistía en una lista de buscadores y una lista de búsquedas formada por los nombres de los buscadores, de tal forma que un buscador nunca debería recibir una consulta sobre su propio nombre (ya que la leyenda urbana dice que el Universo podría implosionar ). Un sistema centralizado tiene acceso a la cola de búsquedas a realizar por lo que debe calcular el mínimo número de cambios de buscador necesarios para terminar con la cola de búsquedas. Datos.

En todos los casos he usado un esqueleto con HTML en el que la entrada de datos se hace con un simple textarea y la salida se muestra en un div llamado output cuando se pulsa sobre el botón de inicio:

<html><body>
<textarea id="input" style="width:99%;height:40%;"></textarea>
output
<pre><span id="output" style="width:99%;height:40%;overflow:scroll;" style="font:22px monospace; border:1px inset gray;"></span></pre>
<input type="button" value="go" onclick="run()" />
</body></html>

El código Javascript para el primer problema es el siguiente:

function $(elm) { return document.getElementById(elm); }
function log(s) { $('output').innerHTML+= s+"\n"; }
var search= {
 engines: [],
 queue: [],
 init: function (input) {
  this.engines= [];
  this.queue= [];
  enginesnum= parseInt(input.shift()) // get engines num
  while (enginesnum--) {
   elm= input.shift();
   this.engines.push(elm);
  }
  queueitems= parseInt(input.shift()) // get queue items
  while (queueitems--) {
   elm= input.shift();
   this.queue.push(elm);
  }
 },
 getbest: function () {
  // search the best engine to switch now in order to delay next switch
  // if some engine is not in the queue, it must be the best:
  for (engine in this.engines) if (!~this.queue.indexOf(this.engines[engine])) return this.engines[engine];
  // well, bad luck, all engines needed in the future. The best the will be the one needed later in time
  // so, copy the candidates and remove them one per one by following the queue until only one is left (the later needed)
  var candidates= this.engines.slice(); // copy the engines array
  var queueindex=0;
  while (candidates.length>1) {
   // remove from candidates every element in queue until one is left
   if (~candidates.indexOf(this.queue[queueindex])) candidates.splice(candidates.indexOf(this.queue[queueindex]),1);
   queueindex++;
  }
  return candidates.shift();
 },
 solve: function () {
  var switches=0;
  var current= this.getbest();
  while (this.queue.length) {
   if (this.queue[0]==current) {
    switches++;
    current= this.getbest();
   }
   this.queue.shift();
  }
  return switches;
 }
}

function run() {
 ii= $('input').value.split(/\n/);
 var total= parseInt(ii.shift()) // get cases num
 var num=1;
 while (total--) {
  search.init(ii);
  var sol= search.solve();
  log('Case #'+num+': '+sol);
  num++;
 }
}

El segundo problema consistía en averiguar el número de trenes necesarios para cada una de las dos estaciones de un simple trayecto entre ambas pero con distintos horarios, duraciones y tiempos de preparación tras un viaje. Si en el instante en el que el horario marca que debe salir un tren no hay uno esperando ya en la estación, se debe añadir a la lista de los que deben estar presentes al principio. Datos.


function $(elm) { return document.getElementById(elm); }
function log(s) { $('output').innerHTML+= s+"\n"; }
sortfunction= function(a,b) {return a-b;}
var trains= {
 departures: [],
 turnaround: 0,
 time2int: function (hour) {
  hour= hour.split(':');
  return hour[0]*60+Math.round(hour[1]);
 },
 init: function (input) {
  this.turnaround= parseInt(input.shift()), // get turnaround time
  this.departures= [];
  froms=input.shift().split(' ');
  fromAtoB= parseInt(froms[0]); // get num departures from a to b
  fromBtoA= parseInt(froms[1]); // get num departures from b to a
  while (fromAtoB--) {
   elm= input.shift();
   elm=elm.split(" ");
   elm= [this.time2int(elm[0]),this.turnaround+this.time2int(elm[1])-this.time2int(elm[0]),'A'];
   this.departures.push(elm);
  }
  while (fromBtoA--) {
   elm= input.shift();
   elm=elm.split(" ");
   elm= [this.time2int(elm[0]),this.turnaround+this.time2int(elm[1])-this.time2int(elm[0]),'B'];
   this.departures.push(elm);
  }
  this.departures.sort(  function(a,b){return a[0]-b[0]}  )
 },
 checktrain: function (obj) {
  for (train in obj.trains) {
   if (obj.trains[train]<=obj.hour) {
    //delete the train available from trains and return it
    mytrain= obj.trains.splice(train,1);
    return mytrain[0];
   }
  }
  return false;
 },
 solve: function () {
  var a=[], trainsA=0;
  var b=[], trainsB=0;
  while (this.departures.length) {
   departure= this.departures.shift();
   // check for availability of train
   trainsAtOrigin= ( departure[2]=='A' ? a : b );
   trainsAtDest= ( departure[2]=='A' ? b : a );
   hour= departure[0];
   spent= departure[1];
   available = this.checktrain( {'trains':trainsAtOrigin,'hour':hour} );
   if ( !available ) {
    // ads new train on destination with spent time and reorder
    trainsAtDest.push(hour+spent);
    trainsAtDest.sort(sortfunction);
    ( departure[2]=='A' ? trainsA++ : trainsB++ );
   } else {
    // else moves existing train from origin to destination available after spent time
    trainsAtDest.push(hour+spent);
    trainsAtDest.sort(sortfunction);
   }
  }
  return [trainsA,trainsB];
 }
}

function run() {
 ii= $('input').value.split(/\n/);
 var total= parseInt(ii.shift()) // get cases num
 var num=1;
 while (total--) {
  trains.init(ii);
  var sol= trains.solve()
  log('Case #'+num+': '+sol.join(' '));
  num++;
 }
}

11 julio, 2008

12 ejemplos de tipografía CSS para párrafos

Jon Tangerine, experto en tipografía, muestra 12 formas distintas de aplicar estilos CSS a párrafos. Desde una simple justificación en anchura para que el párrafo se muestre como un bloque perfecto con los laterales rectos, hasta un ejemplo con la primera letra del párrafo a mayor tamaño y sangrada a la izquierda sobresaliendo del mismo. El artículo sobre la técnica tampoco tiene desperdicio.

10 julio, 2008

Google Calendar en aplicaciones web (PHP)

Via Sentido Web.

Completo tutorial de IBM Developer Works para acceder a los eventos de un calendario de Google Calendar desde una aplicación PHP.

En particular:

  • Cómo recuperar eventos de una lista pública
  • Cómo añadir más eventos
  • Cómo modificar o borrar eventos
  • Cómo buscar eventos por palabra clave o rango de fechas

08 julio, 2008

Whoisi.com

No me llaman en absoluto los sitios como Facebook, MySpace, Hi5 o Tuenti. Los considero una moda pasajera. Y sin embargo la publicación de Whoisi.com sí que me ha llamado la atención. Para empezar toma lo más útil de Twitter (mantenerse informado sobre lo que hace una persona online, ver ejemplo), elimina lo más pesado (darse de alta) y añade lo más flexible (añadir fuentes, corregir datos como en cualquier Wiki o habilitar APIs). Además como explica su autor incluye características que deberían ser más comunes, como permitir prefijar los usuarios para indicar su pertenencia a grupos, mantener un diseño minimalista pero funcional y rápido, o la generación como twitter de direcciones cortas al estilo de Tinyurl.

Respecto a la API, este ejemplo en Python extrae un usuario y todos los sites asociados, pero se puede ver que las llamadas son sencillas:


La API cumple la especificación REST como se puede comprobar al pulsar sobre los enlaces y devuelve los datos en formato JSON.

Acceder desde Javascript a los archivos a enviar (Firefox3)

Firefox 3 implementa las recomendaciones del W3C respecto a subida de archivos y, por tanto, es posible acceder a un archivo seleccionado con un <input type="file" /> y ver su nombre (fileName), su tamaño (fileSize) e incluso acceder a su contenido con las funciones (getAsDataURL,getAsText o getAsBinary). En particular, getAsDataURL convierte el contenido al formato inline (data:image/gif;base64,...), por lo que es posible mostrar una imagen directamente al seleccionarla, por ejemplo.

La implementación no deja claro por qué files es una colección si el input sólo deja seleccionar un archivo. Y en cualquier caso, sigo echando en falta un control (quizás una barra como la de búsqueda o las de permisos para popups e instalación de extensiones) que muestre el progreso de envío del archivo.

Ver ejemplo. Via Andrés Nieto.

06 julio, 2008

Dreaming in Javascript

Cuanto más JavaScript escribo, menos seguro estoy pensando que domino el lenguaje. Justo cuando decido una forma de hacer algo, se me ocurre otra forma asombrosamente mejor de hacerlo.
JavaScript es un lenguaje que parece haber crecido con sin el sol brillando sobre él. Mientras que Java es la vigilante de la playa, rubia de bote y con tetas de plástico rodeada de embobados, JavaScript es la inquietantemente misteriosa doncella del bosque que apenas puedes vislumbrar durante un instante mientras te abres paso entre moras y zarzas.
Dos afirmaciones de lo más curiosas del blog Dreaming in Javascript. Sin duda para él inventaron la palabra recomendable.

El segundo parámetro del Eval

La semana pasada alguien abrió la Caja de Pandora al descubrir (siempre ha estado ahí) que los navegadores Gecko (como Firefox) admiten un segundo parámetro para indicar en qué ámbito se ejecuta la cadena de código del primer parámetro (el que conocemos todos): eval( string [,scope] );

La particularidad de ese segundo parámetro es que indicando un objeto que hemos creado con clausuras (métodos privados) que considerábamos seguro, de repente es posible ejecutar código que accede a esos métodos e incluso es capaz de sustituirlos. John Resig ofrece más detalles y un ejemplo:

// obteniendo valores privados
var obj = (function() {
  var a = 21;
  return {
    // función pública que debe referenciar a la variable privada 'a'
    fn: function() {a;}
  };
})();

var foo;
eval('foo=a', obj.fn);
console.log(foo); // 21 

Al parecer la documentación de 1998 indica que se incluyó el parámetro para permitir hacer eval sobre otros ámbitos. En aquella época, la seguridad no era precisamente una preocupación. El caso es que en Mozilla visto el revuelo causado, la preocupación de los programadores por su código y los tradicionales ataques al lenguaje que se han incrementado1 han decidido considerarlo un bug y eliminarán el parámetro para el futuro Firefox 3.1.

El otro punto de vista

Hasta aquí la versión más conocida. Pero resulta que Andrea Giammarchi está pidiendo justo lo contrario, que dejen ese parámetro en Firefox y que lo implementen en el resto de navegadores. ¿Se ha vuelto loco? Andrea defiende que gracias a ese bug y a la definición de constantes en Firefox, por fin es posible ejecutar/evaluar código de terceros (no fiable) en un entorno seguro:
const entornoSeguro = function(A, B, D, F, N, O, R, S){
    const   Array   = A,
            Boolean = B,
            Date    = D,
            Function= F,
            Number  = N,
            Object  = O,
            RegExp  = R,
            String  = S,
            eval    = function(String){
                return  self.eval("(" + String + ")");
            };
    return  new function(){};
}(
    Array,
    Boolean,
    Date,
    Function,
    Number,
    Object,
    RegExp,
    String
);

eval("Array=function(){}; alert(Array);", entornoSeguro);
/* function Array(){ [native code] } */
Lo que hace es definir un entorno con los mismos objetos que ya tiene Javascript pero como constantes de forma que al hacer referencia a estos desde el código a ejecutar ya no cabría la posibilidad que existía en Javascript de redefinir un objeto básico (como Array).

Dudo que le hagan caso, pero no deja de sonar interesante su petición. Al fin y al cabo, como el dice, hay problemas de seguridad más grandes por resolver como la inyección de código SQL o XSS. Y ofrece como solución para los más neuróticos: usar las constantes de Firefox para definir un eval sin el conflictivo segundo parámetro:
// webreflection "from the space"
try{eval("const $eval=function(eval){return function($eval){return eval($eval)}}(eval)")}catch($){$eval=eval};

Poder hacer cosas como esta última es lo más apasionante de Javascript.

1: Afortunadamente, esta misma semana a Ruby también le ha salido un serio problema que todavía está pendiente de solución cosa que habrá frenado en parte los ataques de los programadores más dogmáticos contra Javascript.

04 julio, 2008

qUIpt


qUIpt
es una pequeña librería que es capaz de cachear en el navegador del usuario archivos Javascript al navegar entre páginas de un mismo sitio - incluso si se trata de SSL. qUIpt accelera la carga de páginas y ahorra en ancho de banda al evitar peticiones repetidas de archivos estáticos al servidor.

¿Cómo funciona?

  • Muy simple
  • Comprueba el contenido de window.name mientras se carga la página.
  • Si no hay nada dentro de window.name, introduce en esa cache los archivos JS que indiques y que se obtienen via XHR (XMLHttpRequest, o Ajax)
  • Lo mismo pasa si los usuarios entran por primera vez en tu sitio en esta sesión/pestaña del navegador o si document.referrer viene de otro dominio o está vacio.
  • Después se evaluan los contenidos de window.name.
  • Si el usuario pide otra página en tu dominio, los archivos JS se toman directamente de window.name - no hacen falta más peticiones
¿Es seguro?
  • Sí - si el usuario viene por primera vez, los archivos JS se piden al servidor independientemente de lo que hubiese antes en window.name.
  • Un atacante no pueden establecer window.name desde otras pestañas que el usuario pueda estar usando en paralelo.
Otros

Los peligros de detectar el navegador

Via Simon Willison. La gente de Opera describe en este post el tremendo problema con el que han tenido que tratar y por qué es una pésima idea detectar el navegador para evitar un bug.

Al liberar la versión 9.5 de Opera, vieron que el famoso componente TinyMCE actuaba de forma caótica. Siguiendo la pista al código descubrieron que por culpa de un bug en la versión 9.2, sus programadores detectaban si se trataba de ese navegador (sin tener en cuenta la versión) para introducir unos párrafos en orden inverso al que sería lógico.

Para cumplir con el test de Acid3 y ponerse al día, Opera resolvió el bug de la versión 9.2, consiguiendo así que la excepción introducida en el TinyMCE se ejecutase sobre un navegador que ya había resuelto el bug. El resultado es que con la versión 9.5, al pulsar intro, los pàrrafos se ordenaban de forma totalmente aleatoria.

El dilema era elegir entre romper el TinyMCE con Opera 9.5 o desistir en su intento de cumplir con Acid3. De momento lo han resuelto parcialmente haciendo que los programadores del componente detecten también la versión, aunque posiblemente haya gente que nunca actualice su versión del TinyMCE y le funcione mal siempre.

Moraleja

Jamás hay que detectar el navegador para rodear un problema. Si no hay más remedio habría que jugar también con los números de versión por si en una futura resuelven el problema. Pero la forma correcta de hacerlo es detectar el bug. Es decir, introducir código que compruebe si el navegador tiene el problema concreto que se quiere rodear y actuar en consecuencia. De esa forma, cuando aparezca una versión de ese navegador que deje de tener el problema, los usuarios sólo verán que todo sigue funcionando perfectamente.

01 julio, 2008

toElement y outerClick

Un par de snippets para MooTools 1.2 publicados por Jan Kassens en su nuevo blog:

1. toElement: con el nuevo MooTools 1.2 se puede definir un método toElement al definir una clase. Ese método será utilizado por MooTools cuando se haga referencia a un objeto mediante la función $(), de forma que se obtenga una representación visual (en HTML) del objeto automáticamente.

Por ejemplo, para la siguiente clase usuario, se define el método toElement que muestra un elemento DIV con el nombre y la foto del usuario. Cuando se haga referencia desde MooTools a objetos de esa clase con métodos como inject o grab, se obtendrá la representación visual definida por el método.

  var User = new Class({
    initialize: function(name){
      this.name = name;
    },
    toElement: function(){
      if (!this.element) {
        this.element = new Element('div', {'class': 'user'});
        var avatar = new Element('img', {
          src: '/avatars/' + name + '.jpg'
        });
        var description = new Element('div', {
          'class': 'username',
          'text': this.name
        });
        this.element.adopt(avatar, description);
      }
      return this.element;
    }
  });
   
  window.addEvent('domready', function(){
    var alicia = new User('Alicia');
    var juan = new User('Juan');
    $(alicia).inject($(document.body), 'after');
    // añadimos juan a alicia
    $(alicia).grab($(juan))
    // o para inyectar juan tras alicia:
    // $(juan).inject(alicia, 'after');
  });

2. outerClick: Se trata de la definición de un nuevo evento (outerClick) que se dispara cuando el usuario pulsa fuera del área del elemento al que se asocia. Es ideal para diseñar componentes como ventanas emergentes (popup) o Cajas de selección desplegables (dropdown).

(function(){
  var events;
  var check = function(e){
    var target = $(e.target);
    var parents = target.getParents();
    events.each(function(item){
      var element = item.element;
      if (element != target && !parents.contains(element))
        item.fn.call(element, e);
    });
  };
  Element.Events.outerClick = {
    onAdd: function(fn){
      if(!events) {
        window.addEvent('click', check);
        events = [];
      }
      events.push({element: this, fn: fn});
    },
    onRemove: function(fn){
      events = events.filter(function(item){
        return item.element != this || item.fn != fn;
      }, this);
      if (!events.length) {
        window.removeEvent('click', check);
        events = null;
      }
    }
  };
})();
 
// utilización
window.addEvent('domready', function(){
  $('test').addEvent('outerClick', function(){
    alert('Has hecho click fuera de "test".');
  });
});

Nótese que el evento se define dentro de una estructura ( function (){} )() lo que permite no machacar con las variables definidas en esta implementación las del código en el que se incluya. La función check comprueba si el objeto del evento (onClick) está fuera del elemento a chequear y dispara el evento (outerClick) en ese caso. Dicha función se asocia a window.onClick al definir un nuevo evento.

Programación artística con Javascript

Processing.js, el trabajo de John Resig, era evidente que iba a hacer despegar el uso de Javascript para aprovechar el objeto Canvas del navegador y hacer surgir trabajos artísticos por doquier.

En el siguiente vídeo, se pueden ver las capacidades de Algorithm Ink, un programa ideado para facilitar la creación artística basada en la programación gráfica. Aquí explican con detalle cómo funciona.


Vimeo.

JSON Diff

Via aNieto2k.com.
JSON Diff es una sencilla utilidad online que permite comparar dos versiones distintas de un mismo código JSON. En realidad, gracias a que muestra una representación visual mediante un árbol de nodos, también sirve como visualizador de JSON, para lo cual hay que poner en ambos recuadros el mismo código.



Últimos links en indiza.com