Part 5: DojoX - Experimental and Specialized Extensions

DojoX is an area for development of extensions to the Dojo toolkit. It acts as an incubator for new ideas, a testbed for experimental additions to the main toolkit, as well as a repository for more stable and mature extensions. Unlike Dojo and Dijit, DojoX is managed by subprojects, each of which has at least one module, a sponsor and a mission statement. [Release cycle policy TBD] The subprojects may have dependencies on Dojo and Dijit code or other subprojects in DojoX. Other projects may choose to keep their dependencies on Dojo minimal, perhaps only depending on Dojo Base, and remain largely toolkit agnostic.

Some Dojox projects directly extend Dojo components, like the Flickr data store. These are documented in Part 3.

Some caveats of using DojoX:

Cometd (client)

Version
Version 0.4
Release Date
Release date: May 29, 2007
Project state: beta

Project authors

Alex Russell Greg Wilkins

Project description

Low-latency data transfer from servers to clients. dojox.cometd implements a Bayeux protocol client for use with most Bayeux servers. See cometd.com for details on Cometd or on the Bayeux protocol.

Dependencies:

Needs a cooperating Bayeux server

Documentation

See http://cometd.com

Installation instructions

Use this library with (preferably through) an existing Cometd server.

DojoX Charting

Version
Version 0.800
Release Date
Release date: 10/31/2007
Project state:

beta

Credits
Tom Trenka
Eugene Lazutkin
Project description

Implementation of simple charting library based on dojox.gfx/dojox.gfx3d.

Dependencies:
Dojo Core
dojox.gfx
dojox.gfx3d
dojox.lang
Documentation

Needs documentation

Installation instructions
Needs documentation

DojoX Collections

Version
Version 0.9
Release Date
Release date: 05/27/2007
Project state: stable

Project authors

Tom Trenka

Project description

DojoX Collections is the port of the original Dojo 0.4.x collection classes. It is intended for use by people who are looking for a little bit more functionality out of common collections, like ArrayLists or Dictionaries.

Included are the Iterator and DictionaryIterator classes, both of which can operate on standard arrays and objects (respectively).

Dependencies:

DojoX Collections has no dependencies, outside of Dojo Core.

Documentation

See the API documentation for Dojo (http://dojotoolkit.org/api).

Installation instructions

Grab the following from the Dojo SVN Repository: http://svn.dojotoolkit.org/var/src/dojo/dojox/trunk/collections.js http://svn.dojotoolkit.org/var/src/dojo/dojox/trunk/collections/*

Install into the following directory structure: /dojox/collections/

...which should be at the same level as your Dojo checkout.

DojoX Cryptography

Version
Version 0.9
Release Date
Release date: 05/27/2007
Project state: beta

Project authors

Tom Trenka

Project description

The DojoX Cryptography project is a set of implementations of public crypto algorithms. At the time of writing, only MD5 and Blowfish are complete and tested; at least 5 other algorithms on the way.

DojoX Cryptography is comprised of both symmetric (Blowfish) and asymmetric (MD5) algorithms. Symmetric algs always implement encrypt() and decrypt() methods; asymmetric algs implement compute().

Dependencies:

DojoX Cryptography has no dependencies, outside of the Dojo package system and DOH.

Installation instructions

Grab the following from the Dojo SVN Repository: http://svn.dojotoolkit.org/var/src/dojo/dojox/trunk/crypto.js http://svn.dojotoolkit.org/var/src/dojo/dojox/trunk/crypto/*

Install into the following directory structure: /dojox/crypto/

...which should be at the same level as your Dojo checkout.

Documentation

See the Dojo API tool (http://dojotoolkit.org/api)

DojoX Data

Version
Version 0.9
Release Date
Release date: 05/29/2007
Project state: beta

Project authors

Jared Jurkiewicz

Shane O'Sullivan

Project description

The DojoX Data project is a container for extensions and extra example stores that implement the dojo.data APIs. It may also contain utility functions for working with specific types of data.

Dependencies:

DojoX Data has dependencies on core dojo (dojo.data) and the D.O.H. unit test framework.

Documentation:

Much of the Dojox Data package is documented in Part 3's Using dojo.dataSee the Dojo API tool (http://dojotoolkit.org/api) for further docs.

Installation instructions

Grab the following from the Dojo SVN Repository: http://svn.dojotoolkit.org/var/src/dojo/dojox/trunk/data/*

Install into the following directory structure: /dojox/data/

...which should be at the same level as your Dojo checkout.

/dojox/data/*

Require in the dojox.data stores you wish to use.

DojoX DTL (Django Template Language)

The Django Template Language is one part of Django, a "high-level Python Web framework that encourages rapid development and clean, pragmatic design." Django is the preferred web framework for several of the Dojo commiters.

The DojoX implementation implements the full infrastructure of the Django Template Language. This language, as implemented in the Django Project is limited to text, since it only deals with page serving. While Dojo's implementation also works with text, it has an additional layer that allows us to dynamically render blocks of HTML.

Existing templates should work without fuss using Dojo's implementation. There are some additional abilities in an HTML environment, obviously, but you can add those as you go.

Learning the Markup

Since Dojo implements markup just as it is in Django's implementation, the best place to visit would be their excellent book or their excellent documentation

But in case you are just itching to know what it looks like, it's made up of some simple parts: {% tags %}, {{ variables }}, and {{ variables|filtered }} and {{ variables|more:"advanced"|filtering }}. Sometimes tags have a start and an end tag, sometimes they work alone.

The Base Constructors: Templated and HtmlTemplated

We have 2 base constructors when using DTL outside of a widget: dojox.dtl.Template and dojox.dtl.HtmlTemplate.

Template works only with text. What this means is that you can't use it to do DOM manipulation, you can only use it to generate text that you might use to set a node's innerHTML.

HtmlTemplate is an extension to Templated, which means in terms of size, that it's the full size of Template plus some more code. But using HtmlTemplate, you can do direct DOM manipulation. This means that if a node is inside of an /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */
.javascript .imp {font-weight: bold; color: red;}
.javascript .kw1 {color: #000066; font-weight: bold;}
.javascript .kw2 {color: #003366; font-weight: bold;}
.javascript .kw3 {color: #000066;}
.javascript .co1 {color: #009900; font-style: italic;}
.javascript .coMULTI {color: #009900; font-style: italic;}
.javascript .es0 {color: #000099; font-weight: bold;}
.javascript .br0 {color: #66cc66;}
.javascript .st0 {color: #3366CC;}
.javascript .nu0 {color: #CC0000;}
.javascript .me1 {color: #006600;}
.javascript .re0 {color: #0066FF;}
{% if %} block, it will be removed from DOM if the logic is false, and will be added to DOM if the logic is true.

Both of these constructors can take either plain text, or a URL. HtmlTemplate can also accept a node.

Creating and Rendering a Template in Plain Text

Creating a new instance

As mentioned in the previous chapter, you can create a new instance of dojox.dtl.Template using either a URL or a string.

Once you've created an instance of this object (which now contains a compiled version of your template that you can render as many times as you want), you have a few options:

The update function

Every dojox.dtl.Template instance will have an update function. This function will change the innerHTML of a node, or a list of nodes. This function accepts a node, a node ID, or a dojo.query result as its first parameter, and an object or URL as its second parameter, to be used as a context.

The render function

This works exactly the same as rendering a template in Django.

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */
.geshifilter {font-family: monospace;}
.geshifilter .imp {font-weight: bold; color: red;}
.geshifilter .kw1 {color: #000066; font-weight: bold;}
.geshifilter .kw2 {color: #003366; font-weight: bold;}
.geshifilter .kw3 {color: #000066;}
.geshifilter .co1 {color: #009900; font-style: italic;}
.geshifilter .coMULTI {color: #009900; font-style: italic;}
.geshifilter .es0 {color: #000099; font-weight: bold;}
.geshifilter .br0 {color: #66cc66;}
.geshifilter .st0 {color: #3366CC;}
.geshifilter .nu0 {color: #CC0000;}
.geshifilter .me1 {color: #006600;}
.geshifilter .re0 {color: #0066FF;}

dojo.require("dojox.dtl");
dojo.require("dojox.dtl.Context");

var template = new dojox.dtl.Template("Hello {{ place }}!");
var context = new dojox.dtl.Context({
  place: "World"
});
console.debug(template.render(context)); //1

Use dojo.query

With the dojo.query extension, you don't even need to create a template instance. What this means is that repeatedly rendering a template will be slightly slower, but your code will be more compact.

To use, make sure you require the dojox.dtl.ext-dojo.NodeList module, which adds the dtl function. It accepts a string or URL as its first parameter, and an object or URL as its second parameter. Like the update function above, it will change the innerHTML values of all nodes in the dojo.query result, using the first parameter as its template and the second parameter as its context.

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */
.geshifilter {font-family: monospace;}
.geshifilter .imp {font-weight: bold; color: red;}
.geshifilter .kw1 {color: #000066; font-weight: bold;}
.geshifilter .kw2 {color: #003366; font-weight: bold;}
.geshifilter .kw3 {color: #000066;}
.geshifilter .co1 {color: #009900; font-style: italic;}
.geshifilter .coMULTI {color: #009900; font-style: italic;}
.geshifilter .es0 {color: #000099; font-weight: bold;}
.geshifilter .br0 {color: #66cc66;}
.geshifilter .st0 {color: #3366CC;}
.geshifilter .nu0 {color: #CC0000;}
.geshifilter .me1 {color: #006600;}
.geshifilter .re0 {color: #0066FF;}

dojo.require("dojox.dtl.ext-dojo.NodeList");

dojo.query(".fruit").dtl("Fruit is: {{ fruit }}", { fruit: "apple" });

Writing a Widget

We'll bypass how to use the raw HtmlTemplate object (we'll get into that later) and explain how to write a widget using Dojo's implementation of the Django Template Language.

Both of the solutions covered here work almost exactly like dijit._Templated, which is covered elsewhere in the book. To use the text version, mix in dojox.dtl._Templated and to use the HTML version, mix in dojox.dtl._HtmlTemplated.

These objects will use templatePath, templateString, and use the dojoAttachPoint and dojoAttachEvent node attributes.

They add a single function: render. This function is to be used in the event of re-rendering. One of the main reasons for re-render would be if not all data was available during instantiation. The template will be rendered during creation even if you don't call the render function.

The template will be rendered using the widget object as its context. If you don't want this behavior, you can pass your own Context object to the render function.

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */
.geshifilter {font-family: monospace;}
.geshifilter .imp {font-weight: bold; color: red;}
.geshifilter .kw1 {color: #000066; font-weight: bold;}
.geshifilter .kw2 {color: #003366; font-weight: bold;}
.geshifilter .kw3 {color: #000066;}
.geshifilter .co1 {color: #009900; font-style: italic;}
.geshifilter .coMULTI {color: #009900; font-style: italic;}
.geshifilter .es0 {color: #000099; font-weight: bold;}
.geshifilter .br0 {color: #66cc66;}
.geshifilter .st0 {color: #3366CC;}
.geshifilter .nu0 {color: #CC0000;}
.geshifilter .me1 {color: #006600;}
.geshifilter .re0 {color: #0066FF;}

dojo.require("dojox.dtl._Templated");

dojo.declare("demo", [dojox.dtl._Widget, dojox.dtl._Templated] {
  templateString: "<div>I like eating {{ fruit }}</div>",
  postCreate: function(){
    this.fruit = "apple";
    this.render();
  }
});

New "Contributed" Tags and Filters for a the Browser and Dojo

To Use

These tag/filter sets can be included with either dojo.require, or the /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */
.javascript .imp {font-weight: bold; color: red;}
.javascript .kw1 {color: #000066; font-weight: bold;}
.javascript .kw2 {color: #003366; font-weight: bold;}
.javascript .kw3 {color: #000066;}
.javascript .co1 {color: #009900; font-style: italic;}
.javascript .coMULTI {color: #009900; font-style: italic;}
.javascript .es0 {color: #000099; font-weight: bold;}
.javascript .br0 {color: #66cc66;}
.javascript .st0 {color: #3366CC;}
.javascript .nu0 {color: #CC0000;}
.javascript .me1 {color: #006600;}
.javascript .re0 {color: #0066FF;}
{% load %} tag.

Attribute Tags

Before we get to the list, it's important to note that HtmlTemplate considers attributes to be a full-fledged part of the template system. What this means is that there are new tags that have been introduced that use named attributes in order perform new actions. For the curious tag author, this is accomplished by registering a named tag with "attr:" in front of it

dojox.dtl.contrib.dijit

These tags are only applicable to the HTML version of the template system. If you want the same behavior in a text-based environment, you have to use the dojox.dtl._Templated mixin on a widget. These tags will work even if they are used outside of dojox.dtl._HtmlTemplated.

  • Node Attribute: dojoType: If widgetsInTemplate is set on a widget, or if the global dojox.dtl.contrib.dijit.widgetsInTemplate is set, the node will be instantiated as a widget of that type.
  • Node Attribute: dojoAttachPoint: Works exactly the same as the Dijit templated equivalent
  • Node Attribute: attach/dojoAttachPoint: Works exactly the same as the Dijit templated equivalent
  • Node Attribute: dojoAttachEvent: Works exactly the same as the Dijit templated equivalent
  • Note Attribute: onclick/onmouseover/etc: The string you have in its value is the name of the function to call when that action is performed. eg onclick="goHome"

dojox.dtl.contrib.html

These are also only applicable to the HTML version. The html tag is especially important because, without it, the variable contents are always rendered as text.

  • html: If you want to render a variable as HTML, you have to use {% html varName %}
  • Node Attribute: tstyle: Use if you want to change style attributes during rendering. eg tstyle="top: {{ y }}px;"

dojox.dtl.contrib.data

This allows you to take a given data store, a given list of items, and assign them to a single variable that can be used as if each item was a normal variable.

In the example below, /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */
.javascript .imp {font-weight: bold; color: red;}
.javascript .kw1 {color: #000066; font-weight: bold;}
.javascript .kw2 {color: #003366; font-weight: bold;}
.javascript .kw3 {color: #000066;}
.javascript .co1 {color: #009900; font-style: italic;}
.javascript .coMULTI {color: #009900; font-style: italic;}
.javascript .es0 {color: #000099; font-weight: bold;}
.javascript .br0 {color: #66cc66;}
.javascript .st0 {color: #3366CC;}
.javascript .nu0 {color: #CC0000;}
.javascript .me1 {color: #006600;}
.javascript .re0 {color: #0066FF;}
{{ item.title }} will be the equivalent of calling: /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */
.javascript .imp {font-weight: bold; color: red;}
.javascript .kw1 {color: #000066; font-weight: bold;}
.javascript .kw2 {color: #003366; font-weight: bold;}
.javascript .kw3 {color: #000066;}
.javascript .co1 {color: #009900; font-style: italic;}
.javascript .coMULTI {color: #009900; font-style: italic;}
.javascript .es0 {color: #000099; font-weight: bold;}
.javascript .br0 {color: #66cc66;}
.javascript .st0 {color: #3366CC;}
.javascript .nu0 {color: #CC0000;}
.javascript .me1 {color: #006600;}
.javascript .re0 {color: #0066FF;}
store.getValue(item, "title"};

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */
.geshifilter {font-family: monospace;}
.geshifilter .imp {font-weight: bold; color: red;}
.geshifilter .kw1 {color: #000066; font-weight: bold;}
.geshifilter .kw2 {color: #003366; font-weight: bold;}
.geshifilter .kw3 {color: #000066;}
.geshifilter .co1 {color: #009900; font-style: italic;}
.geshifilter .coMULTI {color: #009900; font-style: italic;}
.geshifilter .es0 {color: #000099; font-weight: bold;}
.geshifilter .br0 {color: #66cc66;}
.geshifilter .st0 {color: #3366CC;}
.geshifilter .nu0 {color: #CC0000;}
.geshifilter .me1 {color: #006600;}
.geshifilter .re0 {color: #0066FF;}

{% bind_data items to store as flickrItems %}
{% for item in flickrItems %}{{ item.title }}: {{ item.description }}{% endfor %}

You can also use: /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */
.javascript .imp {font-weight: bold; color: red;}
.javascript .kw1 {color: #000066; font-weight: bold;}
.javascript .kw2 {color: #003366; font-weight: bold;}
.javascript .kw3 {color: #000066;}
.javascript .co1 {color: #009900; font-style: italic;}
.javascript .coMULTI {color: #009900; font-style: italic;}
.javascript .es0 {color: #000099; font-weight: bold;}
.javascript .br0 {color: #66cc66;}
.javascript .st0 {color: #3366CC;}
.javascript .nu0 {color: #CC0000;}
.javascript .me1 {color: #006600;}
.javascript .re0 {color: #0066FF;}
{{ item.getLabel }}, {{ item.getAttributes }}, and {{ item.getIdentity }}

Note that there's a little weirdness with getValue and getValues. If you want to be sure that getValues is called, just add an extra "s" to the end of your variable name.

Using the Extends Tag

In Django, the extends tag looks through the installed applications until it finds the named template. In a browser environment, we don't want to have to go searching for templates, so there has to be a way to reference a specific file, while not changing the markup style of the extends tag.

The "easiest" way to do this is to put an explicit reference to the template. This means that you need to specify a URL in relation to your root page. But doing it like this creates a problem if you want to move around your directory structure, or if a page in a different directory wants to use the template.

Django helps us out by allowing a variable name to be used in the extends tag. What we can do with this, then, is set a variable in the Context using dojox.moduleUrl.

If we're using the extends tag in an HTML environment, there's another factor to consider. Let's say we have a blog and there are two ways of viewing the page: a list view, and a detail view. Both of these views use a parent node that contains the page header, a menu, and a sidebar. We don't want the template system to have to redraw the DOM for their parent template, but how do we indicate that? There are two ways, one which is significantly better than the other.

The first is to use a string in the extends tag, outlined in the "easiest" way at the top. Putting "shared:" at the beginning of the string tells the extends tag to reuse the nodes between all other children that also want to share the parent.

The significantly better way is partly outlined in the section above on moduleUrl. You can use a variable containing a moduleUrl, but how do you tell the extends tag that you want to share the parent? Instead of just passing a moduleUrl call, when we have an extends tag that looks like {% extends parent %}, we can use an object that looks like this:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
new dojox.dtl.Context({
  parent: {
    url: dojo.moduleUrl("mymodule", "templates/template.html"),
    shared: true
  }
});

New Context Object Abilities

Unlike the page serving model of Django, we can keep our Context objects around between each template render. What we want to be able to do is quickly clone an existing context, and either reduce, or add to, the data in the object. To do this, there are two new functions:

  • filter: Just put the keys you want to key in its arguments to get only those keys back in a cloned Context.
  • extend: Just pass it an object to get a cloned Context containing all of the old keys, plus the new ones.

Some new functions are added to allow tags to communicate with the rendering object.

  • setThis: Sets the object on which to perform operations. Used by the attach attribute tag, for example.
  • getThis: Used by tags, gets the currently set this object.

Advanced Topics

While Django provides some documentation about how their compiler works, which you should definitely read before continuing this section, there are some areas in which things are done differently, such as tag and filter registration.

The other major difference in Dojo's implementation is the new HTML compiler. This section will go over how much of what we've done was accomplished, and will provide information to those curious about how the normal text-based compiler works.

Writing and Registering Tags and Filters

The actual filter functions are identical in terms of structure to the way they are implemented in Django. What this means is that Django's documentation is great for learning how to write a filter, and doesn't need to be rewritten here

Tags work in the following way: a registered tag is passed the parser object, and the tag string, and is tasked with returning an object that obeys a specific interface. This is how they are implemented in Django, so once again, their documentation will go a long way toward understanding how to write a tag.

The one distinct difference between the two system is rendering a NodeList. The expected behavior would be to pass it the context and buffer objects that the render function accepts, but you must also pass its own instance (this) as the third parameter. Without this, there could be unexpected behavior when rendering in an HTML environment. You should also clone nodelists and then render them, rather than taking the re-rending approach of Django's normal system. Because the rendering in HTML results in a DOM tree, re-rendering without cloning would change the same nodes over and over.

As mentioned earlier, there is a new tag type in the HTML version of the compiler. If you've registered an attribute-based tag, you will be passed a null parser object, and the string contents of the attribute tag are prefixed with the attribute name. What this means is that, for example, in the case of an attach attribute that looks like attach="varName", the string that is passed is "attach varName". This way, you can register your function with a regular expression, but see exactly what was matched.

The only piece of the interface you have to implement for the text-based version is the render function. If your tag will appear in an HTML environment, you must add an unrender and clone function. The unrender function is tasked with removing a node from the DOM if one was previously added, or to tell the "swallowed" nodes to unrender. The clone function should duplicate the object. Note that the same tags are used in both the text and HTML versions of the compiler, so your functions should be written with this in mind.

Registering tags/filters looks like this:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */
.geshifilter {font-family: monospace;}
.geshifilter .imp {font-weight: bold; color: red;}
.geshifilter .kw1 {color: #000066; font-weight: bold;}
.geshifilter .kw2 {color: #003366; font-weight: bold;}
.geshifilter .kw3 {color: #000066;}
.geshifilter .co1 {color: #009900; font-style: italic;}
.geshifilter .coMULTI {color: #009900; font-style: italic;}
.geshifilter .es0 {color: #000099; font-weight: bold;}
.geshifilter .br0 {color: #66cc66;}
.geshifilter .st0 {color: #3366CC;}
.geshifilter .nu0 {color: #CC0000;}
.geshifilter .me1 {color: #006600;}
.geshifilter .re0 {color: #0066FF;}

dojox.dtl.register.filter("my.module", "my.module.obj", ["fn", "anotherFn", [/RegExp/, "fnForRegex"]]);

Obviously, registering a tag would use dojox.dtl.register.tag. The first parameter is the require statement to call if we encounter one of these tags or filters. The second parameter is the base object on which these functions are stored. And the final parameter is an array of function names, or RegExp/function name pairs. Since some words are "reserved words", if we can't find the function on your object, we check for the function name with an underscore on the end of it.

If you write a custom set of tags and filters, the way to use them is exactly the same as in Django: the load tag. At the top of your template, add {% load namespace.tags namespace.filters etc %}. You should register your tags and filters in the same file as you declare them, and where you put the registration code doesn't matter, it can be before or after your object declarations.

How HTML Templates are Compiled

A quick note before we begin: If tags and filters within code are wrapped in comment tags, as in /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .html4strict .imp {font-weight: bold; color: red;} .html4strict .kw1 {color: #b1b100;} .html4strict .kw2 {color: #000000; font-weight: bold;} .html4strict .kw3 {color: #000066;} .html4strict .coMULTI {color: #808080; font-style: italic;} .html4strict .es0 {color: #000099; font-weight: bold;} .html4strict .br0 {color: #66cc66;} .html4strict .st0 {color: #ff0000;} .html4strict .nu0 {color: #cc66cc;} .html4strict .sc0 {color: #00bbdd;} .html4strict .sc1 {color: #ddbb00;} .html4strict .sc2 {color: #009900;} <!--{% tag %}--> then some browsers treat them as full nodes and will help a lot with node traversal. Just something worth knowing.

There are two steps to compiling a template: tokenizing a template, and parsing the tokens. The types of tokens can be found in dojox.dtl.text.types as well as dojox.dtl.html.types. They are: tag, variable, text, change, attribute, and element. Tags and variables are of course the foundation of our markup, text is just plain text that has no real bearing on markup, a change is when the parent node changes, an attribute is an HTML attribute, and an element is a DOMNode. During tokenizing, we actually disassemble the tree structure so that when the tokenization is done, none of the Nodes have a parent Node. The first Node encountered is the "root" Node. This is what we will use to insert our template into our document.

We build these tokens by traversing the Node tree, and when we reach a text node, tokenizing the contents if it has any tags or filters (note: this is where, if you use comments, this will never have to happen). After we're done tokenizing, we move on to parsing.

During parsing, all of the tokens become objects. Many of the tokens are always replaced with the same object. For example, the change type always becomes a dojox.dtl.html.ChangeNode, the element type always becomes a dojox.dtl.html.Node, a variable always becomes a dojox.dtl.html.VarNode, and text always becomes a dojox.dtl.html.TextNode. This leaves us with the attribute and tag types. An attribute token can go in two directions: If there is a tag registered for the name of that attribute, we call that function and the returned object replaces that token. If not, the token becomes a dojox.dtl.html.AttributeNode.

The major player here is the tag token. When the registered tag function is called, the tag doesn't always simply replace the token, it can actually "swallow" tokens until it finds a tag that it's looking for. A good example of this is the if/else/endif structure, where the function parses tokens until it reaches and endif tag. This is how nesting tags is possible. In the case of the if tag, while the first if tag is parsing tokens, looking for an else or endif tag, the parser might run into another if tag. This if tag now the one parsing tokens, looking until it finds the endif tag. Once it's done, that object replaces a whole range of tokens in the content that first if tag has been building. The parent if tag, when finished, returns an object to the parser that replaces a range of tokens, all of which are now in either one of the new objects returned by the if tags. No tokens ever disappear, they can all be accounted for in the various objects.

What we end up with is a very linear set of instructions. We have an array of objects, all our objects have a render function, and many of the objects contain lists of objects, and when their render function is called, they will call the render function on the objects they have swallowed. We have what's basically, a tree that always executes linearly, and is always called in the same order.

The Importance of our Buffer Objects

The render/unrender/clone functions used in registered tag objects all accept a buffer. Since we are using the same tag objects for both text and HTML, and we absolutely need a buffer to render an HTML template (we'll get to that in a second), a buffer will be both accepted and returned by every render/unrender function. When we render a text-based template, we use dojox.string.Builder to ensure the fastest string concatenation possible. But in the HTML version of the compiler, we have a very special Buffer.

Let's propose a situation that happens all the time when writing a template. It looks something like this:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}
<div>
  <h1>Title</h1>
  {% if name %}
  <div>Hello {{ name }}</div>
  {% else %}
  <div><a onclick="login">Please log in </a></div>
  {% endif %}
  < p>Welcome to our wonderful site</ p>
</div>

Now when someone uses this page, we have a situation where, when they first use the application, they are given a link to log in, but once their name is set, they're given a "Hello" message. While we didn't have to write it this way, we've created a situation now where only one of two sibling divs can be shown at a time. For those of you familiar with DOM, you know how complicated it is to insert a node if it's at neither the beginning nor the end of a parent. The HTML buffer allows us to overcome this problem with ease.

As a template is rendered, the setParent function is called on the buffer object, which changes the Node that is considered to be the current parent. Calling concat on the buffer uses this parent to insert the passed node beneath the current parent. Where to place it involves a little bit of magic. As shown in the example above, if we just use appendChild, the second time we render the template, the div in the else tag will end up after the paragraph tag, which is not where it should go.

What we do, then, is check to see if the node we're inserting exists in DOM (by looking at its parentNode). If it doesn't exist in DOM, we check to see if the buffer's currently set parent has any childNodes. If the parent has no child nodes, we can just use appendChild. If there are existing child nodes, we store the child in a cache. When a node arrives that has the same parentNode as the buffer's currently set parent, we take the cache and append it before this node.

Finally, when setParent is called, we get the cache from the old parent, and make sure all the nodes are appended to the end of it, before changing the parent.

The reason this works is that every time we render, every node calls the concat function. While this may seem like a speed hit, it's not only necessary to allow us to fill in those cached nodes, the function does no other work. So in the example above we start out with the opening div set as the current parent, and no value for name. The buffer's concat function is called with the h1, the div in the else block, and finally the p tag. The h1 is added using appendChild, since it's the first node, and the div and p are cached until setParent is called at the end of the render (in code, we actually call setParent twice at the end on the root node so that the cache gets flushed).

But now, if we re-render after giving name a value, the following happens: The h1 is passed to concat and nothing happens since it has a parentNode, and the cache is empty, then the div in the if block gets added to the cache since it has no parentNode, the div in the else block gets removed from the DOM by the unrender function of the if tag, and when the p tag is passed to concat, we see that it has a parentNode, and use insertBefore to place the items in the cache (our div) before the p tag.

Using this method of buffering, we don't need to worry at all about how the DOM is organized. Simply switching the parent and calling concat manages all the complex interactions we would otherwise have to account for.

Using the HtmlTemplate

Another quick note: This page is more to explain how this object works. Using the dojox.dtl.render.html.Render object to display your templates is a cleaner and more error-proof way of rendering and inserting your template into DOM.

Really the only thing you'll be doing with this object is creating an instance of it. You can either pass it a string to use as your template, or an object that has a toString method (we encourage the use of dojo.moduleUrl). Once you have an instance, you should pass it to the dojox.dtl._Widget.render function or the dojox.dtl.render.html.Render.render function.

Ultimately, even the widget's render function utilizes dojox.dtl.render.html.Render, so looking at the code for that will give you a great idea of how these functions are used. Let's actually display some of that code here:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
buffer = buffer || tpl.getBuffer();

You can pass whatever buffer you want to the HtmlTemplate's render function. What this means is that you could either subclass the current HtmlBuffer, or implement your own (not recommended) if you really want to do something crazy with the template architecture. But the HtmlTemplate object provides the getBuffer function so that you really don't need to worry about what Buffer object you should use.

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
if(this._tpl && this._tpl !== tpl){
  this._tpl.unrender(context, buffer);
}
this._tpl = tpl;

The HtmlTemplate object also provides the unrender function to unrender the template. If we want to replace a template, we should call this function on the current template before we call render on the new one.

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
var frag = tpl.render(context, buffer).getParent();

The render function builds a Node tree that's not necessarily in DOM. The getParent call here is actually on the HtmlBuffer, not on the HtmlTemplate. The HtmlTemplate provides a getRootNode function, which returns the first (and should be the only) top-level node in the rendered template. It is unlikely you'll need to use it, and one of the reasons we use it is to batch DOM changes (basically, checking to see if what's in the DOM is the root node, or whether we've removed it from DOM.

The HTML Renderer

This is a component that is completely unique to the Dojo implementation of the Django Template Language and is basically unrelated to the language itself. It is an object that is designed to render templates and make sure they are inserted into the DOM properly. It also add an important component for large templates, the ability to make sure that changes in the DOM are done in batches, though this ability is turned off by default, and even when turned on, can be done at a variety of levels

This is the object to use to make sure that rendering/unrendering happens properly, and what you want to use if you want to render a template, but don't want to use a widget to do it. All users are strongly encourage to use this to render a template if they are not using dojox.dtl._Widget to render their templates.

The object can be found at dojox.dtl.render.html.Render. It has two functions: setAttachPoint and render. It must have an attach point, which can be set in the constructor, or through the setAttachPoint function.

The setAttachPoint function simply sets the node we want to use to append the rendered template to.

The render function does several nice things:

  • Unrenders the previous template if we're now using a new template.
  • Creates a buffer if one hasn't been passed.
  • Manages the batch change functionality discussed above.
  • Inserts the rendered template into the document.

DojoX FX

Version
Version 0.1
Release Date
Release date: 08/02/2007
Project state:

prototype / expermental

Credits

Peter Higgins

Project description

dojox.fx provides a class of animation effects to use.

Dependencies:

dojox.fx requires dojo (core) and the dojo.fx package dojox.fx.easing requires only dojo core.

Documentation

existing API thus far:

dojox.fx.sizeTo - size a node about it's center to a new width/height

dojox.fx.addClass - animate the effects of applying a class to a node dojox.fx.removeClass - " " " " removing a class from a node dojox.fx.toggleClass - wrapper for addClass/removeClass

dojox.fx.easing - a collection of easing functions to use this is a "stand alone" class, and can be used via: dojo.require("dojox.fx.easing"); and provides: dojo.fx.easing.easeIn/easeOut/easeInOut to use in an _Animation easing: property

Installation instructions

checkout dojox/fx* from SVN and place into your sibling dojox folder.

see dojox/fx/tests/* for examples, and API tool for details.

DojoX GFX

Version
Version 1.100
Release Date
Release date: 08/01/2006
Project state:

beta

Credits

Eugene Lazutkin Kun Xi

Project description

Implementation of simple portable 2D graphics library.

Dependencies:

Dojo Core

Documentation

Currently it can be found here: http://docs.google.com/Doc?id=d764479_1hnb2tn

Installation instructions

Just include this line in your code:

dojo.require("dojox.gfx");

Drawing with GFX

dojox.gfx

 

What is it?

dojox.gfx is a cross-platform declarative interactive graphics package. It follows SVG loosely as the underlying model. At present time SVG and VML back-ends are implemented.

What does it do?

The dojox.gfx package provides graphics rendering and manipulation. Under Firefox, Opera and Safari dojox.gfx renders the final product as SVG, under IE it renders as VML.

Potentially the gfx package can allow you to create live and interactive graphing, a web based vector drawing program, view svg files in IE.

Underlying concepts

On conceptual level dojo.gfx has a simple declarative model: All basic primitives required for 2D graphics are defined:

  • 2D coordinates
  • 2D linear transformation matrices
  • colors

There is a surface, which serves as a visual rectangular container for shapes:

  • A web page can have several surfaces defined.
  • Each surface has its own local coordinate system:
    • (0, 0) point is in the left-top corner, where the X axis is horizontal pointing right, and the Y axis is vertical pointing down.
    • Positive direction of rotation is defined as counter-clockwise (CCW).

There is a notion of a shape description object, which represent a simple description of geometry, with corresponding shape objects. dojox.gfx supports following shapes:

  • Rectangle (optionally with rounded corners).
  • Circle.
  • Ellipse.
  • Line.
  • Polyline/polygon.
  • Path (the most versatile shape). It implements the full SVG path language.
  • Image.
  • Text.
  • TextPath (highly experimental at the moment).

Shapes support following set of properties:

  • Geometric properties:
    • Shape description (shape-specific).
    • Linear transformation specified by 3 x 3 2D matrix.
    • Font (only for text shapes).
  • Visual properties (not supported by the image shape):
    • Stroke (outline of a shape).
    • Fill (interior of a shape).

Shapes are stacked from bottom to top in an order of their definition. This z-order can be changed dynamically.

There is a group, which is a pseudo-shape. It combines other shapes (and possibly groups), and can be used to apply transformation to a group. All group members share a single z-order, but can be re-arranged within a group.

In order to draw a picture a programmer constructs a pseudo-DOM from a surface object, shapes, and groups, sets appropriate attributes, and picture is drawn automatically by a browser. Modifications of shapes change picture automatically. It is possible to attach event handlers to shapes using dojo.event.connect(). Following conventions are used:

While a path is the most universal geometric shape, which can emulate almost all other shapes (exceptions: image, and text shapes), all frequently-used shapes are provided as a convenience: rectangle (with optionally rounded corners), circle, ellipse, line, polyline/polygon.

All shape description properties are defined using a duck-typing technique. Incomplete shape description definitions are supported. All missing members will be taken from corresponding default shape definitions listed in common.js or from the current shape description object. Example: rect.setShape({width: 200}) - all missing members will be taken from dojo.gfx.defaultRect object making it equivalent to rect.setShape({x: 0, y: 0, width: 200, height: 100, r: 0}).

All shape description objects and visual property objects have a member called "type", which uniquely identifies a property type. This is a provision for a possible serialization.

Certain shape properties can be defined using shortcuts:

  • Path can be defined as a string of path commands (more on path later):
    • path.setShape("m 0,0 l 100, 100 e") is equivalent to path.setShape({path: "m 0,0 l 100, 100 e"}).
  • Polylines/polygons can be defined as an array of points:
    • poly.setShape([{x: 0, y: 0}, {x: 100, y: 100}]) is equivalent to poly.setShape({points: [{x: 0, y: 0}, {x: 100, y: 100}]}).
  • Stroke can be defined by specifying a color as a string:
    • shape.setStroke("black") is equivalent to shape.setStroke({color: "black"}).

All methods without an apparent return type return their object itself. It is used for chaining multiple operations:

  • surface.createRect({x: 100, y: 50}).setFill("red").setStroke("blue");

DojoX Grid

For user convenience, Grid documentation is located in Part 2 - Dijit.

DojoX I/O

Version
Version 0.1
Release Date
Release date: 08/29/2007
Project state:

experimental

Credits

Bryan Forbes

Project description

Dependencies:

DojoX IO has no dependencies, outside of Dojo Core.

Documentation

Installation instructions

Grab the following from the Dojo SVN Repository: http://svn.dojotoolkit.org/var/src/dojo/dojox/trunk/io/*

Install into the following directory structure: /dojox/io/

...which should be at the same level as your Dojo checkout.

Additional Notes

The information contained in this README does not pertain to DojoX XHR IFrame Proxy. For that information see proxy/README.

DojoX Image

Version
Version 0.9
Release Date
Release date: 08/29/07
Project state:

prototype | expermental

Credits

Peter Higgins

Shane O'Sullivan

Project description

A class to provide a common API for images, and home for image related Widgets.

Dependencies:

LightBox: dojo core, dojox.fx and optionally dojox.data. uses either tundra or soria theme, no standalone icons.

ThumbnailPicker: dojo core, dojox.fx and optionally dojox.data. Uses standalone icons.

SlideShow: dojo core, dojox.fx and optionally dojox.data. Uses standalone icons.

Gallery: dojo core, dojox.fx and optionally dojox.data. Uses SlideShow and ThumbnailPicker. Uses standalone icons.

Documentation

Installation instructions

there is no rollup for dojox.image, so you simply need the dojox/image folder and all of it's children to live in your dojox root.

Additional Notes

LightBox: currently works as individual items, and grouped items, but usage of dojox.data.FlickrStore is broken because all images are grouped in one master group, the API is subject to change, and is marked accordingly.

ThumbnailPicker: displays images in either a horizontal or vertical strip of small thumbnail sized icons. Image data is read from a dojo.data.api.Read data store.

SlideShow: displays a single image at a time, and provides controls to move between them, as well as running a slideshow where images change at a defined interval. Image data is read from a dojo.data.api.Read data store.

Gallery: wraps the ThumbnailPicker and SlideShow widgets, linking them together, so that a click on a thumbnail image results in the SlideShow changing its image. Image data is read from a dojo.data.api.Read data store.

Hoping to implement: Carossel, using a common API provided by dojox.image.Pane (?)

Gallery

dojox.image.Gallery is a widget that displays a series of thumbnail sized images, for quick browsing and selection, and a single large image. It provides a number of navigation controls for moving between images, and for playing an automated slideshow.

The Gallery works as a wrapper around two other widgets, dojox.image.ThumbnailPicker and dojox.image.SlideShow. It provides the following features:

  • A row of thumbnail sized images. Clicking on a thumbnail image shows a large version of that picture in the main image pane. Navigation controls are placed to the left and right of the thumbnails, to move between them.
  • A large image pane, displaying one image at a time. Navigation controls when the mouse pointer is over the image pane, allowing the user to move backwards and forwards between the images, and to start an automated slideshow. The size of the image pane is configurable using the imageWidth and imageHeight attributes.
  • Reads image data from an implementation of the dojo.data.api.Read interface. This means it is completely decoupled from any particular data source, and can be used with any data source at all as long as it is a valid dojo.data store. For example, the Gallery can display images based on data in a simple text file using dojo.data.ItemFileReadStore, can show Flickr images using dojox.data.FlickrRestStore, or images from Picasa Web Albums using dojox.data.PicasaStore.
  • Displays the title of each image, using a template that can be simply overridden.
  • Links an image to a URL, so that clicking the image navigates to a specified web address. This is optional.

Examples

Creating a Gallery and setting a simple data source

<script type="text/javascript">
  //Define the attribute names used to access the items in the data store
  var itemNameMap = {imageThumbAttr: "thumb", imageLargeAttr: "large"};

  //Define the request, with no query, and a count of 20, so 20 items will be 
  //requested with each request
  var request = {query: {}, count: 20};
  dijit.byId('gallery1').setDataStore('imageItemStore', request, itemNameMap);
</script>

<div id="gallery1" dojoType="dojox.image.Gallery"></div>
<div jsId="imageItemStore" dojoType="dojo.data.ItemFileReadStore" url="images.json"></div>

Creating a Gallery with a FlickrRestStore

<script type="text/javascript">
  //Declare a FlickrRestStore data store.  This is used to access images from the 
  //Flickr (www.flickr.com) photo sharing website.
  var flickrRestStore = new dojox.data.FlickrRestStore();

  //Define the request, with a count of 20, so 20 items will be requested with 
  //each request. The query specifies information used to access Flickr, 
  //including a user ID (optional) and API key (required).  
  //You can also specify a sort order, tags to search for, and the matching 
  //mode for the tags, which can be "any" or "all", which equate to boolean "or" 
  //and a boolean "and" respectively
  var request = {
        query: {
            userid: "44153025@N00",//The Flickr user id to use
            apikey: "8c6803164dbc395fb7131c9d54843627",//An API key is required.
            sort: [{
                    descending: true //Use descending sort order, ascending is default.
                }
		],
                tags: ["superhorse", "redbones", "beachvolleyball","dublin","croatia"],
		tag_mode: "any" //Match any of the tags
         },
         count: 20
  };
  
  dijit.byId('gallery1').setDataStore('imageItemStore', request, itemNameMap);
</script>
  <div id="gallery1" dojoType="dojox.image.Gallery"></div>

Setting the image width and height

<div id="gallery1" dojoType="dojox.image.Gallery" imageHeight="400" imageWidth="600"></div>

Setting Page Size and AutoLoad

It is possible to define how many images are requested from the data store with each request. This affects the performance. The larger the page size, the slower a request may be, but there will be fewer requests. The smaller the page size, the quicker a request may be, but there will be more requests. It is specified by altering the pageSize attribute.

By default, the Gallery will preload one page of images at a time. This gives a better user experience, as the user will have to wait less time to view an image. However, it may download more images than the user wishes to view. The autoloading of images can be disabled by setting the autoLoad attribute to "false".

<div id="gallery1" dojoType="dojox.image.Gallery" pageSize="50" autoLoad="false" ></div>

Changing the time interval in a SlideShow

The images in the large pane of the Gallery can be made to run a slide show by clicking its "Play" button. The amount of time between changing images can be configured by setting the slideshowInterval attribute to the number of seconds required.


<div id="gallery1" dojoType="dojox.image.Gallery" slideshowInterval="5" ></div>

Lightbox

TODO: Fill in info on dojox.image.Lightbox / finish / elaborate more

dojox.image.Lightbox

A clone of Lightbox2 for dojo. A Lightbox displays images in a modal dialog. It can display single images, or image groups, with smooth transition animations in between. You can populate the Lightbox group from a dojo.data datastore, or from markup.

Basic Usage

Being a clone, it is almost completely backwards compatible with Lightbox2. The Lightbox will display the image linked to in an anchor tag. Using a Lightbox is as easy as adding a dojoType to a link tag:

show image1

Grouping images together is as simple as adding a group="" attribute. In this example, the first image is a single view, the other two popup a Lightbox with [previous] and [next] navigation, and 2 images in a group.

show image1
show image2
show image3

Your anchor text can be anything: a thumnail of the image to show, text, etc. It can also be anywhere on a page.

With JavaScript disabled, the natural link to the image will pass through, and display the image natively

Slideshow

[inline:slideshow.jpg]

The dojox.image.SlideShow widget displays a series of images, one at a time. It provides controls to move from image to the next or the previous image. It can also run an automated slideshow, moving from one image to the next every specified number of seconds.

Features

The Slideshow widget has the following features.

  • Displays a single image at a time
  • Provides navigation controls to move between images, and to start/stop the automated slideshow.
  • Can optionally pre-load images in the background so that there is no visible delay when viewing images. While this uses more bandwidth, it provides a far better user experience.
  • Links an image to a URL, so that clicking the image navigates to a specified web address. This is optional.
  • Reads image data from an implementation of the dojo.data.api.Read interface. This means it is completely decoupled from any particular data source, and can be used with any data source at all as long as it is a valid dojo.data store. For example, the Slideshow can display images based on data in a simple text file using dojo.data.ItemFileReadStore, can show Flickr images using dojox.data.FlickrRestStore, or images from Picasa Web Albums using dojox.data.PicasaStore.
  • Displays the title of each image, using a template that can be simply overridden.

Examples

Setting the Display Interval

It is possible to define the number of seconds between image transitions when running an automated slideshow. To do this, set the slideshowInterval attribute. For example, to set a three second interval between changing the displayed image, use the code below

<div dojoType="dojox.image.SlideShow" id="slideshow1" slideshowInterval="3"> </div>

Overriding the Title Template

The Slideshow widget has a default title template that is used to display the title of the current image, as well as it's relative position. To do this, set the titleTemplate attribute. The supported place holders are :

  • @title - The title of the image is placed here
  • @current - The current index of the image is placed here
  • @total - The total number of images is placed here.

For example:

<div dojoType="dojox.image.SlideShow" id="slideshow1" 
    titleTemplate="My title is '@title', this is image @current out of @total"
> </div>

Setting the Maximum Image Dimensions

To override the default height and width of the widget, set the imageHeight and imageWidth attributes. Images are automatically scaled to fit either the max height or width, depending on which of their dimensions is greater. E.g

<div dojoType="dojox.image.SlideShow" id="slideshow1" 
    imageWidth="600" imageHeight="300"
> </div>

Disabling the AutoLoad

The Slideshow widget automatically preloads a number of images in the background. While this generally provides a better user experience, it uses more bandwidth, so some users may want to disable it. To do so, set the autoLoad parameter to "false". e.g.

<div dojoType="dojox.image.SlideShow" id="slideshow1" 
    autoLoad="false"
> </div>
This causes a delay when the user attempts to view an image, since it must wait to be loaded.

Disabling Resizing to Fit the Image

By default, if an image is less tall than the Slideshow widget, the widget resizes itself to fit to the image. In some circumstances this may be undesireable, such as when using an inflexible, fixed page layout. To disable this resizing behaviour, set the fixedHeight attribute to "true", e.g.

<div dojoType="dojox.image.SlideShow" id="slideshow1" 
    fixedHeight="true"
> </div>

Setting the Data Store on the Slideshow

The Slideshow widget reads the image information from dojo.data objects. To set the data source for the Slideshow widget, first create one of the available data stores, such as the dojo.data.ItemFileReadStore or dojox.data.FlickrRestStore. Next, create a request object, which optionally contains a query. e.g.

<div dojoType="dojox.image.SlideShow" id="slideshow1" > </div>
<div jsId="imageItemStore" dojoType="dojo.data.ItemFileReadStore" url="images.json"></div>
<script type="text/javascript">
dojo.addOnLoad(function() {
  //Define the request, saying that 20 records should be fetched at a time, 
  //and to start at record 0
  var request= {count:20, start:0};

  //Tell the widget to request the "large" parameter, as different 
  //stores may use different parameter names
  var itemNameMap = {imageLargeAttr: "large"};

  //Call the setDataStore function, passing it the data store, the request object, 
  //and the name map.
  dijit.byId('slideshow1').setDataStore(imageItemStore, request, itemNameMap);
});

</script>

Subscribing to Slideshow Events

The Slideshow publishes information about its state, that can be subscribed to using Dojo's Publish/Subscribe system. Two pieces of information are published to a named topic:

  • Current image - whenever the displayed image changes, a JSON object with the following attributes:
    • index - The current numeric index of the image, that is, it's index in the data store
    • title - The string title of the image, if any
    • url - The URL of the image
    The name of the topic is retrieved by calling the getShowTopicName method on the widget
    dojo.subscribe(
        dijit.byId('slideshow1').getShowTopicName(), 
        function(packet) {
          alert("Got index: " + packet.index 
                     + ", url: " + packet.url 
                     + ", and title: " + packet.title);
    });
  • Loaded Image - when an image finishes loading, whether in the background, or the currently displayed image, information is published about it. The name of the topic is retrieved by calling the getLoadTopicName on the widget. A Number is published, which is the index of the image in the data store. E.g.
    dojo.subscribe(
        dijit.byId('slideshow1').getLoadTopicName(), 
        function(index) {
          alert("Got index: " +index);
    });

Further Examples

For a full example of the Slideshow, see the test file at http://archive.dojotoolkit.org/nightly/dojotoolkit/dojox/image/tests/tes...

ThumbnailPicker

[inline:thumbpicker.gif]

The ThumbnailPicker is a widget that displays a series of images either horizontally or vertically, with controls to page through the images. It reads its image data from data stores, that is, implementations of the dojo.data.api.Read API.

When an image is clicked by the user, information regarding that image is published to a dojo topic, which can be used to integrate the ThumbnailPicker with other objects on the page.

The ThumbnailPicker can be configured in a number of ways:

  • Number of visible images
  • Data source
  • Can be horizontal or vertical
  • Enabling/disabling following hyperlinks when an image is selected
  • Notification of load status for images

Examples

Number of Visible Images

To set the number of visible images, and thereby the width or height of horizontal and vertical widgets respectively, set the <b>numberThumbs</b> attribute, e.g.

<div dojoType="dojox.image.ThumbnailPicker" id="picker1" numberThumbs="4"> </div>

Setting the Data Source

To set the data source for the ThumnailPicker widget, first create one of the available data stores, such as the dojo.data.ItemFileReadStore or dojox.data.FlickrRestStore. Next, create a request object, which optionally contains a query. e.g.

<div dojoType="dojox.image.ThumbnailPicker" id="picker1" > </div>
<div jsId="imageItemStore" dojoType="dojo.data.ItemFileReadStore" url="images.json"></div>
<script type="text/javascript">
dojo.addOnLoad(function() {
  //Define the request, saying that 20 records should be fetched at a time, 
  //and to start at record 0
  var request= {count:20, start:0};
  //Tell the widget to request the "thumb" parameter, as different 
  //stores may use different parameter names
  var itemNameMap = {imageThumbAttr: "thumb"};

  dijit.byId('picker1').setDataStore(imageItemStore, request, itemNameMap);
});

</script>

Using a Vertical Layout

To make the ThumbnailPicker display itself vertically, set the isHorizontal attribute to "false". To leave it as horizontal, either omit the isHorizontal attribute, or set it to "true", e.g.

<div dojoType="dojox.image.ThumbnailPicker" id="picker1" isHorizontal="false"> </div>

Enabling/disabling following hyperlinks

To enable following a hyperlink when a thumbnail image is clicked, set the useHyperlink attribute to "true". By default it is false. When hyperlinks are enabled, by default the URL is opened is a new window. To open the link in the current window, set the hyperlinkTarget attribute to "this". e.g.

<div dojoType="dojox.image.ThumbnailPicker" id="picker1" useHyperlink="true" hyperlinkTarget="this"> </div>

Notification of load status for images

The ThumbnailPicker can display a notification for each image stating whether another version of it has loaded or not, for example when it is combined with the dojox.image.Slideshow widget. When this is enabled, the ThumbnailPicker relies on other code calling it's markImageLoaded method to change the notification from its loading state to loaded state.

To enable the load state notifier, set the useLoadNotifier to "true". By default, it is disabled, since it only really makes sense to use it in combination with other widgets or elements on a page. e.g.

<div dojoType="dojox.image.ThumbnailPicker" id="picker1" useLoadNotifier="true"> </div>

Full Example

This example will put a horizontal and a vertical dojox.image.ThumbnailPicker widget on a page, with a variety of settings, and using separate data stores.
<html>
<head>
  <style type="text/css">
    @import "../resources/ThumbnailPicker.css";
  </style>
  
  <script type="text/javascript" src="dojo/dojo.js" djconfig="parseOnLoad:true, isDebug: true, defaultTestTheme:'soria'"></script>
  <script type="text/javascript" src="/dijit/tests/_testCommon.js"></script>

  <script type="text/javascript" src="dojox/image/ThumbnailPicker.js"></script>
  
  <script type="text/javascript">
    /*
      The FlickrRestStore and the ItemFileReadStore data stores will be used to provide data to the widgets
    */
    dojo.require("dojox.data.FlickrRestStore"); 
    dojo.require("dojo.data.ItemFileReadStore"); 
    dojo.require("dojo.parser");  // find widgets
    
    /*
      Initializes the ThumbnailPicker with a data store that reads from the Flickr REST APIs.
    */
    function initFlickrWidget() {
      //Create a new FlickrRestStore
      var flickrRestStore = new dojox.data.FlickrRestStore();
      
      //Create a request object, containing a query with the 
      //userid, apikey and (optional) sort data.
      //Extra query parameters 'tags' and 'tag_mode' are also 
      //used to further filter the results
      var req = {
        query: {
          userid: "44153025@N00",//The Flickr user id to use
          apikey: "8c6803164dbc395fb7131c9d54843627",//An API key is required.
          sort: [
               {
              descending: true //Use descending sort order, ascending is default.
              }
               ],
          tags: ["superhorse", "redbones", "beachvolleyball","dublin","croatia"],
          tag_mode: "any" //Match any of the tags
        },
        start: 0, //start at record 0
        count: 20 //request 20 records each time a request is made
      };
      
      //Set the flickr data store on two of the dojox.image.ThumbnailPicker widgets
      dijit.byId('thumbPicker1').setDataStore(flickrRestStore, req);
    }
    
    /*
      Initializes the second ThumbnailPicker widget with a data store that
      reads information from a JSON URL.  This also tells the ThumbnailPicker
      the name of the JSON attributes to read from each data item retrieved
      from the JSON URL.
    */
    function initItemStoreWidget(){
      var itemRequest = {
        query: {},
        count: 20
      };
      var itemNameMap = {
        imageThumbAttr: "thumb",
        imageLargeAttr: "large"
      };
      
      //Set the dojo.data.ItemFileReadStore on two of the dojox.image.ThumbnailPicker widgets
      //Note the use of the 'itemNameMap', which tells the widget what attributes to
      //read from the store.  Look in the 'images.json' file in the same folder as this
      //file to see the data being read by the widget.
      dijit.byId('thumbPicker2').setDataStore(imageItemStore, itemRequest, itemNameMap);
    }
    
    //Subscribe to clicks on the thumbnails, and print out the information provided
    function doSubscribe(){
      function updateDiv(packet){
        alert("You selected the thumbnail:"
            + "Index: " + packet.index
            + "Url: " + packet.url
            + "Large Url: " + packet.largeUrl
            + "Title: " + packet.title
            + "Link: " + packet.link)
            ;
      }
      
      //When an image in the ThumbnailPicker is clicked on, it publishes
      //information on the image to a topic, whose name is found by calling
      //the 'getTopicName' function on the widget.
      dojo.subscribe(dijit.byId('thumbPicker1').getTopicName(), updateDiv);
      dojo.subscribe(dijit.byId('thumbPicker2').getTopicName(), updateDiv);
    }
    
    dojo.addOnLoad(initFlickrWidget);
    dojo.addOnLoad(initItemStoreWidget);
    dojo.addOnLoad(doSubscribe);
  </script>
</head>
<body>
  <h2>From FlickrRestStore:</h2>
  This ThumbnailPicker should have 8 thumbnails, with each of them linking
  to a URL when clicked on, changing the current page.  The cursor should also change when over an image.
  The widget is laid out in the default horizontal layout.
  <div id="thumbPicker1" dojoType="dojox.image.ThumbnailPicker" numberThumbs="8" useHyperlink="true" 
        hyperlinkTarget="this"></div>

  <h2>From ItemFileReadStore:</h2>
  This ThumbnailPicker should have 5 thumbnails. Clicking on a thumbnail should NOT
  open a URL, and the cursor should not change when over an image that is not an arrow.
  The widget is laid out in a vertical layout.
  
  <div id="thumbPicker2" dojoType="dojox.image.ThumbnailPicker" numberThumbs="5" isClickable="false"
        isHorizontal="false"></div>
  
  Create an ItemFileReadStore that reads data from the file "images.json".  This is used by the ThumbnailPicker "thumbPicker2"
  <div jsId="imageItemStore" dojoType="dojo.data.ItemFileReadStore" url="images.json"></div>

</body>
</html>

The code above would result in a page that looks like the image below:

[inline:testPage.gif] Further examples can be found in the test file at http://archive.dojotoolkit.org/nightly/dojotoolkit/dojox/image/tests/tes...

TODO: put info on dojox.image.ThumbnailPicker

DojoX Layout

Version
Version 0.003
Release Date
Release date: 2007-06-01
Project state:

[expermental]

Credits

Pete Higgins Fredrik Johansson

Project description

placeholder for dijit.layout extensions. Currently only:

dojox.layout.FloatingPane - an extension on TitlePane for drag/drop operation, "docking" [minimize/maximize], and [soon] resizing.

dojox.layout.ResizeHandle - resize handle to attach to a domNode. works well on normal domNodes, but will require adding a resizeTo(w,h) method to any widget you wish to use it on. [experimental]

dojox.layout.ContentPane - an extension on dijit ContentPane. Supports inline scripts, inline styles, relative path adjustments and having a table tag as domNode.

dojox.layout.StretchPane -- an extension on dojox ContentPane Supports animated resizing and calculated sizes for nodes see test files for examples.

Dependencies

require Dojo Core, Dojo Base (fx), and Dijit.TitlePane, Dijit.layout.ContentPane

Installation:

checkout:

http://svn.dojotoolkit.org/var/src/dojo/dojox/layout/* http://svn.dojotoolkit.org/var/src/dojo/dijit/*

and require via: dojo.require("dojox.layout.FloatingPane"); or: dojo.require("dojox.layout.ContentPane");

Basic Usage:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}
<div dojoType="dojox.layout.FloatingPane" title="my title">
                Content To be Floated
        </div>
/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}
<div dojoType="dojox.layout.ContentPane"
                 adjustPaths="true"
                 renderStyles="true"
                 executeScripts="true"
                 href="my/page/containing/scripts/and/styles/in/a/sub/folder.html"
        >

                Initial content, will be replace by href.
                paths in folder.html will be adjusted to match this page
        </div>
/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}
<div id="panex" dojoType="dojox.layout.StretchPane"
                style="border: solid blue 1px; overflow:hidden"
                x="90" y="60" w="40+0.2*w" h="40"
     >
Stretch horiz</div>

DojoX Offline

Note: This document is a copy of the Dojo Offline Tutorial hosted on Google Docs as of August 18, 2007. Every effort is made to keep this copy in sync with the original

Dojo Offline is an open-source toolkit that makes it easy to create sophisticated, offline web applications. It sits on top of Google Gears, a plugin from Google that helps extend web browsers with new functionality. Dojo Offline makes working with Google Gears easier; extends it with important functionality; creates a higher-level API than Google Gears provides; and exposes developer productivity features. In particular, Dojo Offline provides the following functionality:

  • An offline widget that you can easily embed in your web page with just a few lines of code, automatically providing the user with network feedback, sync messages, offline instructions, and more
  • A sync framework to help you store actions done while offline and sync them with a server once back on the network
  • Automatic network and application-availability detection to determine when your application is on- or off-line so that you can take appropriate action
  • A slurp() method that automatically scans the page and figures out all the resources that you need offline, including images, stylesheets, scripts, etc.; this is much easier than having to manually maintain which resources should be available offline, especially during development.
  • Dojo Storage, an easy to use hashtable abstraction for storing offline data for when you don't need the heaviness of Google Gear's SQL abstraction; under the covers Dojo Storage saves its data into Google Gears
  • Dojo SQL, an easy to use SQL layer that executes SQL statements and returns them as ordinary JavaScript objects
  • New ENCRYPT() and DECRYPT() SQL keywords that you can mix in when using Dojo SQL, to get transparent cryptography for columns of data. Cryptography is done on a Google Worker Pool thread, so that the browser UI is responsive.
  • Integration with the rest of Dojo, such as the Dojo Event system

Dojo Offline is built to work with the 0.9 release of Dojo, and will not work with older versions of Dojo, such as 0.4. It also requires the Google Gears plugin to function; if users do not have it installed Dojo Offline will prompt users to download it.

Using Dojo Offline

Let's dive in and start using Dojo Offline.

Download Dojo Offline SDK

First download the Dojo SDK and unzip it.

SDK Layout

When you unzip the SDK, you will find the following directories:

  • dojo - The core of Dojo
  • dijit - The Dojo widget system, named Dijit
  • dojox - Optional Dojo extensions

Dojo Offline is an optional Dojo extension, and is therefore located in the dojox directory.

If you are looking to track down Dojo Offline's source code, most of it is in dojox/off/. The Dojo SQL layer is in dojox/_sql/, while Dojo Storage is in dojox/storage/. An autogenerated, JavaDoc-like API is available. When looking at the API docs or source code, many advanced options are available to deeply customize Dojo Offline; you can almost always safely ignore these unless you are an advanced user, and they usually say "For advanced usage; most developers can ignore this" in their documentation.

Demos

Dojo Offline has three main demos, a Hello World example, a more complicated web-based editor named Moxie that includes an example server-side written in Java, and a demo of Dojo Offline's SQL cryptography. You can play with hosted versions of the Hello World example here; a hosted version of the Moxie editor here; and the SQL cryptography demo here

If you want to study the demo examples' source code, the Hello World example is located in dojox/off/demos/helloworld/, while the Moxie editor is located in dojox/off/demos/editor/. You can see the SQL cryptography demo source in dojox/_sql/demos/customers/customers.html.

The Hello World example has no server-side requirement; Moxie, however, includes a full Java server-side that you can use as a template and scaffolding. Running the server-side of Moxie is simple. Make sure you have Java 1.5+ installed, and then just type: java -jar editor.jar
while inside the directory dojox/off/demos/editor/server/, and the Moxie server-side will start running, with an embedded web-server (Jetty) and relational database (Derby) already set up for you.

In a web-browser, you can now go to the following URL:

http://localhost:8000/dojox/off/demos/editor/editor.html
to run Moxie against the local server you just started.

For more details on the server-side portion of Moxie and how to build see the README file at dojox/off/demos/editor/server/README.

Creating an Offline Application

Now that you have awareness of the SDK, it's file layout, and the provided demos, let's get down to illustrating what you need to do to create an offline-aware application using Dojo Offline.

Bring in Dojo and Dojo Offline

For our example source code we will pretend that you are creating your application with the Dojo Offline SDK in a subdirectory named offline-sdk.

First, bring in Dojo and Dojo Offline: /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}

<script type="text/javascript" src="offline-sdk/dojo/dojo.js" djConfig="isDebug: true"></script>
<script type="text/javascript" src="offline-sdk/dojox/off/offline.js"></script>
isDebug is a useful flag that when set to true will print out debug messages, and when set to false will hide them. In your own code you can add console.debug("some message"); to have these printed out to help with debugging. If you are using Firefox in conjunction with Firebug then these messages will print out to the Firebug console; otherwise they will print on to the web page itself, such as in Internet Explorer.

Notice that we do not bring in Dijit; Dojo Offline has no dependencies on Dijit, the Dojo Widget system, helping to keep your code size smaller. We only include it in the SDK because Moxie, the example editor, uses the Dijit rich text editor.

Next, bring in Dojo and Dojo Offline's style sheets:
/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}

<style type="text/css">
        @import "offline-sdk/dojo/resources/dojo.css";
                       
        /* Bring in the CSS for the default Dojo Offline UI widget */
        @import "offline-sdk/dojox/off/resources/offline-widget.css";
</style>

Dojo Offline Widget

Dojo Offline includes a default offline widget that does much of the hard work of providing a good offline UI to your end-users. It updates the user with online and offline feedback; provides sync messages; and delivers help and instructions on using your application offline. If Dojo Offline did not provide these you would have to roll them yourself, so providing a default UI makes developing offline applications easier.

Getting this widget is as easy as adding a bit of HTML to your page, with a special, predefined ID:
/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}

<!--
        Place the Dojo Offline widget here; it will be automagically
        filled into any element with ID "dot-widget".
-->

<div id="dot-widget"></div>
When the page loads, Dojo Offline will automatically find an element with the ID dot-widget and automagically embed the offline widget.

The offline widget is a small, self-contained unit that provides feedback to the user, giving you a bunch of great functionality for free.

Here is a screenshot of Moxie, with the offline widget circled:

For more screenshots of the offline widget and a full description of what it gives you for free, see here. It is recommended that you use the offline widget in your own offline applications unless you are an advanced developer; information on customizing its look and feel, including dropping it, can be found here.

JavaScript

Let's take a look at our JavaScript now. All you have to do is set your application name:
/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}

// set our application name
dojox.off.ui.appName = "Example Application";

The Offline Widget will use your application name to customize it's user-interface; that is how the offline widget, for example, can insert "Learn How to Use Hello World Offline" into its instructions without you having to manually edit the offline widget's HTML.

Slurp! Bringing Your Application's Files Offline

For your application to be able to work offline, you must make sure that all of your JavaScript, HTML, CSS, etc. are available even when away from the network. While the browser may have these in its cache, you can't depend on this. The underlying Google Gears plugin that Dojo Offline uses makes it possible to specify what files you want to have available when offline, such as your JavaScript.

It can be tedious, however, to build up a full list of all your files, especially if you have many images and supporting libraries and are doing rapid development. Dojo Offline provides a single method, named slurp(), that will slurp the page and automatically figure out all of the resources you need to have available offline:
/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
// automatically "slurp" the page and
// capture the resources we need offline
dojox.off.files.slurp();
The slurp() method is awesome; it will automatically scan your page and quickly figure out all your JavaScript and CSS files; grab any IMG tags in your source; and even grab any background URLs you might have in your attached CSS. The only thing it doesn't do is look at inline styles or scan your JavaScript for dynamic additions.

You can also manually add files if you want or need to; more information here.

slurp() has a nice debug method if you want to see all the files that have been slurped; make sure that Dojo's isDebug is true, and call dojox.off.files.printURLs() after the page and Dojo Offline are finished loading (more details on how to know when Dojo Offline is done loading in the next section).

During development, as you hit your offline application, it will always be pulled from the Google Gears file cache first, even if you are online. This can sometimes make development tricky and tedious. If you just made a change locally, hitting your web application, even if you are offline, will cause the older version to be pulled in first.

To make this process easier, I have created a bookmarklet that you can drag to your links toolbar in your browser. View this page to get the bookmarklets for Firefox and Internet Explorer.

These bookmarklets clear the Google Gears cache, removing all of the files that slurp() added offline. Press it during development when you have made a new change that you want to show up; you must press it when you are at your web application (i.e. it won't work if you just press it when you are at a different or blank page). Then, refresh the page to see your change.

Knowing When Dojo Offline Can Be Used

Dojo Offline can't be used until the page is finished loading and Dojo Offline itself is finished initializing (such as the offline widget finishing being placed into the page). You should wait until the page and Dojo Offline are finished loading, and then your application can start doing its own custom thing:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
var myApp = {
        initialize: function(){
                alert("Dojo Offline and the page are finished loading!");
        }
}
// Wait until Dojo Offline is ready
// before we initialize ourselves. When this gets called the page
// is also finished loading.
dojo.connect(dojox.off.ui, "onLoad", myApp, "initialize");
                       
// tell Dojo Offline we are ready for it to initialize itself now
// that we have finished configuring it for our application
dojox.off.initialize();

myApp is some object that will hold all of the methods for our application; myApp.initialize() is the method in particular that would initialize our application on page and Dojo Offline load in some way. We use dojo.connect to connect to the dojox.off.ui.onLoad event; when this fires, myApp.initialize() is called. At this point we could begin to manipulate the DOM on the page, since it is loaded, or do further actions specific to your application; in this case we just print an alert message.

The final call to dojox.off.initialize() is there to tell Dojo Offline that we are done configuring it, and that it can now initialize itself; we don't want Dojo Offline to initialize itself before we have set our appName and called slurp(), for example, so this final call tells Dojo Offline that we are finished doing configuration.

Let's look at all of our code so far in this tutorial:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}
<html>
        <head>
                <script type="text/javascript" src="offline-sdk/dojo/dojo.js" djConfig="isDebug: true"></script>
                <script type="text/javascript" src="offline-sdk/dojox/off/offline.js"></script>
                <style type="text/css">
                        @import "offline-sdk/dojo/resources/dojo.css";
                       
                        /* Bring in the CSS for the default
                           Dojo Offline UI widget */
                        @import "offline-sdk/dojox/off/resources/offline-widget.css";
                </style>
                <script>
                        // set our application name
                        dojox.off.ui.appName = "Example Application";
                        // automatically "slurp" the page and
                        // capture the resources we need offline
                        dojox.off.files.slurp();
                        var myApp = {
                                initialize: function(){
                                        alert("Dojo Offline and the page are finished loading!");
                                }
                        }
                        // Wait until Dojo Offline is ready
                        // before we initialize ourselves. When this gets called the page
                        // is also finished loading.
                        dojo.connect(dojox.off.ui, "onLoad", myApp, "initialize");
                       
                        // tell Dojo Offline we are ready for it to initialize itself now
                        // that we have finished configuring it for our application
                        dojox.off.initialize();
                </script>
        </head>
       
        <body>
                <!--
                        Place the Dojo Offline widget here; it will be automagically
                        filled into any element with ID "dot-widget".
                -->

                <div id="dot-widget"></div>     
               
        </body>
</html>

Important: I've noticed over and over in the frameworks I have created, such as Dojo Storage and the Really Simple History library, that developers get confused about an important point. Notice that configuring Dojo Offline must be done before the page loads:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}
<script>
        // set our application name
        dojox.off.ui.appName = "Example Application";
        // automatically "slurp" the page and
        // capture the resources we need offline
        dojox.off.files.slurp();
        // ...
        // Wait until Dojo Offline is ready
        // before we initialize ourselves. When this gets called the page
        // is also finished loading.
        dojo.connect(dojox.off.ui, "onLoad", myApp, "initialize");
       
        // tell Dojo Offline we are ready for it to initialize itself now
        // that we have finished configuring it for our application
        dojox.off.initialize();
&