In this article I hope to give an overview of a number of interesting techniques used in the construction of the MapSurface prototype. The core of the design is a modular architecture which is built using a combination of on-demand script loading and JSON data transfers.
MapSurface has a widget called the Dashboard which can be loaded into web pages sitting above the general content. It is basically a secondary ‘in context’ interface. I have used this interface to display web page activity information, but it could also be used to provide page level administration functionality.
If you press the “Alt” and “X” keys the MapSurface Dashboard appears. If you are interested, I have written a more general post about the MapSurface features. You can add the functionality of MapSurface to any site, by the inclusion of a single JavaScript file link, in any page you wish to track.
Module Architecture Overview />
/>
The loading file />
When you sign up for a MapSurface Account you are given a JavaScript file link appended with a unique account number
[sourcecode language="html" wraplines="true"] />
type="text/javascript"> />
[/sourcecode]
This file contains code to provide two different functions. The first is to gather as much information about each request as possible. The second is to />
start the construction of the Dashboard when ever it’s requested.
Sending request information to the server />
It sends the request information back to the server based on three events. The page load and unload and the mousedown on any hyperlink. As this information is sent in a fire and forget mode, I have chosen the simplest method of communication with the server, a request for an image. Each request is appended with a querystring.
function msAddImage( passedurl )
{
eltImg = document.createElement( ‘img’ );
eltImg.setAttribute(‘id’,'tackerloadimg’);
eltImg.setAttribute(‘width’,1);
eltImg.setAttribute(‘height’,1);
document.body.appendChild( eltImg ); />
addEvent(eltImg, ‘load’, msRemoveImage , false);
urlcompsite = passedurl + ‘&rand=’ + Math.random();
eltImg.setAttribute("src", urlcompsite);
}
function msRemoveImage()
{ />
if( document.getElementById(‘tackerloadimg’) )
{
eltImg = document.getElementById(‘tackerloadimg’)
eltImg.parentNode.removeChild(eltImg); />
}
}
The image is appended to the bottom of the body element and then a load
event is attached to the new image. The funtion called by this load
event will remove it from the body.
On-demand script loading />
On-demand script loading is a technique where you use JavaScript to add a JavaScript file to the current page. Using this approach you can divide your JavaScript into modules that provide distinct functionality. The initial page load is very fast as you only need the code to provide the on-demand loading. The subsequent loads are also fast as you are only load the smallest required amount of JavaScript.
AddScript function />
There are a number of libraries that have on-demand script loading functionalities, but I have used my own version for a couple of reasons. I have added a random query string attribute to stop the JavaScript being cached. The second difference is that I have removed any checks to see if the file has been previously load. This allows me to overload JavaScript files that contain data.
function msAddScript( url )
{
eltScript = document.createElement("script");
eltScript.setAttribute("type", "text/javascript");
if( url.indexOf(‘?’) > -1)
url += ‘&’;
else
url += ‘?’;
url += ‘rand=’ + Math.random();
eltScript.setAttribute("src", url);
document.getElementsByTagName(‘head’)[0].appendChild(eltScript);
}
Separating presentational code and data />
As each module loads it needs both presentation code to build the interface elements and data to populate them. I decided to split the code and data into different files. This allows me to refresh the data or to display it in more than one way. The data is returned using the JSON format. JSON is a lightweight data format based on a subset of JavaScript. It basically describes an object with a collection of name/value pair’s i.e. {“Total”:345}
.
What no Ajax! />
I could have chosen Ajax as the method of retrieving data from the server, but it does not work across domains. You can only make a XHR (XMLHttpRequest) call from the same domain as the HTML page came from. This security feature limits Ajax and although there are methods around this issue, (server-side proxies) none of these are practical for this application.
Using callback functions />
I have followed the Yahoo model of providing the server API with the ability to specify the name of a callback function. The API’s callback parameter wraps the JSON output in parentheses and a function name. This means as soon as the data JavaScript file loads successfully it call’s a function in the presentation code.
The JavaScript src attribute would be something like;
http://www.mapsurface.com/api/1.0/getURLRequests.aspx?callback=msVPLoadViewsData
which returns a text stream containing
msVPLoadViewsData( {“Total”:345} );
Because JSON output is in a JavaScript object format, you do not have to parse or evaluate the JSON text in your function. The object is just passed directly into the function as in the example below
function msVPLoadViewsData( obj )
{
if( obj == null )
alert(‘load error’ );
else />
alert( obj.Total );
}
Building the interface />
MapSurface is a piece of unobtrusive DOM Scripting which follows the page-hijacking technique. The interface is created by directly appending HTML elements into the DOM. I did consider using the innerHTML property to inject downloaded HTML segments, but I believe that using DOM scripting is a much more flexible approach. I can use one piece of code to produce multiple variants of an interface part i.e. bar charts.
Loading CSS on-demand />
As well as the additional HTML elements created by DOM Scripting I created a single stylesheet for each module. These where loaded using the same technique as the on-demand JavaScript. I created a new link node and appended this to the header of the page.
var newstyle = document.createElement("link");
newstyle.setAttribute(‘rel’,"stylesheet");
newstyle.setAttribute(‘type’,"text/css");
newstyle.setAttribute(‘href’, url);
document.getElementsByTagName(‘head’)[0].appendChild(newstyle);
The backend system />
The backend system stores all the request information sent from the client and where possible extends what has been collected by the browser. The API calls return JSON, but can be configured to output RPC-XML. I have provided this additional format in case I wished to extend the functionality of the site.
URLs of interest />
http://www.crockford.com/JSON/ />
http://developer.yahoo.net/common/json.html />
http://ajaxpatterns.org/On-Demand_Javascript
Note />
I will be developing different commercial applications from these concepts through my company Madgex. I am unsure about the path of the MapSurface prototype itself, if there is enough interest it could be developed into a commercial service. So for now, apart from the code display on this page I am not making the MapSurface code open source.