Tuesday, December 21, 2010

Tips in Upgrading Google Map Code From V2 To V3

Since I am kind of stuck on testing for my library, I decide to move to another direction of my project. I need to use my library to rebuild the visualization that I created last semester(Monitor, GeoMap, Stoplight), so that I can compare the code to tell if my library really help the WattDepot web application development. The Stoplight and Monitor are pretty easy to implemented (just retrieve the data and display). You may visit the project hosting page and check it out. The GeoMap visualization is little bit harder, because it contains some nested requests and data manipulation, also because it was using Google Map API version 2 and I need to migrate my code to adapt version 3 now. In this blog post, I will discuss about some changes that I dealt with when upgrading my code with Google Map API V3.

1. Map Object
The most fundamental object in Google Map API is the map object. In V3, MapTypeId is added as the required property to render the map. This property tells the map type is either "RoadMap", "Satellite", or something else. Another thing new in V3 is the way it calculates the zoom level by the bound. In V2, you need to call getBoundsZoomLevel(bound) to get the zoom level and set the map with the zoom level it returned. In V3, you simply need to call fitBounds(bound) and the whole thing is done.

2. Marker Object
In V2, marker object is a subclass of overlay. Once you created a marker, you need to call map.addOverLay(marker) to add marker to the map. In V3, the marker object has a map property. If this property is set, the marker will be added to the map stored in this property. Another minor change is that in V3, it uses markerImage object to replace the icon object in V2.


3. Infowindow Object
In V3, Google Map API introduce the infowindow object. Infowidnow object is basically the dialog box that usually comes up when a marker is clicked. In V2, only one dialog box can be showed in the map at one time and In V3 multiple dialog box can be showed simultaneously. In V2, this effect is invoked by openInfoWindowHtml() function inside an overlay object. You simply pass the html code showing in the dialog box as the parameter to the openInfoWindowHtml() function. In V3, infowindow object has a content property, which manages the content of the dialog box, and it also has a size property, which controls the size of the dialog box. (Note: dialog box size can only be set in size property. In V2, it can be set inside the content.)

By far, these are the changes that affacts my code the most. Upgrading the code to adapt a new version of a library always involves a lot of learning, debugging, and coding. This is exactly how I upgrade my code to use Google Map API V3. First, I found some sample code that using the Google Map API V3 and had a general idea of how much the api was changed. Then I went back to my code and checked which functions are replaced or gone. Last, I used the new way to rewrite the code for those broken functions and debug the program. Hopefully my experience can help some one who just start migrating his/her code to a new version of Google Map API.

Friday, December 17, 2010

Jsunit Try Out

After the basic functionality of my library is implemented, it is the time to add some quality assurance to the project. One of the most important quality assurance methods is unit testing.
However, I was kind of lost because I don't have any unit testing experience in JavaScript web application. At this point, my graduate adviser Dr. Johnson introduced Jsunit to me provided several links to help me get start on it. In this blogpost, I will discuss about something I learned about Jsunit.

Get Jsunit Running:
Jsunit is an open source software for JavaScript unit testing. It is very easy to download. You may simply go to the download page in Source Forge. Inside the Jsunit package, there is a file called "testRunner.html", which is the unit testing user interface. In testRunner page, I can input the path of my testing file in "File" field and click "Run" to run the unit test.
The test result would look like this:
Create a Unit Test:
Writing a unit test in JavaScript is nothing different from writing it in other languages. The only thing need to mention is that, in order to run Jsunit for unit testing, you must include the "JsUnitCore.js" in your test file. (Inside "app" folder) This is the place that contains all the Assert functions.

Problems in Jsunit:
After some experiment in Jsunit, I found some difficulties that I cannot get over with.

1. Test file must locate inside the Jsunit folder or its sub folders, otherwise in testRunner.html, your test file cannot be loaded to the Jsunit. For example, I put my test file in the same level as the Jsunit folder. Then I changed the script tag to:
<script type="text/javascript" src="jsUnit/app/jsUnitCore.js"></script>
Then, when I try to run my test in testRunner, it will tell me time out in loading the test file. However, if I put my file inside the Jsunit folder, and change the script tag accordingly, it works. This is very confusing to me. I have no idea why this happens. Its documentation on how to setup the testing environment is very short and not quite helpful.

2. Jsunit cannot test the AJAX response and its callback. Since my library is heavily related with AJAX, I need to test if my AJAX request will get the correct response. However, in Jsunit, I didn't find a way to test it. I think it is because Jsunit runs the test suite asynchronously, so it cannot wait for the AJAX response. I did some research online about the callbacks testing. Some people suggest using the clock object in "jsUnitMockTimeout.js" to simulate the state of after some time(say 10 secs). I tried it, it only works for the "system.timeout()" thing but not for the AJAX response.

Anyway, I realize that Jsunit is not supported by its developer any more, so I guess I need more research on how to overcome these problems. In all, my opinion is that Jsunit is an acceptable tool for unit testing. I certainly hope I can find another tool to replace it.

Tuesday, December 14, 2010

Something about converting XML to JSON

For the past few weeks, I have worked on several tasks and in the following several blog posts I would like to talk about what I have learned. This time, I am going to talk about something tricky I found in converting XML to JSON.

Actually, there is no universal standard in converting XML to JSON. Every one may have his/her own standard while converting. Here, I will explain the mechanism I used in my conversion function first.

Basic Concept:
Basically, convert Xml to JSON is to parse Xml and translate each node into a JSON object, then nest the JSON objects according to the Xml structure. The most common way to parse Xml is using a recursive function, so that it can recursively go through each node level. For each recursive cycle, the parser normally needs to handle three situations:

Use the following simple xml as an example:
  <book state="new">Lord of the Ring</book>
1. Attributes of an element. In this case, "state".
Only element can have attributes. So the function needs to loop through and save all the attributes before it goes further.

2. Elements with child nodes (nodeType = 1). In this case, "book"(it has a child node which contains the Text of "Lord of the Ring". So create an object with the node name and start parsing all its child nodes.

3. Text of an element (nodeType = 3).
In this case, "Lord of the Ring". So simply return the node value.

A little problem came up:
I followed these rules and my function appears working well. However, when I used it to parse a self-closed Xml element, it failed. For example: Parsing following xml,
  <book state="new" title="Lord of the Ring" />
Interesting discovery:
After some investigation, I found some thing very interesting. It looks like self-closed Xml element will have a leading empty Text node before the element node, which means the parser will get a Text node with a value of " " before the Element node with a value of "book". Since the parser will not continue parsing if the input is a Text node, it will not be able to parse the element node. Therefore, I need to add an condition statement to take care of this situation.

In the end, I posted the xmlToJson conversion function I built. Hopefully, it will help some developers that working on this subject.
 function xmlToJson(xml) {
var obj = {};
// element
if (xml.nodeType == 1) {
// check attributes
if (xml.attributes.length > 0) {
for (var j = 0; j < xml.attributes.length; j++) {
obj[xml.attributes[j].nodeName] = xml.attributes[j].nodeValue;
}
}
}
// check child node
if (xml.hasChildNodes()) {
for(var i = 0; i < xml.childNodes.length; i++) {
// check if the child node has another child node, if not,
// this child node is the text value of the parent node.
if (xml.childNodes[i].nodeType != 3 || i != xml.childNodes.length - 1) {
//check if this node name is existed in obj, if not, add
//this node as a property to current obj
if (typeof(obj[xml.childNodes[i].nodeName]) == 'undefined') {
obj[xml.childNodes[i].nodeName] = xmlToJson(xml.childNodes[i]);
}
else {
//check if there is the array for this node name, if not, create
//an array and add the existing one to the array
if (!(obj[xml.childNodes[i].nodeName] instanceof Array)) {
var old = obj[xml.childNodes[i].nodeName];
obj[xml.childNodes[i].nodeName] = [];
obj[xml.childNodes[i].nodeName].push(old);
}
obj[xml.childNodes[i].nodeName].push(xmlToJson(xml.childNodes[i]));
}
}
else {
//if it is a text node, just return the value. No object created
//for text node.
return xml.childNodes[i].nodeValue;
}
}
}
else {
//if it is a self closed element, it will have a blank text node before parser
//reach the element. Ignore it.
if (xml.nodeType == 3) {
return xml.nodeValue;
}
}
//return this object
return obj;
}