Ecrire une commande est très simple, il suffit de connaître un peu JavaScript. Ce n'est pas plus compliqué qu'écrire une bookmarklet. A vrai dire, Ubiquity pourrait presque être considéré, en première approche, comme une interface élégante pour lancer des bookmarklets. D'ailleurs bon nombre de bookmarklets peuvent être reconverties en commandes, l'API contient même une fonction, CmdUtils.makeBookmarkletCommand() permettant de traduire une bookmarklet en commande :-).

Ubiquity est donc un moyen très simple d'étendre les fonctionnalités de Firefox, ne nécessitant que peu de connaissances. Beaucoup moins en tout cas que pour écrire une extension, alors que certaines extension peuvent être remplacées par des commandes. Pour faciliter le développement, l'application dispose de plus d'un éditeur intégré, avec coloration syntaxique. Le code qui y est tapé est enregistré automatiquement (un souvenir d'Archy) et testable immédiatement, sans avoir à relancer le navigateur (c'est identique à la console de Firebug, pour ceux qui l'utilisent). L'éditeur est accessible en tapant dans Ubiquity command-editor.

Ubiquity "embarque" plusieurs bibliothèques JavaScript, ce qui facilite encore le développement:

  • jQuery, que je ne présente plus;
  • DateJS, pour la manipulation de dates;
  • Trimpath, un moteur de templates;

L'API est relativement bien documentée, via un mécanisme développé par Atul Varma (cf ci-dessous). Elle comporte pour l'instant deux objets, Utils et CmdUtils. Le premier contient des fonctions génériques et d'interface avec le navigateur, le second offre des fonctions spécifiques aux commandes. Un troisième objet, à venir avec Ubiquity 0.2, permettra de gérer les abonnements[1].

Le moyen le plus simple de créer une commande Ubiquity est d'écrire une fonction JavaScript dont le nom commence par cmd_. La fonction cmd_hello_world créera la commande hello-world.

Pour créer des commandes plus complexes, on utilisera la fonction CmdUtils.CreateCommand() en lui passant en argument un objet. Cet objet peut contenir les propriétés suivantes (je ne liste que les plus intéressantes, reportez-vous à la documentation de l'API pour la liste complète):

  • name: le nom de la commande, sans espace;
  • execute: la fonction à exécuter;
  • preview: pour gérer la prévisualisation, cf plus bas;
  • takes: un objet décrivant l'argument principal de la commande, si elle en prend un;
  • modifiers: un objet décrivant d'éventuels autres arguments de la commande;
  • description: une courte description de la commande, qui sera affiché dans la liste des commandes disponibles;
  • help: description plus détaillée de la commande, affichée dans la liste des commandes disponibles;

CmdUtils offre un certain nombre de fonctions permettant de récupérer la sélection en cours, de la remplacer, de récupérer des informations sur la fenêtre ou le document, d'insérer des styles ou des scripts dans la page, d'afficher un template (en utilisant le moteur de templates JavaScript Trimpath), d'effectuer une requête AJAX, etc.

La prévisualisation

Ubiquity dispose d'une zone de prévisualisation, pratique pour afficher des informations sans toucher au document. C'est une zone dans laquelle on peut afficher du HTML, utiliser des feuilles de style, des scripts, etc. Elle est gérée via la propriété preview de l'objet passé en paramètre à CreateCommand. Sa valeur peut être soit une chaîne de caractères, soit une fonction qui recevra en premier argument une référence à la zone de prévisualisation, et en deuxième les paramètres de la commande.

Exemple: afficher une citation au hasard de BashFR. On va effectuer une requête asynchrone vers http://www.bashfr.org/?sort=random, récupérer via le DOM de la réponse la première citation, et l'afficher

Pour effectuer la requête, on utilisera la fonction CmdUtils.previewGet(), dont le fonctionnement est similaire à jQuery.get(), si ce n'est que la fonction de callback ne sera appelée que si la prévisualisation n'a pas été annulée par l'utilisateur.

Lancez donc Ubiquity avec Alt-espace, tapez la commande command-editor et saisissez le petit bout de code suivant:

CmdUtils.CreateCommand({
 name: "bashfr",
 preview: function(pblock, arg) {
   CmdUtils.previewGet( pblock,
                        "http://www.bashfr.org/?sort=random",
                        null,
                        function(data, status) {
                          pblock.innerHTML = jQuery(data).find(".quote1").html()
                        },
                        "html" );
 }
})

Et c'est tout. Appelez Ubiquity, tapez bashfr, sous vos yeux esbaudis s'affiche une preuve qu'IRC est au réseau ce que les salons furent au siècle des Lumières. Voilà, désormais Ubiquity vous est indispensable ;)[2]

Gérer les arguments

Chaque commande peut prendre un ou plusieurs arguments. Le premier est défini avec la propriété takes des paramètres de CreateCommand, les suivants avec modifiers. Chaque argument est transmis à la commande sous forme d'un objet comportant quatres propriétés:

* text : le texte brut;
* html : le code HTML;
* data : quand l'argument n'est pas un texte;
* summary: un résumé si l'argument est long;
Typer les arguments

Ces arguments sont obligatoirement typés. Pour définir leur type, on utilise le concept de "noun type"[3]. Cela permet à Ubiquity de proposer des suggestions et des auto-complétions avec plus de pertinence. Un noun type est soit une énumération de valeurs autorisées, soit un objet comportant entre autres une fonction gérant la suggestion. Un certain nombre de noun types sont implémentés par défaut: noun_arb_text pour n'importe quel texte, noun_type_url pour une URL, etc. cf la documentation détaillée de chacun d'eux et leur implémentation.

Exemple: accéder au ressources du CNRTL

Le Centre National de Ressources Textuelles et Lexicales est un portail créé par le CNRS et fédérant de très nombreuses ressources linguistiques. Il propose moult outils, dont une extension pour Firefox, permettant de le consulter. Cette extension peut facilement être remplacée par une commande Ubiquity.

Le CNRTL a la bonne idée de fonctionner avec des URL facilement utilisables. On trouvera par exemple la définition d'un mot à l'adresse http://www.cnrtl.fr/definition/mot, ses synonymes à http://www.cnrtl.fr/synonymie/mot... Créer des commandes Ubiquity pour l'interroger est donc simple. Par exemple, pour afficher la définition d'un mot:

CmdUtils.CreateCommand({
 name: "cnrtl",
 takes: {"mot": noun_arb_text},
 preview: function(pblock, arg) {
   CmdUtils.previewGet( pblock,
                        "http://www.cnrtl.fr/definition/" + arg.text,
                        null,
                        function(data, status) {
                          pblock.innerHTML = jQuery(data).find("#contentbox").html()
                        },
                        "html" );
 }
})

Pour choisir le dictionnaire à interroger, on peut peut rajouter un argument en créant un noun type contenant la liste des dictionnaires disponibles. Ce qui donnera par exemple:

noun_type_cnrtl = new CmdUtils.NounType( "CNRTL",["morphologie", "definition", "etymologie", "synonymie", "antonymie", "concordance"]);
CmdUtils.CreateCommand({
 name: "cnrtl",
 takes: {"mot": noun_arb_text},
 modifiers: {type: noun_type_cnrtl},
 preview: function(pblock, arg, mod) {
   var ressource = mod.type.text || "definition"
   CmdUtils.previewGet( pblock,
                        "http://www.cnrtl.fr/" + ressource + "/" + arg.text,
                        null,
                        function(data, status) {
                          pblock.innerHTML = jQuery(data).find("#contentbox").html()
                        },
                        "html" );
 }
})

Pour connaître les synonymes d'un mot, il suffira, en utilisant l'autocompletion de sélectionner le mot, fair Alt-espace et taper:

cn<tab> type s<tab>

La première tabulation choisit automatiquement la bonne commande et lui passe en argument la sélection, la seconde complète le "s" en "synonymie".

Simple, non ?

Ce n'est qu'un exemple de ce que peut faire Ubiquity, l'API offre de nombreuses autres possibilités mais je m'arrêterai là pour cette introduction. Pour aller plus loin, n'hésitez pas à aller consulter le tutorial sur la création de commandes et la documentation complète. Et faites tourner vos créations !

Bonus : Code Illuminated

Code Illuminated est un projet créé par Atul Varma, un des cofondateurs d'Humanized qui travaille aujourd'hui sur Ubiquity. C'est une petite bibliothèque JavaScript qui extrait la documentation d'un script et affiche côte à côte le code et la documentation. Celle-ci peut être mise en forme en utilisant dans les commentaires une syntaxe de type wiki, celle de WikiCreole. La bibliothèque utilise JQuery et un parser Creole en JavaScript. Le but d'Atul, expliqué dans son billet Beautiful Documented Code est que la documentation soit mise à jour directement, sans devoir passer par une phase de compilation: la page d'index charge dynamiquement le code du script à documenter, et l'affiche avec une jolie mise en forme. Cf par exemple la documentation de Code Illuminated lui-même.

Bonus 2 : météo

Une dernière commande pour afficher la météo à partir du site http://fr.weather.com, à partir d'un code postal. J'utilise ici la fonction CmdUtils.previewAjax() (mais comme c'est pour faire un GET, CmdUtils.previewGet() aurait eu le même résultat), et un template pour l'affichage.

CmdUtils.CreateCommand({
 name: "meteo",
 execute: function() {
 },
 takes: {"postal code": noun_arb_text},
 preview: function(pblock, arg) {
   var callback = function(data, status) {
     tpl="<div style='width: 400px'><div style='width:230px; float: left'>${left}</div><div style='width: 120px; float: left'>${right}</div></div>"
     pblock.innerHTML = CmdUtils.renderTemplate(tpl, {left: jQuery(data).find("#current_left").html(), right: jQuery(data).find("#current_right").html()});
   };
   var ajaxopt = {
     url : "http://fr.weather.com/search?rdoWebWeather=Weather&searchText=" + arg.text + "&searchSourceType=1&sourcePageId=1&localeCode=fr_FR&searchType=0&locationId=+",
     type: "GET",
     dataType: "html",
     success: callback
   }
   CmdUtils.previewAjax( pblock, ajaxopt);
 },
 description: "description",
 help: "help"
})

Notes

[1] de même qu'on installe des extensions et que celles-ci peuvent vérifier régulièrement si une mise à jour est disponible, pour installer des commande on s'"abonne" à un flux qui peut être mis à jour par l'auteur de la commande. La sécurisation de ce mécanisme est un des axes de travail de la prochaine version 0.2

[2] astuce: la prévisualisation est lancée automatiquement au bout de quelques millisecondes pendant que vous tapez la commande. Pour afficher une autre citation, interrompez la saisie quelques secondes et reprenez-là, ou effacez le dernier caractère tapé. Bon, faut pas abuser pour pas couler le site. Dis Remouk, quand est-ce que tu fournis une API ?

[3] si quelqu'un sait comment traduire ?