Parse NaN and infinities in JSON strings in JavaScript

The official JSON specification doesn’t allow IEEE754′s NaN or infinities, which is  unfortunate if you happen to work on scientific or industrial matters.

For infinities, you might use some trick, like passing 1e999, which evaluates as Infinity, but  it forces you to build the JSON by hand and you can’t do this for NaN.

For  simple and well defined objects you might use a specific value that you’d replace after parsing. But this is very inconvenient with deep and changing structures.

It’s so evidently unfortunate that some JSON builders like Google’s gson overcome it by allowing them.

But you’re still with the problem of parsing  this « JSON » in JavaScript if you’re sending it to the browser.

There’s the usual solution of calling eval('('+json+')') but it fails on IE with big strings.

That’s why I forked Crockford’s code to add support for NaN, Infinity and -Infinity.

Test it online : JSON.parseMore tester

Check it on GitHub : JSON.parseMore

Parse, transform and write HTML files in Go

A recent task I had was to revive a 1 GB web site, made of more than 15000 files written between 1996 and 1998 with random case as it used to run on Windows.

Solutions in this case might be to mount a FAT disk or to play with Apache settings to ignore case but they’re obviously short sighted. So I wrote a small Go program copying a site, renaming all files to lower case and fixing all internal links.

I could have changed the links with regular expressions but I wanted to test the Go HTML parser, just recently renommed from exp/html to go.net/html.

The result is impressively fast. 20 seconds are enough to parse, transform and rewrite 5800 HTML files and also  copy 11000 other files (mostly images). And as with other Go libraries, it’s very simple.

Install the library :

go get code.google.com/p/go.net/html

As I found no documentation online, I simply launched the godoc tool :

godoc -http :6060

which lets me read the documentation of all the packages I have by pointing my browser to

http://localhost:6060/

Import the go.net/html package into a program :

import "code.google.com/p/go.net/html"

Open and parse an HTML file into a DOM object (a html.Node) :

r, _ := os.Open(path)
defer r.Close()
doc, err := html.Parse(r)

Lowercase all the internal links of a page :

func FixLinks(n *html.Node) {
	if n.Type==html.ElementNode {
		for i, attr := range n.Attr {
			if attr.Key == "href" || attr.Key == "src" {
				n.Attr[i].Val = GetFixedURL(attr.Val)
				break
			}
		}
	}
	for c:= n.FirstChild; c!=nil; c=c.NextSibling {
		FixLinks(c)
	}
	return
}

Write the fixed file :

w, _ := os.Create(dest)
defer w.Close()
html.Render(w, doc)

The whole code is available here : https://github.com/Canop/weblowercaser

Open in POST from JavaScript

If you want to open a page or start a download from JavaScript, you can do

window.open('http://canop.org/blog/?p=220')

but sometimes you don’t want to make a GET request but a POST one, for example when you have many parameters to pass. There is no equivalent simple function that I know for POST requests, that’s why I rolled my own, which builds an in-memory form and submit it :

// Arguments :
//  verb : 'GET'|'POST'
canop.open = function(verb, url, data, target) {
  var form = document.createElement("form");
  form.action = url;
  form.method = verb;
  form.target = target || "_self";
  if (data) {
    for (var key in data) {
      var input = document.createElement("input");
      input.name = key;
      input.value = typeof data[key] === "object" ? JSON.stringify(data[key]) : data[key];
      form.appendChild(input);
    }
  }
  form.style.display = 'none';
  document.body.appendChild(form);
  form.submit();
};

As I mainly send arguments in JSON to my servers, this function automatically stringifies the data arguments when they’re objects.

Usage examples :

// open a link in the current tab
canop.open('GET', 'http://canop.org/blog/', {p:220});

// download a file
canop.open('POST', 'fileServer.jsp', {request: {key:"42", cols:[2, 3, 34]}});

Note that the only reason the form is added to the page is because of a bug in IE : IE, even IE9, can’t submit a form if it’s not in the page.

Préchargement efficace et indolore de ressources

Mon site de recettes contient de nombreuses images, souvent présentées ensemble, et affichées en haute résolution au passage de la souris. Un diaporama de photos appétissantes est affiché lorsque cela ne risque plus de gêner le chargement des images nécessaires pour les menus ou la consultation agréable des pages.

Cela fait longtemps que je voulais un système de préchargement des images

  • invisible : cela ne sert à rien de précharger si un sablier s’affiche et donne à l’utilisateur le sentiment qu’il doit attendre
  • gérant à la fois des images prioritaires et des images pouvant attendre
  • acceptant que les listes d’images, tant prioritaires que secondaires, puissent être enrichies n’importe quand
  • simple
J’avais en particulier besoin d’accorder une plus haute priorité aux images apparaissant dans les menus, et de ne précharger qu’ensuite celles qui sont utilisées dans le reste du site.
La solution la plus courante est basée sur la création d’éléments Image, mais elle pose un problème rédhibitoire : elle entraîne l’affichage d’un sablier qui provoque l’attente de l’utilisateur.
J’ai préféré l’usage de XmlHttpRequest, la gestion d’une queue à deux entrées (prioritaire ou non) et le parallélisme des chargements (4 téléchargements en parallèle, on pourrait sans doute monter à 6).
Voici le résultat :
// Préchargement de ressources
// usage :  preload( url [ ,priority [,callback] ] )
//   preload(url);  // précharge la ressource en priorité normale
//   preload(url, true); // précharge la ressource en haute priorité
//   preload(url, false, callback); // appelle un callback quand la ressource est disponible
var preload = (function(){
	var queue = [], nbActives = 0;
	function bip(){
		if (queue.length==0 || nbActives>=4) return;
		nbActives++;
		var req = new XMLHttpRequest(), task=queue.shift();
		req.open("GET", task.src, true);
		req.onload = function () {
			nbActives--;
			bip();
			if (task.callback) task.callback(task.src);
		};
		req.send();
	}
	return function(src, priority, callback) {
		queue[priority?'unshift':'push']({src:src, callback:callback});
		bip();
	}
})();

Le site obtient maintenant la note de 99/100 sur Google PageSpeed. Obtenir la note maximale nécessiterait de mettre en place une minimisation de la page HTML, une gestion fine du cache par Apache et l’abandon de jQuery, opérations fastidieuses que le nombre de consultations de mon site ne justifie pas.

Visibilité d’une page

L’API de visibilité (lien en Anglais) n’est pas encore très connue, sans doute parce qu’elle n’est pas pleinement normalisée.

Elle permet de savoir si une page est visible (l’onglet est au premier plan, dans un navigateur lui même au premier plan) ou non. Elle permet aussi de s’abonner aux changements de cet état.

Comme je suis en train d’écrire un chat en javascript (il faut prévenir l’utilisateur qui reçoit un message) et que je voulais une API pratique qui pallie aux incohérences entre navigateurs, je me suis écrit un petit utilitaire.

Usage :

var visible = vis(); // donne l'état courant
vis(aFunction);   // s'abonne aux changements de visibilité

Exemple :

vis(function(){
	document.title = vis() ? 'Visible' : 'Pas visible';
});

Page de démonstration

 

Code complet :

Full Hipster code...Et au cas où le bloc de code ci dessus ne serait pas pleinement visible :

// "librairie" de gestion de la visibilité
//  var visible = vis(); // donne l'état courant
//  vis(function(){});   // enregistre un callback
var vis = (function(){
	var stateKey, eventKey, keys = {
		hidden: "visibilitychange",
		webkitHidden: "webkitvisibilitychange",
		mozHidden: "mozvisibilitychange",
		msHidden: "msvisibilitychange"
	};
	for (stateKey in keys) {
		if (stateKey in document) {
			eventKey = keys[stateKey];
			break;
		}
	}
	return function(c) {
		if (c) document.addEventListener(eventKey, c);
		return !document[stateKey];
	}
})();

 

 

Remote console.log, JSON based serialization and instant log

I can hardly code in Javascript without the console, especially the ability to dump and browse objects using console.log.

That’s why I had a really hard time debugging touch events on an Android browser. I always wanted to do

console.log(event);

but there was no easy way to do it and browse my object.

This made me develop a remote console.log tool, enabling me to simply log in the console of another browser on another computer.

Here it is : rol : a Remote Object Log

You can use it like this :

rol(event); // the event appears on any connected browser

In the making, I encountered a few unexpected problems, among them the fact you can’t serialize any object with JSON.stringify, especially the ones I wanted the most, the events.

You may see the problem by typing this in your console :

JSON.stringify(window);

The reasons are

  • that the event structure is cyclic : some property values points to properties up in the tree
  • that the event is very big. It points for example to the window object
  • that some properties can’t always be requested : the getter throws an exception
So I took Crockford’s stringify code and I changed it to address those concerns, at the cost of pruning some of the properties. The result is a strong function allowing you to execute, for example, this code :
JSON.prune(window)

This utility is free too and can be found and tested here : JSON.prune

 

A strong serialization function makes the basis for a deep (but not too deep) clone function, as you can simply do JSON.parse(JSON.prune(someObject));.

This was the opportunity to add another small function, this one aiming at fixing another debugging problem of console.log : the delay between logging and console building by the browser, delay during which the objects can change, leading to many programmer headaches (see this question on SO for example). So I added a JSON.prune.log function :

// make sure someObject is logged as it was at logging time
JSON.prune.log(someObject);

 

Pitfalls of jQuery.each

Even a very simple function like the convenient $.each can hide some non obvious traps.

Not the usual magic

Let’s say you want to iterate over the elements in your page having the class indent. You might write

$.each('.indent', function(){

Maybe you immediately saw the problem, maybe not. The reason it doesn’t work is that this won’t iterate over $('.indent') but over the '.indent' string (this being every character in turn).

In my opinion, what is the most interesting here is why many developers accustomed to jQuery fall into this trap, even while the documentation never pretends $.each should iterate over the DOM elements. The reason is that we’re used to jQuery functions magically guessing the nature of the arguments they receive. We’re used to jQuery finding on his own how to deal with the following ones :

$(function(){...}) // a function, obviously
$({}) // some object
$(document) // a DOM node
$('.indent') // a selector
$('<div>Hi!</div>') // a string too, but this one looks like HTML

Here, the « problem » is that the usual magic isn’t applied. $.each mainly tries to guess if the argument is array-like (for example a string) or object-like (then it must iterate using for...each). jQuery argument interpretation makes it intuitive and concise but sometimes you’d better check how the arguments are interpreted.

Callback arguments order

Another pitfalls is that it’s hard to remember the order of the callback arguments, because it’s just not the same as the standard ECMAScript foreach :

[1, 2, 3].foreach(function(value, index) { ... });
$.each([1, 2, 3], function(index, value) { ... });

Confusing, isn’t it ? Why does jQuery give as first argument the least interesting one ?

In fact there is a good rationale : Like many jQuery functions, $.each conveniently provides the value as context of the function call.

Just like you’re accustomed to using this in

$('.indent').each(function(){ $(this).text( ....

you can get the value with this :

var product = 1;
$.each([1, 2, 3], function(){ product *= this });

Now it seems you don’t need to remember the callback arguments order if you just want the value ? Marvelous ?

In fact, it’s useful but it leads to the third pitfall :

this isn’t really the value

this is often the value but not always. Look at this code :

​[1, 2, 3].forEach(function(v) {
    if (v===2) console.log('found!');
});

Output in the console :

found!

Now with jQuery :

$.each([1, 2, 3], function() {
    if (this===2) console.log('found!');
});

What’s the output ?

Nothing.

I explain it in more details in Stack Overflow but basically it’s because the context of a function can’t be a primitive value so it has to be embedded into a boxing object, here an instance of Number.

Conclusion

$.each is convenient and useful. It lets you iterate on arrays and objects in a concise and cross-browser way. In my opinion it’s globally well designed, as there were compromises to make. You should use it when the need arises. But as I saw answering on Stack Overflow it can be sometimes confusing and you’d better think about what it does and why.

HTML Canvas et coordonnées polaires

Je suis tombé hier sur StackOverflow sur un développeur qui ne voyait pas comment placer des points sur un canvas à partir d’un angle et d’un rayon. Comme, moins d’une heure avant, je venais de dessiner des diagrammes radar, je lui ai donné la formule (hautement sophistiquée) de conversion polaire->cartésien puis, comme ça ne débloquait pas, je lui ai montré mes diagrammes.

Suite au commentaire d’un autre développeur, je publie la démo ici, pour montrer ce qu’on peut faire de joli et d’utile en quelques lignes de javascript :

Pour ceux qui s’interrogent… la formule de transformation de coordonnées :

// r : rayon
// θ : angle en radians (dans [0, 2π[)
function polarToCartesian(r, θ) {
    return {x:r*Math.cos(θ), y: r*Math.sin(θ)};
}

Le reste du source est disponible dans la page de démo. Notez que je n’utilise pas d’autre framework que Vanilla JS.

Concaténation de fichiers ou minification pour le web : Attention au BOM

Le BOM Unicode, pour ceux qui l’ignorent, c’est la rune Unicode  U+FEFF qui, inséré au début d’un document, permet d’en déduire l’ordre de lecture des octets (big endian ou low endian) si l’on lit les runes par mots de 2 ou 4 octets (UTF-16 ou UTF-32)

Evidemment, dans un document encodé en UTF-8, ça n’a aucun intérêt. Ca n’empêche pas certains logiciels, en particulier Notepad, de l’écrire, sous prétexte de repérer plus facilement les fichiers Unicode.

Mais loin d’être simplement inutile, cette pratique est en fait nocive dés lors que l’on utilise des opérations standard sur les fichiers (vus comme des paquets octets) afin par exemple de concaténer tous les fichiers CSS ou Javascript d’un site pour l’accélérer. Ces runes parasites se retrouvent alors disséminées n’importe où et rendent les fichiers invalides.

Il faut donc absolument vérifier les réglages de son éditeur pour éviter d’écrire ce BOM.

Mais comme il se trouve toujours quelqu’un pour éditer rapidement un fichier dans Notepad (lequel ne propose pas ce réglage), il faut aussi parfois partir à la chasse des BOM existants.

Lorsque l’arborescence de fichiers est importante, il peut être difficile de repérer tous les fautifs. Voici une commande à exécuter à la racine du projets pour les lister :

grep -rl $'\xEF\xBB\xBF' .

A propos des commentaires

Heureusement que WordPress permet aux foules ébahies d’exprimer leur émerveillement.

Sinon j’aurais manqué ça :



Je ne suis pas d’un naturel suspicieux, mais déjà le premier commentaire que j’avais reçu m’avait paru un peu douteux :

  • je testais WordPress pour mon beau-frère, je n’avais donné de lien à personne, l’audience était donc inattendue
  • un commentaire en Anglais pour un blog en Français, vraiment ?
  • cette page vide (celle ne contenant que l’image d’en-tête) pouvait-elle légitimement être considérée « enlightening » ?

A ce moment je me suis dit que si je publiais quelques articles, avec des vrais morceaux de texte dedans, mon principal filtre anti-spam serait la langue : commentaire en Anglais, commentaire supprimé.

Depuis, j’ai écrit quelques petits articles, certains d’entre eux en Anglais. Mais curieusement aucun des commentaires reçus n’a été proche de cette frontière qui sépare le spam (généralement élogieux) du commentaire humain (plus critique). La dernière salve est exemplaire : même le corbeau le plus sensible à la flatterie ne s’y laisserait pas prendre.

Par contre, un jour, les auteurs de bot seront un tout petit peu plus subtil (facile), ils exploiteront les autres articles sur le même thème pour produire des commentaires plausibles. Ce jour là, je ne vois pas comment les systèmes de commentaires des petits blogs survivront.

Et qu’on ne me parle pas des CAPTCHA sous forme d’image ou de son, les bots sont déjà meilleurs que moi pour les déchiffrer…