Dynamically updating SVG based on serverside information.

Well, it's in development...

Forever Reloading...

There has been much trumpeting by Macromedia about the ability in the new Flash tools for dynamic updating of pages with reloading/redrawing (see maxify). There's nothing unique to flash in this (or indeed anything new.) this document hopes to describe some techniques you can use in SVG. If you want to update HTML webpages using javascript, my examples on the XML HTTP Request Object will hopefully be useful.

A simple graphing example

I originally created a simple updating graph to demonstrate the technique. This uses a simple graphing library to draw a graph, with the data for each point coming from the server - This is supported by both Adobe SVG Viewer 3.0 and Batik 1.5b2, support for Mozilla SVG can be added.

<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"  onload="init(evt)">
<script type="text/ecmascript" xlink:href="/svg/drawing.js"/>
<script type="text/ecmascript"><![CDATA[
SVGDoc=null
L1=null
count=0
function init(evt) {
 SVGDoc = evt.getTarget().getOwnerDocument();
 LG=new LineGraph(100,100,0,0,30,1000,300,600,"Live updating graph from server data.")
 L1=LG.AddLine('red',0,0)
 setTimeout('go()',5000)
}
function go() {
 getURL('/2002/4/rnd.1',fn)
}
function fn(obj) {
 L1.AddData(++count,+obj.content)
 if (count<30) setTimeout('go()',5000)
}
]]></script>
</svg>

Most of this is just standard SVG stuff /svg/drawing.js is my library of functions. LG=new LineGraph(100,100,0,0,30,1000,300,600,"Live updating graph from server data.") and L1=LG.AddLine('red',0,0) set up the graph - the interesting part is the go() function. This uses Adobes getURL method, which is not part of any standard so this is limited to Adobes SVG viewer and Batik who have implemented it as an extension - when Mozilla SVG becomes more widespread a simple wrapper to the XMLHTTPRequest Object can be created to support Mozilla.

getURL takes two parameters, the URL to retrieve and a function to call when the request completes. The URL, is just a URL, a simple text file, or anything can be there, so if you want to "call a function on the server", all you do is give your script a url (i.e. "http://jibbering.com/2002/4/rnd.1") which then executes any script and returns data back simply by writing text. When using postURL it's the same, however there's a complication because the posted data is not URI Encoded as it is when forms are submitted in HTML webpages. This means that convenience methods like PHP's $elementname do not work, so you have to parse the data direct from the PHP global variable $HTTP_RAW_POST_DATA. Other server side technologies have similar methods.

In my example, the url is /2002/4/rnd.1 this is a simple script, which returns a text/plain document containing a random number. When the resource is loaded the viewer will call the function that is the second parameter of getURL - fn in my example. This function recieves an object as a parameter with 2 properties, contentType and content. L1.AddData(++count,+obj.content) AddData is a method from my drawing.js library which adds a point to the graph with x,y parameters, count is a simple counter for the X values and +obj.content is the data returned converted to a number by the unary + operator. setTimeout then kicks it all of again if the count is still below 30.

The actual updating of the SVG is done in javascript using DOM methods (see http://www.kevlindev.com/dom/ )

Alternative methods

Instead of have the server send just numbers, you can use more structured data,XML, RDF or something else. An approach used by RO IT Systems is to send SVG fragments which the client then parses (using the Adobe extension parseXML method ). This has the effect of simplfying the client code and keeping all logic on the server - upgrading the capability of the system is simply a job of upgrading the server. An example of this technique from RO IT Systems - Live Financial Data Graphic.

The cost of sending SVG is in re-using the server side resource in other enviroments. Another example similar to the above is the Stock exchange feed example which draws graphs of the current stock market indices, and updates over time (30 second intervals) The same server side script is used to provide information to an HTML stock exchange feed demonstration. That still only sends simply structured data, rather than something more structured.

Can Adobes SVG viewer talk XML-RPC or SOAP/RPC ?

Adobes SVG viewer has no ability to set HTTP headers, so whilst it can post (with the postURL() method) it cannot add the required headers. This is not too limiting as you can only talk to your own server so simply not using XML-RPC SOAP/RPC is easy to implement. REST is probably a better model than SOAP in any case so the lack of XML-RPC SOAP/RPC support is not onerous.

Implementing getURL/postURL for Mozilla ( and IE...)

Mozilla has some SVG support, but does not include getURL or postURL however it does have the xmlhttprequest object which can be used to provide them easily.

<script type="text/javascript">
function HTTP() {
 var xmlhttp
 /*@cc_on @*/
 /*@if (@_jscript_version >= 5)
   try {
   xmlhttp=new ActiveXObject("Msxml2.XMLHTTP")
  } catch (e) {
   try {
     xmlhttp=new ActiveXObject("Microsoft.XMLHTTP")
   } catch (E) {
    xmlhttp=false
   }
  }
 @else
  xmlhttp=false
 @end @*/
 if (!xmlhttp) {
  try {
   xmlhttp = new XMLHttpRequest();
  } catch (e) {
   xmlhttp=false
  }
 }
 return xmlhttp
}

if (typeof getURL=='undefined') {
 getURL=function(url,fn) { 
  var xmlhttp=new HTTP();
  if (xmlhttp) {
   xmlhttp.open("GET",url,true);
   xmlhttp.onreadystatechange=function() {
    if (xmlhttp.readyState==4) {
     fn({status:xmlhttp.status,content:xmlhttp.responseText,
      contentType:xmlhttp.getResponseHeader("Content-Type")})
    }
   }
   xmlhttp.send()
  } else {
   //Some Appropriate Fallback...
  }
 }
}
if (typeof postURL=='undefined') {
 postURL=function(url,txt,fn,type,enc) {
  var xmlhttp=new HTTP();
  if (xmlhttp) {
   xmlhttp.open("POST",url,true);
   if (enc) xmlhttp.setRequestHeader("Content-Encoding",enc)
   if (type) xmlhttp.setRequestHeader("Content-Type",type)
   xmlhttp.onreadystatechange=function() {
    if (xmlhttp.readyState==4) {
     fn({status:xmlhttp.status,content:xmlhttp.responseText,
      contentType:xmlhttp.getResponseHeader("Content-Type")})
    }
   }
   xmlhttp.send(txt)
  } else {
   //Some Appropriate Fallback...
  }
 }
}
</script>

Above script in javascript file - getURL-postURL.js