Login Register

SitePen

Syndicate content
SitePen Services and notes about Dojo, DWR, Cometd, JavaScript, and the Web
Updated: 2 days 3 hours ago

Amazon S3 + Dojo

Thu, 07/03/2008 - 07:03

Dojo’s improved RPC and new REST services can be used with a wide array of web services. One particular use is Amazon’s Simple Storage Service (S3). Dojo can connect to Amazon S3’s with a trivial proxy and be used as a normal RPC service. Not only that, Amazon S3 is a REST service and therefore it can be used as a data store with Dojo’s new REST implementation of dojo.data, JsonRestStore. You can read and write to your S3 database using the convenient Dojo Data API, and use the data store in Dojo widgets.

s3store.png

Getting Started

To get started with Dojo + S3:

  • First, you must be signed up to use Amazon S3.
  • Dojo uses a simple proxy to communicate with S3 (doing so directly from the browser would be insecure). The proxy PHP script, proxy.example-php, located in /dojox/rpc/s3/, must be renamed to proxy.php to be used.
  • The proxy requires PHP 5 (other languages hopefully will be added) and the following modules:
  • Next you must enter your Amazon access key and secret access key into the proxy.php file on line 3 and 4:
    $accessKey = "access key";
    $secretAccessKey = "secret access key";

Now we can start using Dojo services to connect to S3. You use the Dojo RPC service with the “PROXIED-PATH” envelope:

dojo.require("dojox.rpc.Service");
dojo.require("dojox.rpc.ProxiedPath");
var s3Buckets = new dojox.rpc.Service({
	target:"http://s3.amazonaws.com/",
	proxyUrl:"../s3/proxy.php", // the path to the proxy
	transport:"REST",
	envelope:"PROXIED-PATH",
	contentType:"application/json",
	services:{
		myBucket:{ // name of the service (usually the bucket)
			target:"myBucket", // enter you bucket here
			parameters:[{type:"string"}]
		}
	}
});

To use the S3 as a Dojo data store you can use the S3Store module. First setup an RPC service as shown above and then pass the RPC service to the S3JsonRestStore:

dojo.require("dojox.data.S3Store");
// and create a store for it
s3Store = new dojox.data.S3Store({service:s3Buckets.myBucket});

You can then use the s3Store as a normal Read/Write Dojo Data store. And since the S3Store is an extension of the JsonRestStore, you can use all the additional features of that store including fast, compact syntax, referencing, and offline capabilities are coming soon. With the S3Store you can build sophisticated apps with very thin server architecture; UI logic can be handled on the browser, and database storage can be delegated to Amazon’s S3. This is just one of the many new features available with Dojo 1.2.

Dojo In 6K

Wed, 07/02/2008 - 01:44

Some sites can defer most, if not all, of their JavaScript-driven progressive enhancements until well after the page has loaded. Even so-called “lightweight” libraries like JQuery are far too heavy for some environments…not because they (like Dojo) pull in all the code needed to use them, but because they do it all up-front. Often the best time to pay the expense of loading, parsing, and executing JavaScript code is when the user takes an action that needs the enhancement to run. Dojo already gives you the best tools available anywhere to defer loading modules until you actually use them; other than those provided by dojo.js itself…but what about dojo.js? What if even the small size of Dojo is too big for your page? This need to load as fast as possible and defer work is never greater than in mobile applications. The best-performing thing to do is always to hand-write all the code you’ll need and never use more script than that…but there are complications. Not being able to share your code between developers (let alone between pages) can be a huge disadvantage. The code is also likely to be slow, buggy, and unmaintainable. There’s a real reason why toolkits like Dojo are so incredibly popular: they make much of this pain go away at the cost of initial download size. Why would anyone want to invite the pain back?

So we need some middle ground: a common, expected API surface area combined with a trivial initial footprint to ensure that pages load as fast as possible. How small could we make such a thing? Turns out, 6K.

Dojo has, for many versions now, provided the ability to use the build system to cut out parts of dojo.js which weren’t in use by the application and load them later. In fact, everything but the package loader and the few functions it needs to operate are located in source packages in the dojo/_base/ directory. When running Dojo from source, these files get pulled in with dojo.require() the same way that any other module might. One possible option then is to use the build system to create a custom version of dojo.js which includes just the modules your application uses at page load. But we’re still faced with the essential problem created by hand-rolling everything: it becomes much harder to work with Dojo. You no longer know what functions and classes might be provided by the dojo.js file loaded in the header of your page, meaning that the advantages of maintainability, shared team knowledge, and dependability are reduced. Code developed for this situation is littered with statements like dojo.require("dojo._base.array"); in order to ensure that the right modules are available.

A better solution might be one which preserves the super-lightweight foot-print of including just the Dojo loader but which also provides access to the full, rich API that is provided by the stock dojo.js without developers needing to spend a lot of time worrying whether a particular base module has already been loaded.

Enter the stub loader.

I’ve prototyped an alternate dojo.js which you can download as part of a build (256K tarball, includes tests) of just Core (the stuff in the dojo.* namespace). When served gzipped, this new version of dojo.js weighs in at roughly 6K, yet it provides the full API of the normal dojo.js, provided off of the AOL CDN at roughly 24K, or 4x the size. Instead of including all of the modules in Dojo’s baseline set of functionality, this new version instead pulls in just the bootstrapping code for the module system and then creates stub functions and constructors for all of the functionality which isn’t included. When a function is first called, the stub loader ensures that dojo.require() is called to pull in the appropriate functionality synchronously. The result is an API which behaves exactly as though all of dojo.js had been loaded up front but which defers loading until the code is actually used.

On an iPhone with a clean cache the stubbed-out dojo.js cut in half the time required to load and evaluate. Sure, it’ll take more time on the network when parts of the toolkit are actually used (say, in response to a click event), but for mobile device scenarios, it’s going to be hard to beat the flexibility and speed of the stub loader when pulling Dojo into a page.

The code to implement this strategy can now be viewed. If you view that file, you’ll note that the first several lines read like this:

dojo.provide("dojo._base");
dojo.provide("dojo._base.browser");
dojo.provide("dojo._base._stubs");

This implies that if some other code subsequently calls dojo.require("dojo._base");, as the build system normally would, then no code will be loaded. In a source version of Dojo, dojo/_base.js reads as such:

dojo.provide("dojo._base");
dojo.require("dojo._base.lang");
dojo.require("dojo._base.declare");
dojo.require("dojo._base.connect");
dojo.require("dojo._base.Deferred");
dojo.require("dojo._base.json");
dojo.require("dojo._base.array");
dojo.require("dojo._base.Color");
dojo.requireIf(dojo.isBrowser, "dojo._base.browser");

By loading our stub loader instead, all of the modules in dojo._base.* which would normally be included are left out of the build and replaced with their stub counterparts. To get some bit of code included in a build instead of the stub, we’d then just add the corresponding require (e.g., dojo.require("dojo._base.lang")) at the top of dojo/_base/_stubs.js, at which point the corresponding stub functions won’t be generated.

In a build which uses dojo/_base/stubs.js, extra code is not loaded until the functions and constructors which are stubbed out there are actually called. You can drop this file into your dojo/_base/ directory if you’re working with Dojo from source and then use the following profile definition to create your own stubbed-out dojo.js. I placed mine in util/buildscripts/profiles/stubs.profile.js:

dependencies = {
	layers: [
		{
			name: "dojo.js",
			customBase: true,
			dependencies: [
				"dojo._base._stubs"
			]
		},
		// your layers here!
	],
 
	prefixes: [
		// uncomment these to copy dijit and dojox into your build output
		// [ "dijit", "../dijit" ],
		// [ "dojox", "../dojox" ]
	]
};

And would normally build with:


%> cd dojo_repo/util/buildscripts/
%> ./build.sh profile=stubs action=clean,release copyTests=false cssOptimize=comments optimize=shrinksafe
clean: Deleting: ../../release/dojo
release: Using profile: profiles/stubs.profile.js
...

A Quick JavaScript Load Profiler

Tue, 07/01/2008 - 20:02

I was doing some research on script loading speed tests. Each script load required the page to be refreshed, making it difficult to log the time to Firebug and get an average. It was certainly too much trouble to write some PHP scripts and connect to a database; and possibly even worse would be having to pull out a pencil and paper and write the times down. I’m not even sure I have a pencil.

The obvious solution was to write the data to a cookie. I also thought the solution was universal enough to blog about so others could use it too.

The first thing we need is a timer. Firebug has a timer, but it doesn’t return the results so that they can be saved. The timer will be a singleton since we will only need one instance holding all of the data, and it will be a global object so we can access it anywhere. The code is quite simple:

dojo.provide("mikespace.timer");
timer = {
	_map:{},
	start: function(msg){
		this._map[msg] = new Date().getTime();
	},
	end:   function(msg){
		this._map[msg] = new Date().getTime() - this._map[msg];
		return this._map[msg];
	},
	show: function(msg){
		console.log( "---------> " + msg + ": " + this._map[msg]);	
	}
};
// usage:
// timer.start("first test");
// var time = timer.end("first test);

This code is in a directory (or package) named mikespace. As you can see, the times are set and retrieved based on the label parameter, and stored in a hash map. There’s also a show() method - although I’m not going to use it here, I use this often. When conducting speed tests in an application, the timers are usually sprinkled everywhere throughout the code and show in various places in the log messages. By just calling timer.end() I store the time, and after all of the tests have been run, I call timer.show(msg) for each test, and log them all in one place.

Next we need our cookie aggregator. This too could be a singleton, but after considering that there could be multiple tests in a page, I decided to make it a PODO (Plain Old Dojo Object). Here is the full code for cookieData, and I’ll explain after:

dojo.provide("mikespace.cookieData");
dojo.require("dojo.cookie");
 
dojo.declare("mikespace.cookieData", null, {
	cookieName:"scriptTests",
	expires:1,
	testData:[],
	constructor: function(args){
		for (var nm in args){
			this[nm] = args[nm];	
		}
	},
	loadData: function(){
		this.cookie = dojo.cookie(this.cookieName);
		if(this.cookie){
			this.testData = dojo.fromJson(dojo.cookie(this.cookieName));
		}
	},
	getData: function(){
		return this.testData;
	},
	showData: function(){
		console.log("Test Name:", this.cookieName);
 
		var data = this.getData();
		var avr = 0;
		var amt = 0;
		dojo.forEach(data, function(d, i){
			console.log(i, "test:", d);
			avr+=d;
			amt++;
		});
 
		console.log("Average:", Math.ceil(avr/amt));
	},
	saveData: function(data){
		this.testData.push(data);
		 dojo.cookie(
			this.cookieName, 
			dojo.toJson(this.testData),
			{ expires: this.expires }
		);
	},
	clearData: function(){
		dojo.cookie(
			this.cookieName, 
			dojo.toJson([]), 
			{ expires: this.expires }
		);
	},
	deleteCookie: function(){
		dojo.cookie(this.cookieName, null, { expires: -1 });
	}
});

I’m utilizing dojo.cookie, so it is required. I declare mikespace.cookieData, and I’m not extending anything (like dijit._Widget), so the second parameter is null. Because I’m not using dijit._Widget, I’m mixing in the arguments object myself in the constructor. I then instantiate a few variables. expires is set to one day, which could be left as-is if you don’t want to leave the cookie hanging around on your system when your done. Or set it to an arbitrarily large number and use the deleteCookie() method.

The rest of the code should be quite easy to understand. in loadData() we check if the cookie exists, and if not, we create one with new data. In showData() I log each test and number it, so I can easily see how many tests I’ve run, and then I show the average. The showData() method could obviously be any kind of code, including parsing tests within tests. And semantically, it probably should be outside of this cookieData; if we wanted to just use it as a getter and setter and then create another object for displaying the results.

Now we’ll create our HTML page where we will run the tests. After including the Dojo script tag, and the djConfig, I register the mikespace namespace, and then require the files:

dojo.registerModulePath("mikespace", "../../tests/mikespace");
dojo.require("mikespace.timer");
dojo.require("mikespace.cookieData");

Now we’ll build our test. The test I’m conducting is how long it takes to load a script, and I chose dojox.data.jsonPathStore, because it’s nice and big and has a few dependencies. I modified the script to call the end of the test, after it loaded it’s dependent scripts:

...
dojo.require("dojo.date.stamp");
 
if(window.endTest){
	window.endTest();
}
...

And our test functions:

startTest = function(){
	timer.start("script loaded");
}
 
endTest = function(){
	var time = timer.end("script loaded");
	var testName = "dojox.data.jsonPathStore Load";
 
	var c = new mikespace.cookieData({cookieName:testName});
	//c.clearData();
	c.loadData();
	c.saveData(time);
	c.showData();
}

When endTest() is called, the cookieData object is created and the test data is saved and logged. Since the timer has already ended, the amount of time it takes to create this object is not factored into the results. If I want to start the test over, I uncomment c.cleardata() and refresh the page.

Finally, to launch our test, start the timer and load the script:

startTest();
 
dojo.require("dojox.data.jsonPathStore");

Refresh the page ten times and you have a legitimate average of how long it takes to load the script.

cookietestdataresults.png

And there you have a quick and dirty method for logging tests that involve page refreshes. This example could be taken even farther, enclosing the startTest() and endTest() in its own PODO which requires the timer and cookieData. Then it’s a simple matter of dropping in the one require to conduct some quick tests.

Connect to the Mobile Web with SMS

Mon, 06/30/2008 - 07:03

SMS is a great way to push small amounts of text to mobile users. But what happens when your application needs to send more than 140 characters of information? Most modern phones, including Apple’s iPhone, support the ability to launch the mobile web browser using the URL embedded in the SMS message. Your application can create a short URL that points to the content you need to send and send the URL in the body of the SMS instead of the content itself. The user experience varies from phone to phone. On the iPhone, the user simply touches the link; on other phones, there is usually a menu option that will activate the url.

sms_url.jpg The URL is subject to the same size limits as any other SMS message. Keeping the URL as short as possible is key, allowing you to send descriptive text along with the message to give the user an idea as to what they will be viewing when they click the link. URL shortening services like tinyurl will keep your URL to around 25 characters. Twitter users are no doubt familiar with this idea, whether they send Tweets from their phone or not.

The web page that the URL points to needs to detect the capabilities of the mobile device and return the appropriate markup. In the case of the iPhone, a full HTML page can be returned, but for many phones, the content needs to be limited to the XHTML mobile profile, or as a worst case with the oldest of phones, WML might be the only supported markup. Device capability databases like WURFL will allow you to look at the user-agent of the devices and return the appropriate markup.

Make sure that your users actually want to receive the SMS before you send it. Most users have to pay for each SMS message, so filling up their phone with messages without their permission is not a good idea. Allow your users to opt-in to receiving SMS messages and allow them to place a maximum cap on the number of messages they can get per day or month. Also include a verification system that ensures that your user actually controls the phone number they give you. This can be as simple as sending an SMS message with a PIN number that they then feed back into your signup form. Lastly, include disclaimer text that lets the user know that standard text messaging rates apply to any messages you send them. Adding these safeguards will help to ensure that your users only receive the messages they want.

Adding SMS notification with embedded URLs has many advantages: you can reach your users wherever they are, provide links to detailed rich content, increase awareness of your mobile web offering, and drive additional traffic deep into your mobile web site. If your application already works on mobile devices and you already have the infrastructure in-place to send email or SMS alerts, then sending URLs in SMS messages is a simple addition that can add significant value to your users. A great example for this is the airline industry. Instead of just sending a user an SMS with their gate information and arrival and departure time, they could also send a link to a convenient, mobile optimized “My Flight” page that would include other essential details such as seat assignment, arrival time, airline phone number, first class update options, and airport map.

Web Service to dojo.data Store in 4 Easy Steps

Thu, 06/26/2008 - 02:12

A very useful feature of Dojo is the dojox.data.ServiceStore data store. It allows you to layer a dojo.data API on top of any web service, opening up a world of uses from your own client-side components. Kris Zyp briefly mentioned the topic in his recent article on JsonRestStore, and the past couple of weeks have seen a bunch of refinements to the component to get it ready for next month’s Dojo 1.2 release. Let’s take a quick look at how to make it work with the web service of your choice.

Building the WikipediaStore

Dojo recently received a new data store that demonstrates exactly what we want: dojox.data.WikipediaStore. It does just what it sounds like, turning Wikipedia into a simple object you can query from your code. Building it with Dojo’s handy dojox.rpc package makes for a simple, compact, DRY implementation.

In only four steps:

  1. Create the web service object
  2. Declare the new data store, inheriting from ServiceStore
  3. Give it a fetch method
  4. Give it a _processResults method

That’s all there is to it.

Step 1: Create the web service object

Probably the simplest way to build an object that can talk to a web service is to create a Service Mapping Description (SMD) for the web service, and let dojox.rpc.Service do the heavy lifting. Since Wikipedia’s API can speak JSONP, it’s a natural fit. The SMD doesn’t need to be thorough, since it’s not necessarily meant for human consumption; the wikipedia.smd only defines a single API endpoint and lets the data store worry about how to talk to it:

{
    "SMDVersion": "2.0",
    "id": "http://en.wikipedia.org/w/api.php",
    "description": "Wikipedia API",
 
    transport: "JSONP",
    envelope: "URL",
    additionalParameters: true,
    target: "http://en.wikipedia.org/w/api.php",
    parameters: [
        { name: "format", optional: false, "default": "json" }
    ],
 
    services: {
        query: {
            parameters: [
                { name: "action", type: "string", "default": "parse" }
            ]
        }
    }
}

This simple specification is enough to let you do something like the following, which stuffs the content of Wikipedia’s Dojo Toolkit page into the current page:

dojo.require("dojo.io.script"); // for cross domain JSONP
dojo.require("dojox.rpc.Service");
 
dojo.addOnLoad(function(){
    var mu = dojo.moduleUrl("dojox.rpc.SMDLibrary", "wikipedia.smd");
    var wikipedia = new dojox.rpc.Service(mu);
 
    wikipedia.query({
        action: "parse",
        page: "Main Page"
    }).addCallback(this, function(article){
        dojo.body().innerHTML = article.parse.text["*"];
    });
});

Once we have this, we just need to wrap it in a data store.

Step 2: Declare the new data store

Out of the box, the ServiceStore does just about everything you likely need; however, it doesn’t automatically understand how to format queries for your service, nor does it automatically know the format of the incoming data. It simply acts as a bus, passing through whatever it is given. That’s acceptable if you don’t mind enforcing the underlying web service’s structure on people who use the data store, but for Wikipedia, it doesn’t take much to clean things up a little.

The declaration for the new store is pretty basic. It’s just the common dojo.declare approach (the real code has a tiny bit more than this, but this is the idea):

dojo.provide("dojox.data.WikipediaStore");
 
dojo.require("dojo.io.script");
dojo.require("dojox.rpc.Service");
dojo.require("dojox.data.ServiceStore");
 
dojo.declare("dojox.data.WikipediaStore", dojox.data.ServiceStore,{
    constructor: function(){
        var mu = dojo.moduleUrl("dojox.rpc.SMDLibrary", "wikipedia.smd");
        var svc = new dojox.rpc.Service(mu);
        this.service = svc.query;
 
        // this lets ServiceStore's getLabel(), fetchItemByIdentity(),
        // etc. all work correctly
        this.idAttribute = this.labelAttribute = "title";
    },
    fetch: function(/* object */ request){
        // to come
    },
    _processResults: function(results, def){
        // to come
    }
});

We haven’t done much yet, but we’re already halfway done! All that’s left is to shuttle data back and forth.

Step 3: Give it a fetch method

The dojo.data API uses the fetch method to query data stores, but it leaves it up to the store to determine the actual format of the query. The ServiceStore can pass query data right through to the underlying web API, but since we don’t want our data store’s users to have to know the Wikipedia API itself, we can define our own convention for creating queries and map that to the real API.

WikipediaStore defines two basic operations, query (to do full text searches) and parse (to load page data).

  • The query operation will be called thusly:
    store.fetch({
        query: {
            action: "query",
            text: "dojo"
        },
        // onItem, onComplete, etc...
    });
  • For parse, we want to be able to do the following:
    store.fetch({
        query: {
            // action: "parse", -- this is the default, so we can leave it out
            title: "Dojo Toolkit"
        },
        // onItem, onComplete, etc...
    });

So we define our own fetch that takes the request object passed in and massages it a little bit to transform our custom query structure into something that will work for Wikipedia’s API (by virtue of the dojox.rpc.Service). After doing this in-place modification, we call the ServiceStore’s fetch to do the actual RPC call:

fetch: function(/* object */ request){
    var rq = dojo.mixin({}, request.query);
    if(rq && (!rq.action || rq.action === "parse")){
        // api.php?action=parse&page=PAGE_TITLE
 
        // default to a single page fetch
        rq.action = "parse";
        rq.page = rq.title;
        delete rq.title;
 
    }else if(rq.action === "query"){
        // api.php?action=query&list=search&srwhat=text&srsearch=SEARCH_TEXT
 
        // perform a full text search on page content
        rq.list = "search";
        rq.srwhat = "text";
        rq.srsearch = rq.text;
        if(request.start){
            rq.sroffset = request.start-1;
        }
        if(request.count){
            rq.srlimit = request.count >= 500 ? 500 : request.count;
        }
        delete rq.text;
    }
    request.query = rq;
    return this.inherited(arguments);
}

Now we can use our simplified dojo.data query format above to retrieve data.

Step 4: Give it a _processResults method

Once we’ve requested data, the store needs to know how to process it. That’s where the _processResults function is useful.

_processResults: function(results, def){
    if(results.parse){
        // loading a complete page
        results.parse.title = dojo.queryToObject(def.ioArgs.url.split("?")[1]).page;
        results = [results.parse];
 
    }else if(results.query && results.query.search){
        // loading some search results; all we have here is page titles,
        // so we mark our items as incomplete
        results = results.query.search;
        var _thisStore = this;
        for(i in results){
            results[i]._loadObject = function(callback){
                _thisStore.fetch({
                    query: { action:"parse", title:this.title },
                    onItem: callback
                });
                delete this._loadObject;
            }
        }
    }
    return this.inherited(arguments);
}

The theory here is simple; it’s the same process as with fetch, just in reverse. We receive JSON data from Wikipedia, and we need to restructure it such that when we call the ServiceStore’s _processResults at the end, it’ll set up the internal object correctly.

The most interesting thing about our _processResults function is thection with the _loadObject callback. When we do a search on Wikipedia, all we get back is a list of page titles in request.query.search; we can use these to create stub objects, but there obviously isn’t enough data here to really use. We’d like to be able to fill in the stub object with something like store.loadItem({ item: someStubItem }), and that’s exactly what _loadObject allows: it tells ServiceStore how to load individual items (ServiceStore even supports lazy loading of objects, but only with transports that can be made synchronous; since JSONP always runs asynchronously, WikipediaStore can’t take advantage of it).

That’s really all there is to it; WikipediaStore seamlessly integrates Wikipedia into our apps. The Wikipedia store demo shows how to use it.

Integrating with Dijit

Of course, one of the big advantages of using the dojo.data API is that quite a few Dijit components can already grok it. For example, one of the demos for the Grid is built around pulling in data from Yahoo. Using the same principles as the WikipediaStore, this demo creates a YahooStore that wraps the Yahoo webSearch API endpoint. By implementing custom fetch and _processResults functions (see the component source), we get a beautifully Yahoo-ish grid.

Yahoo search ServiceStore grid demo screenshot

(Image shamelessly borrowed from Kris Zyp’s article on JsonRestStore)

So put together an SMD, a dojo.declare, and a couple of functions for your web service and create your own Dojo data store. When you can easily turn web services into data stores, the web is your oyster!

Creating Dojo Widgets with Inline Templates

Tue, 06/24/2008 - 21:59

I recently came across a situation where I needed to manage a set of nodes and widgets to perform a number of visual operations as well as manage some data between the client and a server back end. While this was a custom operation, it was common to a number of different places within the app. Initially, things were quite simple and I managed with a few functions and connections primarily using dojo.query(). In the end, I ended up with a simple way to make a dijit._Templated widget that uses its source node as a template instead.

Widgets can easily take advantage of existing source nodes to define how they might end up rendering. They might use the source nodes to define a data set. They could be widgets that manage a number of child widgets as is done with the various Dijit Layout widgets. However, under normal circumstances, a widget’s source node is replaced by the nodes generated from its template or the original source nodes are moved to a container node within your template. What we are looking for is a way to define a flyweight widget that can encapsulate behaviors and data, provides for dynamic template generation, and retains the utility of dojoAttachPoints and dojoAttachEvents from the templating system.

The good news is that it is very easy to do. Let’s look at a normal templated widget declaration:

dojo.declare("my.widget",[dijit._Widget,dijit._Templated],{
	templateString: '<div><div dojoAttachPoint="container"></div>
		<div dojoAttachEvent="onMouseOver:mouseOver">Go</div></div></div>"',
	postCreate: function(){
		// do something after I'm ready. Note: My children might not be 
		// ready yet, so dont' access them here!
	},
	startup: function(){
		// This fires after my children are available
	}
});

This is is a simple widget that does not do much. As you can see, it will end up creating a div containing two child divs at its declared location on the page. The first of these children has a dojoAttachtPoint attribute, which will create a reference to this DOM node at this.container in the widget instance. The second div has a dojoAttachEvent attribute which will connect that DOM node’s onMouseOver event to the widgets this.mouseOver() method.

There are numerous examples of using templates inside of Dijit and plenty of articles over at Dojo Campus or even here at the SitePen blog! But that’s not what we want as it is too static. Our templates will be defined as part of the Instance declaration instead of the widgets JavaScript declaration. To accomplish this goal, we simply need to declare the widget, and then override the buildRendering() and startup() methods provided by the dijit.Templated widget. We want to do something like this on our page:

<div dojoType="my.fly">
	<div dojoAttachPoint="container">Some Stuff</div>
	<button dojoType="dijit.form.button" dojoAttachEvent="onClick: go">
		Go!
	</button>
	<button dojoType="dijit.form.button" dojoAttachEvent="onClick: stop">
		Stop!
	</button>
</div>

So now we have a template:

<div dojoAttachPoint="container">Some Stuff</div>
<button dojoType="dijit.form.button" dojoAttachEvent="onClick: go">
	Go!
</button>
<button dojoType="dijit.form.button" dojoAttachEvent="onClick: stop">
	Stop!
</button>

The instance of my.fly will have a reference to the div at this.container, and the buttons, when clicked will call the fly’s go() or stop() methods respectively. As you can see there isn’t really any difference between this and a normal template provided by string or in a template HTML file. However, it’s easy for complex servers to generate a bunch of HTML and then wrap it with a fly widget to maintain it.

Let’s look inside my.fly to see how it ticks.

dojo.declare("my.fly", [dijit._Widget, dijit._Templated],{
	buildRendering: function(){
		// we already have the srcNodeRef, so lets just
                // keep it and use it as the domNode
		this.domNode = this.srcNodeRef;
 
		// call this._attachTemplateNodes to parse the template,
                // which is actually just the srcnode
		// this method is provided by the _Templated mixin
		this._attachTemplateNodes(this.domNode);
	},
 
	startup: function(){
		var node=this.domNode;
		this._supportingWidgets = [];
 
		// use a dojo.query to find all the child nodes that are widgets
		// and then get references to those widgets.  The widgets are
		// collected into this._supportingWidgets
 
		dojo.query("[widgetId]", node).forEach(function(n){
			if(n==node){return;}
			var id = dojo.attr(n,"widgetId");
			if (!id){return;}
			this._supportingWidgets.push(dijit.byId(id));
		}, this);
 
		// parse the widgets for dojoAttachPoints and dojoAttachEvents. 
		// Unlike the parse done in buildRendering, we're only going 
		// through the array of widgets in this._supportingWidgets;
 
		this._attachTemplateNodes(this._supportingWidgets, function(n,p){
			return n[p];
		});
	},
 
	go: function(){
		// do someting
	},
 
	stop: function(){
		//stop doing whatever it was go made me do.
	}
});

This flyweight mixin provides the above buildRendering() and startup() methods for easy reuse among a variety of flyweight widgets. Also included in this download are an example widget and test file, making it extremely easy to try this out with your application.

Replacing the Flash Flickr Badge with Dojo

Mon, 06/23/2008 - 07:04

A few weeks ago, some of the guys at SitePen were commenting that I was using Flash on my personal web site for a Flickr Badge. I responded that if Dojo had similar functionality, I would easily replace the badge. A couple of weeks later, Peter Higgins and Bryan Forbes of SitePen rose to the task and created dojox.image.FlickrBadge.

This weekend I had a chance to put their work to the test, and I’m impressed. Using the new Flickr badge is quite easy:

<head>
...
<link rel="stylesheet" href="/dojo/dojox/image/resources/image.css" />
<style type="text/css">
	img.thing { width:50px; height:50px; }
</style>
...
</head>
<body>
...
<div dojoType="dojox.image.FlickrBadge" rows="8" cols="3" username="dylans" 
	tags="dojotoolkit,italy"></div>
...
<script type="text/javascript" src="/dojo/dojo/dojo.js" 
	djConfig="parseOnLoad: true"></script>
<script type="text/javascript">
	dojo.require("dojox.image.FlickrBadge"); 
</script>
</body>

FlickrBadge. There are a number of configurable parameters, most of which are optional:

  • apikey: your Flickr API key
  • columns: # of columns of images to display
  • rows: # of rows of images to display
  • searchText: free text search of title, description, and tags
  • setid: id of a Flickr set to use in displaying images
  • tags: a comma separated list of tags or an array of tags to grab from Flickr
  • userid: your flickr userid (saves a lookup request)
  • username: your flickr username

Give them a try and create a badge that’s just right for your site.

Performance Optimizations

The badge included to this point is a great start, but it requires a number of JavaScript files to be loaded prior to the completion of page rendering, so I started making tweaks to improve performance. The next logical step is to create a Dojo build to layer all of the non-core Dojo code required for this module into a single file. First, I’ll create a very simple profile file for my build:

dependencies = {
	layers: [
		{
			name: "badge.js",
			resourceName: "dojo.badge",
			layerDependencies: [
			"dojo.js"
			],
			dependencies: [
				"dojox.image.FlickrBadge"
			]
		}
	],
 
	prefixes: [
		[ "dijit", "../dijit" ],
		[ "dojox", "../dojox" ]
	]
}

And then I ran the build with the following configuration options:

./build.sh action=release optimize=shrinksafe profile=dylan 
	cssOptimize=comments.keepLines cssImportIgnore=../dijit.css 
	version=1.2.pre

This reduced the number of requests by compressing a number of JavaScript files into badge.js. However, it still required a decent amount of additional JavaScript to load before the page renders. Fortunately, I’m using the latest Dojo trunk, and Dojo 1.2 has a new mechanism for lazy-loading a built layer! That’s right, just point to a layer as you would any Dojo module, and defer loading until later. All with a simple, single line of JavaScript:

dojo.addOnLoad(function(){ dojo.require("dojo.badge"); 
	dojo.addOnLoad(function(){ initBadge(); })});

A couple of notes:

  • the nested dojo.addOnLoad is the new way to add a load callback for a lazy-loaded layer
  • my use of dojo.badge as the resourceName is not a coincidence… it follows Dojo’s standard naming conventions, looking for the file badge.js as dojo/badge.js automatically

Finally, inside initBadge, I decided to turn off Dojo’s parser, and instead make an explicit widget instantiation on the node to replace, in order to reduce code execution time:

function initBadge(){
	console.log("init");
	new dojox.image.FlickrBadge({rows: 8, cols: 3, username: "dylans", 
		tags:"italy,dojotoolkit"},"flickrSidebox").startup();
}

That leaves us with this performance-optimized code fragment:

<head>
...
<link rel="stylesheet" href="/dojo/dojox/image/resources/image.css" />
<style type="text/css">
	img.thing { width:50px; height:50px; }
</style>
...
</head>
<body>
...
<div id="flickrSidebox"></div>
...
<script type="text/javascript" src="/dojo/dojo/dojo.js"></script>
<script type="text/javascript">
	function initBadge(){
		new dojox.image.FlickrBadge({rows: 8, cols: 3, 
			username: "dylans", tags:"italy,dojotoolkit"},
			"flickrSidebox").startup();
	}
	dojo.addOnLoad(function(){ dojo.require("dojo.badge"); 
		dojo.addOnLoad(initBadge)});
</script>

In a future release, I’d like to see us add an option to randomize which images get selected from the set, rather than the first X results from a query. This is a complaint I have with the official Flickr Badge as well, and the API itself which generally returns the first X entries. dojox.image now has a number of great features: Lightbox, Badge, Gallery, Magnifer, and SlideShow. dojox.image.FlickrBadge uses the new dojox.data.FlickrRestStore, a Dojo Data store implementation that makes it extremely easy and flexible to work between Flickr data sources and Dijits.

Dojo + jabsorb

Wed, 06/18/2008 - 07:01

jabsorb is lightweight Ajax framework that uses JSON-RPC to communicate method calls from JavaScript to the Java server. Because of Dojo’s pluggable RPC and SMD capabilities, Dojo can easily be used to communicate method calls to the jabsorb server framework. Together, Dojo and jabsorb provide a very simple solution for integrating a UI with a Java back-end. Here we will see a quick demonstration of making a Java object available for remote calls from Dojo.

First, we build a Java class that will return a polite hello to whomever called it:

public class Hello {
   public String sayHello(String who)
   {
     return "hello " + who;
   }
}

Next, we expose an object instance of this class to the jabsorb framework in JSP:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
		"http://www.w3.org/TR/html4/loose.dtd">
<jsp:useBean id="JSONRPCBridge" scope="session"
		class="org.jabsorb.JSONRPCBridge"/>
<jsp:useBean id="hello" scope="session"
		class="org.jabsorb.test.Hello"/>
<% JSONRPCBridge.registerObject("hello", hello); %>
<html>...

Here we register an object called “hello” which is an instance of the Hello class. The jabsorb method naming convention defines that in order to call the sayHello method on the hello object, we call the method “hello.sayHello” using JSON-RPC. Now we create an RPC service in Dojo. We use an SMD definition to define the service that is available jabsorb:

dojo.require("dojox.rpc.Service");
dojo.require("dojox.rpc.JsonRPC");
var services = new dojox.rpc.Service({
	target:"/jabsorb-1.2/JSON-RPC",
	transport:"POST",
	envelope:"JSON-RPC-1.0",
	contentType:"application/json",
	services:{
		"hello.sayHello":{ // this is the method that we created in jabsorb
			returns:{"type":"string"},
			parameters:[{"type":"string"}]
		}
	}
});

We now have a services object that we can use to call the hello object. We can call the sayHello method on the server by calling the “hello.sayHello” method on the services object:

var deferred = services["hello.sayHello"]("Fred");
	deferred.addCallback(function(result) {
		alert(result);
	})

This calls the sayHello method with argument of “Fred”, which will return “hello Fred”. When the response is received from the server, the deferred object will be fulfilled, and the alert will give the friendly response.

In Dojo 1.2, we have provided “namespaced” service resolution, so this method would be available as services.hello.sayHello rather than services["hello.sayHello"]:

var deferred = services.hello.sayHello("Fred");

jabsorb is an easy to use server framework for handling remote method calls to Java objects using JSON-RPC. With Dojo’s pluggable service architecture, it is simple to connect Dojo to jabsorb to interact with existing Java code. jabsorb is one of a growing number of server frameworks that integrates well with Dojo.

JSON Referencing in Dojo

Tue, 06/17/2008 - 07:04

references.png
Non-trivial data often has structures that cannot be well-defined with normal linear, acyclic data descriptions. Data that consists of cycles, many-to-one relationships, and non-local references often requires custom strategies for serialization and transfer of the data over JSON. Dojo 1.2 has added support for JSON referencing with the dojox.json.ref module. This module provides support for several forms of referencing including circular, multiple, inter-message, and lazy referencing using id, path, and combined referencing forms.

These references can be automatically generated on serialization and resolved on deserialization. JSON referencing is designed to decouple the topology of data structures from the meaning of the data structures in a way that is consistent with runtime object models of modern OO languages (foremost JavaScript). Separating graph connectedness concerns allows referencing and dereferencing to be handled by dojox.json.ref with minimal or no knowledge of and integration with higher level JSON consumers and producers, and permits more extensive JavaScript serialization capabilities.

dojox.json.ref API

First we will look at how to use the new module to serialize JavaScript objects. Perhaps the simplest data structure that requires referencing is a circular reference. We can easily create an object with a circular reference in JavaScript:

var obj = {};
obj.me = obj;

Most JSON libraries cannot serialize such an object, and will generally result in a stack overflow error. However, we can serialize this with dojox.json.ref:

var jsonWithCircularRef = dojox.json.ref.toJson(obj);

And de-serialize the JSON back to an object:

obj = dojox.json.ref.fromJson(jsonWithCircularRef);
obj.me == obj // -> true, the reproduced object will
	//have a property named "me" with a value of itself.

Another common data topology is multiple references:

var me = {
	name:"Kris",
	father:{name:"Bill"},
	mother:{name:"Karen"}};
me.father.wife = me.mother;
var jsonMe = dojox.json.ref.toJson(me); // serialize me
var newMe = dojox.json.ref.fromJson(jsonMe); // de-serialize me
newMe.father.wife == newMe.mother // -> true, the reproduced object will
			// properly reproduce both references to the same object.
JSON Referencing Convention

Now we will look at how to create JSON with references that can be parsed and resolved by dojox.json.ref. The JSON referencing format is primarily based on a reference object which is a special JSON object with a single property named $ref with a value indicating the target of the reference. The simplest way to create references is with id references. This is done by adding ids to objects, and then referencing the object by id. A JSON object with a circular reference as in the first example would look like:

{"id":"1","me":{"$ref":"1"}}

This could be loaded from a file, and then de-serialized by calling:

dojox.json.ref.fromJson(jsonFromFile)

We could also reproduce the second example with id-based referencing:

{"name":"Kris",
  "father":{"name":"Bill","wife":{"$ref":"2"}},
  "mother":{"name":"Karen","id":"2"}}
Path-Based Referencing

Id-based referencing is convenient and readable in situations where objects already have an existing identity property. However, when objects do not already have an identity property, generating ids for every object that might be referenced can be difficult, verbose, time-consuming, and may generate extra properties that could interfere with the intended structure of the data. In these situations, path-based referencing is a cleaner approach. Path-based referencing is inspired by JSONPath, which is a language agnostic convention for querying JSON data, with a syntax that is similar to JavaScript (and supported by Dojo). With a path-based reference, $ denotes the root of the JSON object, and .property and [’property’] syntax are used to denote property values in the JSON object. Therefore, to create the circular reference example with path-based referencing:

{"me":{"$ref":"$"}}

And to create the multiple reference example with path-based referencing:

{"name":"Kris",
  "father":{"name":"Bill","wife":{"$ref":"$.mother"}},
  "mother":{"name":"Karen"}}
Combined Path/Id Referencing

Id and path based referencing can be combined. Rather than starting a path reference with the $ root reference, a path reference may begin with an id reference. For example one could have a reference:

{"$ref":"5.foo.bar"}

This would indicate that this reference should be resolved by finding the object with an id of “5″, and finding the value of the foo property of that object and then finding the value of bar property of that value. Combined path/id referencing can be very valuable in situations where not all objects have ids. In particular, it is impossible to give keyed properties (and consequently ids) to arrays with JSON, so combining path/id referencing can be essential in referencing arrays and values in arrays when id referencing is necessary (inter-message referencing).

dojox.json.ref.toJson will automatically determine the optimum referencing technique to use when serializing JavaScript objects. If ids are present on objects, than id-based referencing (or combined) will be used, otherwise path-based referencing is used. In the first example where a JavaScript object was serialized, path-based referencing would be used since there were no ids on the objects. dojox.json.ref.fromJson can also automatically handle all three forms of referencing.

Inter-Message Referencing

Referencing is not limited to within a single JSON string/message. References can be made to other objects that have been de-serialized in previous (or future) messages. This can be very important for preserving identity of referenced objects as well as reducing the amount of data that must be transmitted. For example if we loaded an object describing me from a server:

{"name":"Kris",
 "father":{
    "id":"bill",
    "name":"Bill"}
}

And in the next message, we loaded an object describing my sister, we would the object to reference the same father object (and not have to download it again):

{"name":"Kari",
 "father":{"$ref":"bill"}
}

When dojox.json.ref.fromJson() parses the second JSON message, it should resolve the reference to the object that it created for my father in the first message. firstObject.father should be equal to secondObject.father and resolve references from the other. Not only does this preserve object identity, it can significantly reduce the size of the transferred data by eliminating the need to retransmit large objects that referenced multiple times.

Inter-message references are dependent on providing an index map object as a parameter to fromJson() (see the API description below). If the same index object is provided, than the inter-message references can be resolved. Otherwise references will be limited to that message. The new JsonRestStore module uses the Rest service with the referencing module, and a single index is used across Rest requests with automatic contextualization.

Lazy Referencing

dojox.json.ref also allows objects to reference other objects that have not been loaded and parsed yet. For example, if this object comes first:

{"name":"Kari",
  "father":{"$ref":"bill"}
}

Then the father property will be a lazy reference to an object that has not been loaded yet. Lazy references are denoted with a special dojo.Deferred value. Lazy referencing can be used in conjunction with the dojox.rpc.Rest service to automatically load referenced values when they are needed. To load a lazy value, simply a callback listener using addCallback to the Deferred object. As soon as the addCallback is called, the referenced value will be loaded, if it has not already been loaded. Lazy referencing can be valuable for maintaining the topology of JSON object structures, while deferring the loading of large sets of data until necessary, using the standard Dojo concepts of Deferred values.

JSON Referencing + Relative URLs

Using ids within a single logical domain can limit the scope of referencing. Data stores may wish to reference data accessible from different paths, and decentralized cross-site data structures may be advantageous. dojox.json.ref supports standard ids along with references outside of origin id domain using relative URLs for references. By equating references to relative URLs, references have a natural correspondence to resource location. JSON referencing leverages the ubiquity and flexibility of the standard relative URL convention to extend the scope of potential referents without compromising the simplicity of simple intra-message references.

To illustrate with an example, suppose we retrieve the resource from http://mysite.com/Person/1 and receive:

{"id":"1","name":"Kris", "father":{"$ref":"2"}}

This is a simple id-based lazy reference, with the father property referencing the object with an id of “2″ within our context. However, using the concept of relative URLs in conjunction with our knowledge of where the resource was requested we can infer absolute resource locations for the objects and their references. We can determine that the target of the reference can be found at a location of http://mysite.com/Person/2. Note that we didn’t have to change the syntax of the JSON referencing at all with references within the message and context.

Now imagine that we want to load an object that does make a reference to a different context. Suppose, we load a purchase order object from http://mysite.com/PO/1:

{"id":"1",
 "amount":5.99,
 "purchasedBy":{"$ref":"/Person/1"}
}

Now we can use relative URLs to determine that the purchasedBy property is referring to the object from http://mysite.com/Person/1. If this object has already been loaded, the property can be directly resolved by dojox.json.ref.fromJson, and if not, it can create a lazy reference that will load the object from the correct URL as needed. It is important to note that the message and client can remain agnostic of how this reference was formed. The reference may be a result of a relationship in a RDBMS, a direct reference in non-relational databases, or manually created. The client does not need to understand the details of how the reference was formed.

We can even go a step further and use cross-site references. Suppose we are a reseller, and we want to actually link directly to the manufacturer’s data for the products in our purchase order. We might have a purchase order like:

{"id":"1",
 "amount":5.99,
 "purchasedBy":{"$ref":"/Person/1"},
 "product":{"$ref":"http://sitepen.com/t-shirt"}
}

This is allows us to maintain a much more DRY data storage system. Once again, dojox.json.ref can handle resolving references and client code can deal directly with the data without concern for the underlying details of recreating the topology of the data structure even if connections span domains.

Additional dojox.json.ref Options

In order for dojox.json.ref to properly handle references that cross messages, domains, and contexts, it is necessary to provide additional information to the functions. dojox.json.ref.fromJson takes the following parameters:

dojox.json.ref.fromJson(str, args)
  • str - The JSON string to parse
  • args - A keyword set of arguments that can have the following properties (all are optional):
    • index - The index object (map) to use to store an index of all the objects. If you are using inter-message referencing, you must provide the same object for each call.
    • defaultId - The default id to use for the root object (if it doesn’t define it’s own id)
    • idPrefix - The prefix to use for the ids as they enter the index. This allows multiple tables to use ids (that might otherwise collide) that enter the same global index. For example, if you request an object from the /Person/1, then you should provide an idPrefix of “/Person/”.
    • idAttribute - Indicates what property is the identity property. This defaults to “id”. It is recommended that you use “id” as the identity property for simplicity and consistency.
dojox.json.ref.resolveJson(object, args)

resolveJson works the same as fromJson, but it operates on a pre-parsed JSON object. Therefore dojox.json.ref.fromJson(str) is roughly the same as dojox.json.ref.resolveJson(eval('('+str+')'))

dojox.json.ref.toJson(it, prettyPrint, idPrefix)
  • it - The value to serialize.
  • prettyPrint - Indicates whether to use a pretty printing of the JSON.
  • idPrefix - Indicates what context to use for serializing the JSON.

dojox.json.ref is used for reference resolution, indexing, and serialization by dojox.data.JsonRestStore. JsonRestStore combines the REST service module with JSON referencing and automatically handles the interaction with dojox.json.ref to provide the proper context and index information to the serializer and de-serializer.

dojox.json.ref.fromJson also supports a non-JSON, JavaScript syntax, referencing convention. Rather than using:

...
property:{"$ref":"reference-target"}
...

to define a reference, you can alternately create a reference:

...
property:{ref("reference-target")}
...
Performance

JSON referencing adds additional processing to the serialization and de-serialization process, so it inevitably is slower than standard dojo.toJson and dojo.fromJson. The dojox.json.ref.fromJson is approximately 2-3 times slower than standard dojo.fromJson. However, fromJson is usually used for parsing data sent from a server, and dojox.json.ref.fromJson is generally still faster than most download speeds. For example, on IE8, dojox.json.ref.fromJson can parse JSON at about 1MB per second (of course, this is highly dependent on the structure of the data). When referencing can be used to eliminate redundancy in JSON messages, the performance gains due to the reduced data transfer can easily outweigh the extra client side processing costs. dojox.json.ref also simultaneously indexes objects as it is parsing, and this capability is leveraged by the new JsonRestStore to avoid any extra steps of indexing, generally canceling any client side processing performance hits in such situations.

dojox.json.ref.toJson has a negligible performance hit in comparison to the standard dojo.toJson (less than 5%).

Conclusion

Dojo’s new support for JSON referencing allows a wide range of referencing possibilities using a consistent simple convention, and permits much more flexibility in describing complex data structures and serializing JavaScript data.

A Beginner’s Guide to Dojo Charting, Part 2 of 2

Mon, 06/16/2008 - 07:06

Part 1 of this guide on Dojo charting covered a basic charting example and the options available in defining our chart type. Today we will examine the options for defining our axes and data sets.

Accessing the Axis

The addAxis() call on a chart has several options for defining axes. Similar to addPlot(), this call takes 2 parameters, a name and an options array. You will need to use “x” and “y” as your axes names unless you gave them custom names in your addPlot() call. Additionally, you don’t have to define the axes if you wish to create charts with 1 or zero axes, you can also make charts with more than 2 axes by adding a second plot and attaching axes to it. Using this approach, you can display up to 4 different axes, 2 vertical and 2 horizontal, using 2 to 4 plots. Also, a single axis can be shared by more than 1 plot, meaning you could have 2 plots that use the same horizontal axis, but have different vertical axes. Let’s look at all the addPlot() options that make this and more possible.

The first option is vertical, this determines if the axis is vertical or horizontal, it defaults to false for a horizontal axis. Make sure that your alignment matches with values set for hAxis and vAxis, which are “x” and “y” by default, on your plot or your chart will not render.

chart1.addPlot("default", {type: "Lines", hAxis: "x", vAxis: "y"});
chart1.addAxis("x"); 
chart1.addAxis("y", {vertical: true});

Next we have the fixUpper and fixLower options, which align the ticks and have 4 available options; major, minor, micro, and none. These default to none, and when set will force the end bounds to align to the corresponding tick division. If none is chosen, the end bounds will be the highest and lowest values in your data set. Another related option is the includeZero option, which will make your lower bound be zero. If your lowest data value is negative the includeZero option has no effect.

chart1.addAxis("x", {fixUpper: "major", fixLower:"minor"});
chart1.addAxis("y", {vertical: true, fixUpper: "major", includeZero: true});

Now let’s examine the leftBottom option. This option defaults to true, and along with the vertical option determines the side of the chart the axis is placed. At the end of Part 1 we examined adding a second plot to our chart. Let’s use that sample and give the second plot its own set of axes and anchor them on the top and right using leftBottom.

var chart1 = new dojox.charting.Chart2D("simplechart");
chart1.addPlot("default", {type: "Lines"});
chart1.addPlot("other", {type: "Areas", hAxis: "other x", vAxis: "other y"});
chart1.addAxis("x");
chart1.addAxis("y", {vertical: true});
chart1.addAxis("other x", {leftBottom: false});
chart1.addAxis("other y", {vertical: true, leftBottom: false});
chart1.addSeries("Series 1", [1, 2, 2, 3, 4, 5, 5, 7]);
chart1.addSeries("Series 2", [1, 1, 4, 2, 1, 6, 4, 3], 
        {plot: "other", stroke: {color:"blue"}, fill: "lightblue"}
);
chart1.render();

Multiple Axes

The one thing you may have noticed is that this changes the perspective because the second data set is now charted against a different axis. You are in luck because you have full control to adjust the axis in almost every way possible. For example, you can set min and max options.

min: 0
max: 7

You can turn on and off the tick marks at the minor and micro level, and turn labels on and off for the major and minor levels

majorLabels: true
minorTicks: true
minorLabels: true
microTicks: false

There’s natural, which forces all ticks to be on natural numbers, and fixed which will fix the precision on your labels.

natural: false		
fixed: true

If you want you can define the step between the ticks.

majorTickStep: 4
minorTickStep: 2
microTickStep: 1

You can control the color of the axis, the color and length of your tick marks and the font and color of your labels.

chart1.addAxis("other y", {vertical: true, 
	leftBottom: false, 
	max: 7,
	stroke: "green",
	font: "normal normal bold 14pt Tahoma", 
	fontColor: "red",
	majorTick: {color: "red", length: 6},
	minorTick: {stroke: "black", length: 3}
});

When added to our previous example, the result is:

Multiple Axes

You can also add a grid at your tick marks to your entire chart by adding a Grid plot. The grid plot allows you to turn the grid on and off for major and minor ticks in both directions, and you can assign axes names if you have multiple axes. Let’s add a grid to the other axes in our above example.

chart1.addPlot("Grid", {type: "Grid",
	hAxis: "other x",
	vAxis: "other y",
	hMajorLines: true,	
	hMinorLines: false,	
	vMajorLines: true,	
	vMinorLines: false,	
});

Grid Plot

Last, but not least you have the ability to assign custom labels to your axis. Make sure to allow sufficient space in your div for the text to display properly. Here is an example using abbreviated month names with a Columns plot.

chart1.addAxis("x", { 
	labels: [{value: 1, text: "Jan"}, {value: 2, text: "Feb"}, 
		{value: 3, text: "Mar"}, {value: 4, text: "Apr"}, 
		{value: 5, text: "May"}, {value: 6, text: "Jun"},
		{value: 7, text: "Jul"}, {value: 8, text: "Aug"},
		{value: 9, text: "Sep"}, {value: 10, text: "Oct"},
		{value: 11, text: "Nov"}, {value: 12, text: "Dec"}]
	});

Month Labels

Data, it’s Series(ous) Business

Finally, we get to addSeries(), where we define the data sets that will be displayed on our chart. addSeries() accepts 3 parameters, a name, a data array and an options array. There is also an updateSeries() call that takes a name and data array for when you want to refresh your data. Let’s run through the options available in the addSeries() call, then look at the data array.

There are only a few options to cover for the addSeries() call. First up is stroke, which covers the color and width of your line or the border of your bar and column type graphs.Along with stroke we have fill, and it determines the color of the fill area under the line in area type line graphs and determines the bar fill color for bar and column type graphs. If you are familiar with SVG or dojox.gfx, stroke and fill should be very familiar.

chart1.addSeries("Series 1", [1, 2, 4, 5, 5, 7], {stroke: {color: "blue", width: 2}, 
	fill: "lightblue"});

The other option is marker and it allows you to define custom markers using SVG path segments. Here are some of marker types as defined in the Dojo Charting source code. Note that each is just defined internally as an SVG path:

CIRCLE:		"m-3,0 c0,-4 6,-4 6,0 m-6,0 c0,4 6,4 6,0", 
SQUARE:		"m-3,-3 l0,6 6,0 0,-6 z", 
DIAMOND:	"m0,-3 l3,3 -3,3 -3,-3 z", 
CROSS:		"m0,-3 l0,6 m-3,-3 l6,0", 
X:		"m-3,-3 l6,6 m0,-6 l-6,6", 
TRIANGLE:	"m-3,3 l3,-6 3,6 z", 
TRIANGLE_INVERTED:"m-3,-3 l3,6 3,-6 z"

Now take a look at these options in action using our above example:

Series Options

The only thing we have left is the data array, and it’s just that, an array of data. All plot types can accept a one dimensional array, but there are some additional format options available based on the type of chart. With a 1 dimensional array for line type graphs the X axis will be integers; 1,2,3… and the data will be the Y axis. For bar type plots the data is the length of the bar and the choice between column or bar type determines the orientation. And for pie type charts the sum of the array is your whole pie. All the plot types except pie can have multiple series.

chart1.addSeries("Series A", [1, 2, 3, 4, 5]);

For any non “stacked” line plot type you can specify coordinate pairs. You need to use keys that correspond to the hAxis and vAxis parameters defined in the addPlot() call. These default to x and y.

chart1.addSeries("Series A", [{x: 1, y: 5}, {x: 1.5, y: 1.7}, 
	{x: 2, y: 9}, {x: 5, y: 3}]);
chart1.addSeries("Series B", [{x: 3, y: 8.5}, {x: 4.2, y: 6}, {x: 5.4, y: 2}]);

Here is an example of using coordinate pairs with a scatter plot:

Coordinate Pairs

With any of the stacked plot types each data set added with addSeries() is placed relative to the previous set. Here is a simple example that shows this concept. Instead of the second data set being a straight line across at 1, all the points are 1 above the point from the first data set.

chart1.addSeries("Series 1", [1, 2, 3, 4, 5]);
chart1.addSeries("Series 2", [1, 1, 1, 1, 1], {stroke: {color: "red"}});

Stacked Data Series

For pie type charts you can specify additional information: the text label for each slice, the color of the slice and even a font color that overrides the font color definable in the addPlot() call.

chart1.addSeries("Series A", [
	{y: 4, color: "red"},
	{y: 2, color: "green"},
	{y: 1, color: "blue"},
	{y: 1, text: "Other", color: "white", fontColor: "red"}
]);

Mmm...Pie

Charting your own course

That’s it! You now know all the basics of Dojo Charting. Congratulations! If you are wondering where to go from here, I have a few suggestions. First if you are as bad with colors as I am, you will want to check out charting themes. They are simple to use with your charts, just call setTheme(). There are a handful available and pretty straight forward if you want to make your own.

chart1.setTheme(dojox.charting.themes.PlotKit.blue);

You’ll also want to check out Eugene’s posts on the new Dojo 1.2 Charting features, including scrolling and zooming, charting events and widgets, tooltips, and legend.

Have fun!

RESTful JSON + Dojo Data

Fri, 06/13/2008 - 07:03

jsonreststore-simple.png
Dojo 1.2 now includes an infrastructure for interacting with JSON REST services and data storage systems. JsonRestStore is a new Dojo Data store for interacting with the RESTful JSON data sources. This new data store allows you to communicate with server side database/persistent data storage using the Dojo Data API with JavaScript and efficiently handles create, read, update, and delete (CRUD) operations. This can greatly simplify client server communication, interacting can be done simply using straightforward JavaScript instead of having to build your communication for CRUD actions. In addition, Dojo data stores can plugin to many Dojo widgets (Dijits).

Complex database driven widgets can be utilized with minimal coding effort. RESTful JSON is an increasingly popular database interface, and in later posts we will look at how JsonRestStore can be used with Amazon S3, CouchDB, Persevere, and Ruby on Rails. The JsonRestStore fully implements the Dojo Data read, write, notification, and identity interfaces. Therefore it can be used with any widget that can utilize these data stores including widgets with editing and live auto-updating features.

Also new to Dojo 1.2 is the ServiceStore. JsonRestStore is an extension of ServiceStore. ServiceStore provides Dojo Data read and identity interface for remote web services. However, I will primarily focus on the JsonRestStore in this article.

Using a JsonRestStore

The JsonRestStore can be created with a REST service function, or it can be instantiated directly with a target URL. To get a JsonRestStore instance with a target URL:

recipeStore = new dojox.data.JsonRestStore({target:"/recipeStore"});

A JsonRestStore can be created from a REST service function which can be generated with dojox.rpc.Service:

services = dojox.rpc.Service("/mySMD");
recipeStore = new dojox.data.JsonRestStore({service:services.myRestService});

jsonrest-servicestore.png

Fast and Compact Syntax

While you can use JsonRestStore and ServiceStore as you would another data store, they have been designed to support a more compact syntax for data interaction as well. This can be very beneficial if you have extensive code that interacts with the data store. First, several of the commonly used store methods are available as “static” functions, including getValue and setValue. Therefore we can easily define a convenient global or local functions for common operations.

The get, set, and save functions can be used with multiple JsonRestStore stores; it is not limited to the JsonRestStore from which it was retrieved.

With a few simple guidelines, you can actually interact with JsonRestStore items using standard JavaScript property syntax. JsonRestStore items are actually simple JavaScript objects, therefore you can always directly read properties from items.

get = recipeStore.getValue;
set = recipeStore.setValue;
save = recipeStore.save;
... // fetch an item
get(item,"foo"); // instead of using recipeStore.getValue(item,"foo");
set(item,"foo","bar"); // instead of using // recipeStore.setValue(item,"foo","bar");
save();
value = item.foo;
// instead of calling value = recipeStore.getValue(item,"foo");
var prop = "baz";
anotherValue = item[prop];
// instead of calling value = recipeStore.getValue(item,prop);

You can also modify properties of items using standard JavaScript syntax. However, in order for the JsonRestStore to know that an item has been modified, you must call the changing() method prior to modifying properties. Once you have called changing(), the object will be denoted as being dirty and you can make as many changes to the object (the object’s properties) as you want until you call the save() method. Once you call the save() method, you need to call changing() again before modifying an item again. Note, that it is safe to call changing() multiple times.

recipeStore.changing(item);
// mark item as dirty
item.foo = "bar";
// instead of calling value = recipeStore.setValue(item,"foo","bar");
item[prop] = 4;
// instead of calling value = recipeStore.setValue(item,prop,4);
recipeStore.save();
// this always must be called to commit the changes,
// regardless of whether you use setValue or not.
Property Access Performance Comparison

Browser Direct
Property
Access JsonRestStore
getValue ItemFileReadStore
getValue FF3 0.037µs 1.44µs 6.3µs FF2 0.047µs 1.75µs 9.1µs Safari3 0.055µs 1.10µs 4.1µs IE8 0.28µs 3.0µs 13.7µs IE6 0.28µs 5.0µs 21µs

If you are writing extensive code based on a data store, this can make your code much more compact, readable, and maintainable. In addition, there are significant performance benefits from direct property access. Property access is 10-50 times faster than using getValue. For large or computationally intense use of data stores, direct property access may be very important for optimal performance.

There are a couple of things to remember with direct property access. First, setting properties directly does not trigger notifications. Since setting properties is usually much less frequent than reading properties, it is recommended that in most situations that you only use direct property access for reading, and use the standard simple setValue (or a convenience copy) for writing.

Also, the JsonRestStore architecture supports lazy loading using JSON referencing. When a value is accessed that is referenced, but not yet loaded, the property value can be tested with the isItemLoaded function. You can load and access the value by calling the loadItem function. You can also use the standard getValue method (or a copy of it as demonstrated above) in situations where the property may be lazy, and the getValue method will automatically download the value on demand. If you are not using JSON referencing and lazy loading you can always directly access properties using normal JavaScript syntax. However, if you are using lazy loading, when a property value is a lazy loaded you can test for values that have not been loaded and load them asynchronously:

myValue = item.foo;
if(recipeStore.isItemLoaded(myValue)){
	recipeStore.loadItem({item:myValue,onItem:function(result){
		... resume with result ...
	}});
}

or simply using synchronous loading (for lazy loaded properties, the Dojo Data getValue function is the easiest form of access):

myValue = recipeStore.getValue(item,"foo");

Remember, this is only necessary if you are using lazy loading via JSON referencing. Also, using direct property access is purely optional, you can use the JsonRestStore with the same API as any other Dojo Data store.

JsonRestStore also provides a constructor for aesthetic creation of new objects. This constructor is accessible with the getConstructor method:

Recipe = recipeStore.getConstructor();
// create a new recipe object instead of recipeStore.newItem();
var recipe = new Recipe();

The constructor also includes a load function for convenient access to the Rest service querying and fetching items by id.

Now we can put this all together, If we first create aliases for getValue, setValue, save, and changing and then create the Recipe constructor we can write:

// make Apple Pie
var query = Recipe.load("?type='Pie'"); // query for the recipes for pie
query.addCallback(function(queryResults){ // when the results are returned
	var recipe = queryResults[0]; // get the first result
	if(recipe){
		if(recipe.name!="Apple Pie"){ // get the name property
			changing(recipe); // indicate we are changing properties
			recipe.name = "Apple Pie"; // rename this recipe to apple pie
		}
	}else{
		// create a new Recipe for apple pie
		recipe = new Recipe({name:"Apple Pie"});
	}
	save(); // save our changes to the database
});

JsonRestStore and ServiceStore also support synchronous mode. Synchronous requests can create a poor user experience since they generally lock up the browser while the browser waits for a response from the server. However, synchronous mode can also simplify programming. Since it is not necessary to use nested callbacks for handling responses, they can be accessed directly after making a request inducing call. Firefox 3 has also eliminated the browser lock-up associated with synchronous calls, making it a more attractive mechanism. To use synchronous mode, you include the syncMode option when instantiating a data store:

recipeStore = new dojox.data.JsonRestStore({target:"/data",syncMode:true});

In synchronous mode, one can fetch without providing a callback, by directly accessing the results property from the request object that is returned from the fetch operation:

var queryResults = recipeStore.fetch({query:"?tastes='good'"}).results;
var firstItem = queryResults[0];
Implementing a RESTful JSON Server

The JsonRestStore can be used with a number of server storage systems that support HTTP JSON/REST interface without any server-side coding. Several JsonRestStore extensions designed to easily connect with Persevere, CouchDB, and Amazon S3 with minimal configuration are included with Dojo 1.2. You can also easily create your JSON/REST interface for an existing web application and database. This is essentially done by implementing the HTTP methods GET, PUT, POST, and DELETE according to the HTTP specifications and providing the data in JSON format. While the JsonRestStore can be configured (using dojox.rpc.Service) to support other configurations, generally rows/items/objects should be accessible using URLs of the form /table/id. GET is used to retrieve objects and perform queries, POST is used to create new objects (by POSTing to /table/), PUT is used to modify objects, and DELETE deletes objects. You can also follow the example used by other JSON/REST interfaces like Persevere and CouchDB.

Transactions

JsonRestStore provides transaction state information so that servers can implement transactions that correspond to the Dojo Data it saves if desired (this is not necessary for a server to implement in order to support REST). Transactions are indicated by a X-Transaction header in the modifications requests. If the X-Transaction header has a value of open, this means that further requests will be delivered that should be included in the current transaction. Once a request is received without an X-Transaction header of open, the server can commit all the changes from the current request and the previous requests that indicated an open transaction. It is recommended that you utilize deterministic request ordering and page sessions if you implement JsonRestStore directed transactions on the server.

JsonRestStore also features a shared repository of transactional data between all JsonRestStore instances. Therefore, if you save a change, all the JsonRestStore data store’s unsaved data will be committed. This means that you don’t have to track which data stores have modified data, and it also means that you transactions can involve modifications across multiple data stores and corresponding server tables.

Build with, not on JsonRestStore

The JSON REST infrastructure is composed of a layer of modules that can easily be utilized and extended on their own, following the Dojo philosophy of extensibility. First, JsonRestStore is an extension of the ServiceStore data store and is designed for provider/store separation. The ServiceStore is a read-only data store built such that various different remote data providers can plugin to the ServiceStore. Thus the ServiceStore provides an adapter between the remote communication provider and widgets. Within dojox.rpc, several commonly used remote communication plugins are provided including REST, JSON-RPC (version 1 and 2), JSONP, and direct POST and GET. You can also easily create custom remote data providers, by creating a service function that takes parameters and returns a dojo.Deferred object that will receive the result of the query. These can all be used with the ServiceStore. The demonstration of using ServiceStore with a Yahoo search web service shows this in action. You should use the ServiceStore whenever you are working with a read-only web service (that doesn’t support full bi-directional REST), as it is lighter than JsonRestStore.

yahoo-search-demo.png

ServiceStore only provides read capabilities (the Dojo Data Read API), however, writable remote communication providers (REST services) can plugin to the JsonRestStore for full read-write capability. A writable provider is one that provides put, post, and delete functions as properties of the main retrieval function. Once again, you can use the HTTP/REST compliant provider included with Dojo (dojox.rpc.Rest) or you can create your own.

Build with REST Infrastructure

dojox.rpc.Rest can also be used directly. A REST service can be constructed with dojox.rpc.Rest:

var restService = dojo.rpc.Rest("/myRestTarget",true);

This indicates that the rest service can be found at /myRestTarget and it is a JSON service, which enables additional caching and optimization capabilities. You can retrieve data from this service:

restService("10") // retrieve the object with an id/resource location of 10

This would result in a fetch from the local resource /myRestTarget/10. You can modify this resource with a PUT:

restService.put("10",{foo:"bar"});

This will store the provided JSON object into the target resource. You can also use restService.post and restService.delete to further directly modify resources. JsonRestStore uses these access points to persist all data changes in the data store. New objects are added with a service.post, deleted objects are removed with a service.delete and modifying objects are updated with a service.put.

jsonrest-infrastructure.png

The dojox.rpc.JsonRest module is the core engine behind JsonRestStore. JsonRest can be used directly, providing a compact API with the core features of JsonRestStore, including transactional data interaction and query and object loading. dojox.rpc.JsonRest is a singleton object, and provides functions: fetch, commit, revert, changing, deleteObject, getConstructor, and isDirty. These correspond to the same functions on JsonRestStore, but the single JsonRest object can be used without requiring separate data stores to be used to access the API. If you do not need the Dojo Data API, and you only need JSON/REST interaction, you can go lighter, and directly interact with dojox.rpc.JsonRest. However, because the Dojo Data API enables you to plug stores into numerous widgets with minimal effort, generally using JsonRestStore is preferable to directly using JsonRest.

JsonRestStore also has comprehensive referencing capabilities, including circular, multiple, and cross-store, and cross-site references. This feature is also provided by a separate JSON referencing module, dojox.json.ref for modularity and extensibility. Therefore, you can use the JSON referencing module to resolve and serialize references for data exchange outside of JsonRestStore. For example, you could easily use referencing for JSON-RPC data, and even allow RPC parameters and return values that reference data from REST stores. This modular structure also allows you to extend or use alternate referencing conventions for JsonRestStore.

Live and Offline Data Stores

Because the JsonRestStore uses the extensible Rest service, JsonRestStore also works with HttpChannels, the new cometd transport/protocol module, to provide a Comet-powered real time view of data on a server. This does not require writing any additional event handlers. If you are using a server that supports HTTP Channels (like Persevere), you can simply add the HttpChannels and you can will have a live view of your data. Be sure to check out the JsonRestStore in action with live updates.

Likewise, JsonRestStore will also run in offline mode by simply adding the forthcoming dojox.rpc.OfflineRest module. The OfflineRest module augments the REST service. As a result, a JsonRestStore running off of a REST service will automatically be able to run in offline mode, with data changes saved locally when offline and automatically re-synced when connectivity is restored.

Dojo 1.2 now has a comprehensive infrastructure for RESTful interaction with servers. JsonRestStore is a modular data store with built in capabilities for interacting with servers in standards-based manner that already works with a number of existing server technologies, and provides a widely usable data store for server side storage that is easy for servers to interact with. Stay tuned for more posts on using JsonRestStore with various storage servers.

Dojo Charting: Widgets, Tooltips, and Legend

Thu, 06/12/2008 - 07:06
table.elchart {padding: 1px; border: none; border: 1px solid lightgray;} table.elchart th {margin: 1pt; padding: 1pt; border: none; border: 1px solid lightgray; font-weight: bold;} table.elchart td {margin: 1pt; padding: 1pt; border: none; border: 1px solid lightgray;} table.elchart td.highlight {font-weight: bold;}

According to the previous article (Dojo Charting: Event Support Has Landed!) the next items on our plan for improving Dojo Charting are the widgetization of charting, events, and integration with the Tooltip widget. In addition to all of these features now being available in trunk, the Legend widget was developed. These features will all be available in the release of Dojo 1.2.

Actions

Actions are self-contained objects, which use events to implement certain effects when users interact with a chart. In general they are designed to attract attention and indicate which charting element is selected, or to show additional information.

While you can create your own actions, we took liberty to package some generally useful actions. The default library contains five classes: Highlight, Magnify, MoveSlice, Shake, and Tooltip. All of them take advantage of the Dojo animation support. It is the best to see them live on the demo page (it demonstrates examples of legends as well).

All actions except Tooltip support the following common keyword parameters:

Name Type Default Description duration Number 400 the time of effect in milliseconds. easing Function dojox.fx.easing.elasticOut the easing function that specifies how controlled parameter changes over time.

 

You can further experiment with easing functions at DojoCampus.

Highlight

This action highlights (changes a color by modifying a fill) individual elements of a chart, when a user hovers over an element with the mouse. Affected elements include: markers, columns, bars, circles, and pie slices.

Highlight supports one additional parameter:

Name Type Default Description highlight String, dojo.Color, or Function the default highlight function this parameter defines the highlight color for an individual element.

 

The parameter can be any valid value for a color, e.g., “red”, “#FF0000″, “#F00″, [255, 0, 0], {r: 255, g: 0, b: 0}, and so on. In this case this color will be used to fill an element.

If the parameter is a function, it receives a charting event object (see the previous article for details), and should return a valid color.

The default highlight function uses special heuristics to select the highlight color. It makes it fully saturated, and light for dark colors, or dark for light colors. In many cases this default is more than adequate. But if you feel a need to implement a custom highlighting scheme, you can easily create your own function.

The picture below demonstrates Highlight (with a constant color) and Tooltip actions.

Highlight and Tootltip

Magnify

This action magnifies an individual element of a chart, when users hover over them with the mouse. Affected elements include markers and circles.

Magnify supports one additional parameter:

Name Type Default Description scale Number 2 the value to scale an element.

 

The picture below demonstrates Magnify and Tooltip actions.

Magnify and Tooltip

MoveSlice

This action moves slices out from a pie chart, when users hover an element with the mouse.

MoveSlice supports the following parameters:

Name Type Default Description scale Number 1.5 the value to scale an element. shift Number 7 the value in pixels to move an element from the center.

 

The picture below demonstrates MoveSlice, Highlight (with default highlighting parameter), and Tooltip actions.

MoveSlice, Highlight, and Tooltip

Shake

This action shakes charting elements, when users hover over an element with the mouse. Affected elements include markers, columns, bars, circles, and pie slices.

Shake supports the following parameters:

Name Type Default Description shiftX Number 3 the maximal value in pixels to move an element horizontally during a shake. shiftY Number 3 the maximal value in pixels to move an element vertically during a shake.

 

Shake is the highly dynamic effect, so a picture cannot do a justice for it. Please go to the demo page and see it in action.

Tooltip

This action shows a Tooltip, when users hover over a charting element with the mouse. Affected elements include markers, columns, bars, circles, and pie slices.

Tooltip supports the following keyword parameters:

Name Type Default Description text Function the default text function the function to produce a Tooltip text.

 

The default text function checks if a data point is an object, and uses an optional “Tooltip” member if available — this is a provision for custom Tooltips. Otherwise, it uses a numeric value. Tooltip text can be any valid HTML, so you can specify rich text multi-line Tooltips if desired.

The picture below demonstrates Tooltip, and Highlight actions.

Tooltip and Highlight

Using Actions

All action objects implement the following methods (no parameters are expected by these methods):

Name Description connect() Connect and start handling events. By default, when an action is created, it is connected. You may need to call fullRender() on your chart object to activate the sending of messages. Typically you create an action object after you define plots, but before the first render() call — it takes care of everything. disconnect() Disconnect the event handler. destroy() Call this method when you want to dispose of your action. It disconnects from its event source and destroys all internal structures, if any, preparing to be garbage-collected.

 

All actions can be constructed like this:

var a = new dojox.charting.action2d.Magnify(
  chart1, 
  "default", 
  {duration: 200, scale: 1.1});

The first parameter is a chart. The second parameter is the name of a plot. The third parameter is an object (property bag) with all relevant keyword parameters.

An even easier options is to use the Chart2D widget. The example below is taken from the Dojo Chart2D widget test:

<div dojoType="dojox.charting.widget.Chart2D" id="chart4"
    theme="dojox.charting.themes.PlotKit.green"
    style="width: 300px; height: 300px;">
  <div class="plot" name="default" type="Pie" radius="100"
      fontColor="black" labelOffset="-20"></div>
  <div class="series" name="Series C" store="tableStore" 
      valueFn="Number(x)"></div>
  <div class="action" type="Tooltip"></div>
  <div class="action" type="MoveSlice" shift="2"></div>
</div>

Yes, it is that simple: just define a <div> with the class “action” and supply the type. If you want to specify a plot’s name, use the “plot” parameter: plot=”Plot1″. By default it will connect to the plot named “default”. If you want to change default keyword parameters, just add them to the <div>, e.g., duration=”500″.

As you can see from the example above you can mixin several actions. In order to avoid unnecessary interference between actions, use your best judgment when selecting them. Try to avoid actions that modify the same visual attributes, like geometry. You can safely mix Tooltip, Highlight, and one geometric action (Magnify, MoveSlice, or Shake).

Legend

Finally one of the most requested features has landed: the legend widget. This widget was contributed by Chris Mitchell of IBM. It can handle all existing chart and plot types and supports horizontal and vertical modes.

Charts and Legends

By default it uses the “legend” parameter of a series. It reverts to the “name” parameter if legend is not specified.

For a pie chart, the behavior is different: if the chart was specified with an array of numbers, it will use numbers. Otherwise it will check object properties in the following order: “legend”, “text”, and the numeric value.

Pie Chart and Legend

Coming Attractions

This article on charting may be the last one before the Dojo 1.2 release. We’re starting work on planned features for Dojo 1.3 and future releases.

The next area we plan to explore is financial charts. We will create new plots including the Bullet plot, and the Box plot (a.k.a. the Candle). Financial charting is not complete without the timeline axis. The latter one is extremely complex mostly due to the fact that we need to calculate business days skipping bank holidays, and there are several ways to accomplish this. Thankfully within the Dojo community we have several committers versed in these problems who will help us get there quickly.

Stay tuned!

Playfulness in Interaction Design

Wed, 06/11/2008 - 07:07

I recently attended a lecture by Matt Jones on the topic of Playful Design. Matt was talking primarily about engaging users and customers through a process of playful discovery, in which fun and quirky features are designed into products, allowing users to engage in entertaining passive exploration of the product. Playful features could have a purpose or simply be there as a wink to the user. The main idea is to create an atmosphere of play that enhances the intrinsic value of the software or product. This playful attitude can be added as part of error messages, quirky functions, or in-product mini-games.

Although the lecture didn’t really focus in any specific product categories, the overall concept seemed to be aimed at electronic consumer devices, social networks, and the kind of fast & fun web 2.0 applications that are popping up like mushrooms. It got me thinking: If playfulness has value, it stands to reason that play could be incorporated into more serious contexts as well. What about products that are notoriously unplayful?

Some companies are finding playful success in the technological gaps that users expect to be dehumanizing, such as the usually stale e-commerce purchasing process. Woot.com, the ubiquitous retailer of zero-day bargains, has made an art of infusing a pretty standard process with a lot of nudges, winks, and playful jibes. For example, instead of just telling customers “your order has been completed” after checkout, Woot does a good job of humanizing the interaction, telling the customer that everything appears to have gone smoothly. Order history pages are populated with playful language such as “What’s going on here?” as well as a tongue in cheek nods to their business model, e.g. “Hey, here’s a fun game: try to get the longest list of purchased woots of anyone! Better get started though, some people have a pretty good start on you. If you win, we’ll be really happy for you.” Perhaps because Woot treats its customers like a friend with whom the site plays around, Woot’s customer base tends to be loyal, engaged, and unsurprisingly, just as playful towards Woot.

Another fun example is Twitter. When Twitter gets flooded, the server responds with a playful image that is far from serious, but incredibly endearing.

Too many Tweets!

Some other playful examples:

Other sites like Digg and YouTube have rather boring and unplayful error or away messages, though on the rare occasion when Digg is down, they offer a nice touch for their news-starved user base: a list of every Digg employee’s favorite blog.

These are all great little features, but there’s a bigger question lingering:

Can we work the same kind of playfulness into enterprise applications?

Playfulness in a business context would have to be executed carefully, but the goal remains the same: Engaging users and creating an enjoyable user experience.

Business applications are very task-oriented beasts. They are used to get things done, and that means that any included playfulness should either be helpful or at the very least not get in the way of users.

The current generation of open-ended games, including massive sandbox games like World of Warcraft and Grand Theft Auto, can really be boiled down to long chains of deliverables and task goals, the completion of which lead to psychological payoffs which have no real value outside of entertainment. It may be a little much to ask that our office apps be as fun as a car chase in a video game, but it seems like we could bootstrap the same ideas of challenge, feedback, and psychological payoff in order to instill some sense of playful gamesmanship in productivity software.

How could we work a playful context into a ho-hum application like a task manager / to-do list? Taking a cue from exploratory games, we’d want to tie in pleasurable feedback (e.g. mission complete!), some kind of progress indicator (two more goals left), and perhaps performance based recognition (level up).

One example of a playful infusion to an otherwise boring set of tasks can be seen at LinkedIn. In order to encourage users to fully complete their online profiles, LinkedIn suggests which sections of the user profile to fill in next and rewards users with a percentage-complete score. This is a much more fun interaction model than the standard, dry web form we might expect. This kind of model can inform the possibility of yoking playfulness in robust applications like our previously mentioned task manager. It also makes the network more useful overall by encouraging users to add more detail to their profiles over time.

Task priority ratings, difficulty estimates and due dates are already found in many task managers, so using this data to create playful feedback seems reasonably straight forward.

Our playful feature might manifest itself by keeping track of user progress and comparing the statistics across a historical data set. Did the user complete more goals today than their historical average? If so, let them know with a pleasant notification. Did the user just finish a task with a very high difficulty rating? A different message might be called for when the user checks that big task off their list. To tie this playfulness to our idea of noting progress, completion data and accomplishment notifications could be charted historically within the task manager for later reference. If a user is actually improving their historical performance regularly, that might call for another feedback cycle, preferably something engagingly visual, like a slick chart that shows their progress.

How these kinds of notifications are implemented can also make the difference between fun and lame. A sterile text message reading “You have completed 3 tasks today. Good work.” could be dull and even patronizing. The user might be more engaged by a quirky little animation with a distinctly non business message, congratulating the user on hitting a performance indicator, slogging through a challenging product launch, or achieving a sale that has been on the user’s calendar for a while.

It’s actually not that hard to imagine this kind of playfulness being useful from a business context. I could even envision a more social structure in which team members also get notified when someone is having a particularly productive day. If the members of a team all complete their current batch of tasks ahead of time, could the system give them a collective pat on the back ( or optionally inform their manager of excellent performance)?

Would there be value in assigning real-life bounties or rewards to certain tasks either explicitly or randomly? It’s tempting to consider the possibilities, though we’d want to make sure to avoid creating perverse incentives and unintentional stressors. Above all, a playful context always means -positive- interaction. If it edges into negativism, playfulness just turns into pressure.

There might also be value in engaging in some purely playful, non-functional elements. These could be features that activate based on predefined conditions and which do nothing other than offer the user a chance to smile or to be surprised. These elements would be discovered spontaneously by users, and should be clever enough to spur an inter-office email along the lines of “Hey, check this out!”

For example, imagine building a playful egglet into a text editor. For example, let’s consider an egglet that provides useless but fun feedback based on typing speed. When the user starts typing at a very high rate, a small egglet in the form of a speed gauge would appear at the bottom right of the interface, possibly along with a subtly implemented sound, like that of a revving engine. What’s the point? None. It’s purely there as playful fun, a way for us to tell the user “wow, you su