Ecrire sa première commande Ubiquity
Par Clochix le lundi 9 février 2009, 01:43 - Technoweb - Lien permanent
Ubiquity est ce projet des laboratoire Mozilla pour essayer de renouveler l'interface en proposant aux internautes d'effectuer des actions en tapant des commandes dans un langage relativement naturel.
Créer de nouvelles commandes est très simple, à la portée de n'importe qui ayant quelques notions de JavaScript. Comme Ubiquity n'en est qu'à ses balbutiements, et va sans doute évoluer rapidement, plutôt que de tenter une traduction de la documentation, qui risque d'être bientôt obsolete, je vais essayer de présenter rapidement les bases pour vous permettre de commencer à créer des commandes.
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 ?
Commentaires
"noun type" ça veut tout simplement dire "type de nom", puisqu'une commande Ubiquity se compose de "verbs/verbes" (un seul pour l'instant, un ou plusieurs dans le futur) et de "nouns/noms" : )
@lrbabe: certes
mais la traduction littérale ne me plait guère, je la
trouve peu parlante. Je cherchais justement quelque chose en rapport avec la
grammaire puisqu'on est dans le lexique de la langue. Quelque chose comme
''famille nominale''. Y a-t-il des grammairiens dans la salle ?
Salut,
J'essaye de faire une commande Ubiquity mais j'en chie trop :p J'y vais pas à pas mais j'ai quasiment jamais fais de JavaScript alors.
J'en suis là : http://pastebin.com/m272d66f
Le problème c'est que la notification n'apparait pas du tout sauf si j'enlève le +this._getURL(). Vois-tu un problème ?
Merci d'avance !
Salut Thomas,
quand tu développes une commande Ubiquity, avoir une console d'erreur JavaScript à portée d'yeux peut être utile. Celle par défaut de Firefox ou celle de Firebug par exemple. Tu aurais ainsi vu qu'une de tes variables n'est pas définie.
Pour récupérer l'object Document de l'onglet en cours, utilise
CmdUtils.getDocument( )Quel imbécile ! Ça m'apprendra à développer sans débugueur, merci