main page - page initiale - hauptseite - página principal projects - projets - projekte - proyectos authors - auteurs - autoren - autores papers - exposés - berichte - papeles help - aide - hilfe - ayuda

carto:papers:svg:examples:serverside:network requests

Network Requests with getURL()/postURL() and XMLHttpRequest() (Version 1.1, 2007-05-01 - see history at the end of the page)

Example provided by Andreas Neumann

This ECMAScript object (getData) should assist SVG developers to do network requests to fetch text or XML data on request, without having to reload the SVG file. This functionality is also known as the buzzword "Ajax" (Asynchronous Javascript and XML) ... However, this is nothing new and has been around for many years, both in the Adobe SVG viewer (since 2000) and in Microsoft's Internet Explorer (since version 5), which first introduced the XMLHttpRequest object, albeit as an ActiveX component. Internet Explorer 7 can do it natively without the hassle of the ActiveX component.

To complicate things a bit, not all SVG user agents support the same network request methods. The getData object is therefore a wrapper object around the three methods getURL(), postURL() and XMLHttpRequests(). The author can just use the wrapper function and does not have to care which function is actually available or used. getURL() and postURL() is supported in Apache Batik and the Adobe SVG viewer, XMLHttpRequest() is implemented in all webbrowsers. This object can be used under the terms of the LGPL license.

See an example of the getData object. This example asks the web server for its current time and displays it in a text element (left text). This is done using the get method of the object. Alternatively, a demonstration of the post method transfers the client's date to the server and the server sends this information back to the client (right text).

Documentation

Features

Dependencies on external files and functions

  1. helper_functions.js:
    • all the global variables at the top of the file
    • object getData, including all its (prototype) methods

Step by Step Instructions

Step 1: Download and link the file helper_functions.js in your svg file

Download the helper_functions.js and place it in the same directory where your svg file resides in (or adopt pathes as appropriate). Link the javascript file using the following syntax:

	<script type="text/ecmascript" xlink:href="helper_functions.js" />
Step 2: Create an instance of the getData object

The following code creates an instance of the getData object. Place this code in the appropriate position of your javascript function or object. The following example uses the get method.

var getDataObj = new getData(url,callBackFunction,returnFormat,method,postText);

Constructor of the getData object in order of the parameters to be passed over:

Useful Properties of the getData Object

Examples:

var getDataObj = new getData("myWeatherData.xml",parseWeatherData,"xml","get",undefined,undefined);
//preparing the get request
var currentYGet = 80;
var baseUrlGet = "senddatetime.php?currentY=";
var getDataObj = new getData(baseUrlGet,receiveData,"xml","get",undefined,undefined);
//preparing the post request
var currentYPost = 80;
var postDataObj = new getData("sendBackClientTime.php",receiveData,"xml","post","",undefined);

The second and third example are preparing the get and post requests in our example file. Note that creating the object instance does not yet trigger the network request.

Step 3: Triggering the .getData() method of the getData object

When creating the instance of the getData object, the network request is not immediately made, until the .getData() method is called. The idea being that the same object instance can be used to repeatedly trigger network requests, potentially changing URL get parameters or the .postText property in between. The following 2 examples trigger the get and post requests in our example:

//calling the method .getData() of the getDataObj instance
//this is triggered by the click event on the left text element
currentYGet+=20;
getDataObj.url=baseUrlGet+currentYGet;
getDataObj.getData();
//calling the method .getData() of the getDataObj instance
//this is triggered by the click event on the left text element
currentYPost+=20;
var curDateTime=new Date();
postDataObj.postText=currentYPost+'$'+curDateTime.toGMTString();
postDataObj.getData();

Note that currentYPost and currentYGet are increased by twenty units each time the network request is made. This information is passed to the PHP script (server) and used for creating the response.

Step 4: Create the resource to be requested (often a dynamic serverside script or application)

In the simplest case the resource to be loaded with the getData network request is a static XML or text file. Often, it is a serverside scriping application or CGI program. Below is a simple PHP code (of our example) processing the get request:

<?php
header('Content-type: text/xml');
$currentY = $_GET['currentY'];
print '<g xmlns="http://www.w3.org/2000/svg">'."\n";
print "\t".'<text x="10" y="'.$currentY.'" font-size="15">'.date("M d Y H:i:s",time()).'</text>';
print "</g>\n";
?>

This code reads currentY from the get request and then sends a SVG snippet back containing a text element with the current date of the server and the currentY value for the text value (which is increasing whith each request). Also not the text/xml content type and the use of the SVG namespace, which is required that the client correctly determines the namespace of the XML fragment received. In case of a SVG <g/> (group) element or other grouping structure, only the parent element needs to include the SVG namespace. The following PHP code processes a post request:

<?php
header('Content-type: text/xml');
list($currentY,$clientDate) = explode("$",$HTTP_RAW_POST_DATA);
print '<g xmlns="http://www.w3.org/2000/svg">'."\n";
print "\t".'<text x="500" y="'.$currentY.'" font-size="15">'.$clientDate.'</text>'."\n";
print "</g>\n";
?>

Compared to the get request, the whole post textstring resides in one variable, not every parameter separately, as with the get variables.

Step 5: Create the callBack function

Finally, we need to create the callBack functions which are triggered when the web browser receives the data he requested from the resource. In our example we requested XML data chunks in both cases. This means that our callBack functions receive ready to use XML nodes. Because the PHP scripts return a chunk of SVG (text elements) we can directly use these SVG elements and add them to the DOM tree of the SVG document. This is done by the following callback function found in index.svg:

function receiveData(node) {
	document.documentElement.appendChild(node);
}

In this case we receive the SVG <text/> element and append it in the root element of the document, which means at the end of the file.

Discussion

Note that the XMLHttpRequest() can do much more besides requesting data with the get and post method. As an example, one could monitor download progress for larger resources that are loaded, using the progress event. Additionally, one could create, rename, move and delete resources on a web server, assuming that the web user has the permissions to do so. See the W3C XMLHttpRequest() specification for a full list of options.

It is also important to know that for security reasons, network requests (at least in the case of getURL/postURL) are restricted to the same resource where the currently loaded SVG files came from. One cannot load the SVG file from server A and then do network requests to server B. If one needs data from server B, server A has to act as a proxy server and request the data from server B and forward it to the client.

Other Resources:

The SourceCode for the getData wrapper object

This code is provided for the curious who would like to know how the getData wrapper object works:

/**
 * Wrapper object for network requests, uses getURL or XMLHttpRequest depending on availability
 * The callBackFunction receives a XML or text node representing the rootElement
 * of the fragment received or the return text, depending on the returnFormat. 
 * See also the following <a href="http://www.carto.net/papers/svg/network_requests/">documentation</a>.
 * @class this is a wrapper object to provide network request functionality (get|post)
 * @param {String} url												the URL/IRI of the network resource to be called
 * @param {Function|Object} callBackFunction						the callBack function or object that is called after the data was received, in case of an object, the method 'receiveData' is called; both the function and the object's 'receiveData' method get 2 return parameters: 'node.firstChild'|text (the root element of the XML or text resource), this.additionalParams (if defined) 
 * @param {String} returnFormat										the return format, either 'xml' or 'json' (or text)
 * @param {String} method											the method of the network request, either 'get' or 'post'
 * @param {String|Undefined} postText								the String containing the post text (optional) or Undefined (if not a 'post' request)
 * @param {Object|Array|String|Number|Undefined} additionalParams	additional parameters that will be passed to the callBackFunction or object (optional) or Undefined
 * @return a new getData instance
 * @type getData
 * @constructor
 * @version 1.0 (2007-02-23)
 */
function getData(url,callBackFunction,returnFormat,method,postText,additionalParams) {
	this.url = url;
	this.callBackFunction = callBackFunction;
	this.returnFormat = returnFormat;
	this.method = method;
	this.additionalParams = additionalParams;
	if (method != "get" && method != "post") {
		alert("Error in network request: parameter 'method' must be 'get' or 'post'");
	}
	this.postText = postText;
	this.xmlRequest = null; //@private reference to the XMLHttpRequest object
} 

/**
 * triggers the network request defined in the constructor
 */
getData.prototype.getData = function() {
	//call getURL() if available
	if (window.getURL) {
		if (this.method == "get") {
			getURL(this.url,this);
		}
		if (this.method == "post") {
			postURL(this.url,this.postText,this);
		}
	}
	//or call XMLHttpRequest() if available
	else if (window.XMLHttpRequest) {
		var _this = this;
		this.xmlRequest = new XMLHttpRequest();
		if (this.method == "get") {
			if (this.returnFormat == "xml") {
				this.xmlRequest.overrideMimeType("text/xml");
			}
			this.xmlRequest.open("GET",this.url,true);
		}
		if (this.method == "post") {
			this.xmlRequest.open("POST",this.url,true);
		}
		this.xmlRequest.onreadystatechange = function() {_this.handleEvent()};
		if (this.method == "get") {
			this.xmlRequest.send(null);
		}
		if (this.method == "post") {
			//test if postText exists and is of type string
			var reallyPost = true;
			if (!this.postText) {
				reallyPost = false;
				alert("Error in network post request: missing parameter 'postText'!");
			}
			if (typeof(this.postText) != "string") {
				reallyPost = false;
				alert("Error in network post request: parameter 'postText' has to be of type 'string')");
			}
			if (reallyPost) {
				this.xmlRequest.send(this.postText);
			}
		}
	}
	//write an error message if neither method is available
	else {
		alert("your browser/svg viewer neither supports window.getURL nor window.XMLHttpRequest!");
	}	
}

/**
 * this is the callback method for the getURL() or postURL() case
 * @private
 */
getData.prototype.operationComplete = function(data) {
	//check if data has a success property
	if (data.success) {
		//parse content of the XML format to the variable "node"
		if (this.returnFormat == "xml") {
			//convert the text information to an XML node and get the first child
			var node = parseXML(data.content,document);
			//distinguish between a callback function and an object
			if (typeof(this.callBackFunction) == "function") {
				this.callBackFunction(node.firstChild,this.additionalParams);
			}
			if (typeof(this.callBackFunction) == "object") {
				this.callBackFunction.receiveData(node.firstChild,this.additionalParams);
			}
		}
		if (this.returnFormat == "json") {
			if (typeof(this.callBackFunction) == "function") {
				this.callBackFunction(data.content,this.additionalParams);
			}
			if (typeof(this.callBackFunction) == "object") {
				this.callBackFunction.receiveData(data.content,this.additionalParams);
			}			
		}
	}
	else {
		alert("something went wrong with dynamic loading of geometry!");
	}
}

/**
 * this is the callback method for the XMLHttpRequest case
 * @private
 */
getData.prototype.handleEvent = function() {
	if (this.xmlRequest.readyState == 4) {
		if (this.returnFormat == "xml") {
			//we need to import the XML node first
			var importedNode = document.importNode(this.xmlRequest.responseXML.documentElement,true);
			if (typeof(this.callBackFunction) == "function") {
				this.callBackFunction(importedNode,this.additionalParams);
			}
			if (typeof(this.callBackFunction) == "object") {
				this.callBackFunction.receiveData(importedNode,this.additionalParams);
			}			
		}
		if (this.returnFormat == "json") {
			if (typeof(this.callBackFunction) == "function") {
				this.callBackFunction(this.xmlRequest.responseText,this.additionalParams);
			}
			if (typeof(this.callBackFunction) == "object") {
				this.callBackFunction.receiveData(this.xmlRequest.responseText,this.additionalParams);
			}			
		}		
	}	
}

Version history




Last modified: Tuesday, 01-May-2007 11:53:06 CEST
© carto:net (andré m. winter & andreas neumann)
original URL for reference: http://www.carto.net/papers/svg/network_requests/index.shtml
del.icio.us Add this page to del.icio.us