Friday, June 23, 2006

A taste of REX, AJAX and SVG

(C) Jean-Claude Moissinac - ENST - June, 2006

Abstract

The purpose of this note is to present my implementation of REX to dynamically modify SVG documents via the XMLHttpRequest instruction, popularized by AJAX. REX is a simple XML grammar - proposed by the W3C in February 2006 - which allows us to describe modifications we want to bring to an XML document: insertion, replacement or deletion of a sub-document, and attributes change. Thanks to this implementation, with a little bit of JavaScript code, we can make any manipulation of a graphic SVG page through commands stemming from a server.

THANKS: to Berthele Rodriguez and Thierry Arbelot for useful reviews

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 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.

Une version française de cet article se trouve à
http://svgmpeg4.blogspot.com/2006/07/un-zeste-de-rex-ajax-et-svg.html.

SVG

SVG means Scalable Vector Graphics. It is a XML grammar, defined by the W3C, which allows us to describe graphic documents, even animated and interactive. SVG is often perceived as a competitor of Flash. We shall see in a future note how techniques proposed here allow to use a common practice of Flash developers: send an almost empty initial graphic scene then gradually complete it according to the preferences and the interactions of the user, the navigator, the size of the window...

Numerous developers developed their own JavaScript code case by case in order to modify SVG; we shall see below that with a short JavaScript code, we can have a useful generic solution.

AJAX

By naming it, Ajax popularized the method of dynamic publishing Web sites based on the use of the XMLHttpRequest function to get back data via script, allowing to modify one page without reloading it. A common practice is to ask the server for a piece of HTML page through an XMLHttpRequest and bring it in the current page by simple copy. This function gives us an extremely flexible and light method for dynamic pages construction.

This way of working also applies to SVG documents. When navigators support the use of HTML and SVG elements in the same page, we can even manipulate the HTML and the SVG parts by this method. This is the case, for example, of the current versions of Firefox (1.5 when writing this article).

REX, short introduction

The REX proposition is short and clear. We can find it at http://www.w3.org/TR/rex/. REX means Remote Events for XML.

REX uses a subsection of the XPATH syntax to point to the XML tree portion we wish to modify. This subsection is simple to understand, close to the description of a path in a file tree. For example:

/ points to the root of the document

/svg/g[3] points to the third element g directly found as child of the root of the svg document (rem.: g allows to group svg elements).

id('bouton1') points to the node of the tree having the identifier (id) bouton1

id('boutons1')/@fill points to the fill attribute of the element with identifier bouton1

As seen in these examples, the syntax is quickly discovered, at least for the most common uses.

Now that we know how to point to the part of the document to be modified, here are some simple modifications examples:

<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 contains the name of the executed command and DOMNodeRemove let us to delete a node; in this example, DOMNodeRemove is used for deleting a node. We thus delete the thirteenth path node (and its possible sub-nodes) found under the element with the S1 identifier.

_______________________________________ __________

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

Here, I show only the central line changed from the previous message. DOMAttrModified indicates that we modify the value of an attribute. The attribute is the color filling attribute of the first text node found under the element with the S1 identifier; the new value given to this attribute is blue.

_______________________________________ __________

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

DOMCharacterDataModified indicates that we modify the textual content of the first text element found under the element with the S1 identifier; the new value is ‘changed text’.

_______________________________________ __________

We can group several 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" >

In this way, the previous three modifications are grouped in a single REX message.

For more details, your best bet is to look at the W3C proposition (15 pages).

_______________________________________ __________

_______________________________________ __________

Implementation of REX in Javascript

The first step to implement REX is to have methods to find a node in a document from an XPATH expression, REX compliant. My first implementation used an implementation of XPATH from Google (see http://goog-ajaxslt.sourceforge.net/). I would comment this implementation in a future note.

Then, I took into account the fact that Firefox has a support for SVG and XPATH. It was interesting to study this solution. This solution is compact and elegant: the whole REX support for documents used with Firefox takes approximately 200 lines of code Javascript.

The demonstration can be seen at the following address:

http://shadok.enst.fr:8080/modifxml/purMozilla/index.htm

The REX code used in this example:

http://shadok.enst.fr:8080/modifxml/commandeRex.xml

The Javascript code to execute the REX commands:

http://shadok.enst.fr:8080/modifxml/purMozilla/rexfox.js

The SVG document used in this example:

http://shadok.enst.fr:8080/modifxml/frame1.svgm

_______________________________________ __________

Note: the attributes position and timeStamp of REX commands are not implemented at the time being; position is going to be quickly added; timeStamp still seems to be under-defined in the general case of modifications of XML documents, but I believe I can make an implementation for SVG.

_______________________________________ __________

Obtaining the XMLHttpRequest object

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

I shall not comment on these 9 lines of code and point to the many tutorials about Ajax, for example from http://en.wikipedia.org/wiki/AJAX.

We shall suppose that the previous function allowed to get back an object named xmlupdater.

_______________________________________ __________

Obtaining the list of command to be executed

We suppose that a server supplies the commands and that the other elements of the modified page allow determining the URL of the REX message.

The request of the REX message requires the following 6 lines of code:

function sendRequest(urlcommand)
{
xmlupdater.open("GET", urlcommand);
xmlupdater.onload = handleRexResponse;
xmlupdater.send(null);
}

The management of the answered REX message is managed by the handleRexResponse function.

_______________________________________ __________

Execution of the REX message

The execution represents approximately 50 lines of code to interpret the REX message and call the functions, which execute the document modifications. The complete code is accessible on the demonstration site (see below).

For example, DOMNodeRemove begin by determining the targeted node by means of the nodesetFromPath function, then by calling the removeNode function; this is the following code sequence:


if(commandType == "DOMNodeRemove")

{

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

for (var k = 0; k <>

removeNode(pointedSet.snapshotItem(k));

}

}


_______________________________________ __________

Obtain a set of nodes from an xpath

I mentioned in the previous paragraph the nodesetFromPath function.

It is defined in the following way:

function nodesetFromPath(doc, resolver, target)
{
return ret = doc.evaluate( target, doc, resolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );
}

Where target is the string representing the xpath of the targeted object

doc is the reference of the XML document XML in which we look and

resolver is a function for the resolution of namespaces (see below)

The method evaluate of the doc object is a method defined in the Javascript implementation of Firefox.

resolver is the function reference that allows to associate a namespace prefix to a string, in order to resolve namespaces. In our example, where an SVG document is contained in a HTML document, the function is defined as follows:

function nsResolver(prefix) {
var ns = {
'xhtml' : 'http://www.w3.org/1999/xhtml',
'svg': 'http://www.w3.org/2000/svg'
};
return ns[prefix] || null;
}

_______________________________________ __________
_______________________________________ __________

Attention: it is possible that patents apply to the methods exposed in this article