Thursday, July 20, 2006

Un zeste de SVG, Ajax et REX

(c) Jean-Claude Moissinac - ENST Juin 2006

Résumé

Dans cette note, je présente une implémentation de REX pour modifier dynamiquement des documents SVG à l'aide de XmlHttpRequest popularisé par AJAX. REX est une grammaire XML simple, proposée par le W3C (http://www.w3.org/TR/rex/) en février 2006, qui permet de décrire des modifications qu'on veut apporter à un document XML: insertion, remplacement ou suppression de sous-arbre, et changement d'attributs. Grâce à cette implémentation, avec très peu de code Javascript, on peut faire toutes sortes de manipulation d'une page graphique SVG par des commandes issues d'un serveur.

ATTENTION: le paragraphe 'les problèmes avec REX' vers la fin de cette note contient un avertissement potentiellement important pour les développeurs qui utilisent AJAX

Abstract
In this article, I present a REX implementation to modify dynamically SVG documents by means of the XmlHttpRequest function popularized by AJAX. REX is a simple XML grammar, proposed by the W3C (http://www.w3.org/TR/rex/) in February, 2006, which allows to describe modifications which we want to bring to an XML document: insertion, replacement or deletion of a sub-tree, and change of attributes. Thanks to this implementation, with a short piece of Javascript, we can make many transformation of a graphic SVG page by commands obtained from a server.

An english version of this article is available:
http://svgmpeg4.blogspot.com/2006/06/taste-of-rex-ajax-and-svg.html

ATTENTION: the end of this note contains a warning potentially important for the developers which use AJAX

SVG


SVG signifie Scalable Vector Graphics. Il s'agit d'une grammaire XML, définie par le W3C, qui permet de décrire des documents graphiques, éventuellement animés et interactifs, souvent perçu comme un concurrent de Flash. Nous verrons d'ailleurs dans une prochaine note comment les techniques proposées ici permettent d'exploiter une pratique assez courante des développeurs Flash: envoyer une scène graphique initiale presque vide et la compléter ensuite progressivement en fonction des préférences et des interactions de l'utilisateur, du navigateur utilisé, de la taille de la fenêtre...

De nombreux développeurs ont développé au cas par cas leur propre code javascript pour modifier des scènes SVG; nous allons voir ci-après qu'avec très peu de code, on peut avoir une solution générique.


AJAX


Ajax a popularisé, en lui donnant un nom, la méthode de développement de sites Web dynamiques basée notamment sur l'utilisation par script de la fonction XMLHttpRequest pour récupérer les données permettant de modifier une page sans la recharger (voir par exemple http://www.adaptivepath.com/publications/essays/archives/000385.php). Une pratique courante consiste à demander au serveur un morceau de page HTML à l'aide de XMLHttpRequest et à l'injecter dans la page courante par simple copie. Cela procure une méthode extrêmement souple et légère pour la réalisation de pages dynamiques.

Cette façon de travailler s'applique aussi à des documents SVG. Dans le cas des navigateurs qui supportent l'utilisation d'éléments HTML et SVG dans une même page, on peut même manipuler à la fois le HTML et le SVG par cette méthode; c'est le cas, par exemple, avec les versions courantes du navigateur Firefox.


REX, brève introduction

La proposition faite pat le W3C est courte et claire. On peut la trouver à l'adresse http://www.w3.org/TR/rex/. REX signifie Remote Events for XML.

REX utilise une sous-partie de la syntaxe Xpath pour désigner la portion de l'arbre XML que l'on souhaite modifier. Cette sous-partie est simple à comprendre, proche de la description d'un chemin dans une arborescence de dossiers. Par exemple:
/ désigne la racine du document
/svg/g[3] désigne le troisième élément g directement trouvé comme fils de la racine du document svg (rem.: en svg, g permet de grouper des éléments).
id('bouton1') désigne le noeud de l'arbre ayant l'identificateur (id) bouton1
id('boutons1')/@fill désigne l'attribut fill de l'élément d'identificateur bouton1

On le devine à travers ces exemples, la syntaxe est vite assimilable, au moins pour les utilisations les plus courantes.

Une fois que nous savons désigner la partie du document à modifier, voilà quelques exemples simples de modifications possibles:

<rex xmlns="http://www.w3.org/2006/rex" >
<event target="id('S1')/svg:path[13]" name="DOMNodeRemove">
</rex xmlns="http://www.w3.org/2006/rex" >

name contient le nom de la commande exécutée, ici DOMNodeRemove sert à supprimer un noeud; dans cet exemple, on supprime donc le treizième noeud path (et son sous-arbre éventuel) trouvé sous l'élément d'identificateur S1.
_________________________________________________

<event target="id('S1')/svg:text[1]/@fill" name="DOMAttrModified" newvalue="blue">

Ici, je ne montre que la ligne centrale changée par rapport au message précédent. DOMAttrModified indique qu'on modifie une valeur d'attribut. L'attribut est l'indication de couleur de remplissage (fill) attribuée au premier noeud text trouvé sous un élément d'identificateur S1; la nouvelle valeur donnée à cet attribut est blue
_________________________________________________

<event target="id('S1')/svg:text/text()" name="DOMCharacterDataModified" newvalue="Texte changé">

DOMCharacterDataModified indique qu'on modifie le contenu textuel du premier élément text trouvé sous l'élément d'identificateur S1; la nouvelle valeur est Texte changé
_________________________________________________

Il est possible de groupe plusieurs modifications:
<rex xmlns="http://www.w3.org/2006/rex" >
<event target="id('S1')/svg:path[13]" name="DOMNodeRemove">
<event target="id('S1')/svg:text[1]/@fill" name="DOMAttrModified" newvalue="blue">
<event target="id('S1')/svg:text/text()" name="DOMCharacterDataModified" </rex xmlns="http://www.w3.org/2006/rex" >

Les trois modifications précédentes sont groupées dans un seul message REX.

Pour plus de précisions, le mieux est surement de regarder les 15 pages de la proposition du W3C.

_________________________________________________
_________________________________________________

Implémentation en Javascript

La première chose à faire pour implémenter REX est de disposer de méthodes pour trouver un noeud dans un document à partir d'une expression XPATH supportée par REX. Comme j'avais
connaissance de l'implémentation Open Source de XPATH en Javascript effectuée par Google (voir http://goog-ajaxslt.sourceforge.net/), et que je voulais l'évaluer, j'ai commencé par cette solution. J'y reviendrais surement dans une autre note.

Ensuite, j'ai considéré que Firefox assurant à la fois un support de SVG et de XPATH , il était intéressant d'étudier cette solution. C'est elle que nous allons d'abord commenter du fait de l'élégance et de la compacité qu'elle autorise: la totalité du support de REX pour des documents utilisés avec Firefox prend environ 200 lignes de code Javascript.

La démonstration peut être vue à l'adresse suivante:
http://shadok.enst.fr:8080/modifxml/purMozilla/index.htm

Le code REX utilisé dans l'exemple:
http://shadok.enst.fr:8080/modifxml/purMozilla/commandeRex.html

Le code Javascript pour exécuter les commandes REX:
http://shadok.enst.fr:8080/modifxml/purMozilla/rexfox.js

Le document SVG utilisé dans l'exemple:
http://shadok.enst.fr:8080/modifxml/frame1.svgm
_________________________________________________

Note: les attributs position et timeStamp des commandes REX ne sont pas traités pour l'instant; position va être rapidement rajouté; timeStamp me parait encore insuffisament défini dans le cas général de modifications de documents XML, mais je crois pouvoir en faire une implémentation pour SVG.
_________________________________________________

Obtention de l'objet XMLHttpRequest


function getHTTPObject() {
var xmlhttp;
if (!xmlhttp && typeof XMLHttpRequest != 'undefined')
{
try { xmlhttp = new XMLHttpRequest(); }
catch (e) { xmlhttp = false; }
}
return xmlhttp;
}

Je ne commenterai pas ces 9 lignes de code et renverai aux nombreux tutoriels sur Ajax qu'on peut trouver par exemple à partir de http://en.wikipedia.org/wiki/AJAX.

Nous supposerons que la fonction précédente a permis de récupérer un objet nommé xmlupdater.

XMLHttpRequest n'est pas standardisée. On trouve cependant sur le Web diverses techniques pour utiliser des objets équivalents avec Firefox et Internet Explorer. Le W3C travaille à la normalisation de ce type de technique dans le groupe de travail WEB API (voir http://www.w3.org/2006/webapi/).
_________________________________________________

Obtention de la liste de commande à exécuter

Nous supposons que les commandes sont fournies par un serveur et que d'autres éléments de la page en cours de modification permettent de déterminer l'URL du message REX à exécuter.

L'envoi de la demande de mise à jour depuis le document nécessite les 6 lignes de code suivantes:
function sendRequest(urlcommand)
{
xmlupdater.open("GET", urlcommand);
xmlupdater.onload = handleRexResponse;
xmlupdater.send(null);
}

La gestion spécifique d'une réponse composée d'un message REX est gérée par la fonction handleRexResponse.
_________________________________________________

Exécution du message REX


Cette exécution représente environ 50 lignes de code pour interpréter le message REX et appeler les fonctions qui exécutent les modifications proprement dites. le code complet est accessible sur le site de démonstration.

Par exemple, DOMNodeRemove conduit déterminer le noeud visé à l'aide de la fonction nodesetFromPath, puis à l'appel de la fonction removeNode; c'est à dire la séquence de code suivante:

if(commandType == "DOMNodeRemove")

{

pointedSet = nodesetFromPath(ru.doc, ru.nsResolver, target);

for (var k = 0; k < pointedSet.snapshotLength; k++) {

removeNode(pointedSet.snapshotItem(k));

}

}



Obtenir un ensemble de noeuds à partir d'un xpath

J'ai mentionné dans le paragraphe précédent la fonction nodesetFromPath.
Elle est définie de la façon suivante:
function nodesetFromPath(doc, resolver, target)
{
return ret = doc.evaluate( target, doc, resolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );
}


target est la chaîne de caractère représentant le xpath de l'objet ciblé
doc est la référence du document XML dans lequel on cherche
resolver est une fonction de résolution de namespace (voir ci-dessous)

La méthode evaluate de l'objet doc est une méthode définie dans l'implémentation Javascript de Firefox.

resolver est la référence à une fonction qui permet d'associer un préfixe d'espace de nommage à une chaîne de caractère, pour assurer la réslution des espaces de nommage. Dans le cas de notre exemple, où du SVG est contenu dans du HTML, la fonction est définie comme suit:
function nsResolver(prefix) {
var ns = {
'xhtml' : 'http://www.w3.org/1999/xhtml',
'svg': 'http://www.w3.org/2000/svg'
};
return ns[prefix] || null;
}
_________________________________________________
_________________________________________________

Les problème avec REX

Le premier problème posé par REX est qu'il s'agit de XML. Donc:
- c'est ralativement verbeux,
- c'est un codage textuel peu compact de l'information,
- c'est une technologie inadaptée pour le streaming.

Ces deux problèmes n'ont rien de spécifique à REX et se posent pour l'ensemble des technologies qui s'appuient sur XML. Il faudra attendre les résultats des travaux sur le XML binarisé pour changer cette situation.

Cela n'enlève rien au fait que les propositions ci-dessus proposent une solution générique simple d'emploi pour faire évoluer une scène multimédia SVG A L'INITIATIVE D'ACTIONS SUR LE CLIENT. Il faut en effet que quelquechose -une interaction, un timer...- déclenche l'appel approprié à XMLHttpRequest. Nous verrons dans le prochain paragraphe qu'il existe au moins une solution qui donne l'initiative au serveur.

Le deuxième problème est d'ordre juridique. France Telecom revendique la possession de brevets qui empêcheraient la libre exploitation de REX. N'étant pas expert en brevet, je me limiterai à quelques commentaires sur les brevets en question et les technologies auxquelles ils semblent s'appliquer.

Les brevets que j'ai vu portent sur l'application de commandes de modification à une scène graphique représentée sur un écran. Les commandes prises en considération sont:
- insertion d'un nouvel élément dans la scène,
- modification d'un élément de la scène,
- suppression d'un élément de la scène.
Ces brevets ont été déposés lors de la définition de la technologie BIFS qui est une partie de MPEG-4 (voir http://gpac.sourceforge.net/tutorial/bifs_intro.htm).

Il apparait clairement que tous les documents multimédia peuvent être qualifiés de scènes graphiques. Peuvent être couverts par ce brevet les modifications de documents XHTML et Flash.

Nous voyons également que les commandes REX appliquées à du SVG semblent couvertes par ce brevet.

Les commandes ci-dessus sont les commandes de base de tout éditeur de document: insérer, modifier, supprimer. Si le brevet est valide, il semble que toute édition/modification de document multimédia soit couverte par ce brevet.

Je regretterais beaucoup de ne pas pouvoir utiliser REX et la méthode que j'ai proposée ci-dessus sans négocier des licences avec France Telecom, mais c'est le principe des brevets logiciels.

Je dois surtout mettre en garde tous les développeurs Ajax qui utilisent XMLHttprequest pour insérer ou modifier un morceau d'une page HTML: leur travail est probablement couvert par les brevets de France Telecom.

LASER, une vision complémentaire des scènes multimédia dynamiques

Nous avons vu ci-dessus un assemblage de technologies qui se prête bien à une utilisation simple sur le WEB, mais relativement peu performante. Nous allons introduire ici ce qui pourrait être un développement important de SVG.

SVG et REX sont des grammaires XML et donc des formats textuels.

Dans le cadre de MPEG-4, une application originale de SVG a été proposée sous le nom de LASER. Elle pourrait contribuer de façon significative au développement de l'utilisation de SVG en environnement mobile; son adoption par le 3GPP est envisagée.

En effet, en s'appuyant sur SVG, LASER introduit les possibilités suivantes:
- offrir une représentation compacte binaire de la scène SVG,
- permettre des commandes de modifications du types de celles présentées ci-dessus dans cet article,
- permettre de streamer l'envoi progressif, défini temporellement, de ces modifications.

Nous reviendrons dans une prochaine note sur cette technologie prometteuse.