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:
Alex Russell Greg Wilkins
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.
Needs a cooperating Bayeux server
Use this library with (preferably through) an existing Cometd server.
beta
Implementation of simple charting library based on dojox.gfx/dojox.gfx3d.
Needs documentation
Tom Trenka
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).
DojoX Collections has no dependencies, outside of Dojo Core.
See the API documentation for Dojo (http://dojotoolkit.org/api).
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.
Tom Trenka
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().
DojoX Cryptography has no dependencies, outside of the Dojo package system and DOH.
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.
See the Dojo API tool (http://dojotoolkit.org/api)
Jared Jurkiewicz
Shane O'Sullivan
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.
DojoX Data has dependencies on core dojo (dojo.data) and the D.O.H. unit test framework.
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.
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.
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.
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.
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.
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:
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.
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;}
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;}
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;}
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.
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
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.
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.
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;}
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.
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;}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:
Some new functions are added to allow tags to communicate with the rendering object.
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.
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;}
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.
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 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;}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.
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;}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;}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;}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.
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:
prototype / expermental
Peter Higgins
dojox.fx provides a class of animation effects to use.
dojox.fx requires dojo (core) and the dojo.fx package dojox.fx.easing requires only dojo core.
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
checkout dojox/fx* from SVN and place into your sibling dojox folder.
see dojox/fx/tests/* for examples, and API tool for details.
beta
Eugene Lazutkin Kun Xi
Implementation of simple portable 2D graphics library.
Dojo Core
Currently it can be found here: http://docs.google.com/Doc?id=d764479_1hnb2tn
Just include this line in your code:
dojo.require("dojox.gfx");
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.
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.
On conceptual level dojo.gfx has a simple declarative model: All basic primitives required for 2D graphics are defined:
There is a surface, which serves as a visual rectangular container for shapes:
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:
Shapes support following set of properties:
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:
All methods without an apparent return type return their object itself. It is used for chaining multiple operations:
For user convenience, Grid documentation is located in Part 2 - Dijit.
experimental
Bryan Forbes
DojoX IO has no dependencies, outside of Dojo Core.
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.
The information contained in this README does not pertain to DojoX XHR IFrame Proxy. For that information see proxy/README.
prototype | expermental
Peter Higgins
Shane O'Sullivan
A class to provide a common API for images, and home for image related Widgets.
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.
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.
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 (?)
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:
<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>
<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>
<div id="gallery1" dojoType="dojox.image.Gallery" imageHeight="400" imageWidth="600"></div>
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>
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>
TODO: Fill in info on dojox.image.Lightbox / finish / elaborate more
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.
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
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.
The Slideshow widget has the following features.
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>
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 :
For example:
<div dojoType="dojox.image.SlideShow" id="slideshow1"
titleTemplate="My title is '@title', this is image @current out of @total"
> </div>
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>
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.
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>
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>
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:
dojo.subscribe(
dijit.byId('slideshow1').getShowTopicName(),
function(packet) {
alert("Got index: " + packet.index
+ ", url: " + packet.url
+ ", and title: " + packet.title);
});
dojo.subscribe(
dijit.byId('slideshow1').getLoadTopicName(),
function(index) {
alert("Got index: " +index);
});
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:
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>
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>
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>
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>
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>
<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
[expermental]
Pete Higgins Fredrik Johansson
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.
require Dojo Core, Dojo Base (fx), and Dijit.TitlePane, Dijit.layout.ContentPane
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;} /* 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;}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:Let's dive in and start using Dojo Offline.
First download the Dojo SDK and unzip it.
When you unzip the SDK, you will find the following directories:
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.
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.
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.
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;}
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;}
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;}
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.
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;}
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(), that will slurp
the page and automatically figure out all of the resources you need to have
available offline: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.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.
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;}
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;}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;}