Archives pour la catégorie JavaScript

Présentation de Leaflet et CartoDB au LyonJS en ligne (vidéo / slides)

La semaine dernière, j’ai donné une présentation au Lyon JavaScript User Group (alias LyonJS) sur 2 outils:

  • Leaflet (http://leaflet.cloudmade.com/ / @leafletJS) : Librairie JS permettant d’afficher des cartes dans une page web. Similaire à l’API Google Maps par de nombreux points mais plus ouverte et extensible
  • CartoDB (http://cartodb.com/ / @cartoDB) : Service en ligne pour avoir une « Geodatabase dans le cloud ». CartoDB est aussi bien un espace de stockage dans lequel vous pouvez importer vos CSV, KML ou SHP mais aussi une API puissante basée sur PostGIS, vous permettant de faire des analyses sur de gros volumes de données)

Cette présentation n’est pas très technique et présente surtout les problématiques « classiques » de la cartographie, les pièges à éviter et les solutions que vous avez pour arriver à vos fins.

En plus de cela, pas mal de démonstrations d’applications que je trouve sympa et de différents outils.

Les slides

LyonJS-Leaftlet-CartoDB

La vidéo

http://www.youtube.com/watch?v=XYcD9vVgZto

C’est plutôt dommage mais les démonstrations n’ont pas marché pendant la présentation (classique). Le mieux est d’essayer par vous-même ces 2 outils pour vous rendre compte :).

Gérer ses dépendances JavaScript avec Google Closure Compiler / Library (2 / 2)

Suite de Gérer ses dépendances JavaScript avec Google Closure Compiler / Library (1 / 2)

Compiler plusieurs fichiers en un seul

Jusque là, on a vu comment ajouter des directives goog.provide(..) et goog.require(…) dans son document, mais il faut maintenant s’en servir. Vous pouvez passer au JAR du compilateur, une liste de fichier JS à assembler avec l’option –js:

java -jar compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js file1.js --js file2.js --js file3.js

Il y a plusieurs niveaux de compilation:

  • WHITESPACE_ONLY : remplace uniquement les espaces et les sauts de lignes. Ne modifie pas la code en lui-même
  • SIMPLE_OPTIMIZATIONS : Fait en plus une minification « simple » en renommant les variables locales par des noms plus courts (a, b, c, …). Similaire à ce que font les autres minifiers comme Uglify & co
  • ADVANCED_OPTIMIZATIONS: Niveau « hardcore » qui va aller bien plus loin en renommant aussi les objets globaux. Attention, bien lire la documentation car ce mode est « agressif » et peux casser la compatibilité de votre code si vous n’utilisez pas les mécanismes d’externs / exportSymbol. Ce mode va supprimer le code « mort » (jamais appelé) et permet un taux de compression maximal.

Dans la ligne de commande que j’ai donné plus haut, vous voyez que j’ai explicitement donné les chemins vers les 3 fichiers JavaScript. Ce n’est pas trop laborieux quand il y en a 3 mais quand vous aurez plusieurs dizaines de fichiers, je peux vous assurer que vous n’aurez pas envie d’aller rajouter tout cela à la main. D’autant plus qu’il ne faut pas vous tromper dans l’ordre d’inclusion des fichiers :).

Construire un arbre de dépendances avec les scripts Python fournis

Au lieu de compiler simplement plusieurs fichiers, on va demander aux outils de Google Closure Compiler de nous construire un arbre de dépendances de manière « intelligente ». En effet, grâce aux indication goog.require(…), vous allez pouvoir déterminer quel script doit être présent en premier dans le fichier final.

Pour faire simple, les fichiers qui ont le moins de dépendances vont se trouver en premier dans le fichier. Si com.ns.A dépend de com.ns.B, com.ns.B devra apparaître en premier dans le fichier final.

On a donc à notre disposition un fichier closurebuilder.py:

https://developers.google.com/closure/library/docs/closurebuilder?hl=fr

Attention, pour fonctionner, vous devrez utiliser Python 2.7.2, pas plus!

On va donner à celui-ci plusieurs paramètres:

  • Une liste de « root », c’est-à-dire une liste de répertoire qui seront scannés de manière récursive pour aller chercher vos fichiers source.
  • Un « namespace » qui va être le namespace (d’un goog.provide) dont on va aller chercher les dépendances

Imaginons que vous ayez le fichier suivant dans le répertoire « myproject/start.js »:

goog.provide('myproject.start');

goog.require('goog.dom');

myproject.start = function() {
  var newDiv = goog.dom.createDom('h1', {'style': 'background-color:#EEE'},
    'Hello world!');
  goog.dom.appendChild(document.body, newDiv);
};

// Ensures the symbol will be visible after compiler renaming.
goog.exportSymbol('myproject.start', myproject.start);

Pour calculer les dépendances, vous allez utiliser la ligne de commande suivante:

closure-library/closure/bin/build/closurebuilder.py \
  --root=closure-library/ \
  --root=myproject/ \
  --namespace="myproject.start"

Ici, on donne comme « root » le dossier contenant la librairie Closure et le dossier « myproject » qui contient les sources (ici, un fichier seulement). Voici ce que l’on obtient:

closure-library/closure/bin/build/closurebuilder.py: Scanning paths...
closure-library/closure/bin/build/closurebuilder.py: 596 sources scanned.
closure-library/closure/bin/build/closurebuilder.py: Building dependency tree..
closure-library/closure/goog/base.js
closure-library/closure/goog/debug/error.js
closure-library/closure/goog/string/string.js
closure-library/closure/goog/asserts/asserts.js
closure-library/closure/goog/array/array.js
closure-library/closure/goog/dom/classes.js
closure-library/closure/goog/object/object.js
closure-library/closure/goog/dom/tagname.js
closure-library/closure/goog/useragent/useragent.js
closure-library/closure/goog/math/size.js
closure-library/closure/goog/math/coordinate.js
closure-library/closure/goog/dom/dom.js
myproject/start.js

On a simplement indiqué que l’on avait besoin du namespace « goog.dom » et le script Python est allé scanner ce fichier pour aller voir de manière transitive, de quoi dépendait « goog.dom » pour les ajouter à la liste dans le bon ordre. Par exemple, « goog.dom » dépend de « goog.math.coodinate », qui est donc inclut avant.

De base, vous avez donc généré votre arbre de dépendance que vous pouvez par exemple stocker dans un fichier. Cela peut aussi vous servir pour créer un fichier nommé « deps.js » qui va, en environnement de développement, charger les sources une par une dans la page. C’est plus long mais beaucoup plus facile à debugger.

Mais ce que l’on veut, c’est compiler en un seul JS, on va donc ajouter 2 paramètres:

closure-library/closure/bin/build/closurebuilder.py \
  --root=closure-library/ \
  --root=myproject/ \
  --namespace="myproject.start" \
  --output_mode=compiled \
  --compiler_jar=compiler.jar \
  > myproject/start-compiled.js

On indique qu’on veut le compiler en donnant le chemin vers le JAR de Google Closure Compiler puis avec un « > », on indique que l’on va rediriger la sortie vers le fichier myproject/start-compiled.js.

Par cette étape de compilation, on pourra vous avertir sur des erreurs que l’on aura fait, comme par exemple avoir un goog.require(« ns.SomeClass ») sans un seul fichier qui déclare goog.provide(« ns.SomeClass »). Mais surtout, vous voyez qu’ici, on a plus à faire la liste des dépendances, on a simplement donné une référence vers le namespace qui sera « le plus haut dans l’arbre des dépendances ».

Notez que ce processus peut être facilement intégré à votre système de Build (Maven & co), notamment à des projets comme Plover ou  wro4j.

Le point noir : la gestion des dépendances qui ne sont pas à soi (third-party)

Alors bien sûr, c’est juste superbe, plus besoin de s’occuper de ses dépendances, un système facile à intégrer à son build, des annotations pas trop envahissantes, très bien.

Mais il est très commun d’intégrer dans son code source, des librairies externes comme jQuery. Ces librairies ne comportent pas les annotations goog.require et goog.provide, et si vous compilez en mode « avancé », le Closure Compiler va donc considérer que ce code ne fait rien et éliminer le code mort.

Google a prévu ce cas-là et propose un mécanisme d’externs:

https://developers.google.com/closure/compiler/docs/api-tutorial3#externs

Vous allez en fait dans un fichier séparé, déclarer l’API publique de la librairie pour qu’elle ne soit pas renommée pendant la compilation. Pour vous faciliter un peu la tâche, Google vous propose des externs pour les librairies les plus populaires:

http://code.google.com/p/closure-compiler/source/browse/#svn%2Ftrunk%2Fcontrib%2Fexterns

comme jQuery:

http://code.google.com/p/closure-compiler/source/browse/trunk/contrib/externs/jquery-1.7.js

Cela fonctionne et vous n’aurez pas à modifier les sources de jQuery, juste à préciser ce fichier au moment de la compilation.

Seulement, si vous avez une librairie dont les externs n’ont pas été générés, il ne vous restera plus qu’à créer ce fichier à la main. Certains script de génération auto existent mais cela reste très peu confortable.

Après pas mal d’essais en vain, j’en ai conclu que vous devrez laisser tomber le mode de compilation « avancé » pour revenir au « simple » qui effectue une minification classique, aussi bonne que les autres outils disponibles sur le net.

Faire fonctionner la résolution des dépendances avec des fichiers third-party

Même si vous repassez en mode « simple », vous aurez toujours un problème qui est que vos librairies 3rd-party ne contiennent pas les annotations goog.require() mais surtout goog.provide(). A partir de là, vous avez 2 solutions:

  • Les ajouter en en-tête du fichier en modifiant le fichier de la librairie que vous avez téléchargé. Ce n’est pas très propre car vous venez d’altérer la librairie à la main, donc vous devrez y penser quand vous aurez à mettre à jour la librairie. Cependant, cela peut être une solution simple et rapide si vous savez que vous ne modifiez pas vos dépendances tous les 2 jours.
  • Ecrire les instructions goog.provide() à la volée en Python. Cette technique est expliquée sur ce blog: Learning Closure: managing dependencies and compiling

Conclusion

Comme d’habitude, vous devrez utiliser le bon outil qui va convenir à votre projet. Le principal problème de Google Closure est la gestion des dépendances externes, qui peut rapidement devenir laborieuse, sauf si on prend quelques raccourcis. Mais les autre systèmes de gestion de dépendances ont aussi leur défauts ;).

Dans l’ensemble, le mode de compilation « avancé » sera réservé aux projets « pur Google Closure » ou presque mais la minification simple fait déjà du très bon travail. Pour moi, les points les plus importants sont:

  • Gestion des dépendances automatiques, par analyse du code
  • Syntaxe légère et compréhensible
  • Dépendance vers base.js pas encombrante (ce n’est pas comme si vous étiez obligé d’embarquer tout jQuery)
  • Possibilité d’intégrer cela au système de build

Comme d’habitude, si vous avez un mot à dire, une critique ou un retour d’expérience, n’hésitez pas à utiliser les commentaires!

Gérer ses dépendances JavaScript avec Google Closure Compiler / Library (1 / 2)

JavaScript ne propose pas (encore) de moyen d’organiser son code et notamment de déclarer les dépendances entre les différents fichiers JavaScript de votre projet. Dans un langage compilé comme ActionScript ou Java, vous avez les directives « import pack.sub.MaClasse » qui vous permettent d’indiquer au compilateur qu’une classe requiert une autre classe pour fonctionner.

Si vous avez quelques dizaines de lignes de code JavaScript dans votre projet, vous pouvez toutes les mettre dans un seul fichier ou directement dans le fichier HTML mais quand vous commencez à avoir plusieurs centaines / milliers de lignes de code, vous allez rapidement découper votre code en différent fichiers.

Comme il n’y a encore rien dans le langage pour vous aider, plusieurs librairies / systèmes ont été créés, notamment:

Exemple simple avec RequireJS

Pour en apprendre plus sur RequireJS, une des plus populaires, je vous conseille de lire ce très long et très bon article @mklabs :

RequireJS ➤ mo-du-la-ri-té !

Même si RequireJS est devenu assez « standard » (il y a quelques trolls sur le net pour déterminer le champion des champions), vous pouvez toujours décider de ce que vous allez utiliser.

Vous trouverez plein d’exemples dans l’article cité plus haut mais voici comment on déclare un module requireJS:

//my/shirt.js now has some dependencies, a cart and inventory
//module in the same directory as shirt.js
define(["./cart", "./inventory"], function(cart, inventory) {
        //return an object to define the "my/shirt" module.
        return {
            color: "blue",
            size: "large",
            addToCart: function() {
                inventory.decrement(this);
                cart.add(this);
            }
        }
    }
);

On utilise la méthode globale define (amené par require.js), on lui passe (si nécessaire) en premier argument la liste des dépendances puis une fonction (callback) qui sera appelée lorsque les dépendances auront été chargées. A cette callback, on passe autant d’arguments que de dépendances (dans l’ordre). Ces arguments sont des références vers les dépendances chargées.

Il y a plusieurs manières plus ou moins élégantes de le faire mais il faut que le module renvoie son « interface publique », c’est-à-dire les méthodes qu’il va exposer. Le reste sera « privé » grâce à l’utilisation d’une closure, ici la callback principale.

Ensuite, pour aller chercher la dépendance, vous allez utiliser la méthode globale « require() » qui prend en argument l’identifiant du module JavaScript que vous voulez utiliser:

require("module/name").callSomeFunction()

Voilà, cela fonctionne très bien mais, et c’est très personnel, plusieurs points me chagrinent:

  • La syntaxe qui peut rapidement devenir verbeuse
  • Le fait d’enfermer son module dans une callback permet de ne pas polluer le namespace globale mais pour une application (pas une librairie), je ne trouve pas cela « trop » grave d’arriver avec son namespace
  • Code plus difficile à lire (pour moi) qu’avec une approche simple par prototype « à plat »

Bref, j’ai décidé d’aller voir ailleurs pour enfin tester la méthode de Google Closure

Google Closure en deux mots

Google Closure est une famille de projets soutenus par Google et utilisés par Google dont:

Voici une vidéo du Google IO 2010 qui explique un peu tout:

Ici on va principalement s’intéresser au Compiler pour notre gestion de dépendances. Le compilateur en lui-même se compose d’un JAR mais Google propose aussi d’autres outils en Python pour vous aider à automatiser votre travail.

La Google Closure Library est un ensemble de « classes » JavaScript et de composants que l’on présente souvent comme une alternative à jQuery (alors que ce n’est pas vraiment le cas). Pour notre exemple, on va utiliser seulement un fichier, « base.js » qui contient certaines méthodes qui nous seront utiles pour déclarer nos dépendances.

Un exemple

Le plus simple pour trouver des exemples et d’aller voir dans le code de la librairie Google Closure directement:

http://code.google.com/p/closure-library/source/browse/#svn%2Ftrunk%2Fclosure%2Fgoog

Vous verrez que les fichiers sont très verbeux, avec énormément de commentaires JSDoc qui sont en fait utilisés par le compilateur pour transformer JavaScript en un langage presque typé.

Prenons par exemple le fichier ClickToEditWrapper.js:

http://code.google.com/p/closure-library/source/browse/trunk/closure/goog/editor/clicktoeditwrapper.js

Voici ce que vous allez trouver dans les premières lignes du fichier:

goog.provide('goog.editor.ClickToEditWrapper');

goog.require('goog.Disposable');
goog.require('goog.asserts');
goog.require('goog.debug.Logger');
goog.require('goog.dom');
goog.require('goog.dom.Range');
goog.require('goog.dom.TagName');
goog.require('goog.editor.BrowserFeature');
goog.require('goog.editor.Command');
goog.require('goog.editor.Field.EventType');
goog.require('goog.editor.range');
goog.require('goog.events.BrowserEvent.MouseButton');
goog.require('goog.events.EventHandler');
goog.require('goog.events.EventType');

et ensuite, le protoype est enrichi de manière dynamique:

/** @return {goog.editor.Field} The field. */
goog.editor.ClickToEditWrapper.prototype.getFieldObject = function() {
  return this.fieldObj_;
};

/** @return {goog.dom.DomHelper} The dom helper of the uneditable element. */
goog.editor.ClickToEditWrapper.prototype.getOriginalDomHelper = function() {
  return this.originalDomHelper_;
}

Ici, on utilise 2 méthodes qui sont en fait présentes dans le fichier base.js de la Google Closure Library: goog.provide(…) et goog.require(…).

goog.provide(…)

L’instruction goog.provide(…) prend en seul paramètre, un namespace comme « goog.editor.ClickToEditWrapper ». Avec cette instruction, vous indiquez que le fichier qui contient cette directive est celui qui va pouvoir fournir le namespace « goog.editor.ClickToEditWrapper ». C’est un peu comme si vous déclariez le package et le nom de la classe en même temps (le fully-qualified classname pour les intimes).

Notez qu’un fichier peut contenir plusieurs instruction goog.provide(…), en effet, un fichier peut définir plusieurs « classes » JavaScript.

goog.require(…)

Comme son nom l’indique, goog.require(…) indique que la classe en question a une dépendance forte vers un autre namespace. Si vous faîtes un peu de Java / ActionScript 3, cela ressemble aux instructions « import » ou aux fichiers .h en C. Bien entendu, vous pourrez avoir autant de goog.require(…) qu’il en faudra.

Ici, plusieurs points que j’apprécie:

  • La syntaxe est moins intrusive et plus élégante à mon goût (ressemble *très* grossièrement à de l’AS3)
  • Pas de callback à laquelle on passe des références aux dépendances, on utilisera simplement nos objets définis dans nos namespaces.
  • Plus facile à lire car assez linéaire et peut être séparé par sub-package, comme ce que font les formatters de code Java / AS3 (séparer bg.model.xxx de bg.application.xxx)
  • Dépendance au fichier base.js (8Ko minifié, 2.5Ko gzippé + min) qui contient en plus pas mal de « petits trucs » bien pratiques (mixins, typeOf, inherits, …)

La suite dans la seconde partie, où j’expliquerai comment compiler en ligne de commande, puis comment utiliser les scripts Python fournis pour construire son arbre de dépendances.

Adobe Shadow – Outil de debug sur devices mobiles (iOS et Android) sorti des labs

Adobe l’avait clairement annoncé, ils se concentrent sur les technologies autour de HTML5, notamment en contribuant à jQuery, Webkit et autres PhoneGap. Aujourd’hui, ils viennent de sortir un nouveau produit de leur labs, Adobe Shadow.

Pour l’instant, celui-ci est encore en phase de beta et peut être téléchargé sur les Adobe Labs:

Adobe Shadow sur Adobe Labs

Un outil de debug sur mobile

Faire du développement HTML5 sur son PC / Mac, c’est assez pratique car on peut suivre un cycle de développement / debugging « cyclique ». On modifie le code dans son IDE, on va dans son navigateur, on recharge la page et on peut inspecter ce qu’il se passe directement avec les outils intégrés au navigateur. Google Chrome se révèle d’ailleurs excellent sur ce point, encore meilleur que le couple Firefox + Firebug quand on a pris un peu l’habitude.

Mais quand on passe sur mobile, c’est une autre histoire, les outils de debug sur mobile sont extrêmement limités. Bien sûr, on peut toujours caler des « alert() » comme en 1990 mais c’est plus long qu’autre chose.

Sur Safari (iPhone / iPad), vous pouvez activer la console qui va vous afficher de manière succincte, ce que vous loggez avec console.log(…). Sur Android, vous avez le navigateur Google Chrome pour Android (disponible sur le Marker) qui vous permet d’aller plus loin mais on est bien loin du confort que l’on a sur son PC/Mac.

Adobe Shadow tente de vous rendre la tâche plus facile en réalisant une synchronisation de votre navigateur (Google Chrome seulement) avec de multiples devices connectés. Le nombre de devices n’est pas limité, vous pouvez donc y connecter une armada de téléphones / tablettes. Seule contraintes, tous vos appareils doivent être sur le même réseau mais si vous avez un réseau Wifi, ce n’est pas un souci.

Le système se compose de 2 parties, une extension Google Chrome pour votre browser PC et une application native disponible sur l’AppStore / Google Play que vous devrez installer sur votre appareil. Notez que pour les utilisateurs Windows, vous devrez aussi installer le logiciel « Bonjour » présent dans l’installation de Shadow pour que votre machine soit automatiquement détectée.

Vous lancez l’application sur votre mobile et on va vous donner un mot de passe (pour éviter un conflit avec d’autres utilisateurs du réseau Wifi) que vous allez rentrer directement dans l’extension Google Chrome

Votre device est désormais connecté. Lorsque vous naviguez sur une page web sur votre PC, celle-ci va automatiquement s’afficher sur toutes les devices connectés. Ce n’est pas un simple screenshot qui vous est balancé mais bien la page telle que vous la verriez sur Safari sur iOS par exemple. Cela vous permet de tester facilement le look de vos pages web sur vos appareils sans aller taper l’adresse dans tous les navigateurs. Voici par exemple la page Twitter sur mon PC, sur un iPad et sur un smartphone Android:

On voit bien que l’affichage n’a rien à voir (cela vient aussi du fait que je ne suis pas connecté avec mon compte sur les devices).

Jusque là, c’est pratique mais ce qui est vraiment intéressant, c’est la suite!

Debugging sur device depuis le Desktop

Comme je le disais en introduction, les outils de debug des navigateurs mobiles sont assez ridicule par rapport à ceux que l’on a sur Desktop. Grâce à Adobe Shadow, on peut avoir « le meilleur de deux mondes » en ayant les outils de debug de Google Chrome qui agissent directement sur le rendu sur device avec notamment:

  • Inspection et modification des éléments du DOM
  • Highlight des éléments du DOM au survol sur le device (pratique)
  • Utilisation de la console JavaScript
  • Onglet Network pour voir le trafic entrant / sortant

Voici un aperçu de l’interface que vous devez connaître si vous utilisez déjà Chrome:

Démonstration

Under the hood

Comme je suis assez curieux, j’ai lancé un petit tweet pour savoir grâce à quoi était réalisé Adobe Shadow, ce à quoi le compte officiel @AdobeShadow m’a répondu:

@fnicollet Shadow 4 iOS/Android use Native SDKs. Shadow 4 Chrome uses HTML, CSS & Javascript. Remote inspect uses @phonegap weinre. cc@adobe

Pour info, « weinre » (à prononcer winery) est un projet qui était déjà existant, pour le debug des applications PhoneGap:

http://phonegap.github.com/weinre/

Il existe d’autres outils du même genre comme  Shim (merci @jsgeneve), un projet pour synchroniser la navigation qui tourne sur node.js:

https://github.com/marstall/shim#readme

Perso, j’ai utilisé Shadow cette après-midi et cela fonctionne déjà très bien, même pour une beta. Je pense qu’il devrait me faire gagner pas mal d’heures par la suite.

N’hésitez pas à proposer des évolutions pour rendre cet outil encore meilleur ;)

Plus d’informations: