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:

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…