Cross Browser SVG Using Moo + Raphael + XML for Script

I’m used to a very easy to implement, callback oriented php SAX parser from working with PHP for so long, and I’ve found a number of instances where I found myself wanting that capability in javascript. In addition, I tend to favor Extending objects to callback passing, as you don’t end up messing with scope issues. So what I really wanted was a simple base class I could extend to build whatever I happened to be working on at the moment. So I went out searching for something like this. The only remotely suitable option I found was XML for Script 3.1 which seemed dubious if only for the amount of time since it was last touched. I did a simple test though, and it seemed to be pretty solid. Relating to my earlier fondness for php’s simple 3 function interface made me want less of what I percieved to be an archaic interface, so I set out to build a MooTools wrapper I could then extend in the way I wanted.

// SAX constructor
MidasSAXEventHandler = function() {
this.characterData = "";
}
// My Non-exposed hooks (all existing hooks require fucking with char data
// internally, so we call cleanly out to here to make truly pluggable functions)
MidasSAXEventHandler.prototype.startTag = function(name, attrs){
}
MidasSAXEventHandler.prototype.charData = function(chars){
}
MidasSAXEventHandler.prototype.endTag = function(name){
}
//MidasSAXEventHandler Object SAX INTERFACES
MidasSAXEventHandler.prototype.characters = function(data, start, length) {
this.characterData += data.substr(start, length);
}
MidasSAXEventHandler.prototype.endDocument = function() {
this._handleCharacterData();
//place endDocument event handling code below this line
}
MidasSAXEventHandler.prototype.endElement = function(name) {
this._handleCharacterData();
//place endElement event handling code below this line
this.endTag(name);
}
MidasSAXEventHandler.prototype.processingInstruction = function(target, data) {
this._handleCharacterData();
//place processingInstruction event handling code below this line
}
MidasSAXEventHandler.prototype.setDocumentLocator = function(locator) {
this._handleCharacterData();
//place setDocumentLocator event handling code below this line
}
MidasSAXEventHandler.prototype.startElement = function(name, atts) {
this._handleCharacterData();
var attrs = {};
//place startElement event handling code below this line
for(var lcv =0; lcv < atts.getLength(); lcv++){
attrs[atts.getName(lcv)] = atts.getValue(lcv);
}
this.startTag(name, attrs);
}
MidasSAXEventHandler.prototype.startDocument = function() {
this._handleCharacterData();
//place startDocument event handling code below this line
}
//MidasSAXEventHandler Object Lexical Handlers
MidasSAXEventHandler.prototype.comment = function(data, start, length) {
this._handleCharacterData();
//place comment event handling code below this line
}
MidasSAXEventHandler.prototype.endCDATA = function() {
this._handleCharacterData();
//place endCDATA event handling code below this line
}
MidasSAXEventHandler.prototype.startCDATA = function() {
this._handleCharacterData();
//place startCDATA event handling code below this line
}
// MidasSAXEventHandler Object Error Interface
MidasSAXEventHandler.prototype.error = function(exception) {
this._handleCharacterData();
//place error event handling code below this line
}
MidasSAXEventHandler.prototype.fatalError = function(exception) {
this._handleCharacterData();
//place fatalError event handling code below this line
}
MidasSAXEventHandler.prototype.warning = function(exception) {
this._handleCharacterData();
//place warning event handling code below this line
}
//MidasSAXEventHandler Object Internal Functions
MidasSAXEventHandler.prototype._fullCharacterDataReceived =
function(fullCharacterData){
//place character (text) event handling code below this line
this.charData(fullCharacterData);
}
MidasSAXEventHandler.prototype._handleCharacterData = function() {
if (this.characterData != ""){
this._fullCharacterDataReceived(this.characterData);
}
this.characterData = "";
}

This created my interface into the existing library, and now I need a base class to extend:

MidasXMLParser = new Class({
initialize: function(){
this.buildXMLcomponents();
},
buildXMLcomponents: function(){
this.handler = new MidasSAXEventHandler();
this.parser = new SAXDriver();
},
open: function(tagName, attributes){
var node = document.id('widget_panel');
//var pro = profile(attributes);
var pro = attributes["d"];
node.innerHTML = node.innerHTML + pro +"<br/><br/>";
},
content: function(text){
},
close: function(tagName){
},
parse: function(xml){
this.handler.startTag = this.open;
this.handler.endTag = this.close;
this.handler.charData = this.content;
this.parser.setDocumentHandler(this.handler);
this.parser.setLexicalHandler(this.handler);
this.parser.setErrorHandler(this.handler);
this.parser.parse(xml);
}
});
view raw xml_parser.js hosted with ❤ by GitHub

Now to test this I wanted to parse some XML that was entertaining, and I had just been working with the Raphaël Library and had casually mentioned to a coworker that I supposed it would be possible to draw an SVG using raphael in all 3 browsers, most significantly IE if you could just parse it, and here I was with a pretty simple to implement parser. It couldn’t be that easy…

So I gave it a whirl:

//set up the raphael canvas
var panel = Raphael("widget_panel", 1000, 600);
//setup our own XML Parser
SVGParser = new Class({
Extends : MidasXMLParser,
open: function(tag, attributes){
if(tag == "path"){
//when we find a path, draw it on the raphael canvas
var shape = panel.path(attributes["d"]).attr({
fill:"#000000",
"stroke-width": 1
});
}
}
});

and now we just need to load an svg….

var parser = new SVGParser();
var svgRequest = new Request({
url: 'awesome.svg',
onSuccess: function(responseText, responseXML) {
//once we recieve the SVG, we parse it
parser.parse(responseText);
}
}).send();
view raw load_parse_render.js hosted with ❤ by GitHub

Imagine my surprise staring at an SVG in IE after comparatively little work. Excellent.

I have since been using Raphael, this XML parser, MooTools and jQuery in a much more sophisticated, yet still happy arrangement. Please, check out the demo, which uses Al MacDonald’s low resolution world image. Lemme know if you have questions…

  • Paddy

    Hello! 🙂 Thanks for this post. I have a doubt and i hope you can answer.. how do you mark points on the “low resolution image” created by Al MacDonald using lat/long? I saw a tweet in his profile which says that the map is of Mercator type. However, most of the points that i tried plotting are slightly off by few pixels. Do you have any suggestions at how best to mark points using his map? Thanks for any help! 🙂

    • khrome

      While I don’t have a complete solution for you, I can tell you that mercator maps are elongated along the vertical axis as they move out from the equator, so if you determined the locations of the different cells and an approximate scale for each, that *should* work.

      This map should help: http://www.math.ubc.ca/~israel/m103/mercator.png

      let me know how it works out..

  • Anonymous

    [This is a repost of a comment disqus didn’t import]
    While I don’t have a complete solution for you, I can tell you that mercator maps are elongated along the vertical axis as they move out from the equator, so if you determined the locations of the different cells and an approximate scale for each, that *should* work.

    This map should help: http://www.math.ubc.ca/~israel/m103/mercator.png

    let me know how it works out..