Zend JSON-RPC with Dojo Howto

I have been using the Dojo toolkit as my Javascript library of choice since back in early 2006 when it was still around version 0.4. Since then, the project has made tremendous strides including the release of version 1.0 and 1.1 with 1.2 on the way. At the beginning of 2008 I started using the Zend Framework to build MVC PHP applications and, with the release of 1.5, it has become my PHP framework of choice.

To my delight, the Zend Framework and Dojo have recently announced a partnership which will lead to tighter integration of these two great open source frameworks.

One of the most exciting additions to the Zend Framework is the Zend_Json_Server support for JSON-RPC. I have been using JSON-RPC with Dojo for quite some time and, up until now, it has been challenging to find a design pattern that plays nice with the Model View Controller implementation in the Zend Framework. However, with the addition of the Zend_Json_Server this is no longer the case. Read the rest of this entry »

Automatically Require Dijit Widgets

Recently I have been playing with the dojox.dtl: the javascript port of the Django templating engine. So far I am quite impressed, not only is it fast and full featured, but by writing a wrapper class it is easy to make it behave like server side templating systems: you specify a template and pass it an object and it will render that object according to the template rules.

The only trouble I ran into was when I wanted to used Dijit Widgets in my templates. Since on my main page I didn’t know what template I would be calling, I didn’t know which Widget classes to include with dojo.require(). To fix this, I have come up with a little hack that does the job, although not too elegantly.

Bascially my approach includes hooking into dojo.parser and changing its behaviour. Now when it is parsing widgets from the page it checks to see if they exist and, if not, it does the appropriate dojo.require() to try to pull them in.

var fn = dojo.parser.instantiate;
dojo.parser.instantiate = function(nodes){
    dojo.forEach(nodes, function(node){
        var className = node.getAttribute(dojo._scopeName + "Type")
        if(!dojo.isFunction(dojo.getObject(className))){
            //It is not an object... yet
            dojo.require(className);
        }
    });
    return fn(nodes);
}

The above code-block shows the implimentation. Simply place this in between <script> tags in your header. Now you should be able to do dojotypes anywhere on your page and have the appropriate classes automatically included.

There are several disadvantages to my approach. For example, if you have a typo in one of your dojoType, it will try to pull down a file that doesn’t exist. We also have the impact of running through the array of nodes an additional time, although since the dojo.parser.instantiate function is already bounded by O(n) this shouldn’t make a noticeable impact.

Dojo: Meet Google Book Search

For those of you who read about the re-launching of the SFU Bookswap website (http://sfubookswap.com), you may remember that one of the new features was the integration of the Google Books database.
In this entry I am going to talk about how I integrated the Google Book Search functionality into my existing Dojo framework.

First things first: you need to sign up for a Google AJAX Search API key. You can get your key, as well as a description of the API and documentation from Google here: http://code.google.com/apis/ajaxsearch/signup.html.

Google offers a Search container, which allows the integration of several search sources, including the web, images, blogs, news items and books. I only wanted the Book source so I chose to take the simple route and not use the container class. Instead I used the GbookSearch object directly, which allows me to access the data more easily as well as utilize only the desired functionality.

The very first step in integrating the Google Books search results is including the Google class on your page:

<script type="text/javascript" src="http://www.google.com/jsapi?key=<yourapikey>"></script>

Next, we need to ensure that the Google Search namespace is loaded so we have access to the searching functions. This is as simple as including script tags with the following

google.load("search","1");

Because objects are swell, and will allow us to instantiate multiple searches on the same page, we are going to encapsulate the Google Book Search object in a wrapper object which exposes only the functionality we desire.

dojo.declare("GoogleBookSearch", null, {
    constructor: function(){
		this._bookSearch = new GbookSearch();
		this._bookSearch.setNoHtmlGeneration();
		dojo.connect(GbookSearch,"RawCompletion",this,"resultCallback");
    },
    query: function(q){
        this._bookSearch.execute(q);
    },
    resultCallback: function(){
    	console.debug("Got Data Back, Lets Take A look:");
    	console.dir(this._bookSearch.results);
    	for(var x=0; x<this._bookSearch.results.length; x++){
    		document.body.appendChild(this._bookSearch.results[x].html);
    	}
    },
    goToPage: function(p){
    	this._bookSearch.gotoPage(p);
    },
    getNextPage: function(){
    	this._bookSearch.gotoPage(this._bookSearch.cursor.currentPageIndex+1);
    }
});

The first line of this class (the dojo.declare) creates a new GoogleBookSearch object, which does not inherit from any object (the null parameter), which has several functions and attributes.

The Constructor Function:

The constructor function sets up the search object by creating a new GbookSearch instance. To gain a slight speed improvement, we disable HTML generation, which will prevent the GbookSearch class from downloading images and generating Google’s HTML representation of the search results. Finally we connect the “RawCompletion” event that is fired by the GbookSearch object to a callback function in the local object. After every search query is completed, this event is fired by the global object.

The rest of the functions are fairly self explanatory: the query function accepts one argument, which is the query from the user. The query can be an ISBN, author, title or any other combination of book properties that Google stores in their book database. The resultCallback function processes the data after the GbookSearch class has received search results from the server. Notice that they do not come in the form as an argument to the function but, rather, are accessed by iterating through the _bookSearch.results array. The gotoPage and getNextPage move to the next page of search results (as only 4 are returned by default). No other code is needed, as after getting the results for the next page, the “RawCompletion” event is fired once again.

To use your shiny new class, simply instantiate it after the page has loaded. So, for example:

dojo.addOnLoad(function(){
    myBookSearch = new GoogleBookSearch();
});

Make sure you don’t put a var in front of bookSearch, otherwise the object will have only local visibility, and be removed after the function has executed.

Tips and Tricks:

Modifying the callback function.

In my case, I wanted to have a default callback function that is executed when the search results are returned. However, I wanted separate instances of the GoogleBookSearch object to handle search results differently. To do this, we want to “overload” the resultCallback function using dojo’s hitch functionality.

dojo.addOnLoad(function(){
    myBookSearch = new GoogleBookSearch();
    myBookSearch.resultCallback = dojo.hitch(this,function(){
        if( myBookSearch._bookSearch.results.length>0){
           alert('Showing '+myBookSearch ._bookSearch.results.length+' of '+ myBookSearch._bookSearch.cursor.estimatedResultCount +' Results');
        }
    });
});

The dojo.hitch function accepts two arguments, a function or method name as the second argument, and the scope in which the method executes as the first argument. It is crucial that this scope is set correctly as the default behaviour is to execute in the scope of the GoogleBookSearch object which may be ok if only global or Google BookSearch attributes and functions are used, but is insufficient if we are wrapping the GoogleBookSearch object inside another object and want the callback to be defined as a function in the outer object.

Cleaning Up The Results

When returning the results, if any of the attributes of the book were a keyword in the search, for example, the author field contained a keyword, then the keyword in the author field would be enclosed in bold tags. In my application I am importing data into a database so I want it to be as clean as possible. My solution is using a regular expression to remove all HTML tags. For example

myBookSearch._bookSearch.results[0].authors.replace(/(<([^>]+)>)/ig,"")

Extracting the image of the book cover

You may notice that the book covers are dynamically generated for each query and expire after a short time. My workaround for this is to use regular expressions to parse out the book ID and then use the same (permanent) address that Google does when displaying the book covers on the book details page.
This can be accomplished as follows:

var imgUrl = "http://books.google.com/books?id="+/.*id=([^&]*)/g.exec(myBookSearch._bookSearch.results[0].tbUrl)[1]+"&printsec=frontcover&img=1&zoom=1"

The regular expression extracts the book ID from the thumbnail URL and combines it with the persistent book cover URL

There are a bunch of other attributes stored by the GbookSearch object. Look in the Google API to see a bunch of them or, alternatively, use Firebug to snoop the object after results have been returned.

Happy Coding!

External Links
Google AJAX Search: http://code.google.com/apis/ajaxsearch/
Google AJAX Search API: http://code.google.com/apis/ajaxsearch/documentation/reference.html
Firebug: www.getfirebug.com/

Greasemonkey And Dojo Integration

Dojo (http://dojotoolkit.org/) is a wonderful javascript toolkit which just reached version 1.0 at the beginning of November. I have been watching and developing with Dojo for a couple years now and I can’t tell you how excited I am to have passed the version 1 milestone

Greasemonkey (http://www.greasespot.net/) is a handy Firefox extension which allows the injection of javascript (called userscripts) into the webpage currently being viewed. This allows for the customization of the look and feel of a website: improving the user interface or adding additional functionality.

In this example, we are going to use greasemonkey and Dojo to display a dialog widget on an arbitrary website.

To simplify the deployment of a userscript we are going to use the AOL Content Distribution Network to pull in a crossdomain build of the dojo toolkit. This is helpful to us as developers as we don’t have to build or maintain Dojo ourselves and is useful to the user as they get an optimized download from a distributed CDN.

First thing’s first: If you haven’t already, download and install the Greasemonkey extension, and create a new user script.

To access the power of Dojo, we must include the Dojo crossdomain build on the page we are visiting. To do this, we will dynamically create a new script element containing the address of the build and append it to the document head:

var script = document.createElement(’script’);
script.src=”http://o.aolcdn.com/dojo/1.0.0/dojo/dojo.xd.js”;
document.getElementsByTagName(’head’)[0].appendChild(script);

Next, since we are going to need a Dijit Dialog object, we will need to also include the Tundra Theme CSS file by appending it to the head object:

var link = document.createElement(’link’);
link.rel = “stylesheet”;
link.type= “text/css”;
link.href=”http://o.aolcdn.com/dojo/1.0.0/dijit/themes/tundra/tundra.css”;
document.getElementsByTagName(’head’)[0].appendChild(link);

Great! Now we have the Dojo javascript file included on the page as well as the Dijit Tundra theme setup and ready to use. Next, we will actually include the Dojo dependencies we need and display our Dialog!

To ensure that the Dojo Javascript file and the Tundra Stylesheet have been downloaded and parsed we must include the rest of the code in a function that will run when the window fires the “load” event To do this, we will add an event listener to the window object with the function as an argument:

window.addEventListener(’load’, function(event) {
var dojo = unsafeWindow["dojo"];
dojo.require(”dijit.Dialog”);

Also notice that we have created a local variable called Dojo which is a refrence to the unsafeWindow dojo object. Greasemonkey runs all userscripts in a sandbox which provides enhanced security as well as ensures that they do not conflict with any preexisting scripts already on the page. Because, by default, Dojo is instantiated with a visibility relative to the window object it is not visible to our script. By creating a reference to it in a local variable, this allows us to access its functionality.

Also important is the dojo.require statement. This will pull in the dijit.Dialog object which we will be instantiating later on in our script. To ensure that all the dependences have been loaded before we try to create a dialog, we must wait until dojo knows that the external resource has been loaded.

dojo.addOnLoad(function(){
var dijit = unsafeWindow["dijit"];
dojo.addClass(document.getElementsByTagName(’body’)[0], “tundra”);

var pane = document.createElement(’div’);
pane.id = “floatingPane”;
pane.innerHTML=”Dojo Lives… In GreaseMonkey!”;
document.getElementsByTagName(’body’)[0].appendChild(pane);

dialog = new dijit.Dialog({
title: “Dojo Integration Test”
},pane);
dialog.show();
});
}, ‘false’);

A lot of stuff happens in the above code snippit:
First we grab a local reference to the global dijit object, next we assign a “tundra” class to the body tag, which will allow us to have the correct theme for our Dialog. We create a div node which will eventually become our Dialog, populate it with appropriate attributes and append it to the document’s body tag. Finally we create a new dialog object with a catchy title and the previously created div node as the contents, and display it on screen. Lastly, we close the window.addEventListener object that we created in the previous step.

Downloaded the complete userscript here:
Dojo Integration Userscript

Special Thanks to Shane Sullivan’s Dojo Debug Script which shows how to access the Dojo object from the Greasemonkey sandbox.