See also:MyDungeonRunner (how to add the gadget to your blog)
This Might Be All You Really Want
There’s all the javascript required to parse a Dungeon Runners’ XML file into javascript arrays.
Odds are, you don’t need my little tutorial here the least bit whatsoever, and are just looking for some pre-written script because you don’t want to rewrite it, rather than because you don’t know how.
Fine.
But you will miss-out on my description of the arrays and have to rely on the comments in that file if you leave now… Those very thorough comments…
Still Here? Ok! I Shall Continue…
I am now going to embarrass myself by revealing some exceptionally sloppy work. This is just one example of how far I am willing to go to help you.
First, let’s get some additional things open in new windows so you can glance over at ‘em from time to time.
View the XML character data we’ll be using.
Dungeon Runners character XML is located at this URL:
var charName = "Dundee"; var url = http://www.dungeonrunners.com/characters/" + charName;
Open that link in a new tab or window, then view the page source, and I’ll explain briefly what you are looking at.
<?xml version="1.0" encoding="ISO-8859-1"?>
Ok, first line of the file identifies it as an XML file. Perfect!
And for now, let’s skip over the line with <DRCharacter Version=“1″>. I’ll explain why in an hour or two.
Let’s get to the character data. I’m going to snip-out some of the actual data for the sake of space.
<Name>Dundee</Name> <Gender>Male</Gender> <Level>9</Level> <Title>Toned Rookie White Paladin</Title> <PVPWins>0</PVPWins> <Appearance> ... </Appearance> <Attributes> ... </Attributes> <Combat> ... </Combat> <Skills> ... </Skills> <Equipment> ... </Equipment>
As you can see, the data we want is in appropriately named lavels. The character’s name is found in <Name></Name>, his gender is in <Gender></Gender>, and so on.
Those are going to be the names by which we reference the data, once we’ve loaded it all into an array.
If we call that array charData, then the number of PvP battles the character has won, for example, will be stored in charData [PVPWins]. Ha! Zero!
You’ll also notice that most of the data is more complex than that. Let’s look at his Attributes.
<Attributes>
<Strength>
<Innate>28</Innate>
<Effective>51</Effective>
</Strength>
<Agility>
<Innate>19</Innate>
<Effective>36</Effective>
</Agility>
<Endurance>
<Innate>23</Innate>
<Effective>34</Effective>
</Endurance>
<Intellect>
<Innate>10</Innate>
<Effective>8</Effective>
</Intellect>
<HP>1436</HP>
<Mana>164</Mana>
</Attributes>
As you can see, characters have multiple attributes, and furthermore some of those attributes are composed of multiple values. A character’s strength attribute has both an innate and an effective value, for example, whereas his mana is a single number.
Again, these are the names by which we’ll be referencing this data, once we’ve loaded it into the charData array.
Here’s how: One of the character’s Attributes is Strength, which consists of Innate and Effective values. We’ll reference charData['Attributes']['Strength']['Innate'] to find that his innate strength is 28, and charData['Attributes']['Strength']['Effective'] to get the effective strength value of 51.
Similarly, one of the character’s Attributes is HP. When we need to know how many hit points he has, we’ll find that charData['Attributes']['HP'] is 1436.
Using a widget framework
We want to use the above data to display a character in widget. There are many, many, many widget frameworks from which to choose. All of them are slightly different, in as much as you won’t be able to just paste Google gadget code into a Yahoo! widget and expect it to work.
In fact, you could write your own widget entirely in javascript. The advantage of using a framework like Google gadgets is that a lot of work has been done for you: setting user preferences, providing a simple ‘add this to your page’-interface, some of the layout, and so on.
For a very short example, here’s a javascript function to fetch an XML file:
var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
function loadXML(xmlFile) {
xmlDoc.async="false";
xmlDoc.onreadystatechange=verify;
xmlDoc.load(xmlFile);
ticker=xmlDoc.documentElement;
}
function verify() {
return (xmlDoc.readyState == 4)
}
But our Google gadget will be using this function instead:
_IG_FetchXmlContent(url, function (response)
In addition to fetching the XML file for us, the Google function also handles caching the data, so that we’re not hitting the server for the exact same file with every single view, and which also means the environmental impact on the fragile ecosystem of South America’s precious rain-forests is virtually non-existent.
Whichever function is used, what you’ll get back is the XML data in a DOM object. If you know what that is, how to deal with it, and don’t find it enraging to do so - then why are you reading this?
The first and last thing we’re going to do with that DOM object is convert it to a javascript array. Presumably javascript is what you’re using to write your widget regardless which, if any, widget framework you choose, and if not then the question is - once again - why are you here?
The Google Gadget Editor
I’m going to explain how all this works in my Google gadget, but the purpose of this tutorial is not really to explain how to make a Google gadget. Google has already provided some excellent instruction in that regard.
We’ll be focusing on the javascript section, which I have extracted and formated for you to open in a new window. If you do that now, then you won’t have to come back looking for this link later.
Additionally, you may want to open the gadget source code in the gadget editor.
As Google gadgets are public, you can look at the entire gadget mess and even change it if you like. Any of your changes will be considered a different gadget altogether, which may be good (like, if you break it - because you won’t be breaking the one some people are actually using), but it also means that if you make any improvements then those won’t be applied to my gadget. To see them in action, you’d have to add your (altogether different) gadget to a website, rather than mine.
Ok, so if you want to do this, open the GGE in a new window. Pull-down the GGE file menu, and select Open from Url.

MyDungeonRunner’s URL is right here:
http://hosting.gmodules.com/ig/gadgets/file/116650004683272111876/my_dr.xml
Once the file is loaded, you’ll be able to view the gadget source code in the editor, make changes willy-nilly, and even see your improvements on-the-fly by clicking the Preview tab.
Notice that the editor likes to steal your code’s indents (Google employs a lot of software engineers, and their appetite for tabs is insatiable). So although you can make changes and see them execute right there, it’s also a bit of an eye-sore.
Now aren’t you glad I gave you that link to the javascript all beautifully indented?
UserPrefs
If you’ve checked out the gadget code, then you’ll notice I am not skipping over the ModulePref section. If you are not using GGE for your widget, these sections will not apply to you (but for some other widget framework, something equivalent to it, might exist). If you are using GGE to create a widget, then I urge you to read Google’s “getting started” docs on Gadgets for anything there that confounds you.
I do want to touch on UserPrefs for a moment. Though these are Google gadget specific, they are referenced later in the javascript we’re going to run through.
The options I have exposed to users are these:
<UserPref name="fetchName" display_name="Character Name" default_value="Anson" /> <UserPref name="backImg" display_name="Background Image" default_value="http://hosting.gmodules.com/ig/gadgets/file/116650004683272111876/dr_guy.png" /> <UserPref name="brightText" display_name="Bright Text" default_value="#FFFFFF" /> <UserPref name="dullText" display_name="Dull Text" default_value="#cecece" /> <UserPref name="iconBaseURL" display_name="Icons folder" default_value="http://mythicalblog.com/dungeonrunners/icons/" />
The first and most important enables the user to specify the name of the character he wishes to display.
Next, the user may set the background image to one of his own choosing.
Because my default image is dark, the default text colors that I use are white and light-gray. However, having allowed the user to change the background image to something else, I then felt it necessary to allow the user to change those text colors as well. If the user opts for an image that is very light, then he will be able to change the text to something dark.
Lastly, since the Dungeon Runners’ developers allow users to host the icon images on their own servers, users must be able to tell MyDungeonRunner where that is.
You’ll notice that the default value for the image icons is not the official location, but instead is my own website here. The only reason for that is to work around some bugs with the image names on the official site, and I’ll change it to default to the official location when those bugs are fixed. More on that later.
If you are not using GGE to create a gadget, you’ll need to decide which user options are necessary and how to enable your widget’s users to edit the options. Like I said, one of the nice things about widget frameworks is that they tend to handle that sort of thing for you.
Likewise, if you are not using GGE, then the javascript which reads these user preferences isn’t going to work for you either.
I Have Almost no Style
Ok, we are almost there. There’s just one last thing before we arrive at the script.
I have a tiny little style section in my gadget, of which I have just barely made use. This is a really horrible thing for me to have done, and you should never, ever do something like this.
I just… uhm… wanted to show you what not to do.
Yeah… yeah, that’s the reason, right there.
Throughout my javascript, you’ll see things like this:
"<td style='background-color:red;color:white;font-weight:bold;text-align:center'>" + charData['Attributes']['HP']+ "</td>";
Sometimes, you’ll find multiple lines all in a row with the very same values for style.
What you should do, to demonstrate that you are a better person than I am, is to add an id section to your style section, somewhat like this:
#hp {
background-color:red;
color:white;
font-weight:bold;
text-align:center;
}
Then instead of repeating all of the above text every time you wish to use that format, you’d assign that id, instead.
"<td id='hp'>" + charData['Attributes']['HP']+ "</td>";
Much better, eh? So, you should do that, and not what I did.
But I should also say that this in no way is an admission on my part that CSS does not, in fact, still suck. While it is better than the alternative, you will surely make this discovery yourself soon enough, when the id you are assigning to a thing is for no discernible reason being ignored or overridden by the very worst possible format, and you cannot for the life of you figure out how to simply make this thing use that format.
Do not ask for my assistance with that crap. I don’t know why it’s doing that, either. I don’t even know if it’s fair to blame CSS for there not being an affordable WYSIWYG html editor after all these decades of it sucking to format web pages. I only know that it might be.
Seeing What is Going On
Eventually, you are going to omit a semi-colon or leave off a curly brace or otherwise misplace one of the symbols on your keyboard which are not part of any human language. When that happens, you will want something to be able to provide you with a hint as to where and what might be the problem.
Since I use the Firefox web browser, I use the Firebug add-on to view the javascript console, style sheets, html source, and even to test CSS changes on-the-fly (to see if they’re going to work like they should, or if nope! CSS sucks). I highly recommend it.
If you are not using Firefox, then there’s something similar out there for your browser, probably, and I wish you the best of luck in finding it.
In the meantime, if the prospect of having different browsers that you use for different things is not incomprehensible to you, then you could download and use the above anyway. And you could also grant me three wishes! ‘Cause if that’s true then you are obviously some sort of mystical creature.
XML2Array - the DungeonRunners Edition
Remember the XML data and the javascript? This is a post about them.
We’re going to display character data, so getting the character data is about the first thing we need to do.
We have the user’s character name stored as a user preference, and the Dungeon Runners character data URL is a constant. I don’t know why I put it in a variable instead of a CONST, but I did.
var fetchName = prefs.getString("fetchName");
var url = "http://www.dungeonrunners.com/characters/" + fetchName;
Regardless how you choose to deal with user preferences or to fetch the XML data, the value of url there is where you need fetch it from.
The resulting DOM object, called response, (which contains the XML data), is immediately converted to a javascript array, by passing it to a function called xml2array.
charData = xml2array(response); charData = charData['DRCharacter'];
Remember when I said to forget about the DRCharacter label? Well right there is the reason why. If you’ll look at the XML data, you’ll notice that every other bit of data is nested within the DRCharacter label. The character name, for example, is actually at charData['DRCharacter']['Name'], the effective strength value is charData['DRCharacter']['Attributes']['Strength']['Effective'] and so on.
Since none of the data we need exists outside of the DRCharacter label, and I’m lazy and would prefer not to type that every time I reference a value, that second line after calling xml2array immediately thows the whole thing up to the root of CharData. So now the name is just charData['Name'], and so on.
The one thing we’re not storing in the array, is the value of the version attribute in DRCharacter. At the moment, there’s no need for it.
So commit this to memory. The version number is:
response.getElementsByTagName("DRCharacter").item(0).getAttribute("Version");
Or multi-line, so you can read the whole thing:
response.
getElementsByTagName("DRCharacter").
item(0).
getAttribute("Version");
And that’s why I stuffed all the data I wanted to access into an array.
About Those Attributes
You might have already noticed a problem with using the labels as our array key names. What about where there are multiple labels with the same name? Or are wondering, what about attribute values such as SlotID=”105″?
<Skills> <Skill SlotID="105"> <Name>Butcher</Name> <Level>1</Level> <Icon>butcher</Icon> </Skill> <Skill SlotID="108"> <Name>Gym Freak</Name> <Level>1</Level> <Icon>HealthNut</Icon> </Skill> </Skills>
The xml2array() function returns multiple labels in a numeric array, so the above would be in charData['Skills']['Skill'][0] and charData['Skills']['Skill'][1] respectively.
That is, the value of charData['Skills']['Skill'][1]['Icon'] is going to be HealthNut.
Additionally, attribute values (such as 105 for SlotID) are added with the attribute name as the array index. charData['Skills']['Skill'][0]['SlotID'] will be 105.
Now forget all about that, because the next thing the script does is to put skill data into its own array, called skillData, and likewise for weapon, skill, and equipment data.
Yes, again because I am lazy and skillData is easier than typing charData['Skills']['Skill'] every time., but also for a good reason.
See, apart from skills, those attribute values, such as SlotID=105, are what we actually need as our keys. We’ll be looking for which item is his helmet? and what are the stats of his primary weapon?
Maybe there’s some better way of handling that… Just grabbing the next piece of data, figuring out what it is, and then doing something with it regardless what you’ve already done or not done… but I don’t approve of such things.
So there’s this, a set of constants already defined:
const ITEM_NECK = '1'; const ITEM_BRACER = '2'; const ITEM_LEFT_RING = '3'; const ITEM_RIGHT_RING = '4'; const ITEM_HELM = '5'; const ITEM_TORSO = '6'; const ITEM_BOOT = '7'; const ITEM_SHOULDER = '8'; const ITEM_OFF_HAND = '9'; const ITEM_PRIMARY_HAND = '10'; const ATTACK_PRIMARY = 'primary'; const ATTACK_SECONDARY = 'offhand';
And with this bit of code, the skillData, itemData, and attackData arrays are loaded, such that the above consts may be used to reference the appropriate value.
for ( var index = 0; index < 2; index++ )
attackData[ charData['Combat']['Weapon'][index]['hand'] ] =
charData['Combat']['Weapon'][index];
for ( var index = 0; index < 10; index++ ) {
if ( charData['Equipment']['Item'][index] )
itemData[ charData['Equipment']['Item'][index]['SlotID'] ] =
charData['Equipment']['Item'][index];
};
for ( var index = 0; index < 10; index++ ) {
if ( charData['Skills']['Skill'][index] )
skillData[ charData['Skills']['Skill'][index]['SlotID'] ] =
charData['Skills']['Skill'][index];
};
So now it’s attackData['primary'] and attackData['offhand']. Not only lazy-accessible, but also indexed by the key I feel comfortable using.
Mind, the data is also still contained within charData, too, but if a person’s computer can’t spare the memory then they can’t play the game anyway.
2 Comments
Did you consider any DOM or Xpath implementations? I prefer xpath for all XML work, but DOM would fit more naturally with javascript which uses DOM for the main html page navigation.
It would seem a lot easier
Thing is, finding what a character’s effective strength value is or the like - it’s just torture for me to dig that out of anything but an array structure like this. I just don’t have the competency for it.