The Dojo Book, 0.4

The Dojo Book has two versions because the Dojo project itself has two versions. This book addresses version 0.4. And like the code itself, resources are currently concentrated on writing and editing the 0.9 book.

Part 1: "Introduction"

Dojo provides a lot of power and attempts to make it digestable in layers. For server-side developers, there's "widgets without coding", for HTML+CSS devs Dojo provides wonderful facilities for quickly building template-driven widgets, and for serious JavaScript and DHTML hackers Dojo is the standard library you will wish JavaScript always had.

This book serves as a guide to these layers, introducing concepts as you need them and working downward from high-level usage to getting your hands dirty in building your own widgets, custom namespaces, and unit tests.

In the Introduction, you'll get an overview of how Dojo can help you, what problems it solves, and where in the book you might be able to best find the information you're looking for. Also, remember that because this book is maintained by the community and is online, you can search it (and the rest of the Dojo site) at any time.

Lastly, thanks for checking out Dojo and the Dojo Book. It's your applications that have inspired us to build Dojo and the stories of how people are improving experiences with the toolkit that keep us going.

Dojo Architecture

Dojo is a set of layered libraries. The bottom most layer is the packaging system that enables you to customize the distribution of Dojo for your application. On top of Dojo's package system reside language libraries such as the Dojo event system and the language utilities that greatly improve and simplify the lives of JavaScript developers. Environment-specific libraries are provided in some cases, but in most uses, you'll only need to know that all of Dojo works without incident across every major browser.

The bulk of the Dojo code lives in the application support libraries, which are too numerous to display completely in the diagram. dojo.gfx provides native vector graphics support across the major browser implementations of SVG and VML. dojo.lfx is a lightweight effects library, while dojo.io is "where the ajax lives."

Most of the "action" in Dojo is in the widget toolkit, that contains a template-driven system for constructing widgets, a declarative parser for putting widgets in pages without writing JavaScript, and a set of base components that implement common-case solutions to web interaction problems that HTML+CSS alone cannot solve.

Dojo: What Is It?

Dojo is an Open Source DHTML toolkit written in JavaScript. It builds on several contributed code bases (nWidgets, f(m) and Burstlib), which is why we refer to it sometimes as a "unified" toolkit. Dojo aims to solve some long-standing historical problems with DHTML which prevented mass adoption of dynamic web application development.

Dojo allows you to easily build dynamic capabilities into web pages and any other environment that supports JavaScript sanely. You can use the components that Dojo provides to make your web sites more useable, responsive, and functional. With Dojo you can build degradeable user interfaces more easily, prototype interactive widgets quickly, and animate transitions. You can use the lower-level APIs and compatibility layers from Dojo to write portable JavaScript and simplify complex scripts. Dojo's event system, I/O APIs, and generic language enhancement form the basis of a powerful programming environment. You can use the Dojo build tools to write command-line unit-tests for your JavaScript code. The Dojo build process helps you optimize your JavaScript for deployment by grouping sets of files together and reuse those groups through "profiles."

Dojo does all of these things by layering capabilities onto a very small core that provides the package system and little else. When you write scripts with Dojo, you can include as little or as much of the available APIs as you need to suit your needs. Dojo provides:

  • Multiple Points of Entry - You can start using Dojo at the level you are most comfortable with. For example, expert JavaScript programmers can use the foundation capabilities to be more productive quickly, while Web designers and developers can use the set of easy to use, modify, and extend components that make their applications more responsive without requiring them to learn a large JavaScript API. This fundamental design decision drives the layered implementation of most of the major capabilities of Dojo.
  • Interpreter Independence - Dojo is squarely a JavaScript toolkit but, within the realm of JavaScript interpreters and environments, not everything was created equally. Dojo supports at least the very core of the system on as many JavaScript enabled platforms as possible. This allows Dojo to serve as a "standard library" for JavaScript programmers as they move between client-side, server-side, and desktop programming environments.
  • Forward Looking APIs - No one has a crystal ball when it comes to what technologies will be broadly available or used in 5 years, but Dojo attempts to provide APIs that are generic enough to be (directly) useful with todays capabilities while still building in room for future improvement. The dojo.io.bind() interface is a great example of this principle: when first written it wrapped only a single Transport class, but now provides a normalized interface to many ways of receiving and sending data from JavaScript enabled environments.
  • ReducingBarriersToAdoption - This core philosophy behind Dojo's design acknowledges the fact that tools which are hard to use just won't get used, no matter how good they are. Dojo should be built in every way (licensing to deployment) to not give users any reason to not trust or use Dojo for the tasks it's good at. Many of the project's overall decisions get made on the basis of this principle.

Dojo is being built around a single markup language that provides application authors a (more) simple way of declaring and using responsive DHTML interface components. Renderings can be made available in several rendering contexts (such as SVG, or perhaps even the desktop or Flash), but the markup language (DojoML) and scripting language (JavaScript) will not change. Better yet, the DojoML parser accepts extended HTML and SVG as valid input, and can be used to easily create Degradeable Responsive Applications.

Dojo's homepage is: http://dojotoolkit.org.

How this Book is Written

Imagine for a second that you've just come from looking through the API of Dojo. You've memorized every function, every parameter, every example. All of the API has been studied and digested, but you're still not sure how to proceed. You know that there are many ways to do event handling, but you don't know the difference between them and you certainly don't know which one is best suited for your application.

I've made this assumption in writing the book; it allows me to focus on what's important about the book, and separate it from the API. With understanding Dojo, the why of Dojo, you're now free to learn the how by looking at the API. Or, if you already know the API, you're free to learn why it exists in the first place.

Voice

Me

Though this book is a result of many people's work, I(we)'ll be speaking in the first person throughout this book. It not only allows a more personal interaction between the reader and the author, but quite often, describing an author's action is confusing when expressed in the plural form. "We're now opening our text editor" is an awkward sentence at best.

You

Saying "the reader" every sentence is dumb, and since you already know that you're the one reading it, I'll just refer to you as "you".

We

Decisions on the coding of Dojo aren't made by a single person. Almost every significant change to Dojo is done after discussion of how it is engineered. Because of this, I'll run into situations where I want to discuss how something was decided on. In this case, I'll be using the word "we". As in, "we decided that we wanted Dojo to be awesome."

The Tree Structure

On the way to a final topic, you'll be going through some higher-level topics. In order to understand the lower-level topics, and where they fit in to Dojo, you need to have some explanation of how you got way down there. To do this, each of the points along the tree, all the way down to the edge of the branch will have a few paragraphs of explanation. These are overviews for all topics contained underneath. It's worthwhile, if you have a specific topic that you want to read about, to go down the tree, reading each of the brief descriptions, before you read the article itself.

Articles

Readability

I want this to be more a book than a user manual. Therefore, I've tried to use fun analogies and brief stories. Many topics of this book are very difficult to process, and using existing knowledge to bring you up to speed is a great way to make these easy to digest. As a general guideline, I'll try to start an article with a paragraph, followed by a brief analogy or story, flowing into content. Because I don't want you to feel overwhelmed by code, examples will not be back to back. I think that if I have multiple examples, it's important to explain why I provided each one and the differences between them.

Length

I think that the moral of the story of Goldilocks and the Three Bears is that things should neither be lacking, nor in excess, they should be "just right". That said, there's nothing worse than sitting down to get your learn on and be constantly switching from one topic to another, from one page to another, when you want to stay focused on what originally brought you to the article to begin with. Likewise, you don't want to get halfway through an article and realize that you missed both lunch and dinner. To prevent this, as a general guideline, each article in this book should take you about twenty minutes to read. That sounds just right to me.

Sections

I discuss in the book how Dojo is a toolkit, a framework, and everything in between. It's overwhelming just to learn what Dojo can do for you, much less learn what you can do to Dojo. Because of this, the book is split into three different parts.

Part 1 is all about using Dojo, the toolkit. That is, looking at a set of functions and objects that are available to you "out of the box" that don't require any tinkering. Ideally, you should be able to call a function, or initialize and object, without providing any custom logic to it.

Part 2 is meant to focus on more than just using things. We want you to know how to build your own widgets, packages, extend CSS and HTML and get into the nitty gritty of things, such as replacing the data provider for a widget.

Part 3 is a "look into the engine" explaining not only how thing work but why the Dojo developers designed them that way. Much of Dojo's code seems to work based on voodoo. For the advanced programmer, knowing this voodoo will allow you to write tighter, better code.

How to Create Pages

On any page where the dojo.book sidebar appears (including this page) click "create new page". The new page will have the currently viewed page selected as its parent by default

What Dojo gives you

Because Dojo is a toolkit, it's potential uses are unlimited. The only real restrictions are that you're using it in an envornment that uses JavaScript. Though many of you will be reading this in the context of a browser, it's important to remember that JavaScript isn't limited to just that specific use.

Code Simplification

With "Ajax" becoming such a popular buzzword, many are looking for an end-all solution to its complexities that not only work well in existing environments, but will allow much more complicated interaction in the future, allowing for very complicated transfer of data between client and server. With the aformentioned widget functionality, and an easy way to implement your own widgets, Dojo works very well alongside HTML.

Dojo also abstracts many of the differences between browsers that have pained developers for years. Things such as differing event objects and HTTP transport systems are a worry that will be forgotten.

Reusable Code

Dojo provides a launching point for developing code. This means that you can quickly throw a project together for a client or boss without having to reinvent the wheel. We've built Dojo to be highly reusable, so code you have created for one project can be quickly moved to another without any refactoring.

Portable Tools

Responsive applications make many calls to the back-end of web applications which have different call and response characteristics. Standardizing on a portable set of tools for this is beneficial to understanding and predicting overall system behavior.

Because Dojo's widget system sits on top of standard HTML, designers will be able to dive right in with a very shallow learning curve. Dojo allows for designers to add degradable functionality to their (already existing) page without having to implement any logic or strange programming language.

Additional Resources

This book describes what Dojo provides and how to use the toolkit. You will find detailed information about the Dojo APIs in the API Reference Doc.

What is a Toolkit?

Many people see the words framework, library, and toolkit as synonymous. This is true in the sense that they are all descendants of the same parent. Understanding the difference allows you to go beyond that widely scoped overview.

Three words are used to describe a stereotypical developer: geek, nerd, and dork. Many people assume that these words all mean the same thing when, in fact, they each have very specific meanings. Once you learn the meanings of each of these words, you'll quickly be able to run down the list of all of your friends listening to techno in dimly lit rooms and sort them into each category. Even better, you now have the power of more accurately describing a person.

Geeks, Nerds, and Dorks: A geek has a very focused knowledge of a subject (that guy that memorized the language of myst), a nerd is a master at many subjects (that girl you go to when you need homework help), and a dork is just plain socially inept (Napoleon Dynamite).

Framework

In software development, a framework is a defined support structure in which other project can be organized and developed. A framework typically consists of several smaller components; support programs, libraries, and a scripting language. There may also be other software involved to aid in development and meshing of the different components of a project. As you can see, dojo could be part of a framework, but it isn't a framework in itself.

Library

A library is defined as a collection of related functions and subroutines used to develop software. They are distinguished from executables in that they are not independant programs; rather, they are "helper" code that provide access to common functions in one easy to manage location. After reading this you are probably saying, "Hey! dojo is a collection of libraries!", and you would be correct; however, dojo is much more than just a collection of libraries.

Toolkit

Now on to toolkits. A toolkit is generally used in reference to graphical user interface (GUI) toolkits. Basically, a library that is mainly focused on creating a GUI. Yes, dojo could also fall under this category, in fact our name implies it. Why do we call dojo a toolkit? Certainly not because it focuses mainly on GUI development, right? Well quite simply, because dojo is so much more than just a collection of libraries.

None of the Above?

The previous paragraphs have probably left you still wondering what exactly we consider dojo. Obviously it is not a framework, but is it a toolkit or a library? Let's solve this once and for all. Typically, a library is a predetermined file that you include into your application, and that is how you gain access to those functions. However, with dojo, we have wrapped a package system around our libraries. This brings a slight twist to the idea of a library.

With this system we have broken each library up into several pieces. You have the core functions, and then several sub libraries where related, but less often used, functions are stored. This helps keep dojo's footprint based entirely on your needs as a developer. More about that will be covered later in the book but, for now, know that because of this flexibility, dojo is more than just a library, which falls into the realm of a toolkit with a few added functionalities. So as the name implies, dojo is a toolkit... and yet is more.

Part 2: "Out of the Box" Dojo

Want to know what Dojo is and how it can help you with your javascript development? In this section we will walk through the process of putting Dojo to use with its collection of pre-packaged "Out of the Box" widgets that can get you using Dojo right away. We will focus on developing programs using existing dojo components. This section will not get into any advance features, as they are included in future chapters. If you are new to Dojo or even new to JavaScript, then this chapter is for you. If, however, you are a seasoned programmer, you can still find reading this section purposeful as it will provide a helpful overview to the Dojo toolkit.

The sample application is the familiar Hello World app however, it does introduce the main features of Dojo and will build the foundation for developing much more sophisticated web applications.

Thanks to Lance Duivenbode and Seth Fair for help in writing this chapter.

Development and Debugging Tips

This section talks about some of the tools available to help develop JavaScript programs. With the increasing popularity ofJavaScript there surely will be alot of changes in this space in a relatively short time. In this section you will find helpful tools that may or may not be part of Dojo.

Also remember to check the Dojo FAQs especially the Common Pitfalls section.

Debugging JavaScript

There are excellent tools to help write and debug Javascript - it isn't all about liberally using alert() any more!

IDEs

IDEs are available which support a wide range of web development activity, including editing Javascript and HTML files, deploying code to servers, and integration with existing features like source control. In addition, some include runtime tools and browser integration to assist in debugging (for example, myeclipse and ATF)

If you use one of the JavaScriptEditors that checks your syntax as you enter your code, you can be warned immediately of simple errors and save yourself a lot of time. Some Eclipse plugins even specifically support Dojo idioms by giving you tooltips for common Dojo functions and doing more complex analysis for errors (not just normal syntax checking).

Browser specific tools

Mozilla/Firefox

  • The Web Developer Toolbar is brilliant.
  • The Firebug extension is a must for debugging and inspecting html pages - dojo.debug can also be configured to output directly to Firebug's console. It contains a simple Javascript debugger too. just use dojo.require("dojo.debug.Firebug") in your pages.
  • Venkman is the mozilla javascript debugger - ugly and cantankerous, but it can be useful if you need to debug in FF. Note that in general the IE debugger is much better. Use the venkman port for FF1.5.
  • Live HTTP Headers is a good extension for debugging HTTP traffic (You may prefer to use the equivalent functionality that exists in the in Firebug extension).
  • JavaScript Shell - While it's not a debugger, I've found the JavaScript Shell to be a really great tool for analyzing problems, and even just exploring JavaScript libraries like dojo. Works best as a firefox bookmarklet, in which case you can open it in the context of any given page and invoke javascript functions yourself, evaluate expressions, redefine functions, etc.

Debugging on Firefox may need you to use the djConfig flag debugAtAllCosts (see further below for information). The debugAtAllCosts flag is sometimes necessary to locate exceptions or syntax errors. Even without the debugger, Mozilla and Firefox are unable to locate a line of code loaded by dojo.require() due to a flaw in the way eval() debugging hooks are implemented in Spidermonkey. Currently, the Javascript console will report all source references as the location of the eval() call itself (in the bootstrap code), but with an additional line offset equal to the offset in the corresponding *.js file.

Firefox Safe Mode

If you are having wierd problems with Firefox, it is often worthwhile running Firefox in safe mode.

This is because installed extensions can interfere with the DOM tree, CSS, or even with javascript. In Windows there is a shortcut to start Firefox in safe mode from within the Mozilla Firefox folder, from the Start button.

Internet Explorer

If you are using a debugger with IE, go to Tools | Options | Advanced and make sure that Disable Script Debugging is not ticked. Using debugAtAllCosts can also help significantly - read about it below.

Safari

The debugger is Drosera.

The Safari Developer FAQ has some general information about developing with Safari, as well as instructions on how to turn on a debug menu that allows showing a Javascript console.

There is a "dom inspector" type tool, but it requires using a nightly build of Safari.

debugAtAllCosts

debugAtAllCosts may have unobvious side-effects - you should only use it if you are actually debugging. If you hit problems with files not loading or __package__.js then check that you are using writeIncludes() correctly, and try removing this flag to ensure that the flag is not the issue.

You generally should not use a packaged build if you want to debug, because in a packaged build the majority of code will end up in your dojo.js file (and usually be obfuscated due to compression).

The easiest way to understand how it helps with debugging is to see it's effect within a debugger. For example within Visual Web Developer Express the image on the left is when using debugAtAllCosts and the RHS image is without debugAtAllCosts being used:

To use it needs one more line in your page. Here's how you might use it:

<script>

djConfig = {

isDebug: true,

debugAtAllCosts: true

};

</script>
<script src="/path/to/dojo/dojo.js" />

<script>

// dojo includes here

dojo.require("dojo.myModule");

dojo.hostenv.writeIncludes(); // this is a new line

</script>





// ...

Once you've structured your code this way, if you open up your debugger, you will see all the constituent files listed and you can then set breakpoints. You may need to add in dojo.require() statements for files that you want the files to show, above the writeIncludes(). If you do not, then any javascript files that need loading after the call to writeIncludes().

You can see an example of using debugAtAllCosts with your own widgets within your own namespace in dojo/src/tests/widget/test_Custom_Widget_Debugging.html

How does debugAtAllCosts work?

When debugAtAllCosts is set to true, then dojo.require() does not actually include the package at that point. It only gets included in the page once you call dojo.hostenv.writeIncludes().

Normally when you dojo.require() a file that is not already loaded, it is fetched using using an xmlhttprequest, and loaded into your Javascript runtime environment using an eval() statement (each of the eval items in the RHS screenshot above is actually a js file). When the file is eval()'ed, dojo.require() calls will occur, and those then recursively load any further dependencies (those which are not already loaded).

When you set debugAtAllCosts to true, the file is still fetched using xmlhttprequest but it is not eval()'ed. Instead a regexp is used to find the dojo.require() dependencies, and required files may be fetched and searched recursively for dependencies. Each of the required javascript filenames is added into a list of pending files that need to be loaded (the list is in order so that dependent files are loaded in the correct order). When you then call dojo.writeIncludes(), inside the head of the document script tags are appended with src attributes set to the js files that need to be loaded. The script tags cause the files to be loaded by the browser: files loaded this way are understood by debuggers much better than when the files are eval()'ed.

What alternatives are there to debugAtAllCosts?

If you just need to debug one or two known files, then you can just include those specific files using script tags after dojo.js. e.g.

<script src="/dojo.js" type="text/javascript"></script>

<script src="/mine/myWidget.js" type="text/javascript"></script>

You cannot do this for files that are packaged into your dojo.js (Of course, using packaged files makes debugging hard in other ways too!).

Profiling

Javascript is slow and you want to speed it up?

Use a ProfilingJavascript tool to help you find the functions where all the time is spent, and optimise those routines.

Traffic analysis

A great many problems can be resolved by watching the traffic between the browser and the server. If you are having any problems with Ajax calls such as bind() or with js/html/css/jpg files not loading as you would expect, this is often the best way to diagnose them quickly. Also if you are having problems with required files not loading then this is a good starting point to find out why.

  • Fiddler is a fantastic HTTP header and content inspector for Windows. It understands HTTP and presents the information very clearly (once you get used to its slightly quirky interface). Well integrated with IE, but work s with any browser. With FF a useful extension to help use Fiddler is the Switch Proxy extension (scroll down that page to find it).
  • Ethereal is great for sniffing all kinds of network traffic.

How do I debug syntax errors? aka How can I find this syntax error in bootstrap?

Sometimes Dojo's error messages about syntax errors are really cryptic due to how dojo loads files via dojo.require(), however you can get a better message by simply directly including the js file with the [script] tag.

The debugAtAllCosts flag can also be used.

There's also a lint program that Brian has recommended.

You can also find the debug error message in the dojo source code and remove the corresponding try/catch statement so that when the error occurs you get a debugger breakpoint instead of a dojo debug message.

Javascript debugger statement

Javascript has a debugger keyword that forces a breakpoint to occur. Just insert debugger; and if you have a debugger for your browser, then it will stop at the debugger keyword.

This is especially useful when using Venkman, because otherwise it can be difficult to get Venkman into debugging mode (e.g. you try to click on a line of source and it puts in a [F] future breakpoint).

It also works with the Firebug extension for Firefox.

Debug Messages

You can debug the old fashioned way (print statements in the code). There are three functions for this:

  • dojo.debug - prints a message
  • dojo.debugShallow - prints all the members in an object
  • dojo.debugDeep - opens new window w/tree showing structure of an object

Debugging output:

  • FireBug object - this will print debugging output to the Firebug console
  • DebugConsole - will capture all your debug messages in a floating pane.

Alternately, in djConfig, you can specify which element they are appended to by

providing an id of that element: i.e.:

var djConfig = {

isDebug: true,

debugContainerId : "dojoDebug"

};

and have a div

<div id="dojoDebug"></div> Author: Carla

JavaScript Editors

Editors/IDEs

ATF (Eclipse)

Ajax Toolkit Framework is an incubator project within the Eclipse Web Tools Project. It is also a pluggable framework for other AJAX tools. It provides Javascript syntax checking, server deployment, Mozilla embedding, runtime tools such as XHR Monitor, and a debugger based on Mozilla Spidermonkey, JSD, and the Eclipse debug UI. Eclipse integration provides access to existing plugins like ant, subclipse for SVN, server development tools, etc. AJAX "Personalities" provide the potential for tighter integration with Dojo and other toolkits, although so far very little has been done in this area.

There's visual Javascript validation built into ATF. It includes both basic syntax validation and optionally Jslint validation for less obvious potential syntax problems. It works just like MS Word as-you-type spellchecking and Eclipse as-you-type Java validation - as you type if you make a syntax error (or just do something somewhat sloppy) you'll get a red or yellow squiggly under the warning/error and an explanation in the margin. A background task will run validation against all files and place markers in the code such that you can see errors across a project.



You can download and use ATF and its prerequisite Eclipse components (Eclipse SDK, WTP, EMF, GEF, JEM, xulrunner) and choose to use all of ATF or just install the "javascript" feature which implements this validation.



Note - you have to fetch and manually install jslint.js, Dojo and xulrunner due to Eclipse policies - make sure to read the ATF readme to get the full functionality of ATF.

JSEclipse

JSEclipse is a commercial Eclipse plugin which provides a rich Javascript editor with support for syntax validation, code completion and Dojo idioms.

MyEclipseIDE

To be written...

JS-Sourceror (Eclipse)

JS-Sourceror performs syntax checking and variable type and flow analysis on JavaScript files.

JavaScript plugin for jEdit

See http://skrul.com/blog/projects/javascript

It also does syntax checking, as well as scope checking and structure browsing.

Aptana

Aptana is an open-source, JavaScript-focused IDE, including code assist for Dojo. It works as a stand-alone app on Mac, Linux, or Windows, or as a plug-in for Eclipse.

Profiling JavaScript

Profiling is the term for looking at where time is spent by any Javascript code. If you have a problem with code taking too long, then it helps to use a profiling tool to diagnose exactly where all the time is being used.

When using some profiling tools you may need to use debugAtAllCosts and not a packaged version of Dojo (see DebuggingJavascript). Using debugAtAllCosts will enable the profiling tool to allocate time spent per function to the correct source file -- otherwise you will end up with the elapsed times being allocated to anonymous functions which will make it difficult for you to understand!

Profiling Tools

dojo.profile

dojo.profile is a package that implements timing primitives for recording how much time is spent in particular functions. To learn how to use it, the best resource is to search the tests directory and look at how it is used by various tests.

FireBug 1.0

Displays the time it took to load each file.

Venkman (for Firefox)

Free but a bit buggy to use. I have found it easiest to get into debugging mode with Venkman by using a debugger keyword in your sourcecode. Run the Venkman debugger, then run your code and it will stop at the breakpoint.

Venkman contains a profiling tool, although the reports are a bit difficult to use. It does work.

Tito web studio

It seems it does not work with Dojo. It modifies JavaScript, and obviously in some manner incompatible with Dojo.

Manually do it yourself

Get the time at the start and end of a routine, and calculate the difference. Not a great technique, because you have to make educated guesses as to where the inefficient areas are, and it may take quite a lot of work to home in on the problem area.



But it is easy to do if you are measuring a single function:

function() {

var startTime = new Date();

// do something here

dojo.debug("Total time: " + (new Date() - startTime));

}



Or another example of code to do this is on this page near the top under the heading "Measure your changes".

Getting Started

If you are new to Dojo or want a quick overview of the toolkit then take a look at the HelloWorld Tutorial. This tutorial describes step by step how to build a simple Dojo application. You will learn some basic concepts about widgets, events and how to connect to the server code. Each step builds on the previous until you have a working application. It takes about an hour to go through the tutorial.



Adding Dojo to Your Pages

Dojo offers many editions of its code base. At first, it might seem daunting to try to figure out exactly which one you need. To quickly dispel any worries, let me assure you that every single edition of dojo provides a fully functioning system. Whether you download one of our editions, or the full, uncompressed source code, you'll be able to perform any of the examples discussed in this book.

TODO: fold in information from the README

In order to use dojo in your HTML pages, you need three sections of code, in this order:

1. Flags

<script type="text/javascript">

djConfig = { isDebug: false };

</script>

The flags control various options of dojo; often developers will set isDebug to true in order to get debugging output on their page. (There are also some other flags related to debugging; see the Debugging section of the code for details.)

2. Include the dojo bootstrap

<script type="text/javascript" src="/path/to/dojo/dojo.js"></script>

This includes the bootstrap section of dojo, and if you are using a release build, then dojo.js will also include code for some of the dojo modules.

3. Define what resources you are using

<script type="text/javascript">

dojo.require("dojo.event.*");

dojo.require("dojo.io.*");

dojo.require("dojo.widget.*");

</script>

This section is much like java's "import" statement. You specify every resources that you are using in your code. However, note that widgets are a special case and don't need to be declared explicitly, assuming that (as is the case with the built-in dojo widgets), a manifest file defines which widget is in which resource file.

Pre-Packaged Builds

Even though Dojo is made up of many different packages, it's frequently used in very specific ways. Because of this, we've created special editions of Dojo aimed toward these users. A visit to dojo's download page will show you which editions are currently available, such as the Ajax and Widget edition.

An edition is very simple, really. The important file is dojo.js, which is created by merging the most frequently used packages and compressing the resulting code. This means that when you have a script tag that calls dojo.js, you're getting not just the basic Dojo codebase, but the additional functionality that is most pertinent to your specific use.

You might wonder why so much additional code comes in each edition. This is the full code base, and allows you to use functionality that is outside of your specific build. It means that even if you have a very specifically tailored edition of Dojo, you aren't limited to only using that featureset. If your site uses the event and I/O systems heavily, but one of your pages uses a widget, then you don't have to worry that your widget will break. This also means that any of the examples in this book will work no matter what edition you've downloaded.

Introducing Dojo Events

Events in JavaScript or Dojo based applications are essential to making applications work. Connecting an event handler (function) to an element or an object is one of the most common things you will do when developing applications using Dojo. Dojo provides a simple API for connecting events via the dojo.event.connect() function. One important thing to note here is that events can be mapped to any property or object or element. Using this API you can wire your user interfaces together or allow for your objects to communicate. The dojo.event.connnect() API does not require that the objects be Dojo based. In other words, you can use this API with your existing interfaces.

Here we connect the event handler, helloPressed, to the onClick property of the hello button element. When the button is clicked the funtion helloPressed will be called.
      function helloPressed(){
        alert('You pressed the button');
      }

      function init(){
        var helloButton = dojo.widget.byId('helloButton');
        dojo.event.connect(helloButton, 'onClick', 'helloPressed')
      }

It is also possible to use the Dojo event model to connect simple objects. To demonstrate, lets define a simple object with a couple of methods:

var exampleObj = {
    counter: 0,
    foo: function(){ 
        alert("foo");
        this.counter++;
    },
    bar: function(){
        alert("bar");
        this.counter++;
    }
};

So lets say that I want exampleObj.bar() to get called whenever exampleObj.foo() is called. We can set this up the same way that we do with DOM events:

dojo.event.connect(exampleObj, "foo", exampleObj, "bar");

Now calling foo() will also call bar(), thereby incrementing the counter twice and alerting "foo" and then "bar". Any caller that was counting on getting the return value from foo() won't be disappointed. The source method should behave just as it always has. On the other hand, since there's no explicit caller for bar(), it's return value will be lost since there's no obvious place to put it.

In either case, each time dojo.event.connect is called with the same arguments it will result in multiple connections. Later we will discuss strategies on how to guard against this.

Notice that dojo.event.connect takes a different number of arguments in the examples above. dojo.event.connect determines the types of positional arguments based on usage.

The Dojo event system allows you to connect to DOM elements or nodes or plain JavaScript objects. The API is sophisticated enough that it allows you to connect multiple listeners to a single object so you can have multiple actions as a result of a single event such as a mouse click. Of course there is an API to disconnect the listeners too. The Connecting the Pieces chapter describes the Dojo Event system in more detail.

Introduction to dojo.io.bind

At Dojo, we're committed to making DHTML applications usable, both for authors and for users, and with a lot of help from our friends, particularly Aaron Boodman and Mark Anderson, we have come up with solutions to the usability problems outlined above. We're providing it in a single, easy to use API and a package that requires only two files to function. The dojo.io package provides portable code for XMLHTTP and other, more complicated, transport mechanisms. Additionally, the "transports" that plug into it each provide their own logic to make each of them easier to use.

Most of the magic of the dojo.io package is exposed through the bind() method. dojo.io.bind() is a generic asynchronous request API that wraps multiple transport layers (queues of iframes, XMLHTTP, mod_pubsub, LivePage, etc.). Dojo attempts to pick the best available transport for the request at hand, and in the provided package file, only XMLHTTP will ever be chosen since no other transports are rolled in. The API accepts a single anonymous object with known attributes of that object acting as function arguments. To make a request that returns raw text from a URL, you would call bind() like this:

dojo.io.bind({

url: "http://foo.bar.com/sampleData.txt",

load: function(type, data, evt){ /*do something w/ the data */ },

mimetype: "text/plain"

});

That's all there is to it. You provide the location of the data you want to get and a callback function that you'd like to have called when you actually DO get the data. But what about if something goes wrong with the request? Just register an error handler too:

dojo.io.bind({

url: "http://foo.bar.com/sampleData.txt",

load: function(type, data, evt){ /*do something w/ the data */ },

error: function(type, error){ /*do something w/ the error*/ },

mimetype: "text/plain"

});

Errors and Timeouts

Regular web requests and Ajax requests with dojo.io.bind are much alike. Both use URL's and both use the HTTP protocol. But with browser requests, it is always clear to user when something goes wrong. You may get a 404 - Page Not ound, or a Server Unavailable, or at least something that says "Error". Ajax requests happen in the background, so when they error out the user won't know. Even worse, if the response never comes the browser may appear to lock up.

That's why it's extremely important to provide an error handler and a timeout handler with dojo.io.bind. You should consider these as critical as URL or the load function

At the very least, you should alert the user that something went wrong. Here's an example:

var kw = {

url: "/cgi-bin/timeout.cgi",

load: function(type, data, evt){

document.myForm.myBox.value = data;

dojo.byId("boxLoadTime").innerHTML = new Date();

},

error: function(type, data, evt){

alert("Holy Bomb Box, Batman! An error occurred: " + data);

},

timeoutSeconds: 2,

timeout: function(type, data, evt){

alert("I am tired of waiting.");

}

};

The error() function takes the same arguments that load() does. But unlike load(), the only useful parameter is data, which contains the error message.

The timeoutSeconds and timeout parameters should be used together. timeoutSeconds defaults to 0, which means "wait forever". (In other Ajax libraries, this is called a synchronous request). 0 is not desirable. Even if you expect the request will take a long time, you should set a high value here (e.g. 3600 = 1 hour), not 0. The timeout function takes the same arguments as error() and load(), but they are rarely consulted.

Hello Ajax World

So let's apply this to a trivial example. Suppose you have a text file on the web server with url my_message.txt.

Hello Ajax World!

You would like to load the file contents in an INPUT box without refreshing the page. Here's how:

<html>
<head>
<title>Insert title here</title>
<script type="text/javascript" src="/js/dojo/dojo.js"></script>
<script type="text/javascript">
    function loadRemotely(e) {
        var kw = {
            url:    "my_message.txt",
            load:    function(type, data, evt) { 
                        document.myForm.myBox.value = data; 
                        dojo.byId("boxLoadTime").innerHTML = new Date();
                    },
            method: "GET"
        };   
        dojo.io.bind(kw);
    }
    function initAjax() {
        dojo.event.connect(dojo.byId("loadIt"), "onclick", "loadRemotely");
    }
    dojo.addOnLoad(initAjax);
</script>
</head>
<body>
    Form loaded at:
    <script type="text/javascript">document.write(new Date());</script>
    

    <form name="myForm">
    <input type="button" id="loadIt" value="Click here to load value.">
    

    <input type="text" name="myBox" size="50" />
    Text loaded at: <span id="boxLoadTime">N/A</span>
    </form>
</body>
</html>

Click the button and the value in my_message.txt automatically loads into the box. The date and time stamps on the page prove it does not reload when the user clicks the button.

This demonstrates the bare minimum you need for dojo.io to connect with Ajax. At the very least, you need an object of type dojo.io.Request. In our examples, this is the "kw" variable. dojo.io.Request needs the following:

  • url is the URL containing content - usually generated on the server, and often using a database
  • load(type, data, evt) is the function called after the URL has been retrieved. This is an example of a callback function. It must have both parameters defined, although you don't need to read both of them. data is the most important, and contains the entire content retrieved from the URL. type is always "load". evt captures data about the load event.
  • method is either "GET" or "POST". GET is common - we'll see an example of submitting a formful of data later on. That will require a POST.

There are many more optional parameters, and we'll see these in later examples.

Then just call dojo.io.bind with the dojo.io.Request variable. Here, bind means "connect this page with that URL and let things happen."

Why do all that for some static text? The answer is ... you won't. The URL is going to be a server-side program which returns content - mostly XML (the "X" in AJAX), but it could be text, HTML, or even binary data.

Sending Form Data

The Url of dojo.io.Request may contain parameters, like so:

url:  'myprogram.php?firstname=Chicken&lastname=Little&key=111111'

There are two problems: (1) it's difficult to URL encode everything, (2) it doesn't allow for dynamic parameters. The above works fine for everyone named Chicken Little, but ...

It's easier and more flexible to send an entire form of data. And you can do that with the formNode parameter of dojo.io.Request.

<script>

var kw = {

url: "myprogram.php",

load: function(type, data, evt){

document.myForm.myBox.value = data;

},

error: function(type, data, evt){

alert("Holy Bomb Box, Batman! An error occurred: " + data);

},

timeoutSeconds: 2,

timeout: function(type, data, evt){

alert("I am tired of waiting.");

}

formNode: dojo.byId("myForm");

};

dojo.io.bind(kw);

</script>
<form id="myForm">

<input type="hidden" name="key" value="111111" />

<input type="text" name="firstname" length="50" />

<input type="text" name="lastname" length="50" />




<input type="text" name="myBox" length="50" />

</form>

Modules, Resources, and Widget Namespaces

Modules

Dojo's code is split into logical units called modules. These are much like packages in Java, except that in Dojo a module can contain both constructors (like classes in Java) and simple functions.

For example, the "dojo.html" module contains a number of functions, such as dojo.html.getContentBox(). The "dojo.dnd" module contains a number of constructors for things like HtmlDragObject etc.

Note the naming convention - functions start with a lowercase letter, and constructors (which are technically functions but act more like classes) start with a capital letter.

Modules could be called "namespaces", except for the fact that "namespaces" has a different (but related) meaning w.r.t. widgets.

Resources

In the simple case, a Dojo module is defined in a single JavaScript file. But sometimes, a single module is split into multiple files.

For example, the dojo.html module, although originally defined in a single file, was getting too big, so we split into multiple files. This is for performance reasons, so that the browser only downloads the code it needs.

Unfortunately this implementation detail is not transparent to the Dojo user. You have to know which file contains the functions you need, and then include that file explicitly.

Each of these files is called a resource.

The line:

dojo.require("dojo.html.extras")

will include the file src/html/extras.js, which in turn defines some of the functions (but not all the functions) in the dojo.html module.

A single JavaScript file never defines multiple modules, although often a single file will define multiple constructors. In Java this would be equivalent to defining two classes in the same file.

All of this complication is for performance reasons, trying to balance

  • not downloading more stuff than you need
  • not downloading too many tiny files


Setting up Require Statments

How do you know what resources to dojo.require()?

1. modules

First, find out what modules you are using. In this example we'll assume you are using dojo.lfx.html.

2. resources

By reviewin the API doc you can see that dojo.lfx.html is defined in two files:

  • src/lfx/html.js
  • src/lfx/extras.js

Depending on what functions you are using, you will either do

dojo.require("dojo.lfx.html");

or

dojo.require("dojo.lfx.html");

dojo.require("dojo.lfx.extras");

Wildcards

There is a wild card include, such as dojo.lfx.* . New users might be surprised to learn that this may not necessarily include everything under lfx. Rather, there is an __package__.js file that defines what is included with the wild card, and this may also depend on the environment in which Dojo is loaded. For example, a browser will likely load different modules than Dojo would when loaded with Rhino on the command line.



dojo.provide()

Each file defining a resource should have (exactly) one line at the top defining the name of the resource.



Example:

  dojo.provide("dojo.html.extras")
For historical reasons, the dojo.provide() call serves two functions:

  1. define the name of the resource (and register that the resource is loaded)
  2. make sure that the resource's module exists (ex: make sure that dojo.html exists), so that statements like "dojo.html.foo = ..." don't fail with an error about dojo.html not existing.

Widget Namespaces

Widgets are combined into groups called namespaces. All the widgets built into Dojo are in the "dojo" namespace, but someone else could write their own widgets and put them in a different namespace. For example, you could write your own button and checkbox widgets, and put them into an "acme" namespace. Then "acme:Button" would be your button, and would be unrelated to the button object built into dojo, called "dojo:Button".



Information on writing your own widgets occurs later in this book.





Widgets

When browsing through web sites and online applications, there are hundreds of widgets that come across your screen. Each button in your web browser is a widget. Each text entry box is a widget. We all know what a limited set of widgets that standard HTML provides: an input box, a button, a hyperlink.

Dojo widgets take an item like a text input box and adds functionality of a more user friendly object, like a graphical calendar to choose a date from. And it does this without breaking the original item on which the new functionality is built on.

The Widget Object

The first thing that you'll notice about widgets is that the are somewhat similar to a macro expansion, such as C's #define. Your source HTML is a simple

<button dojoType="Button" id="foo"> Click me </button>

and yet a pretty blue button shows up, and when you look at the generated DOM, it's a complicated tree of DOM nodes with a lot of absolute positioning and background images.

See?

But, that's not all there is. For each widget, besides the visible manifestation, there's also a pure javascript object that manages that generated DOM tree.

In the above case, the generated javascript object is called, unsuprisingly, "foo". You can get it by doing:

var myButton = dojo.widget.byId("foo");

Turning Plain HTML Into Widgets

A Dojo widget wraps around your HTML. It looks at how the HTML is organized, what type of tags have been specified, what attributes they have, which tags are children of what other tags. All of these different variables allow a versatility in how skeletons are laid out, in what tags they use, in how the widget chooses to interpret them.

More Than One Way to...

You can think of a widget as the final form that covers a skeleton. The top widget layer reflects the structure and functionality of the skeleton it sits on rather than covering it up.The simplest example of a skeleton is a single tag. A form input box, for example, is designed to simply accept a value.

<inputvalue="default">

But what happens when we want to help the user choose from an existing list of items? Enter the ComboBox widget.

<input dojoType="ComboBox" value="default">

As you can see, this is a functional skeleton. Not only is there an input box if the user does not have JavaScript enabled, but we can use the widget as part of a normal form. After all, we've only added on to it, we haven't changed the original purpose of the input element.

But this widget provides no data for the combo box to use.

<input dojoType="ComboBox" value="default" dataUrl="comboBoxData.js">

If you want to produce valid W3C HTML, you will have to use an alternative method to building your skeletons. The dojoType attribute is not recognized by W3C and its validation tool will complain about it being their. Below are two examples that do not use invalid attributes to build on your skeletons.

<input class="dojo-ComboBox" value="default" dataUrl="comboBoxData.js">
<dojo:ComboBox value="default" dataUrl="comboBoxData.js">

When there are certain attributes (dataUrl in this example), this widget will use that information to process the information to be contained in the combo box.

Sometimes you'll have skeletons defined in code and not even know it. In fact, many of the widgets provided by dojo assume that the underlying HTML is the same that would be encountered in every day life. Making this select element into a widget is as simple as adding the dojoType attribute.

<select dojoType ="combobox">

<option value="foo">foo</option>

<option value="bar">bar</option>

<option value="baz">baz</option>

<option value="thud">thud</option>

</select>

As you can see, you've just gained a whole lot of something for nothing. And, the data that will be used in the combo box is provided in a way that any web designer would understand.

TODO: this section needs to differentiate between ComboBox and Select.

Why Use Widgets?

You may ask yourself, "Why would I use wigets?" I honestly couldn't have answered this a few months ago, before finding Dojo anyway. The answer is really quite simple once you see how widgets improve the functionality and appearance of your web applications, without taking a long time to implement.

Enhanced User Experience

Widgets "enhance the user experience". In layman's terms, that means that you can design web pages that are easier for people use, more quickly understandable, less error-prone, and flashier than web pages in plain html.

Easier to use - the Select widget for example, will narrow down the list of available choices based on keystrokes the user enters. That makes it faster to use than a normal HTML select box.

More quickly understandable - a web page with tabs will let the user easily navigate between different sections, and helps to make clear all the different sections of code that are on one page.

Less error prone - validation widgets will immediately notify users when they have entered an incorrect value, and/or automatically correct the value.

Flashier - dojo's menu code will fade in / out menus, or use some other effect, rather than a plain appear/disappear that you get with pure CSS menus

Faster Development

Widgets make it easy for web developers to add enhanced functionality. Here's why:

No Javascript Required

Web designers are generally very good with HTML. The really good ones are usually so involved in design that they don't even bother with learning the extra stuff that comes along with its dynamic aspect.

For these kinds of users, specifying widgets via HTML is a great solution. Not only is markup useful for being able to design with a placeholder element laying in wait, but many of the widgets actually analyze what they've laid out and use them as if properties were passed to the JavaScript object.

A great example of this is the tree widget. All that the designer has to do is lay out an HTML list, assign some attributes and they can have things going without having to touch a bit of code.

No worrying required

Widgets solve a bunch of issues (like cross-browser support) behind the scenes, so you don't have to worry it. See the next section for more detail about that.

Part 3: "The Dojo Programming Model"

Using Dojo to add dynamic capbilities to your web applications can be a little daunting at first.

Let's look at the programming model in more detail to better understand how to use Dojo to build some really cool apps. The programming model is object-oriented inspired and includes "classes" with methods and multi-level inheritance coupled with aspect-oriented event model famous in JavaScript. You will find the API doc quite useful to determine the methods and properties that are inherited from the parent "classes".



Thanks to Eugene Lazutkin and Bill Keese for help on this chapter.

Declarative vs Programmatic model

Dojo supports two programming models, declarative and programmatic. Which one you use depends on what you are doing. In most cases, the declarative model may be easier to use as it is markup but there are times when you will use the programmatic model. You can, of course, intermix the models on the same page as needed.

The best way to show the models is through the use of widgets which can be used either declaratively or programmatically. Although both models are available we will mainly use the declarative model through out this book when both are an option.

The tutorial used the declarative model to create a button on the page using the code below.

<BUTTON widgetId="helloButton" dojoType="Button">Hello World!</BUTTON>

The following declarative formats are equivalent.

	<?xml:namespace prefix = dojo /><dojo:widget></dojo:widget>

	<DIV dojoType="widget">

	<DIV class=dojo-widget></DIV></DIV>

We will discuss the declarative model in more detail shortly.

You can also declare widgets programmatically using the dojo.widget.create API as follows. When declaring widgets programmatically, the API returns the widget id which you use to call the appropriate methods.

var myTabPane= dojo.widget.createWidget("TabPane", {id: "myTabPane"}, srcDiv); 

Which approach to use is discussed later in the book but for now we want to introduce the idea that you have different options.

Infrastructure

Before we get into the declarative model let's look at the widget infrastructure to better understand what methods are available when using widgets in your pages.

  • Lifecycle methods are called by the infrastructure when a widget is created. They are not meant to be called by the user.
  • Internal methods are called by the widget class code for supporting functionality and are "private" and not meant to be called by the application developer. Unfortunately there is no easy way to identify the lifecycle or internal methods. We are working on conventions that will help identify these methods in future releases.
  • "on" methods begin with the string "on" and are available to application developers. These are called by the widget infrastructure based on an action or event. Application developers can provide application specific code for these methods and those that application specific code will be called automatically when the related event or action is triggered.
  • Developer methods are the rest of the methods in the widget. They are provided for use in the programmatic model.

In addtion to methods, each class has parameters that are useful. There are two types of parameters listed here.

  • init parms are set at initialization time and then are readonly
  • rest of the list should be in the API doc

Declarative model

dojoType instructs Dojo how to process the element when the page is loading. Keep in mind that Dojo manipulates the DOM as it renders the page so you must use the Dojo APIs to access the widget ids. Dojo keeps a reference of all widgets it has created that can be accessed with the dojo.widget.byId function - providing you specify either the widgetId or id attribute in your markup. Also you can use dojoAttachEvent using this method.

Namespace Details

In order to make namespaces practical and easy to use, Dojo has a concept of modules and resources. This concept was introduced in Part 2 Modules, Resources and Widget Namespaces when we first saw the HelloWorld tutorial. Resources are used to define a namespace and can be dynamically loaded on demand.

In order to load a resource you should request it using dojo.require(). It takes a string of text, which denotes a downloadable component. The content of this string is interpreted and a subject of Dojo conventions. First, it is traced to a single JavaScript file on disk (on web server). There are ways to affect this interpretation but out of the box it has a very sane behavior:



dojo.xxx => dojo/src/xxx.js

dojo.xxx.yyy => dojo/src/xxx/yyy.js

dojo.xxx.yyy.zzz => dojo/src/xxx/yyy/zzz.js



and so on. For example if you see a statement like that:



dojo.require("dojo.json");



It will load a file named dojo/src/json.js. If you don't know what it contains, you can go and look it up. To sum it up: a namespace hierarchy essentially reflects a file system hierarchy. By convention, if you see somebody using dojo.foo.bar.baz(), you can find it's definition in dojo/src/foo/bar.js, or, if it is not there, in one of files of dojo/src/foo/ (more on that case later), or in dojo/src/foo.js.



Dojo allows you to define your own resources and modules. For your custom resource you define your own top-level object. I will use "example" in my examples. If the Dojo loader sees that the first component is not "dojo", it applies following rule:



example.xxx => dojo/../example/xxx.js

example.xxx.yyy => dojo/../example/xxx/yyy.js

example.xxx.yyy.zzz => dojo/../example/xxx/yyy/zzz.js



Be sure to put the dojo.provide() call in your module resource, so that Dojo knows it found the right file. For instance, xxx/yyy/zzz.js should have dojo.provide("xxx.yyy.zzz") in it.



Essentially it means that on your web server next to the "dojo" folder there is an "example" folder, which files are interpreted in the same way.

There is more information about the namespace and how it is used in the Widgets chapter.

Object Oriented concepts and inheritance

JavaScript is at its heart an object oriented language, but it is a prototype based object oriented language which does not have the same structure as class based languages like Java.This concept can be hard for new programmers who are not familiar with its construct. Dojo brings the object orientedness into a more familiar domain by modeling concepts that can be followed from Java and letting the toolkit handle the prototyping, inheritance and odd procedures JavaScript requires to make it work. Because of this, it not only allows people to get programming in object oriented JavaScript quicker, but it makes it faster to program because you can let the toolkit handle all of the odd procedures JavaScript requires to make it work. It all begins with a simple dojo.declare() function.

Simple declaration

Classes in Dojo are declared with a declare statement and assigning it a Class Name.Within the body can be variables, methods and constructors (know in Dojo as an initializer).

dojo.declare("ClassName",null, {
//class body
});

(Note: ClassName is the basic name, but to avoid naming conflicts, use package names like my.class.ClassName. For simplicity sake, we will start out with using just the simple name.)

Let's add some more content to our class by giving it a name and showing what the initilizer can do.Following is a persons class with an initializer and a moveToNewCity() function:

dojo.declare("Person", null, {
		//acts like a java constructor
		initializer: function(name, age, currentResidence){
		this.name=name;
		this.age=age;
		this.currentResidence=currentResidence;
	},
	moveToNewCity: function(newState) 
	{
		this.currentResidence=newState;
	} 

});

To create an object of this class you use the new keyword:

//create an instance of a new person

var matt= new Person('Matt', 25, 'New Mexico');

The initializer function is called once the object is created and the arguments are passed to it initializing the object.Our Matt object who is 25 currently lives in New Mexico, but let's say he moves a little further west to California.We can set his new currentResidence with the Person class method moveToNewCity(): matt.moveToNewCity('California');

Now the current value of matt.currentResidence shows that he now lives in California.

Inheritance

A person can only do so much, so let's create an Employee class that extends the Person class.The second argument in the dojo.declare() function is for extending subclasses.

dojo.declare("Employee",Person, {
	//acts like a constructor
	initializer:function(name, age, currentResidence, position)
	{
		Employee.superclass.initializer(name, age, currentResidence);
		this.password="";
		this.position=position;
	},

	login: function()
	{
		if(this.password!="" && this.password!=null){
		alert('you have successfully loged in with the password '+this.password);
	}
	else
	{
		alert('please ask the administrator for your password');
	}
}});

The first line in the initializer calls Employee.superclass.initializer, the Person class constructor. Dojo handles all of the requirements for setting up the inheritance chain.Methods or variables can be overridden by setting the name to the same as it is in the parent class. The Employee class can override the Person class moveToNewCity(), perhaps by letting the company pay for moving expenses.

You initialize the sub class the same as the Person class with the new keyword.

var kathryn=new Employee(' Kathryn ', 26, 'Minnesota', 'Designer');

The Employee class passes the first three arguments down to the Person class, and sets the position.Kathryn has access to the login() function found in the Employee class, and also the moveToNewCity() function by calling kathryn.moveToNewCity(‘Texas’); Matt on the other hand, does not have access to the Employee login() function.

matt.login() // ERROR can’t log in because he is not an Employee

Array/Object Declarations

If your class contains arrays or other complex objects, they should be declared in the initializer, due to some subtleties of object inheritance in javascript. Note that simple types (strings, numbers) are fine to declare in the class directly.

dojo.declare("my.classes.bar", my.classes.foo, {
	// coupledObjects: [1, 2, 3, 4]	- doesn't do what I want; 
	//                                      ends up being like a static!!
	numItem : 5,        // one per bar
	strItem : "string", // one per bar

	 initializer: function() {
		this.coupledObjects = [ ]; // each bar should have it's own array
		this.expensiveResource = new expensiveResource(); // one per bar
	}
});

Statics

On the other hand, if you want an object or array to be static (shared between all instances of my.classes.bar), then you should do something like this:

dojo.declare("my.classes.bar", my.classes.foo, {
	initializer: function() {
		dojo.debug("this is bar object # " + this.statics.counter++);
	},

	statics: { counter: 0, somethingElse: "hello" }
});

Mixins

The example below inherits from my.classes.foo and then mixes in "my.mixin". This is similar to multiple inheritance but there are some subtle differences, namely that this.inherited can only reference my.classes.foo, not my.mixin.

dojo.declare("my.classes.bar", [my.classes.foo, my.mixin], { initializer: function() { my.mixin.call(this /*, args*/); // invoke some mixin constructor // (note: my.mixin.prototype is ignored) }, valueForPrototype: 3, methodForPrototype: function() { } });

Design Notes

Constructor vs Initializer

In non-trivial cases, constructor definitions contain code. The constructor code performs initialization tasks and defines instance-only properties (properties whose values belong to a particular object, as opposed to prototype-properties which are shared by all objects using the prototype).

However, there is an issue with respect to constructor code in any simple JavaScript inheritence system. The constructor function of an object must be executed to create a prototype object for an inheritor. Therefore the constructor function must serve as both prototype-initializer and instance-initializer. The double duty of inherited constructors can be non-obvious and lead to subtle bugs.

Given the constructor above, when a bar object is created to use as a prototype, the mixin properties and properties created in the constructor become members of the inherited object's prototype. Almost always these properties are not intended to be part of a prototype.

As a practical matter, the extra prototypical properties are usually ignored as matching instance properties are created at object-instantiation time. However, for example, having an extra expensiveResource can be costly. And errors can result if the environment is not ready to create an expensiveResource at inherits-time. Errors caused by these conditions can be hard to track down, especially if the developer is not aware of how constructors are used when inheriting prototypes.

Separating instance-initializer tasks from prototype-initializer tasks eliminates these concerns. Therefore dojo.declare creates a standard, controlled constructor and separates instance-initialization tasks into a separate, optional initializer method.

Note: dojo.declare cannot inherit from an object that has a non-trivial constructor because dojo.declare does not allow constructors to also perform instance initialization.However, you can inherit from a dojo.declare created constructor without restriction

Calling Inherited (Ancestor) Methods

Sometimes one wants to invoke a method on an object from an ancestor prototype. JavaScript allows any function to call any other function in any context via the call and apply built-ins. So there are techniques like:

my.classes.bar.prototype.someMethod == function() {
	// invoke any function in our context
	anyFunction.call(this);
	 // invoke inherited version of this method in our context
	my.classes.foo.someMethod.apply(this, arguments);
}

(Note: in these examples, the == indicates an assertion that the named property is equivalent to the function shown. Actual assignment is done via dojo.lang.extend or dojo.declare.)

As a convenience, dojo.inherits puts a reference to the ancestor prototype into the descendent constructor, and a reference to the descendent constructor into the descendent prototype. These extra references allow a great deal of extra flexibility in general, and also allow calling ancestor methods without explicitly naming the ancestor:

// invoke inherited version of this method in our context this.constructor.superclass.someMethod.apply(this, arguments);

However, the above technique will cause an infinte loop if someMethod is once removed. E.g., if we have foo -> bar -> zot, you can run into an issue like this:

my.classes.foo.prototype.identify == function() { return "I'm a foo"; }

my.classes.bar.prototype.identify == function() { return "I'm a bar and " + this.constructor.superclass.identify.apply(this, arguments); }

my.classes.zot.prototype.identify == function() { return "I'm a zot and " + this.constructor.superclass.identify.apply(this, arguments); } bar = new my.classes.bar(); alert(bar.identify()); // "I'm a bar and I'm a foo" zot = new my.classes.zot(); alert(zot.identify()); // stack overflow

The error results because this.constructor.superclass referenced in bar's identify function refers to zot's superclass (causing bar.identify to call itself).

To resolve these issues, objects created from dojo.declare constructors include a function called inherited that safely invokes an ancestor method.

inherited: function(methodName /*string*/, arguments /* arrayLike */)

inherited correctly handles the problem scenario above:

my.classes.foo.prototype.identify == function() { return "I'm a foo"; } my.classes.bar.prototype.identify == function() { return "I'm a bar and " + this.inherited('identify', arguments); } my.classes.zot.prototype.identify == function() { return "I'm a zot and " + this.inherited('identify', arguments); } bar = new my.classes.bar(); alert(bar.identify()); // "I'm a bar and I'm a foo" zot = new my.classes.zot(); alert(zot.identify()); // "I'm a zot and I'm a bar and I'm a foo"

Syntax

The syntax is:

dojo.declare(className /*string */, superClass /*function*/ [, initializer /* function*/]);

or

dojo.declare(className /*string */, [superClass /*function*/, mixin /* function */, ...] [, initializer /* function*/]);

Including the target className in the argument list allows the object path to be created automatically (i.e. intermediate namespaces are created as needed). Also, dojo.declare stores className in an eponymous property in the created object's prototype (e.g. my.classes.foo.prototype.className == "my.classes.foo").

Naming

Technically speaking, JavaScript does not have classes: object construction is based on prototypes. For this reason reference to the term class has been (mostly) avoided above.

It seems that the difference is not of great practical importance. It's true that constructor functions in JavaScript are actual objects, but they operate like classes in the sense that they generally have no other purpose than as a mold for object instantiation. It is noted that classes are typically compile-time (or at least meta-) constructs and JavaScript constructors exist as Objects at runtime and contain actual data.

The name dojo.declare was chosen after much debate. The name is vague but easy to remember, read, and type. The method name inherited is lifted (at least) from ObjectPascal.ÂÂ

Order matters

In general, a script should do the following in the ... section:

  1. (Optional) set the djConfig options
  2. Load the Dojo script
  3. Call dojo.require(...) for all libraries used in the page
  4. (Optional) define initialization functions and call addOnLoad
As in this example:

<!-- Step 1 (Optional) Set djConfig -->
<SCRIPT type=text/javascript>
	djConfig = {
	  debug: true
	};
</SCRIPT>

<!-- Step 2: Load dojo -->

<SCRIPT src="js/dojo/dojo.js" type=text/javascript></SCRIPT>
<!-- Step 3: call dojo.require -->
<SCRIPT>
   dojo.require("dojo.book.myWidget.*");
   <!-- Step 4 (Optional): define initialization functions -->
   function initMyStuff() {
      ...
   }
   dojo.addOnLoad(initMyStuff);
</SCRIPT>

The order is important! If you do the steps out of order, dojo may not initialize properly, and your page will be a mess.

This script element is responsible for loading the base Dojo script that provides access to all the other Dojo functionality. Following this we add the requires statements which pulls in functionality needed by the application.

Use the dojo.addOnLoad to call functions which use the widget ids because Dojo must completely load the page and finish parsing the HTML before a reference can be made to the id. So, for example, the following will not work:

<BUTTON widgetId="helloButton" dojoType="Button">Hello World!</BUTTON>
<SCRIPT>
// ILLEGAL!!  helloButton does not exist yet
dojo.byId("helloButton").width2height = 0.5;
</SCRIPT>

Instead, place the script in an initialization function:

   
    <SCRIPT src="js/dojo/dojo.js" type=text/javascript></SCRIPT>
    <SCRIPT>
       function initMyStuff() {
          dojo.byId("helloButton").width2height = 0.5;
       }
       dojo.addOnLoad(initMyStuff);
    </SCRIPT>
<BUTTON widgetId="helloButton" dojoType="Button">Hello World!</BUTTON>

The global Dojo Objects

Dojo defines a global object called "dojo" which serves as an umbrella for everything Dojo-related. It simulates a namespace and was created to prevent clashes in the global JavaScript? namespace between the code in Dojo and other toolkits or user supplied code. Unfortunately it cannot be used during a bootstrap process, so special global variables should be used. All of them are prefixed with "dj".

You will need to use exactly two top-level Dojo-defined objects: "dojo", which serves as a namespace, and "djConfig", which is used to supply initialization parameters to Dojo, and should be created before Dojo's bootstrap.

For example, to turn off global widget searching, add these lines just *before* you include dojo.js:
<script type="text/javascript">

djConfig = {

parseWidgets: false

};

</script>

Part 4: "More on Widgets"

P

Advanced ContentPane Usage

Introduction

A common use case for DHTML/ajax is to fetch a fragment of html using XHR or some other way, and change the innerHTML of a div with that content. Problem with this is that it doesn't instanciate widgets and doesn't fire scripts. ContentPane was created to make widgets and scripts work and reduce the potential for memory leaks. ContentPane is a base widget for many (Html)widgets, it handles remote loading as well as local setting of content and instanciating widgets in that content. Think of it as islands in your page that can easily switch content using setContent() or setUrl().

Many other widgets inherits ContentPane, like Tooltip, Dialog, FloatingPane etc. That means that all the methods and properties of ContentPane also applies to them.

ContentPane is often used as children of Layout widgets like LayoutContainer, TabContainer, AccordionContainer

Dont misstake it for a Iframe though, It should not be used on very large html fragments.

Usage

Simple usage ... <div id="cpane" dojoType="contentPane" href="initialContent.html"><div> <a href="javascript:dojo.widget.byId('nextContent.html')">Goto nextPage</a> ...

Basic options

  • loadingMessage Default: "Loading..." Set a custom loading message, see onDownloadStart to avoid showing this message completely
  • adjustPaths Default: true When content is setUrl'ed from a different folder paths to images, links etc. is adjusted so the point to the correct dir
  • href Default: "" Use this to grab initial content when contentpane is created.
  • extractContent Default: true Only insert the html that is inside Script and style tags are not affected by this setting
  • parseContent Default: true Create widgets inside content
  • cacheContent Default: true Use dojo.io.bind javascript cache and, if it exists, browsers cache
  • preload Default: false Lazyload switch, if true it will download content even if domNode is hidden Note: To make use of the default lazyload setting you need to hide your domNode Like this: <div dojoType="Dialog" style="display:none;"></div>
  • bindArgs Default: {} Send in a custom setting to the dojo.io.bind call, like: mypane.bindArgs = {sync: true, preventCache: false}; mypane.seetUrl('nextHtmlFragment.html');
  • refreshOnShow Default: false Re-download content each time ContentPane is shown again
  • executeScripts Default: false Fire scripts in content Note: see scriptSeparation
  • scriptSeparation Default: true Run scripts in a separete scope for ContentPane Note: set to false if you want similar behaviour as a normal pageload
  • handler Default: "" Java function name, generate pane content
  • isLoaded Default: false Tells wheather we are loaded or not, see also: onLoad() and addOnLoad()

Methods apart form those provided by ContentPane's superclass HtmlWidget

  • setContent(String or DomNode) Use this instead of innerHTML
  • setUrl(String or dojo.uri.Uri) Use this to set a new href and download and diplay that href
  • refresh() Re-download and display href
  • loadContents() Like refresh but only when isLoaded is false
  • setHandler(Function) Set a function callback for javacontent generation
  • abort() Abort a async download
  • addOnLoad(Object, "functionname" or Function) Push a callback that will be run when content the next onLoad occurs. It's a fire and forget stack, if you want a callback each onLoad, see onLoad() Works for setContent as well
  • addOnUnload(Object, "functionname" or Function) Same as addOnLoad but for onUnLoad event

Methods (Intended as event hooks using dojo.event.connect)

  • onLoad() Called when everything rendered initialized and ready
  • onUnload() Called before content is cleared
  • onDownloadStart(e) preventDefault'able Called before a download occurs To prevent showing the loading message, do like this:
  • onDownloadEnd(url, string) Called when download is completed, before it is setContent'ed
  • onDownloadError(e) preventDefault'able Called when a load error occures, before The load error message is displayed. Prevent it the same way as onDownloadEnd Tip: During debug, you can display debug info like responseHeaders, responseText etc.
  • onContentError(e) preventDefault'able Called when content insertion generates a error, before error mesage is displayed, like DOM faults, dojo.require() *syntax* faults etc.
  • onExecError(e) Called when there is errors evaling script, doing java setContent and download errors of external scripts
  • In order to prevent the default messages you can do something like this:

    <script>
    	var myLoadMessage = {
    		show: function(event){
    			event.preventDefault();
    			... custom code here
    		}, 
    		hide: function(){...}
    	}
    
    	dojo.addOnLoad(function(){
    		var pane = dojo.widget.byId('myPaneId');
    			dojo.event.connect(pane, "onDownloadStart", myLoadmessage, "show");
    	});
    </script>
    <div dojoType="ContentPane" id="myPaneId">...startcontent...</div>
    
    or
    
    <div dojoType="ContentPane" 
    	onDownloadStart="myLoadMessage.show(arguments[0]);">...startcontent...</div>
    		

When used as a child to TabContainer, AccordionContainer or PageContainer TabContainer, AccordionContainer or PageContainer extends Widgets with these extra options

  • label Tab text
  • selected Preselect this tab after creation
  • closable Display close button
  • Note:
  • Height and width settings is done on the Container, not the ContentPane.
  • In order for lazyload to work you have to hide your domNode initialy

When used as a child of LayoutContainer LayoutContainer package extends Widgets with this option

  • layoutAlign "left", "right", "bottom", "top", or "client" see layout section of the book for more info

FAQ

  • Why doesn't my widgets show up? Most likely you have used mypane.domNode.innerHTML = htmlstr; Use setContent instead: mypane.setContent(htmlstr);
  • Why doesn't lazy load work? You have to hide your domNode, style="display:none;", initialy while creating ContentPane
  • ContentPane displays strange looking characters when loaded remotly in some browsers, why? Like all server communication your browser need to know what charset your html is encoded with. Make sure your server is sending the correct Content-type header. example in php: header("Content-type: text/html; charset=utf-8"); Make sure you type utf-8 and not utf8, else IE will generate a warning and bail out.
  • Why is ContentPane so slow? You probaly send it a big chunk of html with deeply nested tags.
    • Send it a html fragment, not a complete page with doctype and everything
    • Try to make the HTML simpler and use css for styling
    • Turn off the options you dont need: adjustPaths, extractContent, executeScripts, parseContent
    • Consider redesigning your page with serveral ContentPanes which grabs a smaller portion of your html that way you dont have to scan, render and create as many DomNodes/Widgets on each update.
    • Perhaps dojo.io.updateNode("nodeId", "myUrl") is all you need, and ContentPane is to heavy for your needs
  • dojo.addOnLoad() is called to early, before my Content is loaded See onLoad event and addOnLoad for ContentPane, it is usualy easier to do this using <script>_container_.addOnLoad(..)</script> in your downloaded page, just be sure to set executeScripts=true
  • My inline scripts doesnt work when loaded in ContentPane, I have turned executeScripts to true? Short answer: set scriptSeparation=false Long answer: ContentPane separates scope of scripts between different ContentPane's see: scriptScope page in dojo book
  • When I press submit in my form inside a ContentPane, the whole page unloads, why? ContentPane doesn't have a form handling feature, look at dojo.widget.Form or see sample use case below
ContentPane examples

    scriptScope

    Executing scripts in ContentPane in dojo-0.3.1





    This is a explanation about scripthandling in a ContentPane in other words when you set executeScripts to true, it is false by default.


    ContentPane has some convenience functions related to scripts that makes life easier:



    • .addOnLoad()
    • .addOnUnLoad()
    • And the replacement for scriptScope to dojo.widget.byId('thisWidgetId').scriptScope in html content attributes.


    All scripts within content is evaled in the ContentPane property scriptScope, this means that you can have 2 or more content panes in the same page with the same name without risk of collision. This implementation does have its pros and cons. but correctly handled they will be useful. Also the scripts evals before widgets is parsed and after html is inserted.



    So the content scripts is evaled inside a freestanding scope that inherits window. Take this function declaration.



    <script>
        var i = 0;
        function addToI( j ){
            i = i + j;
            return i;
        }
    </script>
    
    becomes (from window scope):
    
    (function( ){
        var i = 0;
        function addtoI( j ){
            i = i + j;
            return i;
        }
    })
    


    Due to the way javascript works addToI will be private and you cant reach it. Read Douglas Crockford excellent description of this. http://javascript.crockford.com/private.html



    You can fix this in a couple of ways, one is to append it to global



    <script>
        i = 0; // note lack of var
        addToI = function( j ){ // we could also do window.addToI = function( j ){ ...
            i = i + j;
            return i;
        }
    </script>
    
    becomes (from window scope)
    
    i = 0;
    function addtoI( j ){
        i = i + j;
        return i;
    }
    


    This is'nt recommended, if you want global scripts It is better to include them in the main page the old fashion way.



    A better way would be to use Privileged functions:



    <script>
        this.i = 0;
        this.addToI = function( j ){
            this.i = this.i + j;
            return this.i;
        }
    </script>
    
    becomes (from window scope)
    
    (function(){
         this.i = 0; // now it is a property of this function and can be reached from the outside.
         this.addToI = function( j ){
            this.i = this.i + j;
            return this.i;
         }
    })
    


    As you might have guessed by now the (function(){...}) is the function scope that is reference held by ContentPane.scriptScope.

    So to call the addToI function from the outside we can do:

    var added = dojo.widget.byId('myPaneId').scriptScope.addToI( 10 );
    dojo.debug(added) // prints 10
    
    added = dojo.widget.byId('myPaneId').scriptScope.addToI( 10 );
    dojo.debug(added); // prints 20
    
    and so on...
    
    Now lets say we have html that would like to alert the value of i (this.i) in plain html that would be:
    <button onclick="alert(i);">Tell me i !</button> 
    // As explained above this wont work in ContentPane(unless you set it to global by omitting var).
    
    Now if we now the ID of the contentPane that pulls in this content we could do:
    <button onclick="alert(dojo.widget.byId('myPaneId').scriptScope.i);">Tell me i !</button>
    // this will work in ContentPane
    
    That is'nt very useful as we don't always know the ID of the contentPane that pulls in the html when we write the content.



    ContentPane has a convenience replacer function the scans the html and replaces all occurrences of the keyword scriptScope in html attributes. So to achieve the above:
    <button onclick=" scriptScope.i">Tell me i !</button>
    // this will work in ContentPane
    // NOTE: Due to a bug in ContentPane 0.3.1 you need to add a extra space before the keyword
    // Thank you Sasha Firsov for finding that! 
    
    The parent scope of scriptScope is window, the reason for that is to avoid messing with widget internals. Just imagine the disaster a redefinition of setUrl function would cause otherwise.



    To enable the content scripts to talk to the containing ContentPane there is set a private variable on scriptScope construction.

    That is the _container_ variable. Lets say we have a widget in our content that we would like to connect a event callback on:
    ... some content
    <div dojoType="DatePicker" id="myPicker"></div>
    ... rest of content
    
    A content script could look like:
    <script>
        var o = {
            storeDate: function( ){
                var datePick = dojo.widget.byId('myPicker');
                var date = datePick.storedDate;
                // save date somewhere
            }
        };
        _container_.addOnLoad(function(){
            var picker = dojo.widget.byId('myPicker');
            dojo.event.connect(picker, "onSetDate", o, "storeDate");
        });
    
        // remember to disconnect onUnLoad, very important!!
        _container_.addOnUnLoad(function(){
            var picker = dojo.widget.byId('myPicker');
            dojo.event.disconnect(picker, "onSetDate", o, "storeDate");
        });
    </script>
    


    When the content is cleared in ContentPane the scriptScope is unreferenced, this means that if there are no other variables that holds reference to any of the scriptScope objects (like event connects, varible connection etc), the script will be garbage collected by the javascript engine (memory freed).



    Different browsers are more or less conservative about GC (garbage collect), IE and Mozilla beeing the more relaxed browsers, khtml and Presto (Opera) engines are more conservative.



    Some usecase samples

    Lets say you have page that you need to run in as both stand alone and within a ContentPane.

    Then you can do something similar to this.

    Courtesy of Sasha Firsov for a pointer to this example!
    <html>
    <head>
    <script>
        var djConfig = {isDebug: true};
    </script>
    <script src="dojo/dojo.js"></script>
    <script>
        var scriptScope = this;
        if(typeof _container_ == 'undefined'){
            var _container_ = dojo;
        }
    
        _container_.addOnLoad(function(){
            dojo.debug("Successfully loaded!");
        });
    
        this.doWhenClicked = function(txt){
            dojo.debug(txt);
        }
    </script>
    <body>
        <a href="javascript:scriptScope.doWhenClicked('You clicked a link!');">Click here!</a>;
    </body>
    </html>
    




    Perhaps you want to prevent all <a href='...' link clicks with a ContentPane from clearing your page, and use the href to set your client ContentPane.
    *********Your mainpage*********** 
    <html>
    <head>
    <script src="dojo/dojo.js"></script>
    <script>
        dojo.require("dojo.widget.ContentPane");
        dojo.require("dojo.widget.LayoutContainer");
    
        function changeUrlInClient(url){
            var client = dojo.widget.byId("client");
            client.setUrl(url);
        }
    </script>
    </head>
    <body>
        <div dojoType="LayoutContainer" layoutChildPriority='none' style="border: 1px solid blue; width: 800px; height: 300px;">
            <div dojoType="ContentPane" layoutAlign="left" style="width: 200px;" executeScripts="true" href="linkpage.html"></div>
            <div widgetId="client" dojoType="ContentPane" layoutAlign="client" style="border:1px solid red;"></div>
        </div>
    </body>
    </html>
    
    *******linkpage.html************
    <html>
    <head>
    <script>
        var o = {
            listen: function(evt){
                // if the onclick came from a 
    </head>
    <body>
        <a href="content1.html">content1</a>
    <a href="content2.html">content2</a>
    <a href="content3.html">content3</a>
    <a href="content4.html">content4</a> </body> </html>




    A simple example of using a form in ContentPane. NOTE! dont use this login example in real world applications, password is sent in cleartext
    **********mainpage************
    <html>
    <head>
    <script src="dojo/dojo.js"></script>
    <script>
        dojo.require("dojo.widget.FloatingPane");
        dojo.require("dojo.widget.Button");
    </script>
    </head>
    <body>
        <div dojoType="FloatingPane"
            title="Login example"
            style="width: 300px; height: 300px;"
            executeScripts="true"
            cacheContent="false"
            href="login.php">
        </div>
    </body>
    </html>
    
    *********login.php**********
    <?php
        session_start();
    
        // are we trying to login?
        if(isset($_GET["login"])){
            // this could of be a database instead
            $users = array(
                    "JohnDoe"=>
                        array("pass"=>"foo", "id"=>1),
                    "JaneDoe"=>
                        array("pass"=>"bar", "id"=>2),
                    "JuniorDoe"=>
                        array("pass"=>"baz", "id"=>3)
                    );
    
            if(isset($_POST["user"]) && isset($_POST["pass"])){
                $pass = $_POST["pass"];
                $user = $_POST["user"];
                if(isset($users[$user]) && ($users[$user]["pass"] == $pass)){
                    $_SESSION["id"] = $users[$user]["id"];
                    exit("(true);");
                }
            }
    
            //if we get here we have failed to login
            exit("(false);");
        }
    
        // logout?
        if(isset($_GET["logout"])){
            unset($_SESSION["id"]);
        }
    
        if(isset($_SESSION["id"])){
            // it is safe to show secret content
    ?>
    
        <script type="text/javascript">
            this.logout = function(){
                _container_.setUrl("login.php?logout=true");
            }
        </script>
        <h3>You have successfully logged in!</h3>
        showing secret content here
    <a href="#" onclick="scriptScope.logout();">log out</a> <?php }else{ //no it wasnt safe, show our login script ?> <script type="text/javascript"> this.ok = function(){ _container_.domNode.style.cursor = "wait"; dojo.io.bind({ formNode: dojo.byId("login"), mimetype: "text/javascript", handler: function(type, data){dojo.debug(data); _container_.domNode.style.cursor = ""; if(type=="load"){ if(data){ _container_.setUrl("login.php"); }else{ dojo.byId("message").innerHTML = "Wrong username or password"; } }else{ dojo.byId("message").innerHTML = "An error occured while login, please try again."; } } }); } this.quit = function(){ _container_.hide(); } </script> <form name="login" id="login" method="post" action="login.php?login=true"> <div id="message" style="text-align:center; color: red;">You need to login</div> <label for="user">Username: <input type="text" name="user"/>
    <label for="pass">Password:</label> <input type="password" name="pass"/> <button dojoType="Button" onClick="scriptScope.ok();"/>login</button> <button dojoType="Button" onClick="scriptScope.quit();">quit</button> </form> <?php } ?>




    In the follwing scenario you wont need executeScripts.

    In fact it wont affect the script at all, the script will run just as any other regular script in ordinary page
    <html>
    <head>
        <script src="dojo/dojo.js"></script>
        <script>
            dojo.require("dojo.widget.ContentPane");
        </script>
    </head>
    <body>
        <div dojoType="ContentPane" >
            <script>
                // this script will fire before dojo makes our parent a ContentPane widget, so it wont be 
                // affected by executeScripts at all.
                // i wont have a _container_ variable and scriptScope wont hide any variables
                // it will work just as a inlne javascript block always has
    
                alert("This alert will fire event if you have executeScripts=false");
            </script>
        </div>
    </body>
    </html>
    






    It should be fairly easy to pull in some small customized scripts that is tweaked to the content, like a form validation script or a button callback.

    The executeScripts and scriptScope has the potential to be very usefull, I cant think of all the possible implementations but probably you can.



    Catches:



    • If you event.connect to _container_ be sure to disconnect onUnLoad, else you get all sorts of strange errors
    • Be sure to unref. all references into and out of scriptScope before setting new content, else there will be a memleak
    • <div dojoType="dijit.form.Button"> <img src="images/flatScreen.gif" width="32" height="32"> <span style="font-size:xx-large">big</span> </div>

      Like HTML buttons, the dojo button sizes to fit its content. Usually, you will provide an onclick="..." attribute to specify what happens when the button is pressed.


      This needs to be rewritten for 0.9

      Using Your Own Backgrounds

      By default, dojo uses a blue gradient background. But you can provide your own. You will need to create three .gif images: one for the left, one for the right, and one for the center. The filenames must end with l, r, or c, respectively. You can specify image sets for four different conditions:

      • activeImg - the mouse pointer is over the button
      • inactiveImg - the mouse pointer is not over the button
      • pressedImg - the button is being pressed
      • disabledImg - the button cannot be pressed

      For example, you can use these files:

      • /images/buttons/disabled-l.gif
      • /images/buttons/disabled-r.gif
      • /images/buttons/disabled-c.gif

      as the disabled image of your button like this:

      <button dojoType="Button" disabledImg="/images/buttons/disabled" >
         Quit
      </button>
      

      API Reference: dojo.widget.Button

      See Also: DropDownButton, comboButton

    comboButton

    Used in HTML Element:button

    A combination Button and DropDownButton. Use this for a button that has a common action (e.g. "Make Regular Dinner") and less common related actions (e.g. "Make Romantic Dinner" and "Make TV Dinner")

    Example

    <button dojoType="comboButton" menuId='saveMenu'>
       <img src="images/editIcon.gif" width="32" height="32">
       Save
    </button>
    
    <div dojoType="PopupMenu2" id="editMenu" toggle="wipe">
    	<div dojoType="MenuItem2" iconSrc="images/save.gif" caption="Save" accelKey="Ctrl+S" onclick="mySave();" />
    	<div dojoType="MenuItem2" iconSrc="images/saveAs.gif" caption="Save As...." accelKey="Ctrl+A" onclick="mySaveAs();" />
    </div>
    
    

    You can also specify your own background images, as in Button.

    API Reference: dojo.widget.ComboButton

    See Also: Button, DropDownButton, PopupMenu2, MenuItem2

    Editor2 (RichText) Widget

    Introduction

    Editor2 Widget in dojo provides a WYSIWYG editor for HTML content. The core is compact and lightweight, while a plugin framework ensures that any functionality can be achieved by plugins.

    Basic html editing capacity is implemented in the core, which is the RichText widget. Currently keyboard shortcuts are also hardcoded in this widget (TODO: generalize this, or use KeyRouter instead?).

    Editor2 is a subclass of RichText Widget which adds a toolbar (Editor2Toolbar Widget) to the top of the editing area.

    Basic Principles of Editor2

    In order to have an extensible structure, the new Editor2 introduced several new concepts.

    Command (dojo.widget.Editor2Command)

    The first and most fundamental one is call Command, which executes a specific function on the editing area. It also provides the API to retrieve the current state of the command. The base class for Command is dojo.widget.Editor2Command (defined in Editor2.js).

    Each command should have a unique name and each command is a singleton object per page: no matter how many Editor2 instances there are in one page, they share the same command objects.

    Toolbar Item (dojo.widget.Editor2ToolbarButton)

    The toolbar (defined in Editor2Toolbar.js) for the editor2 contains serveral toolbar items. The basic class for toolbar item is dojo.widget.Editor2ToolbarButton (defined in Editor2Toolbar.js), which essentially is a simplified version of dojo widget.

    Toolbar item can be of any type, besides buttons, you can have more complex items, such as a dojo combobox like item with a dropdown (see dojo.widget.Editor2ToolbarFormatBlockSelect in Editor2Toolbar.js).

    Available Plugins

    All the builtin plugins are located under src/widget/Editor2Plugin directory. Those files ending with Dialog are the actual popups. The table below lists all the other plugins:



    NameDescriptionFeatures
    ContextMenuCommandToolbarItem
    ContextMenuContext Menu Core, with menu items for builtin commandsCut/Copy/Paste, Link/Unlink, Image properties--
    FindReplaceImplement find and replace functionalities-Find/ReplaceFind/Replace
    TableOperationSupport for table related operationInsert/Delete TableInsert/Delete TableInsert Table
    AlwaysShowToolbarEnsure the toolbar is visible when scrolling the page---
    ToolbarDndSupportToolbar Set/Item drag and drop support---
    SimpleSignalCommandsAdd simple signals to Editor2, such as save() and createLink()---

    Misc

    SetupCopyPasteForFirefox - Copy, Cut and Paste are disabled by default in Mozilla/Firefox, this tip is how to enable it for your trusted websites.

    Form Widgets

    There are many widgets used for forms:

    • Button - just like HTML's button, except with a few advanced features
    • Checkbox - like HTML's checkbox but in soria (blue) theme
    • ComboBox - like a text input field, but w/suggested values
    • DropDownDatePicker - for specifying a date by selecting a cell of a calendar
    • DropDownTimePicker - for specifying a time (scheduled for 0.4 release)
    • Editor2 (RichText) - like HTML's textarea, but allows editing of rich text

    • HslColorPicker - pick a color
    • Select - just like HTML [select] element, except w/autocompletion, and loading of possible values from a remote data source
    • Slider - graphical slider control used to specify a number within a range
    • Spinner - numeric input field that can be adjusted up/down by pressing arrow keys
    • dojo.widget.*Validate - a bunch of widgets that check the user's input and correct it or print an error message if it doesn't conform to a certain format

    The main principle of these widgets is that:

    • each widget corresponds to a native HTML element.
    • each widget (w/the exception of Button) represents a single input value
    • each widget has a (possibly hidden) <input> element, to which it serializes its input value, so that form submission (either normal submission or via FormBind) works as expected

    All these widgets should have these attributes just like native HTML input elements. You can set them during widget construction, but after that they are read only:

    • disabled
    • tabIndex
    • name
    • value

    And they also share some common methods:

    • disable()/enable()
    • onValueChanged() - called with the new value of the widget whenever it's changed
    • setValue() (note: you can get the value by accessing accessing value; exception: Editor)

    (note: some widgets don't conform but we plan to convert them soon)

    Author: Bill

    ComboBox Widget

    The ComboBox widget is a text input box that supplies a list of possible preexisting values for the user to choose from. It can query the server for an updated list of values as the user types, allowing it to offer a large list of values without requiring the entire list to be downloaded to the browser.



    To demonstrate the power of this widget, let's dive right in and make an autocompleting combo box that queries the server for the list of matches for what the user has typed so far. We'll declare the widget using HTML:

    <select dojoType="ComboBox"
        autoComplete="true"
        dataUrl="/suggest.php?match=%{searchString}"
        maxListLength="15"
        mode="remote"
        name="myComboBox">
    
    There are a few attributes of note here.

    • autoComplete should be set to "true" if you want the widget to fill in the rest of the input box with the contents of the first item in the suggestion list. For example, if the user has entered "Ala" and the first suggestion on the list is "Alabama", the widget will put "bama" after the letters the user has typed (selected, so the user can simply continue typing to replace them with something else.)
    • dataUrl is the location of a URL which will be queried each time the user types something into the box. It will be discussed in more detail below.
    • maxListLength is the number of suggestions that will be visible to the user at one time. If the server supplies more suggestions than this, the user will have to scroll the list of suggestions to see them.
    • mode is one of "local" (the default; the dataUrl is fetched once at load time), "remote" (the dataUrl is fetched on each keypress, and is expected to return a JSON object; see below) or "html" (the dataUrl is fetched on each keypress, and is expected to return HTML.)
    When the page containing that widget is loaded, it gets rendered as a text entry box with a little dropdown list button on the right side, not as a normal HTML <select>.

    When you enter text into the box, Dojo tries to find matches for the text you've just entered. For example, suppose you type "a". Because the widget's mode is set to "remote", it will fetch the dataUrl and substitute your input for the magic %{searchString} token. (That token is only valid when mode is set to "remote" or "html".) In this case, it will fetch /suggest.php?match=a from the server. You don't have to use PHP on the server side, of course; it's simply used as an example here. The point is that the widget will replace the magic token in dataUrl with the user's input and fetch the resulting URL from the server.

    What should the server return? In the "remote" mode, the widget expects a JSON array of entries, each entry of which contains a displayable option name and a value. For example, the server might return something like

    [

    [ "Alabama", "AL" ],

    [ "Alaska", "AK" ],

    [ "Arkansas", "AR" ]

    ]

    Many server-side programming languages have existing libraries to output native objects in JSON form. In this case, for simplicity's sake, we'll do it by hand. Here's what an extremely simple, inefficient suggest.php might look like.

    <?php

    $states = array( "Alabama" => "AL",

    "Alaska" => "AK",

    ...

    );



    $userInput = $_GET['match'];

    $result = "[";



    foreach ($states as $state => $abbreviation) {

    if (strpos($state, $userInput) === 0) {

    $result = $result . '[ "' . $state . '", "' .

    $abbreviation . '"],';

    }

    }



    $result = $result . ']';

    print $result;

    ?>



    FormBind

    FormBind allows you to quickly setup your “Web 1.0″ form for asynchronous submission. Basically it sets things up so that whenever the user hits the submit button, rather than submitting the form in the usual way, and refreshing the entire page, the contents are sent over xmlhttp (or any transport), and then the results are passed to the given callback.

    How do you do it? Easy:

    function magicForm() {

    var x = new dojo.io.FormBind({

    // reference your form

    formNode: document.forms[1],



    load: function(load, data, e) {

    // what to do when the form finishes

    // for example, populate a DIV:

    dojo.byId('myDiv').innerHTML = data;

    }

    });

    }



    dojo.addOnLoad(magicForm);

    Note the unfortunate naming between dojo.io.bind() and dojo.io.FormBind.

    dojo.io.bind() is a function that immediately sends the given info to the specified URL. (It would probably better be called something like dojo.io.send() but it isn't.)

    dojo.io.FormBind(), on the other hand, doesn't send anything to the server. It just hooks up events so that when the user presses the submit button then the data is sent via dojo.io.bind(). Also note that you call "new" to make it work.

    Note also that although dojo.io.bind() also takes a formNode argument, it's tricky to use and you are better off using FormBind. That's because for forms containing the Editor/Editor2 widgets, they need to serialize their data back to the [textarea] before the form is submitted, and that only happens when the form's onSumbit handler is called. Just calling dojo.io.bind() and specifying a formNode won't do that. However, with FormBind (and with an actual [input type="submit] button in in the form), everything works perfectly.

    You can play with the demo to see it in action.

    Validation

    There are three methods which you can use to validate your form data on the client side before it is sent to the server - with each having their own benefits and drawbacks. Often, the most effective validation is performed using a combination of these methods.

    It is important to understand that whilst client side validation is effective, it should not be considered a replacement of server side validation techniques. In order to provide an enjoyable and secure user experience it is essential that a combination of both methods is used.

    Manual processing of input fields

    The dojo.validate.* module provides a number of functions for validating user input. Currently, these functions are broken into the following groups:
    • common
    • datetime
    • de
    • jp
    • us
    • web

    Common dojo.validate functions

    dojo.validate.isText

    This function takes two arguments - a value, and an optional flags object. Depending on the properties of the flags object (length, minlength, or maxlength), the value can be tested for exact length, a minimum length, or a maximum length.

    dojo.validate.isInteger


    This function takes two arguments - a value, and an optional flags object. Depending on the properties of the flags object (signed or separator), the value can be tested for the presence of a sign (+ or -) character at it's beginning, or whether it has separators. Whilst there is no default separator, single characters (such as ',') or arrays listing multiple separators can be specified.



    dojo.validate.isRealNumber


    dojo.validate.isCurrency



    dojo.validate.isInRange


    dojo.validate.isNumberFormat


    TODO: summary of each function group and functions within each group

    Using dojo.validate.check on a form

    The second method of validation is known as dojo.validate.check. This function let's you setup a table of rules for checking a form's input elements.

    TODO: more info on this

    Validation widgets

    There are several widgets in the dojo.widget.validate module that will either correct user input (converting lowercase to uppercase, etc.), or print errors when the input doesn't match a certain pattern. A few of the widgets are:

    • dojo.widget.validate.IntegerTextbox - allow only integer input
    • dojo.widget.validate.UsZipTextbox - entering a US zip code
    • dojo.widget.validate.UsPhoneNumberTextbox - entering a US phone number
    Unfortunately currently the validation widgets, although they do display an error message alongside illegal values, do not actually prevent the form from submitting. This needs to be addressed at some point. You need to do some javascript coding yourself to make this happen.

    Graphical widgets

    The alternative to validating user input is to provide such an interface that the user can't enter a bogus value to begin with. For example, the DatePicker widget won't let the user input an invalid date.

    Interacting With Widgets

    Calling Methods

    Previously I talked about the widget object, that you can get access to like this:

    var myButton = dojo.widget.byId("foo");

    If you create a widget programatically you automatically get a pointer to the widget object:

    var myButton = dojo.widget.CreateWidget("Button", {caption: "click me"});

    What is myButton useful for? Calling methods on the button. For example:

    myButton.setCaption("Don't press me!!");

    Note that doing the following won't work, because the myButton object doesn't know that the caption variable has been changed:

    myButton.caption="this won't do anything";

    Also note that to disable/enable a widget, call disable()/enable(), rather than setting the disabled attribute directly. People often make that mistake.

    Read only Variables

    There are some read-only variables, however, that are useful to access. Two of the most important ones are:

    • domNode - points to the node that replaced your original markup (the [button] tag in the example above)
    • containerNode - points to the node that contains the contents of the original markup ("Click me" in the example above)

    That reminds me. In the above example of programmatic creation, you also need a line like this:

    form1.appendChild(myButton.domNode);

    Events

    Consider the markup below:

    <button dojoType="Button" onClick="alert('hello world')">

    It looks familiar, but it's actually quite different than the normal onclick handler on the dom node.

    onClick() is a method in the Button widget object. It's got a similar name to DOM node's onclick (but not identical; there's a capitalization difference). However, it's not the same. As another example, consider

    <input type="Slider"
    
    	onValueChanged="alert('new value is ' + arguments[0]);">...

    In this case, we are using a function of the widget called onValueChanged(newValue), that has no direct equivalent in the dom world.

    Attaching vs. Overriding

    in the case above, the specified code will be run in addition to the widget's original onValueChanged() method. It works the same way as dojo.event.connect(). On the other hand, if you just specify a function name like this:

    <input type="Slider"
    
    	onValueChanged="doit">...

    Then you are overriding the widget's onValueChanged() funtion w/your own.

    Usually, the widget will provide an empty function stub, so it won't matter if you connect to it or override it.

    Using dojo.event.connect directly

    You can also do something like this, although it seems more difficult than the method above:

    dojo.event.connect(myButton, onValueChanged, function(x){
    
    	alert("new val is " + x);
    
    });

    Show and Hide

    Widgets all can be hidden (made invisible) and shown:

    • myButton.show() - display
    • myButton.hide() - make invisible
    • myButton.toggleShowing() - switches between show() and hide()
    • isShowing() - is widget currently displayed?

    For show and hide, there are 4 transitions available, that you set at widget creation time:

    • plain
    • fade
    • wipe
    • explode

    They are set like this:

    <div dojoType="FloatingPane" toggle="fade" toggleDuration="250">

    The explosion effect (often used for tooltips) also requires a point/square from which the element explodes out of, or implodes back into. This is set automatically when using the Toggler or TaskBar widgets.

    Layout

    Introduction

    There are two philosophies to laying out the screen. One way, the "web-way", says that everything should flow naturally from HTML, meaning basically that a bunch of stuff is in the document and if your window isn't big enough, then you use the browser's scrollbar. This is the way traditional web pages work, and is the best choice for many applications.

    There other philosophy is to take the available size of the viewport (basically, the browser window), and then to partition it into smaller and smaller pieces. If you think about a mail application that splits the screen into top/left/right sections, then you are thinking about this kind of design.

    The Layout Widgets

    Dojo provides a number of widgets for implementing the second design listed above. They fall into two basic categories.

    Widgets that split the screen space between a set of widgets

    • LayoutContainer- lets you position the children into top/left/bottom/right positions, with the specified center piece taking all the remaining space
    • SplitContainer- shows children either horizontally or vertically aligned, and you can adjust the relative size of each child by moving the divider bars between the widgets

    Widgets that hold mulitple children but only display one at a time:

    • TabContainer - names of children are printed as tab labels
    • AccordionContainer - stacks children vertically, and you can show one at a time
    • WizardContainer - go through the children in an ordered fashion like a wizard

    In addition, there's one widget that isn't a layout widget per se, but it is often used with the layout widgets:

    • ContentPane - like a div, but it's a widget, and it can load its contents from a specified href.

    These widgets can be nested to arbitrary levels, so that you could have a LayoutContainer with a top/bottom/client section, where the client section is a SplitContainer, and that SplitContainer could contain a TabContainer, which would itself contain a LayoutContainer, and so on.

    The leaf nodes of this hierarchy could be any non-layout node, but often are ContentPane nodes.

    Example

    Example (currently not displaying correctly. wiki needs upgrade?!):

    <DIV> <DIV>hello world </DIV> <DIV> <DIV>left side of split </DIV> <DIV> <DIV>second tab </DIV> <DIV>i'm on the bottom </DIV> </DIV>
    <DIV dojoType="LayoutContainer" >
    	<DIV dojoType="ContentPane"> hello world </DIV>
    	<DIV dojoType="SplitContainer">
    		<DIV dojoType="ContentPane"> left side of split </DIV>
    		<DIV dojoType="TabContainer">
    			<div dojoType="LayoutContainer">
    			 ..
    			</DIV>
    			<DIV dojoType="ContentPane"> second tab </DIV>
    		 </DIV>
    	  </DIV>
    	 <DIV dojoType="ContentPane"> i'm on the bottom </DIV>
    </div>
    

    Note that all these objects are called containers because they just contain a set of other objects; they don't contain mixed content (text and nodes) like a normal <DIV>.

    Also note that there is no "LayoutContainerChild" or "SplitPaneContainerChild" like node. That's to reduce the amount of markup and code required to setup a deep hierarchy of layout widgets.

    Parameters

    Note that the example above is missing some important parameters. For one thing, it doesn't specify whether the SplitContainer arranges its children vertically or horizontally. For that we need:

        <DIV dojoType="SplitContainer" orientation="horizontal">
    
    We are also missing the labels for each of the tabs in the TabContainer, which we can add like this:
        <DIV dojoType="TabContainer">
          <DIV dojoType="LayoutContainer" label="Tab 1">
           ..
          </DIV>
          <DIV dojoType="ContentPane" label="Tab 2"> second tab </DIV>
        </DIV></DIV>

    Note that the labels are specified as parameters to the ContentPane and LayoutContainer, the children of the TabContainer, rather than as arguments to the TabContainer itself. "label" is not technically a property on those two objects, but you can still specify it,and the TabContainer will pick it up.

    Similarly, for a LayoutContainer, you need to say where each child should be located:

    	<DIV dojoType="LayoutContainer">
    	<DIV dojoType="ContentPane" layoutAlign="top"> hello world </DIV>
    	<DIV dojoType="SplitContainer" layoutAlign="client">...</DIV>
    	<DIV dojoType="ContentPane" layoutAlign="bottom"> i'm on the bottom </DIV>
    	</DIV>
    

    You may freely mix sides (top, bottom, left, right) in a layout container. Sides are used from the outside in. The special side name "client" will fill in any part of the container that is not otherwise occupied. Very often you will use fixed-size side panes and a client pane that grows and shrinks as the user resizes the window, for example:

    <DIV style="OVERFLOW: hidden; WIDTH: 100%; HEIGHT: 100%" dojoType="LayoutContainer">
    
      <DIV dojoType="ContentPane" layoutAlign="top" height="2em">
    
        Page header goes here; it stretches across the whole width of the window.
    
      </DIV>
    
      <DIV dojoType="ContentPane" layoutAlign="bottom" height="1em">
    
        And a footer here, also stretching across the whole width.
    
      </DIV>
    
      <DIV style="WIDTH: 120px" dojoType="ContentPane" layoutAlign="left">
    
        Some left-side navigation HTML, bounded by the header and footer
    
        since they were already added to the layout.
    
      </DIV>
    
      <DIV style="WIDTH: 60px" dojoType="ContentPane" layoutAlign="right">
    
        Some right-side navigation HTML
    
      </DIV>
    
      <DIV dojoType="ContentPane" layoutAlign="client">
    
        Main page body here, bounded by all the fixed-size elements above.
    
      </DIV>
    
    </DIV>

    Sizing

    For the top level layout container in a hierarchy, you need to specify a size. If you don't, the contents of the container may be displayed oddly or not at all.

    <DIV style="WIDTH: 500px; HEIGHT: 500px" dojoType="LayoutContainer"></DIV>

    Many web applications will want to fill the whole screen with their top level layout container. Think of a case like a mail application. For any size browser window, you want the top part to have some menu choices, and then have the bottom part be split between a tree on the left and message list/message on the right.

    In this case, you need CSS like this:

    html, body, #mainWindow {
    
     width: 100%;
    
     height: 100%;
    
     overflow: hidden;
    
    }

    And then inside your tag you will have something like:

    <DIV id=mainWindow dojoType="LayoutContainer"></DIV>

    Programmatic creation

    Creating a hierarchy of layout widgets programatically works the same way as normal programatic creation, except that sizing info needs to be specified in a special way.

    // make a dummy div just to specify size
    
    var div = document.createElement("div");
    with(div.style){ height="500px"; width="500px"; }
    
    // create the layout container
    var lc = dojo.widget.createWidget("LayoutContainer", null, div);
    
    // add some children for top, bottom, and center.  Top and Bottom
    // children also need to have a size specified, and possibly a scrollbar
    var topDiv = document.createElement("div");
    with(topDiv.style){ height="30px"; overflow="auto"; }
    lc.addChild( dojo.widget.createWidget("ContentPane", { href: "foo/bar.html", layoutAlign: "top" }, topDiv) );
    var bottomDiv = document.createElement("div");
    with(bottomDiv.style){ height="30px"; overflow="auto"; }
    lc.addChild( dojo.widget.createWidget("ContentPane", { href: "foo/bar.html", layoutAlign: "bottom" }, bottomDiv) );
    

    One other thing to note in this example is that each ContentPane has two parameters. The href parameter applies to the ContentPane itself, but the layoutAlign parameter is really something that the LayoutContainer processes.

    Doing your own positioning

    You also have the option to lay stuff out on the screen like LayoutContainer does, but without using LayoutContainer. There's a function called dojo.html.layout() that will position a bunch of elements just like LayoutContainer does. (Actually LayoutContainer calls this function.)

    Multiple Renderers

    Different browsers have different capabilities when it comes to displaying (rendering) your widget. Dojo provides mechanisms that automatically detect which of these capabilities the browser offers and extends your widget using the most powerful rendering system the widget has code for.

    We'll discuss how these mechanisms work, when you should use them, and how to extend widgets to support multiple renderers.

    Defining And Extending Widgets with Multiple Renderers

    The section discusses how to code a widget that supports multiple renderers, and how to extend such a widget.

    How to support multiple renderers

    // renderer-agnostic portion

    dojo.declare("my.widget.Foo"

    {

    initializer: function() {

    // do initialization tasks, make instance properties

    },

    foo: 5,

    doit: function() { ... },

    ...

    }

    );



    // render-specific portion

    dojo.widget.defineWidget("my.widget.html.Foo", [ dojo.widget.HtmlWidget, my.widget.Foo], {

    initializer: function() {

    // do initialization tasks, make instance properties

    },

    ...prototypical properties (in object notation)...

    }

    );



    dojo.widget.defineWidget("my.widget.svg.Foo", [ dojo.widget.SvgWidget, my.widget.Foo], {

    initializer: function() {

    // do initialization tasks, make instance properties

    },

    ...prototypical properties (in object notation)...

    }

    );



    Subclassing from multiple renderers

    // renderer-agnostic portion

    // add features to my.widget.Foo, but don't explicitly extend or inherit

    // my.widget.Foo properties will come in as part of my.widget.[html|svg].Foo


    // do initialization tasks, make instance properties

    dojo.declare("my.widget.FooPlus", my.widget.Foo, { ... });



    dojo.widget.defineWidget("my.widget.html.FooPlus", [my.widget.html.Foo, my.widget.FooPlus], {

    initializer: function() {

    // do initialization tasks, make instance properties

    },

    ...prototypical properties (in object notation)...

    }

    );



    dojo.widget.defineWidget("my.widget.svg.FooPlus", [my.widget.svg.Foo, my.widget.FooPlus], {

    initializer: function() {

    // do initialization tasks, make instance properties

    },

    ...prototypical properties (in object notation)...

    }

    );

    Understanding The Widget Hierarchy

    Before you can write your own widget, you should understand how dojo's widgets are organized, both in terms of where files are and how the class hierarchy works.

    Renderer Base Classes

    The first thing to notice is the following renderer base classes.

    Widget 

    |-- DomWidget

    |-- HtmlWidget

    |-- SvgWidget

    Each widget implementation will extend either HtmlWidget?, SvgWidget?, or VmlWidget?, according to what browser it supports.

    Class Hierarchy

    For widgets w/only a single implementation (usually "html"), the class hierarchy is pretty simple. For example, there is a dojo.widget.html.Button that extends HtmlWidget.



    However, widgets w/multiple implementations are more complicated, because there's a base class that defines common functionality and the parameter list for the widget. This effectively leads to multiple-inheritance, since the widget implementation to pull in stuff from both from the renderer base class and the widget base class. For example, dojo.widget.svg.Chart needs to effectively inherit from both SvgWidget and from dojo.widget.Chart.

    Technically, multiple implementations are defined using mixins, which are similar (but subtly different) than multiple-inheritance. In the above case, dojo.widget.svg.Chart extends HtmlWidget but mixes in dojo.widget.Chart base class.

    Directory Structure

    For widgets w/a single implementation, like Button:

    • src/widget/Button.js - defines dojo.widget.html.Button
    For widgets w/multiple implementations, like Chart (in the future):

    • src/widget/Chart.js - defines dojo.widget.Chart base class
    • src/widget/svg/Chart.js - dojo.widget.svg.Chart, svg implementation
    • src/widget/vml/Chart.js - dojo.widget.vml.Chart, vml implementation

    Understanding Widget Renderers

    Implementations

    Widgets can have (but are not required to have) multiple implementations, as follows:

    • svg - will run on any svg enabled browser (FF, later safari, soon other browsers)
    • vml - will run on IE
    • html - can run on any browser

    Note that the so-called "html" version of the widget might actually run special code for IE, FF, etc., either through calls to utility functions (such as the graphics library) that branch based on browser version, or "if/else" statements, or whatever.

    Which widget gets run?

    The HTML file just specifies the widget name, without specifying the implementation. For example,

    <div dojoType="Foo">


    Dojo will pick which version of the widget to run based on the user's browser and what versions of the widget are available. For example, on IE, it will run dojo.widget.vml.Foo if it exists, and otherwise run dojo.widget.html.Foo.

    Modules for Implementations

    There are three separate modules, corresponding to the implementations above:

    • dojo.widget.svg
    • dojo.widget.vml
    • dojo.widget.html

    Examples:

    1. The Button widget only has a single "html" implementation. It's defined in dojo.widget.html.Button



    2. In the future, the Chart widget will have both "svg" and "vml" implementations, defined in dojo.widget.svg.Chart and dojo.widget.vml.Chart. (The widget doesn't instantiate at all on browsers that don't match either svg or vml) Author: the stick

    Why Decouple Code?

    Having an object without any code to display it can be a strange idea for many people. "After all," they say, "I'll only be using my widget in an HTML environment." Such a kneejerk reaction is understandable, as many people view the decoupling of code as an effort not worth the time.

    What you end up with is a method that does some logic, does some rendering, does some more logic, in a fairly long loop. Splitting these processes up is merely saying, "Let's get all of our business logic out of the way, and then we can draw the results." Even if you won't be splitting your widget into multiple files, as we'll be discussing shortly, this should still be done. The next time something isn't displaying correctly, you won't have to wade through business logic to find the problem. The next time business logic isn't working correctly, you won't have to search through display-specific code. And the most important part, you won't worry about modifying business logic or display-specific code breaking the other piece.

    What you should end up with is a plain old JavaScript object that doesn't know about how it will be drawn, and doesn't care. It's the guts of your widget, and can be run in the console, in an SVG environment, in a standard HTML environment, or anywhere that Dojo currently supports or will support in the future.

    When a widget is loaded, it has a lifecycle that runs calls several methods. These methods are pretty clearly separated into business logic and display-specific methods. For example, mixInProperties is business logic and setWidth is display-specific. There is no need for these methods to even interact with each other, and splitting these between multiple files make it easier to locate one from the other.

    You should also end up with most reusable code. Instead of loading external data in the same method as display-specific code, you can move it to your main widget object and call it from the display-specific code. Then, when another method needs to use the same information, it's ready for you to use.

    Navigation

    Need help updating this page. Describe the navigation widgets in Dojo including attributes that are common to all these widgets.



    Menu2

    PopupMenu

    ProgressBar

    ToolBar

    Tree2

    FishEye

    Tree widget

    Introduction

    This documentation refers to 3rd major version of the tree widget, sometimes refered to as TreeV3.

    Many mentioned classes (e.g TreeLoadingController) have V3 on the end, but that suffix is sometimes omitted, because it will be removed in dojo 0.5.

    If there exist 2 same classes, but one with V3 at the end - it's the one you need.

    Examples are given in tests (dojo/tests/widget/treeV3), so you might want to check them first and copy-paste exactly the things you need.



    Please browse the Book,

    then ask questions in dojo-interest list

    If you feel the question private

    or want to contribute

    IRC: Freenode, #dojo by nick [algo]

    ICQ: 820317

    "Ilia Kantor" ilia @ dojotoolkit.org

    Extensions



    Extensions are also called plugins, they can be hooked onto widgets in various combinations and provide wanted options.

    Currently there is a couple of extensions

    TreeDisableWrapExtension

    Tree extension, disables wrapping for tree nodes. Also it fixes IE bug when an 'unwrappable' node (e.g single word) will move to next line if no space left.

    TreeDocIconExtension

    Tree extension, places icon to the left of a node, depening on nodeType property

    TreeEmphaseOnSelect

    Selector extension, highlights currently selected nodes

    TreeDeselectOnDblselect

    Selector extension, deselects a selected node when it is clicked. Usually, one should ctrl-click, or click another node.

    TreeLinkExtension

    Tree extension, turns labels into links, merges object property into tag

    Faq



    How to make tree unselectable?

    To make tree (or its elements) unselectable use dojo.html.disableSelection in nodeCreate and treeCreate hooks. Apply disableSelection to every node you want to make unselectable.

    How to bind an object to tree node?

    There is an "objectId" property and "object" property ready to be filled in from markup or program-way.

    How to walk all node descendants ?

    You may use dojo.lang.forEach(nodeOrTree.getDescendants(),function(elem) { ... }) to process all descendants, it will walk children property recursively.

    The safer way would be to call TreeCommon.prototype.processDescendants(nodeOrTree, filter, func), it will process all children with func, but will not descend into nodes if filter(node) returns false. E.g see collapseAll controller method uses it to collapse all widgets, but skip non-folders and data objects.

    How to evade a situation where all nodes are (re)moved and tree is empty without a way to add new child (no nodes) ?

    Make a single root node with actionsDisabled="DETACH;MOVE". User will be unable to remove it, so interface will stay sane.

    Also, you may want to set actionsDisabled="ADDCHILD" to tree itself, so now children can be added besides the root.

    How to create a custom tree node ?

    First, of course, you may explicitly use createSimple for your widget and declare your widgetType in markup.



    But sometimes, tree has to create a node from data object or just from "nothing", e.g in case of createAndEdit.



    Then it checks for widgetName property of data object (can be namespaced), and if no widgetName, then tree.defaultChildWidget property should contain node class, e.g mycustom.tree.Node.



    Usually, when you override a node, all you need is to adjust defaultChildWidget,

    because widgetName uses generic create and hence works slower right now.



    How to make pages open when a user clicks on node?

    There are 2 ways. The first one is to attach TreeSelector and hook on "select" event. So when a user clicks, event handler will change url to node.object.href. Of course, you should fill hrefs.

    A probably more convinient path would be to employ TreeLinkExtension, which will turn your labelNodes into real links, and apply attrbutes from node object to them.

    I open very large tree. But navigation away to another page from the tree takes time. What's up?

    Dojo performs actions not only when a node is created, but also cleanup when a node is destroyed. Lazy features allow node creation be distributed in time, but when you navigate away from a large tree, large cleanup causes visible delay. I don't know a way to evade that.

    How to add icons to nodes ?

    TreeDocIconExtension handles that. You should declare nodeType for your nodes, so they'll get nodeIcon[Your type] CSS class. Default type is Document for leaves and Folder for folders.

    There is also setNodeTypeClass method to update node CSS when its nodeType changes e.g programmatically.



    Introduction

    Introduction

    This documentation refers to 3rd major version of the tree widget, sometimes refered to as TreeV3.

    Many mentioned classes (e.g TreeLoadingController) have V3 on the end, but that suffix is sometimes omitted, because it will be removed in dojo 0.5.

    If there exist 2 same classes, but one with V3 at the end - it's the one you need.

    Examples are given in tests (dojo/tests/widget/treeV3), so you might want to check them first and copy-paste exactly the things you need.



    Please browse the Book,

    then ask questions in dojo-interest list



    If you feel the question private

    IRC: Freenode, #dojo by nick [algo]

    ICQ: 820317

    "Ilia Kantor" ilia @ dojotoolkit.org

    Features

    Features

    Flexible styling

    • All design in CSS through classes and class combinations
    • Different trees be styled with different CSS class families
    • Multiline and rich content support

    Full set of node operations

    • expand/collapse
    • create with JS or markup
    • destroy/move/clone
    • addChild/detach/(de)folderize
    • inline editing
    • multiple selection and drag'n'drop
    • keyboard controls

    Performance

    • batch operations
    • special features
    • profiled and optimized

    Dynamic node loading & RPC features

    • rich API

    • callbacks & errbacks
    • suited to be in-sync with data
    • locking

    Event system

    • publish

    • hook on any tree change

    Customization

    • change everything through inheritance, events and css
    • out-of-the box extensions
    • coded with it in mind

    Tests and demos

    Tree overall structure

    Note: most classes here omitt 'V3' suffix

    Model + View

    The tree itself is a TreeV3 class instance. Hierarchy is maintained in a standard widgety way: through children[] array. Children are usually TreeNodeV3 instances, but you could use your own(overriding?) implementation of course.



    TreeV3 instance also represents an 'invisible root' node, so it shares common methods with TreeNodeV3. These methods reside in TreeWithNode mixin.



    Model contains data and manipulation methods like "addChild", "detach" .. etc. It also publishes events when modified.

    DOM-structure and view is also merged into model.



    Various functionality can be hooked on model's events: controller, menu, drag'n'drop etc.



    Model events should help you to integrate tree with application on data-level, so you hook on actual data changes, not the cause (program call, user click etc).

    Controller

    Main controllers are TreeBasicController -> TreeLoadingController -> TreeRpcController

    Basically, they are responsible for operating on model and performing most logic, besides model's action. It also makes checks / remote calls.

    Usually, one should work with controller only and let it process model.



    TreeLoadingController and TreeRpcController are known to perform remote calls to server. They use dojo.Deferred and dojo.DeferredList for that purpose.



    Most customizations are also about controller.



    And, by the way, model has no idea about its controller... It throws events and delivers API to call, that's all.

    Extensions

    The stuff is loosely coupled, so a bunch of extensions can be hooked on events too









    What's new in TreeV3

    New HTML/CSS structure

    Nested divs

    Previous tree used a list of divs, each of them was indented with grid and spacers to right level. The new tree uses natural nested divs structure (children' divs inside parent's div). Grid is contigous and structure is displayed correctly for any node/font size

    All design in CSS through classes and class combinations

    All image and size information was removed from JS code. There is a bunch of classes applied to nodes, that may denote node folder state, node type, show if there are children, etc. CSS moves this logical classes into style

    Different trees be styled with different CSS class families

    Want to put 2 differently styled trees on a page? Give them different classPrefix.

    Multiline content support

    Rich content support was incomplete, because list-of-divs model could not handle arbitrary-sized nodes. Now you may have <br>, <p> and any other width/height

    modifiers.

    Event system modified

    nodeDOMCreated event was removed. That's because listeners are bound to tree and may want to modify the new node, but that's only possible when the node is being bound to the tree, not when it was created and hanging around. afterTreeChange was introduced to help listeners to (un)bind nodes the right moment.

    All events were renamed to better reflect the moment of their publishing.

    afterExpand, afterCollapse events now fire when the animation (e.g fading in or out) finishes, not when the actual expand/collapse is called.

    Lazy widget creation

    Before TreeV3, all nodes must be widgets. A node is added - hence graphical widget is created. For performance reasons that behavior was altered. Now when you add a node, you may actually add a "data object", containing node data, e.g {title:"new node"}. You may want to add a large nested branch of such data objects, like {title:"new", children:[...data objects..]}.

    Data objects will become real members of children array (you may recursively search them, modify etc), but graphical widgets will be created only when visitor expands them.

    The compatibility drawback of such behavior is that old code may erroneously call widget methods on data objects while recursively traversing a tree, e.g with Widget#getDescendants. You should change such code to use TreeCommon#processDescendants, or handle data objects in special way.

    There are no special mechanisms to add laziliy instantiated "data objects". You may manipulate them simply modifying children array, but no events are thrown until a real widget appears on the scene. In most cases that is fine, but you are free to "disable" lazy widget creation - do not modify children directly and enable tree.eagerWidgetInstantiation

    Tree extensions

    • Many features were moved from core into extensions
    • Added TreeDocIconExtension instead of builtin childIcon support
    • Selector now only throws events, not doing anything with nodes
    • Out-of-the box extensions introduced to be examples and handle well-known requirements

    Implicit helpers removed

    The Tree is actually a pack of loosely coupled components, connected through events. To keep things simple and also for compatibility reasons, such components(controller,selector...) were created implicitly, if not declared. But actually this proved to be a source of questions and misunderstandings. So now nothing is created implicitly, read how-to and declare things.

    RPC has both sync/async modes

    Old callbacks code was removed in favor to dojo.Deferred. Now all operations may be async and run your callbacks at the end.

    Drag'n'drop changes

    Multiple selection and multiple drag'n'drop (incomplete)

    Sounds simple enough.. Select multiple nodes with ctrl and get them with selector.selectedNodes. instead of removed selectorNode call.

    Currently, multiple drag'n'drop does not work with multiple selection because of dojo bugs. Hopefully will be fixed.

    Drop of any source, not just tree node

    If treeNode property is empty, tree will create a new node from the data returned by source.getTreeNode, then source.onDrop will be called to remove old node.

    Inline node editing

    It became possible to edit nodes inline, using TreeEditor. Base variant uses RichText widget, you can make another wrapper though. Remote calls can be made on save only, or on start/cancel too e.g for locking purposes.

    Author: Ilya

    Node creation

    There are few code paths that lead to same purpose: to create a tree node. They differ in effeciency and use patterns

    Markup creation

    You specity a tree and its nodes in HTML, relying upon dojo to parse it and turn into widgets. That is a slowest way, but nice for small trees or if only tree top is specified and the rest is created later.

    dojo widget parser walks DOM and creates a special structure. The next pass creates widgets from the structure.

    Widget#create

    The generic widget creation routine. It basically runs the operations in order:

    • Mix in widget properties from parameters/markup
    • Register widget in widget.Manager
    • Call buildRendering to make fill template and create domNode
    • Call initialize
    • Call postInitialize. registers widget as a child of its parent and after it creates all subwidgetsCall postCreate

    Note that initialize is called in pre-order: parent is initialized before children, postInitialize is called in post-order: a child is postCreated before its parent.

    Manual creation

    If you create nodes with javascript, then you run create calls manyally. So parents are naturally created (and postCreated) before children.

    There seem to be no good way to distinguish betwen markup creation and manual creation. From the one hand its seems good, because allows reuse of generic creation code. From the other hand code paths going through this code are subtly different.

    The reliable thing is that initialize will process widget after its domNode is built, BUT it should not assume anything about children.

    afterChangeTree event is fired on initialization also. If you want to know anything about children and do something at this point - check addChild, but not node creation.

    Input parameters

    children array may be

    • empty
    • contain widgets, e.g if created from markup, or someone created them before parent and pushed in
    • contain data objects, that will be turned into widgets when parent expands.
    • isFolder comes into play only when there are no children. It allows creation of empty folders, with UNCHECKED state that can be filled later.

    Performance

    Tree was coded with performance in mind. Although, JavaScript itself is a slow language. Flexible model requires some code that slows it down. It's not DOM manipulations, but actually javascript that I couldn't make lighter. Being a part of dojo/widget structure implies some overhead, but also power.

    Almost all operations require small constant time when single node is involved. Depending on your application you may notice slowdown when (most common) creating lots of nodes or performing other batch operations.

    Creation from markup or with standard create/addChild routines is 2-3 times slower, because these routines are generic.

    Comparison

    Fast node creation with dojo tree is 2-3 times slower than xtree 1.7, another tree widget, not so featured, but nicely optimized for performance.

    Important

    The results described here refer to operations without any lazy features involved. Most of time you will use lazy creation or lazy loading, or both, and operate with thousands of "virtual" nodes with ease.

    Performance Tricks

    When talking about performance, one should understand, that there are single-node operations that operate on single node... These ones are fast. The examples are: create a node, delete a node, move a node along the tree.

    ... And there are batch operations that touch a lot of nodes. The examples are: initial tree creation, moving a node from one tree to another which has different listeners, etc.

    That performance issues become noticeable at 100-300 tree nodes depending on your trees. All algorithms are linear in worst case, but JS is slow language, DOM is also not that fast.

    There is a number of features one could use to get a speedup.

    Lazy loading

    A node can be created with isFolder=true flag, but without children. Any node has a state, initially UNCHECKED for empty folder, and used by TreeLoadingController.

    When a user presses expand, tree controller (supporting lazy loading) will send a request to server asking for nodes, and parse the answer creating children.

    The benefit is obvious: you don't have to load/process whole tree at once. You can only load a single node and user will load the rest clicking "expand"

    Lazy creation

    Node/tree keeps array of its children in children property. Lazy creation is somewhat a half-way approach to lazy loading. It allows you to put data objects into this array and tree will create widgets of them later, when they are expanded.

    For instance, one can call node.children = [{title:'node1'},{title:'node2'}]. The objects will be set, but no widgets are created. You can also set children to nested array: node.children = [{title:'node1', children:[{title:'node2'}] }].

    You can create tree on server, JSON-serialize it and put to HTML, that is gzip-compressed. Compression will be 6 times or more, so it is not that space hungry.

    The benefit comes from postponing almost all real job: widget creation and attaching it to tree will happen in expansion-time.

    Comparison between lazy creation and lazy loading

    • You need web-service for lazy loading, not for lazy creation
    • No network waits for lazy creation
    • Lazy creation gives you the tree right here. You can search data objects and modify them without spending time and memory on graphical widgets

    Sometimes, lazy creation and loading may work together nicely, providing seamless increase in speed and decrease in memory footprint. For instance, server may pass a whole tree branch in JSON to lazy loading controller. Top nodes will be created right along, because user needs them, but the rest of the branch will be postponed relying on lazy creation feature.

    There are operations, like "expandAll" where such lazy tricks don't help, because all graphical widgets must be processed. That is why widget creation process is well-optimized itself. createSimple is a hacky program-only way to create TreeNodes fast. setChildren is a method to assign (and create if needed) all children at once. It helps to evade some extra work happening when children are added one by one.

    IE image-reloading fixup (!!!)

    IE has a well-known bug. If an image was loaded dynamically - with a new Image(), or img.src= assignment, or even as a background of a new node, it will not be cached. So every time when you create a node, all needed icons get loaded from server (or requested at least). A possible solution is to put a special div into HTML (adjust src to your path):

    <div style="display:none">
        <!--  IE  has  a  bug:  it  reloads  all  dynamically  resolved  images,  no  matter,  is  it  
        new  Image()  or  CSS  background.  If  you  don't  specify  images  like  that,
        it  will  reload  them  every  time  a  new  node  is  created  -->
        <img  src="../../../src/widget/templates/images/TreeV3/i.gif"/>
        <img  src="../../../src/widget/templates/images/TreeV3/i_half.gif"/>
        <img  src="../../../src/widget/templates/images/TreeV3/expand_minus.gif"/>
        <img  src="../../../src/widget/templates/images/TreeV3/expand_plus.gif"/>
        <img  src="../../../src/widget/templates/images/TreeV3/expand_leaf.gif"/>
        <img  src="../../../src/widget/templates/images/TreeV3/i_long.gif"/>
        <img  src="../../../src/widget/templates/images/TreeV3/document.gif"/>
        <img  src="../../../src/widget/templates/images/TreeV3/open.gif"/>
        <img  src="../../../src/widget/templates/images/TreeV3/closed.gif"/>
    </div>
    
    Author: Ilia

    Server communication



    To talk with server, one should use TreeLoadingControllerV3 or TreeRpcControllerV3. They inherit from TreeBasicControllerV3 and override its methods to deliver remote calls possibility.

    TreeLoadingControllerV3 contains main methods for server calls, and allows dynamic node loading. TreeRpcControllerV3 adds server calls to tree manupulations like "createChild/move/edit...".

    Url settings

    All requests go through dojo.io.bind, usually via XMLHttpRequest transport.

    contains basic Url for all requests, e.g "http://site.com/remoteTreeService.do". You can have query string in it also.

    every call adds special action parameter to query string to distinguish between call types. Actions are move, createChild..

    For children loading, the action is getChildren.

    An example url for such action would be "http://site.com/remoteTreeService.do?action=getChildren".



    Most actions imply additional data parameter with information about node/tree and other action details server may want to know.



    This way of composing an url is described in getRpcUrl, feel free to override if need.



    Request format

    data parameter is JSON-serialized. It usually sends some information about involved nodes and position. If you want to extend it somehow,

    1. override method of controller that corresponds your action, your changes will affect this action only,
    2. override getInfo method of node/tree to affect parameters globally
    3. override getInfo method of controller if that's the right place =)



    Response format

    All data is JSON-serialized. There are libraries for JSON in most of programming languages.



    Server response is evaluated as javascript. That means you can embed any javascript, that will be evaluated on client-side. But it should return object. Use object error property to signalize about server-side error.



    Good answer:

    dojo.debug('I can also put javascript in server answer');

    ([{title:"test",isFolder:true,objectId:"myobj"},{title:"test2",children:[ {title:"test2.1"} ]}])




    Good answer:

    ({})



    Good answer, will return dojo.RpcError

    ({error: "Permission denied"})





    Bad answer format (string), will return dojo.FormatError

    Exception: blabla at line 50



    Transport error (e.g 404) will also return dojo.CommunicationError



    If you don't know what to return, return ({}). That means just "ok". Note outer brackets, they are needed to make sure it evaluates to javascript Object.



    Callbacks and Error handling

    Any request may be performed in synchroneous and asynchronous manner.

    Both of them return dojo.Deferred object, but for synchroneous call, it will be called until next script line.



    You can call deferred.addCallback / deferred.addErrback to add your actions.



    An example of usage would be



    var deferred = loadingController.expandAll(tree);



    // add action when operation finishes successfully

    deferred.addCallback(function() { alert('expanded all nodes!'); });



    // process error

    deferred.addErrback(function(err) { dojo.debugShallow(err); });





    More information about Deferred class and asynchronous programming can be found at http://mochikit.com/doc/html/MochiKit/Async.html (dojo implementation is Mochikit port), http://twistedmatrix.com/projects/core/documentation/howto/async.html (python implementation and a nice state-of-art intro).











    Tree Events

    There are many classes of events, published with dojo.event.publish mechanism. Every event has a name and message object, containing more precise information about what happened. You may use events to update your data while tree changes, and to perform additional processing of involved objects.

    There is a default naming scheme for an event class. E.g for a tree with widgetId='mytree', event of class afterTreeCreate will be named "mytree/afterTreeCreate". You may provide other names in eventNames property of the tree.

    afterTreeCreate

    Event occurs after tree creation is complete. There is an alternative to hook on this action by putting your objects in "listeners" property of the tree. The difference is that listeners are guaranteed to hook before nodes get added, and afterTreeCreate is published after Tree widget is created.

    source

    references to tree

    beforeTreeDestroy

    Published right before actual Tree#destroy method is called. Useful for cleanups

    source

    references to tree

    beforeNodeDestroy

    Right before TreeNode#destroy is called. Node is detached after this event fired.

    source

    references to node

    afterChangeTree

    This event is tightly created with node creation process. It is fired when

    • a node is created

      • no parent at this stage
      • fires in initialize(), so children may be not added yet

    • a node was moved to another tree widget

    oldTree

    references previous tree, null if node has been just created

    newTree

    new(current) tree

    node

    target node

    afterSetFolder

    Fires when a node obtains "folder" state. That may happen when a first child is added to a leaf, or if a node was initially created with isFolder=true

    source

    references to node

    afterUnsetFolder

    Fires when a node obtains looses "folder" state. That may happen when a last child leaves the node, and Tree.unsetFolderOnEmptyis set, or when unsetFolder is called explicitly.

    source

    references to node

    (before|after)Move(From|To)

    These events share same arguments and fire when a node is moved. Move process is considered something special. When you move a node, no detach/addChild events get thrown. That allows to tell situations when a node leaves a tree for some time (detached then attached) from situations when a node is simply moved to another location

    oldParent

    previous parent

    oldTree

    previous tree

    oldIndex

    previous index among siblings

    newParent

    new parent

    newTree

    new tree

    newIndex

    new index among siblings

    child

    target node

    afterAddChild

    Published when a node is attached to parent. This may occur at the end of creation process, or when a node is lazily instantiated from data object.

    Also it occurs when a detached node gets attached.

    child

    references to node

    index

    index among siblings

    parent

    current parent who adopted a child

    childWidgetCreated

    flag is set if child was laziliy instantiated. That is: it resided as data object in children array, but user expanded its parent, so node widget came to life.

    afterDetach

    Occurs when a node is detached. This may happen in the process of node destruction. Keep in mind, that detaching a node sets its parent to null, but

    tree remains same.

    child

    references to node

    parent

    references to old parent

    index

    references to index among children of old parent

    after(Expand|Collapse)

    Fire when a node is expanded/collapsed. Some togglers do nice animation hiding/showing node. This event fires when animation finishes.

    source

    target node

    afterSetTitle

    When a node is edited, or explicit setTitle method is called, this event helps to inform interested parts about changes.

    source

    target node

    oldTitle

    replaced node title

    Tree HTML/CSS model

    There are few major approaches to building dynamic trees.

    1. list of idented divs

    Each tree node is a div with indentation. Indentation is e.g 20px * node depth, so everything looks fine. Usually indentation is made of many quadrantic images, each of them represents empty space or grid lines, which visibly link nodes together.nested divs.

    Of course, 'div' can be changed to any tag, e.g 'li'.

    2. nested divs

    Divs are nested same way tree nodes are nested. Can use ul/li instead of divs, there's only symantic difference, of course, if styles are same.

    Each div can be idented relatively to its parent with padding/margin property, or with images.

    If we use images here, then there will be lots of extra tags, so padding/margin seems better.

    Dojo tree adapts the 2nd approach, of course, with padding/margin identation.

    Let's consider a simple tree

    * Node1

    * Node 1.1

    * Node 1.2

    * Node 2















    (Page is unfinished, and content will probably be merged into the Trees intro page -- CAR)

    Trees

    The trees we see in User Interfaces help sort out long, heirarchical lists. A file system is the classic example, with Windows using it in Explorer and Macintoshes with Finder (is it still called that???).

    Nodes are the basis of a dojo tree. A node can include other nodes, and is then called a branch, container or folder. A node containing no other nodes is a leaf. Dojo does not force you to distinguish branches from leaves. It deduces the tree structure from your own code.

    A dojo tree contains at least two dojo widgets:

    But there are many dojo widgets to help you sculpt, mold, and connect behavior to your tree.

    1 Hello Tree World

    Here's a simple example.

    <div dojoType="Tree" >
      <div dojoType="TreeNode" title="Item 1">
        <div dojoType="TreeNode" title="Item 1.1" ></div>
          <div dojoType="TreeNode" title="Item 1.2" >
             <div dojoType="TreeNode" title="Item 1.2.1" >
                <div dojoType="TreeNode" title="Item 1.2.1.1" ></div>
              </div>
              <div dojoType="TreeNode" title="Item 1.2.2" ></div>
           </div>
        <div dojoType="TreeNode" title="Item 1.3" ></div>
      </div>
      <div dojoType="TreeNode" title="Item 2" ></div>
    </div>
    

    Which produces the following lovely tree:

    SCREENSHOT

    You can do open a node and show its contents by clicking the + icon, or hide them with the - icon, just like you're used to. Nice!

    2 Connecting an Action to a Node

    The problem is our tree does nothing but stand around looking beautiful. Nothing wrong with that. Normally, though, you'd want some kind of action to occur when the node is clicked. To do this, you can use the TreeSelector Widget.

    TreeSelector is a widget without a UI. You use it as a placeholder for connecting the tree to various Javascript actions. This makes it easy to construct many trees, and connect them to the same actions.

    <script>
    	dojo.addOnLoad(function() {
    		dojo.event.topic.subscribe("nodeSelected",
    			 function(message) { alert(message.node.title+" selected"); }
    		);
    	});
    </script>
    
    <div dojoType="TreeSelector" widgetId="tSelector" eventNames="select:nodeSelected" ></div>
    
    <div dojoType="Tree"  selector="tSelector" >
      <div dojoType="TreeNode" title="Item 1">
        <div dojoType="TreeNode" title="Item 1.1" ></div>
      <div dojoType="TreeNode" title="Item 2">
    </div>
    
    (Is there an easier way to do this??? -- CAR)

    When you click on a node, an alert box will pop up with the name you selected.

    3 Submitting a Selected Node

    You can make the selection event arbitrarily complex. But many times, you just want to pass the selected node along with a form. Simple!

    <script>
    dojo.addOnLoad(function() {
       dojo.event.topic.subscribe("nodeSelected",
           function(message) { document.menuForm.eatMe.value = message.node.title; }
       );
    });
    </script>
    
    What would you like to eat first?
    
    <form name="myForm">
      <input type="hidden" name="eatMe" value="" />
      <div dojoType="TreeSelector" widgetId="tSelector" eventNames="select:nodeSelected" />
    
        <div dojoType="Tree" selector="tSelector" >
          <div dojoType="TreeNode" title="Dessert (Recommended)">
            <div dojoType="TreeNode" title="Ice Cream" value="ICE76645" />
            <div dojoType="TreeNode" title="Cake" value="CAK85467" />
         </div>
         <div dojoType="TreeNode" title="Entree">
            <div dojoType="TreeNode" title="Meat Loaf" value="MTL18908" />
         </div>
      </div>
    </form>
    

    Clicking a node fills the value ICE76645, CAK85467, or MTL18908 into the hidden field "eatMe".

    In this example, a tree is a standin for a select/options tag. For long lists, a select/option list gets too long to navigate. Humans like their information grouped and organized into smaller chunks. But databases thrive on flat namespaces, like the UPC system or Social Security Numbers. Trees give you the best of both worlds.

    4 Gridlines

    By default, tree nodes always have a gridline on their left. These gridlines helps the user quickly see which nodes are siblings, and which node is the parent.

    You can turn off gridlines at the root level and/or for the entire tree. By default, each 1st level TreeNode connects to a "phantom" root node, as in:

    SCREENSHOT

    You can remove the phantom Root node so the first level nodes appear with no gridlines to their left, as in:

    <div dojoType="Tree" showRootGrid="false">
    

    Or you can turn all the gridlines off, as in:

    <div dojoType="Tree" showGrid="false" showRootGrid="false">
    

    5 Pre-Expanding Content

    Let's say you'd like to highlight a particular Tree node, for example a default value. You can do this easily enough with tags around the node title:
    <div dojoType="TreeNode"...>
      <div dojoType="TreeNode"...>
        <div dojoType="TreeNode" title="<span style='background-color:yellow' >The most popular choice</span>" />
        ...
      </div>
      <div dojoType="TreeNode"...>
        <div dojoType="TreeNode" title="Another Choice" />
      </div>
    </div>
    
    But if this TreeNode is 3 levels down, the user will have to expand both levels above it. A better way is to pre-expand content levels. This requires the attribute "expandLevel", which means "expand all nodes that are n levels below" If n is more than 1, all levels between 1 and n are expanded, since seeing an expanded node requires seeing an expanded node above. For example, if you added expandLevel="2" to the top TreeNode:
    <div dojoType="TreeNode" expandLevel="2" ...>
      <div dojoType="TreeNode"...>
      ...
    
    then both The Most Popular Choice and Another Choice will appear. But:
    <div dojoType="TreeNode" expandLevel="1" ...>
       <div dojoType="TreeNode" expandLevel="1" ...>
       ...
      </div>
      <div dojoType="TreeNode"...>
    </div>
    
    will only expand Most Popular Choice.

    Widget Namespaces

    Overview

    Widgets are combined into groups called namespaces. All the widgets built into Dojo are in the "dojo" namespace, but someone else could write their own widgets and put them in a different namespace. For example, you could write your own button and checkbox widgets, and put them into an "acme" namespace. Then "acme:Button" would be your button, and would be unrelated to the button object built into dojo, called "dojo:Button".

    Usage

    Defaults have been chosen to reduce boilerplate. A namespace maps to a top-level module by default. A top-level module path defaults to dojo/../, and widgets are expected to be in .widget.



    Given

    <img dojoType="acme:Image" />
    acme widgets are expected to be in acme folder next to dojo folder.

    acme.widget
    module is expected to contain the Image widget.

    /dojo

    /acme/

    /acme/widget/Image.js <- defines acme.widget.Image
    Loading acme.widget.Image module is the only requirement for using acme:Image in this configuration. You can load that module as part of a build, by calling dojo.require, or automatically.



    To use a folder location other than ../acme call dojo.registerModulePath.



    To select a widget module other than acme.widget, call dojo.registerNamespace.

    Automatic Loading

    To allow automatic loading of widgets in a namespace, include a manifest file. For the example above, the default resource for the manifest would be:

    <root>/acme/manifest.js
    To customize the folder location of module acme call dojo.registerModulePath.



    For most users employing the auto-require system, the manifest file contains a call to dojo.registerNamespaceResolver.



    A namespace resolver tells Dojo what module to load for a named widget.

      dojo.provide("acme.manifest");

    dojo.require("dojo.string.extras");



    dojo.registerNamespaceResolver("acme",

    function(name){

    return "acme.widget."+dojo.string.capitalize(name);

    }

    );
    The input string name will always be lower-case. So this resolver triggers loading of module acme.widget.Calendar for widget acme:calendar.



    The load-time module is not necessarily the same as the widget class module. For example, the acme.widget.Calendar class might be loaded via acme.widget.allWidgets.



    The resolver tells dojo the module to require to load a widget.



    To select a widget class module other than acme.widget, call dojo.registerNamespace.




    API

    dojo.registerModulePath(module, path): maps a module name to a path (formerly setModulePrefix).



    An unregistered module is given the default path of ../, relative to Dojo root. For example, module acme is mapped to ../acme. If you want to use a different module name, use registerModulePath.



    dojo.registerNamespace(namespace, widget_module [, resolver]): maps a module name to a namespace for widgets, and optionally maps widget names to modules for auto-loading.



    An unregistered namespace is mapped to an eponymous module. For example, namespace acme is mapped to module acme, and widgets are assumed to belong to acme.widget. If you want to use a different widget module, useregisterNamespace.



    dojo.registerNamespaceResolver(namespace, resolver): a resolver function maps widget names to modules, so the widget manager can auto-load needed widget implementations.



    The resolver provides information to allow Dojo to load widget modules on demand.When a widget is created, a namespace resolver can tell Dojo what module to require to ensure that the widget implementation code is loaded.



    The input string in the name argument will always be lower-case.

      dojo.registerNamespaceResolver("acme",

    function(name){

    return "acme.widget."+dojo.string.capitalize(name);

    }

    );

    Examples

    Let's say we have a Dojo install at root:

    /dojo/dojo.js

    /dojo/[whatever else is in the particular dojo install]
    We want to create custom modules, and decide to put them in:

    /acme
    Note that the path to acme from dojo is:

    ../acme
    For the widget examples, let's say we made some custom widgets, including one called acme.widgets.Calendar, and put them in:

    /acme/widgets/variousWidgets.js

    Automatic Loading

    Main document

    <script src="/dojo/dojo.js"></script>

    <script>

    dojo.require("dojo.widget.*");

    </script>

    Include a manifest file: /acme/manifest.js

    dojo.provide("acme.manifest");

    dojo.registerNamespaceResolver(function(name) {

    return "acme.widgets.variousWidgets";

    });

    To support markup like so:

    The acme namespace triggers require of acme.mainfest. The resolver is used to match calendar to a required module (i.e. acme.widgets.variousWidgets). Then acme.widgets module is searched for calendar implementation matching the current rendering environment.

    Explicit Loading

    Main document

    <script src="/dojo/dojo.js"></script>

    <script>

    dojo.require("acme.widgets.variousWidgets");

    </script>

    Supports markup like so:

    acme.widgets module is searched for calendar implementation matching the current rendering environment.

    Non-Widget Resources

    Main document
    <script src="/dojo/dojo.js"></script>

    <script>

    dojo.require("acme.lib");

    </script>

    acme/lib.js file:

    // ... additional code ...

    Builds

    With a build you can use any of these formats, but a manifest is not required.

    Main document

    <!-- dojo.js is a build -->

    <script src="/dojo/dojo.js"></script>

    <!-- dojo.require(s) can be here, although they are ignored -->

    Supports markup like so:

    <div dojoType="acme:calendar"></div>

    Writing Your Own Widget

    This section discusses the internals of widgets, and how to write your own.

    Compound Widgets

    TODO: moved this from "The Memo" page, where it definitely didn't belong, but it could use some expansion

    This is a crucial next step for widget authors - creating widgets which themselves contain inner widgets, resulting in what we could call 'compound widgets'.

    The procedure is simple, just add to your widget .js file, within the widget atributes object, the line:

            widgetsInTemplate:true,

    then, your subwidgets will nest perfectly within the main outer widget. You should also be able to nest to any arbitrary depth. Just remember though to abstain from setting the id or dojoId attributes in your html, rather set dojoAttachPoint instead to insert into your main outer widget a named attribute which references your subwidget. This way, you won't pollute the global element namespace. Otherwise, you'll hit problems if creating multiple instances of your compound widgets.

    The Monolithic App Widget

    One approach to dojo application design is to build the app as one huge compound uber-widget, containing all the needed sub-widgets.



    This approach will likely have a natural feel and appeal to those experienced in desktop GUI programming.



    If you want to go in this direction, then your app can get loaded in the client by a very minimal HTML file which just pulls in a minimal stylesheet, includes dojo.js, dojo.require()s your main app widget, then invokes that widget in a single
    tag within your document . There are those of us who feel that the less javascript code you have within html files, the better!

    Custom Namespace

    TODO:

    - this is old info? current description is at http://dojo.jot.com/WikiHome/Modules%20%26%20Namespaces

    - As indicated below, it is correct for the current stable version (0.3.1). It will need to be updated for the next release.

    If you're planning on creating your own widgets then it's probably a good idea to keep your own code completely separate from the Dojo codebase. This will make life easier if/when you come to install a new version of Dojo, and also prevent any name clashes with native Dojo widgets.



    First of all, you'll want to create a directory structure outside the Dojo source directory where your code will be stored. For example, let's call this new directory 'user', so that your directory structure looks something like this:



    	/dojo
    	/user
    	index.html



    Next you need to tell Dojo that this new namespace exists and where it lives. You can do this with dojo.setModulePrefix( namespace, path ), like this:



    	dojo.setModulePrefix("user", "../user");



    Note that the path (the second parameter) is relative to the root of the dojo source directory.



    [Please note: the use of dojo.setModulePrefix() is deprecated (by Dojo version 0.5), and will be replaced with dojo.registerModulePath(), which takes the same initial parameters.]



    Now since Dojo will look for widgets in a subdirectory (under '/user') called 'widget', we need to create that too:



    	/dojo
    	/user
    		/widget
    	index.html



    Now you can create our own widgets in the user/widget directory and include them using dojo.require() as usual:

           dojo.require("user.widget.MyWidget");

    Unfortunately, in version 0.3.1 you can't use the namespace when calling your widget (this has been fixed in newer versions). So for now, just use the name of the widget:

           <div dojoType="MyWidget" some_property="Some value"/>



    ...or programatically...



           var new_widget = dojo.widget.createWidget
           (
                  'MyWidget',
                  {
                         some_property:'Some Value'
                  }
           );

    In future versions (and current nightly/SVN builds), you would prepend the name of the widget with the namespace and a colon, for example:





    TODO: correct, expand...



    More on Templates

    This chapter discusses how widget templates work.

    The Template

    If you remember, in a previous chapter we looked at the template for the floating pane:

    <div id="" dojoAttachEvent="onMouseDown" class="dojoFloatingPane">
    	<div dojoAttachPoint="titleBar" class="dojoFloatingPaneTitleBar"  style="display:none">
    	  	<img dojoAttachPoint="titleBarIcon"  class="dojoFloatingPaneTitleBarIcon">
    		<div dojoAttachPoint="closeAction" dojoAttachEvent="onClick:closeWindow"
       	  		class="dojoFloatingPaneCloseIcon"></div>
    		<div dojoAttachPoint="restoreAction" dojoAttachEvent="onClick:restoreWindow"
       	  		class="dojoFloatingPaneRestoreIcon"></div>
    		<div dojoAttachPoint="maximizeAction" dojoAttachEvent="onClick:maximizeWindow"
       	  		class="dojoFloatingPaneMaximizeIcon"></div>
    		<div dojoAttachPoint="minimizeAction" dojoAttachEvent="onClick:minimizeWindow"
       	  		class="dojoFloatingPaneMinimizeIcon"></div>
    	  	<div dojoAttachPoint="titleBarText" class="dojoFloatingPaneTitleText">${this.caption}</div>
    	</div>
    
    	<div id="_container" dojoAttachPoint="containerNode" class="dojoFloatingPaneClient"></div>
    
    	<div dojoAttachPoint="resizeBar" class="dojoFloatingPaneResizebar" style="display:none"></div>
    </div>
    

    Basically, the idea is the the source HTML is replaced by this template. But there's a lot more stuff happening.

    Attaching DOM Nodes

    Inside of FloatingPane.js you will notice various variables that correspond to (point to) dom nodes within the instantiated template. It's easier to explain by example.

    Here are some lines from the template above (note the highlighted section):

         <div dojoAttachPoint="titleBar" class="dojoFloatingPaneTitleBar"   style="display:none">
         <img dojoAttachPoint="titleBarIcon"  class="dojoFloatingPaneTitleBarIcon">
    

    And here's the corresponding code from FloatingPane.js:

        titleBar: null,
        titleBarIcon: null,
        ...
    

    Merely by having that code, the titleBar variable points to the dom node generated by the template. So you can do something like:

        this.titleBar.style.color="red";

    The Container Node

    There's a special attach point called the "container node". Consider this source HTML:

    <div dojoType="FloatingPane">
        Hello world!  
       <button dojoType="Button"> press me </button>
    </div>
    

    This is a floating pane that contains some content, including a widget. What happens to the content when the floating pane is instantiated? It goes into containerNode. Notice the line below from the template:

    <div id="_container" dojoAttachPoint="containerNode"
    class="dojoFloatingPaneClient"></div>
    

    In addition, since the floating pane contains contents, we have this line in FloatingPane.js:

        isContainer: true,
    

    Attaching Events

    Another very useful feature is declarative event handling. Notice this line from the template above:

    <div dojoAttachPoint="maximizeAction" dojoAttachEvent="onClick:maximizeWindow"
     	  class="dojoFloatingPaneMaximizeIcon"/>
    

    Just by adding that line, whenever the maximize action div is clicked, the widget's maximizeWindow() function will be called.

    If you don't specify a function name, it defaults to the event name. For example,

    <div id="" dojoAttachEvent="onMouseDown" class="dojoFloatingPane">
    

    due to the above highlighted code, whenever you mouse down, onMouseDown() is called.

    Variable Replacement

    <div dojoAttachPoint="titleBarText" class="dojoFloatingPaneTitleText">
       ${this.caption}
    </div>
    

    When dojo creates the widget from the template, it substitutes the value of this.caption from the object into the template.



    More on The Javascript Object

    Now we'll see how you write the javascript portion of a widget.

    Previously we looked at the CSS and the HTML used to define a widget. The third and final component to widgets is a javascript class to handle widget rendering details and events on the widget.

    defineWidget

    The first step to writing the javascript for a widget is to call defineWidget. Below I'm defining a widget called my.widget.html.Foo that extends dojo.widget.HtmlWidget,the base class for most widgets. (We'll talk about different base classes and what "html" means in a later document.)

    dojo.widget.defineWidget("my.widget.Foo", dojo.widget.HtmlWidget, {

    function() {

    // do initialization tasks, make instance properties

    },

    {

    ...prototypical properties (in object notation)...

    }

    );

    Using dojo.widget.defineWidget, the tasks below are performed automatically:

    • register widget package (tell dojo the namespace contains widgets)
    • add the parse tree handler (identify a markup tag with the widget)
    • set the widgetType
    • invoke ancestor constructor
    • inherit from ancestor prototype

    Alternately, I might want to extend an existing widget. Here I'm making an enhanced version of Foo called FooPlus:

    dojo.widget.defineWidget("my.widget.FooPlus", my.widget.Foo, { 

    function() {

    // do initialization tasks, make instance properties

    },

    {

    ...prototypical properties (in object notation)...

    }

    );

    Parameters

    OK, that's the skeleton for the widget, but what do we put inside? The first thing to think about are the parameters that are used when you construct the widget. Every widget can take parameters. For example:

    Where are the parameters defined, and how do you set their types? Actually, they are just properties in the javascript class. In this case:

        toggle: "",			// string

    toggleDuration: 0, // integer

    onClick: function(){} // function

    Javascript doesn't have types, so how do we specify the types of the parameters? By specifying an example. In the above case, 0 means integer and "" means string.

    Note: don't set them to null or it won't work!

    Default values:

    You can also specify default values in the javascript file. If the user doesn't specify a value for a parameter the default is used. For example:

        toggle: "fade"

    Important Properties To Set

    Next, you need to set certain properties that define how the widget operates.

    isContainer

    True/False. Must be set to true if the widget has child HTML or child widgets

    snarfChildDomOutput

    True/False. Set this to true if you are making something like a container node, where the input is just a list of widgets. It resolves issues where a child's generated DOM tree cannot be put back into the same place the source dom tree was. (Because [td] cannot be a child of [div], etc.)

    templatePath

    The path to the template HTML file, if one exists for the widget. This needs to be a dojo URI object, and is normally one of the two options shown below:

    dojo.uri.dojoUri("src/widget/templates/HtmlFloatingPane.html"),

    or

    dojo.uri.moduleUri("mywidgetset","widgets/html/MyWidget.html"),

    templateCssPath

    The path to the template CSS file, if one exists for the widget. This needs to be a dojo URI object, and is normally one of the two options shown below:

    dojo.uri.dojoUri("src/widget/templates/HtmlFloatingPane.css"),

    or

    dojo.uri.moduleUri("mywidgetset","widgets/css/MyWidget.css"),

    templateString / templateCssString

    If the CSS or HTML for a widget is very simple, you can specify it in the javascript rather than using templatePath/templateCssPath to refer to other files. This is what the dojo build process does automatically to embed templates/CSS in your widget code when you specify the intern-strings option.

    templateString: "
    Simple Template
    ",

    templateCssString: ".simple { color:blue; font-size:12pt; }",

    Initialization Methods

    Inside a widget file you will notice a number of functions for initialization. The most important ones are described below in the order they are called during the widget creation process.

    postMIxInProperties()

    this is called after the properties (see previous section) are initialized to the user specified values, but before the HTML template is instantiated.

    Typical actions to perform here are validating and adjusting parameters provided to the widget.

    fillInTemplate()

    This is called after the template has been instantiated, so this.domNode points to the generated DOM tree. However, the children DOM nodes (for containerNode) and widgets haven't yet been copied over, and the widget's DOM node has not yet been placed in the actual HTML document.

    Typical actions to perform here include:

    • Enabling or disabling parts of the widget
    • Applying styles/classes/etc
    • Creating widgets that attach to nodes in the template
    • Setting initial state

    postCreate()

    This is called after the children dom nodes and widgets have been instantiated. However, for programatically created widgets, none of the children exist yet, because they are added after createWidget() finishes, via the addChild() call.

    Typical actions to perform here include:

    • Connecting event handlers

    • Manipulating parent or child nodes (with the above caveat)

    Arrays, Objects, and Statics

    Widget attributes that are Arrays or Objects need to be declared in the initializer() function, rather than like other variables (numbers or strings), so that they are not inadvertently shared between other instances of the same widget.

    On the other hand - to make a static variable (i.e. a variable that is shared across all instances of the widget), just take advantage of the above issue:

        // static1 and static2 are shared across every Foo widget

    statics: { static1: 0, static2: "" }
        this.statics.static1++;    // increments the single copy

    Important Variables

    this.domNode - points to root of generated tree

    this.containerNode - place where child HTML was attached

    this.children - array of child widgets

    The Memo

    Overview

    This page serves as an introduction to the art and science of creating one's own dojo widgets, and invoking them in html code just like mainstream Dojo widgets.

    The following walk through will take you through all the steps needed to create a simple widget, ie a widget that contains only dom elements, not any nested dojo widgets.

    Following this walk through are some instructions for creating compound widgets (ie, widgets that include other dojo widgets).

    Getting Started

    Let's say that you want to make a memo widget. Just a yellow sticky note to remind yourself of your dentist appointment, or whatever. Something that you can put on the screen and then erase later. Something so that a call like this:

    <div dojoType="Memo" title="Reminder">
    	Pick up milk on the way home
    </div>
    

    will produce something that looks like this:

    Reminder
    X
    Pick up milk on the way home.

    The Template

    The first step is to write HTML and CSS that prototypes how the widget will look. You can do this in any editor of your choice. I made the prototype above using this HTML:

    <div class="memo">
      <div class="title">Reminder</div>
      <div class="close">X</div>
      <div class="contents">Pick up milk on the way home.</div>
    </div>
    

    And this CSS:

    .memo {
    	background: yellow;
    	font-family: cursive;
    	width: 10em;
    }
    
    .title {
    	font-weight: bold;
    	text-decoration: underline;
    	float: left;
    }
    
    .close {
    	float: right;
    	background: black;
    	color: yellow;
    	font-size: x-small;
    	cursor: pointer;
    }
    
    .contents {
    	clear: both;
    	font-style: italic;
    }
    

    Note how I put as much of the formatting code into the CSS. This isn't necessary, but it does make it easier for other people to customize the widget, merely by altering the CSS.

    Turning it into a widget

    To make this memo into a widget, you need to declare a javascript object that connects with the HTML and CSS template above. So, put the HTML in a file called Memo.html, the CSS in a file called Memo.css, and make the Memo.js file below:

    dojo.widget.defineWidget(
    	// widget name and class
    	"acme.widget.Memo",
    	
    	// superclass
    	dojo.widget.HtmlWidget,
    	
    	// properties and methods
    	{
    		templatePath: dojo.uri.dojoUri("../acme/widget/Memo.html"),
    		templateCssPath: dojo.uri.dojoUri("../acme/widget/Memo.css")
    	}
    );
    

    Contents and Parameters

    The obvious problem with this widget is that no matter what is inside the source div, it always says "Pick up milk on the way home". What you need is for the contents of the source div to be inserted into the generated output. This is what the "containerNode" is for, and you use it like this...

    First, in the template, get rid of the static content, and instead mark that the div should hold the content from the source.

    <div class="memo">
      <div class="title">Reminder</div>
      <div class="close">X</div>
      <div class="contents" dojoAttachPoint="containerNode"></div>
    </div>
    

    Then, in the javascript object, denote that this widget is a container:

    dojo.widget.defineWidget(
    	// widget name and class
    	"acme.widget.Memo",
    
    	// superclass
    	dojo.widget.HtmlWidget,
    	
    	// properties and methods
    	{
    		isContainer: true,
    		templatePath: dojo.uri.dojoUri("../acme/widget/Memo.html"),
    		templateCssPath: dojo.uri.dojoUri("../acme/widget/Memo.css")
    	}
    );
    

    OK, what about the title? The title is specified as an attribute:

    <div dojoType="Memo" title="Reminder">
    	Pick up milk on the way home
    </div>
    

    That means that it's a parameter to the widget. Parameters are specified as normal widget properties. In this case, the widget properties would look like this:

    	// properties and methods
    	{
    
    		// parameters
    		title: "Note",
    
    		// settings
    		isContainer: true,
    		templatePath: dojo.uri.dojoUri("../acme/widget/Memo.html"),
    		templateCssPath: dojo.uri.dojoUri("../acme/widget/Memo.css")
    	}
    

    This widget now has a "title" parameter, with a default value of "Note"

    How do you stick this parameter's value into the widget? Luckily widget templates have variable substitution, so no coding is necessary. Just modify the template to use this parameter:

    <div class="memo">
      <div class="title">${this.title}</div>
      <div class="close">X</div>
      <div class="contents" dojoAttachPoint="containerNode"></div>
    </div>
    

    Events

    OK, the content of the widget is showing up correctly, but how to you make clicking the X cause the widget to disappear? It's pretty simple, and hardly requires any javascript. The first step is to modify the template to handle click events on the X:

    <div class="memo">
      <div class="title">${this.title}</div>
      <div class="close" dojoAttachEvent="onClick">X</div>
      <div class="contents" dojoAttachPoint="containerNode"></div>
    </div>
    

    Then you simply add a method to the widget javascript object to handle the click:

    		onClick: function(evt){
    			this.destroy();
    		}
    

    That's it! Your first functioning widget!

    Final code

    Memo.html

    <div class="memo">
      <div class="title">${this.title}</div>
      <div class="close" dojoAttachEvent="onClick">X</div>
      <div class="contents" dojoAttachPoint="containerNode"></div>
    </div>
    

    Memo.css

    .memo {
    	background: yellow;
    	font-family: cursive;
    	width: 10em;
    }
    
    .title {
    	font-weight: bold;
    	text-decoration: underline;
    	float: left;
    }
    
    .close {
    	float: right;
    	background: black;
    	color: yellow;
    	font-size: x-small;
    	cursor: pointer;
    }
    
    .contents {
    	clear: both;
    	font-style: italic;
    }
    

    Memo.js

    dojo.widget.defineWidget(
    	// widget name and class
    	"acme.widget.Memo",
    	
    	// superclass
    	dojo.widget.HtmlWidget,
    	
    	// properties and methods
    	{
    		// parameters
    		title: "Note",
    
    		// settings
    		isContainer: true,
    		templatePath: dojo.uri.dojoUri("../acme/widget/Memo.html"),
    		templateCssPath: dojo.uri.dojoUri("../acme/widget/Memo.css"),
    		
    		// callbacks
    		onClick: function(evt){
    			this.destroy();
    		}
    	}
    );
    

    Containers

    LinkPane

    ContentPane

    Part 5: "Connecting the pieces"


    Event System

    Unlike the DOM events that web programmers normally associate with the word "event", Dojo takes a broad view of events. The tools in dojo.event.* allow developers to treat any function call (DOM event or otherwise) as an event that can be listened to. Using Dojo, code can register to "hear" about any action through a uniform API.

    Events are essential for Dojo based applications as they drive the user interface, result in AJAX requests, and allow widgets to interact with each other. In a sense events are the glue that ties an application together. Cross browser event handling code can difficult to write from scratch as there are many ways in JavaScript of handling events and each browser has its own quirks and issues.

    Dojo abstracts the JavaScript event system in the dojo.event module and provides a few options for handling events which include simple event handlers, event listeners using before, after, and around advice, and topics. The Dojo event APIs are not mutually exclusive; in many cases you will use a combination of the APIs depending on your use cases.

    In this chapter we'll show you:

    • how to use these tools
    • what makes them completely different from other JavaScript event systems you may have used
    • why you'll never start writing JavaScript without dojo.event.connect() again.

    Before, After, and Around Advice

    In addition to being able to call any function or method after any other function or method call, connect() can be used to call listeners before the source function is called. In Aspect Oriented Programming terminology, this is called "before advice" while the previous examples have all be "after advice". The terminology is confusing, but for a lack of anything less mind-bending or better accepted, we adopt it for the advanced cases that connect() supports.

    Here's how we'd ensure that "bar" gets alerted before "foo" when exampleObj.foo() is called:

    dojo.event.connect("before", exampleObj, "foo", exampleObj, "bar");

    As you can see, we just perpended our previous call to connect() with the word "before". In the other cases, the word "after" was the implied first argument, which we could have added if we wanted, but typing more isn't something any of us want, and most of the time "after" is what you want anyway.

    The same connection using kwConnect() looks like:

    dojo.event.kwConnect({

    type: "before",

    srcObj: exampleObj,

    srcFunc: "foo",

    targetObj: exampleObj,

    targetFunc: "bar"

    });

    Before and after advice give us tools to handle a huge range of problems, but what about when the listener and the source functions don't have the same call signatures? Or what about when you want to change the behavior of a function from someone else's code but don't want to change their code? If we take the view that any function call in our environment is an event, then shouldn't we also have an "event object" for each of them? When using dojo.event.connect(), this is exactly what happens under the covers, and we can get access to it via "around advice". Long story short, around advice allows you to wrap any function and manipulate both it's inputs and outputs. This'll let us change both the calling signatures of functions and change arguments for listeners (among other things).

    Unlike the other advice types, around advice requires a little bit more cooperation from the author of the around advice function, but since you'll probably only be using it in situations where you know that you want to explicitly change a behavior, this is isn't really a problem. This example take a function foo() which takes 2 arguments and provides a default value for the second argument if one isn't passed:

    function foo(arg1, arg2){

    // ...

    }



    function aroundFoo(invocation){

    if(invocation.args.length < 2){

    // note that it's a real array, not a pseudo-arr

    invocation.args.push("default for arg2");

    }



    var result = invocation.proceed();

    // we could change the result here

    return result;

    }



    dojo.event.connect("around", "foo", "aroundFoo");

    The aroundFoo() function must take only a single argument. This argument is the method-invocation object. This object has some useful properties (like args) and one method, proceed(). proceed() calls the wrapped function with the arguments packed in the args array and returns the result. At this point, you can further manipulate the result before returning it. If you don't return the result of proceed(), it will appear to the caller as though the wrapped function didn't return a value. At any point you could call another function to do things like log timing information.

    Once this connection is made, every time foo() is called aroundFoo() will check it's argument and insert a default value for arg2. Around advice is kind of like goto in C and C++: if you don't know better you can make huge messes, but when you really need it, you really need it.

    Despite the power of around advice, it's not very often that globally changing a function signature or return value is the best plan. More often, you'll just want to smooth over the differences in calling signatures between two functions that are being connected. As you might have come to expect by now, Dojo provides a solution for this type of impedance matching problem too.

    The solution is before-around and after-around advice. These advice types apply a supplied around advice function to the listener in a connection. They only apply the around advice when the listener function is being called from the connected-to source. Put another way, it's connection-specific argument and return value manipulation.

    To access before-around and after-around advice, just pass in another object/name pair to a normal "before" or "after" connection, like this:

    var obj1 = {

    twoArgFunc: function(arg1, arg2){

    // function expects two arguments

    }

    };



    var obj2 = {

    oneArgFunc: function(arg1){

    // this function expects a two-element array

    // as its only parameter

    }

    };



    // we'd probably connect the functions somewhere else. Perhaps in a

    // different file entirely.



    function aroundFunc(invocation){

    var tmpArgs = [

    invocation.args[0],

    invocation.args[1]

    ];

    invocation.args = tmpArgs;

    return invocation.proceed();

    }



    // after-around advice

    dojo.event.connect( obj1, "twoArgFunc",

    obj2, "oneArgFunc",

    "aroundFunc");

    Each function now gets what it expects, and the code calling obj1.twoArgFunc() never need be the wiser that any of this is happening.

    Connecting Multiple Events

    Multiple Listeners

    Connect also transparently handles multiple listeners. They are called in the order they are registered. This would kick off two separate actions from a single onclick event:

    var handlerNode = document.getElementById("handler");



    dojo.event.connect(handlerNode, "onclick", object, "handler");

    dojo.event.connect(handlerNode, "onclick", object, "handler2");

    We didn't have to change the API we were using, rewire anything for multiple events, etc. It all just works. Now every time you click the node, and object.handler() gets called and then object.handler2() gets called.

    Finally, note that connect can take an array of objects as input:

    dojo.event.connect(

    dojo.byId("id"),“onclick",

    listenerObj, “handleOnClick");

    Disconnection and Multi-Connection

    Connecting is one thing, but what about when you want to stop listening? dojo.event.disconnect() will stop the listening arrangement between functions, but must be pass exactly the same arguments as were passed to connect in order to ensure successful disconnection.

    If there's anything that can trip up new users of dojo.event.connect(), it's inadvertently connecting multiple times. Very often, a piece of code will get called multiple times, and it will contain a dojo.event.connect() call. The developer is then surprised when their listener function is called multiple times for every time the source function fires. What to do?

    Connecting Once And Using Keywords

    One option is to move your connect() call to a location that will get invoked only once, but sometimes that's just not feasible. An optional argument to connect() ensures that the same arguments to connect passed multiple times will result in only one connection between functions. Unfortunately, it's the 8th parameter. Ugh. The last thing we want to do is remember 8 different parameters. The best answer in this scenario is to use the keyword-argument version of connect, aptly named kwConnect(). To use it, we have to give the parameters we've been using so far names. Here's our object connection example using kwConnect() and the once

    parameter:

    dojo.event.kwConnect({

    srcObj: exampleObj,

    srcFunc: "foo",

    targetObj: exampleObj,

    targetFunc: "bar",

    once: true

    });

    As I'm sure you've already guessed, there's an analogous kwDisconnect method. Just pass it what you pass kwConnect, naturally.





    Event Object

    Using dojo.event also masks browser differences by normalizing the event object (for DOM node events) so you can use common event code in any browser.

    Fixed event objects have these modifications:

    For key events, a set of event key code aliases are installed, so you can express (e.keyCode == e.KEY_ESC). Also, a reverse key code lookup is installed, so you can express (e.revKeys[e.keyCode] == 'KEY_ESC').

    These properties are made available in all browsers:

    • target
    • currentTarget
    • pageX/pageY - position of cursor relative to viewport
    • layerX/layerY
    • fromElement
    • toElement
    • charCode

    The following methods are also made available:

    • stopPropagation() - stops other event handlers (on parent domnodes) from firing
    • preventDefault() - stops things like following the href on a hyperlink.
    • callListener() - ???

    Additionally, event (W3) vs. window.event (IE) is taken care of: all connected event handlers get passed a fixed event object (even in IE).

    As an example, the code below will work in any browser:

    dojo.event.connect(dojo.byId("foo"), "onmousemove"),

    function(evt){

    alert("mouse at pos" + evt.pageX + "," + evt.pageY);

    });

    Events And Widgets

    A brief note about events and widgets.

    dojo.event.connect() can be used with widgets just like any other objects. However, there is a shortcut for defining "after" advice on a widget.

    In the above example, the alert is called after the widget's own onClick() function finishes executing.

    On the other hand, in the case below:

    The widget's onClick function is replaced by function foo.

    This is a somewhat confusing discrepancy (the latter behavior is more consistent with widget parameter setting in general), but it's left in place for backwards compatibility.

    Page Load / Unload

    Often you will want to schedule some code to run on page load. Traditionally, this is done like

    	window.onLoad = ...;

    or perhaps

    	

    However, that won't work for Dojo, because Dojo needs to override window load and unload. So, you should do this:

    function init(){

    ...

    }

    dojo.addOnLoad(init);



    function cleanup(){

    ...

    }

    dojo.addOnUnload(cleanup);

    Just like the normal dojo.event.connect() call, addOnLoad() and addOnUnload() can be called multiple times without overwriting the previous values, so you don't have to worry about one piece of Javascript code affecting another.

    The line dojo.addOnLoad(init); tells Dojo to call the init function when it has finished loading correctly. This is very important! If the init function was called before Dojo has finished parsing the HTML then widget objects would not have been instantiated and so would not exist at that point in time - causing a nasty error.





    Publish and Subscribe Events

    Use publish and subscribe to communicate events anonymously between widgets or any JavaScript functions of your choosing. You may also consider customizing the widget to allow the topic name to be passed in as an initialization parameter to make the widget more flexible.

    The following example shows how two objects may use publish and subscribe to communicate with each other.

    var foo = new function() {
        this.init = function() {
            dojo.event.topic.subscribe("/mytopic", this, processMessages);
        }
    
        function processMessages(message) {
            alert("Message: " + message.content);
       }
    }
    
    var bar = new function() {
        this.showMessage = function(message) {
            dojo.event.topic.publish("/mytopic", {content: message});
        }
    }
    
    foo.init();
    bar.showMessage("Hello Dojo Master");
    

    In the exampe above the object foo registers with a topic called '/mytopic' when the init function is called. Bar publishes a message to the topic '/mytopic' which results in the function showMessages being called. You can create any number of topics to publish and subscribe to.



    Using publish and subscibe is very easy and it makes wiring things together easy. Widget communication by default is within the same JavaScipt execution context. Not all event handling need be exposed using publish and subscribe however using these types of events allows your code to be flexible and permit future integration with other widgets.

    Topics

    Dojo provides a means of anynonymous event communication which can be very useful to connect together widgets in a page that may have no previous knowledge of each other. This maybe done using publish/subscribe style events. Publish subscribe style events require that the components that wish to communicate information simply share the name of a topic or queue to which the events are published/subscribed to. Objects may be passed as an argument of the events which provides a powerful means of inter-object/widget communication.

    The API for publishing to a topic is as follows:

    dojo.event.topic.publish("/topicName", args);

    That is pretty much it to publish an event. The arguments are passed as an object literal and will be seen by all clients subscirbed to the corresponding topic "/topicName".



    The API for subscribing to a topic is as follows:

    dojo.event.topic.subscribe("/scroller", targetObj, targetFunc);

    A more detailed example follows:

    var ac;

    var is;



    function init() {

    ac = new AccordionMenu();

    ac.load();

    is = new ImageScroller();

    is.load();

    }



    function Scroller() {

    this.setProducts = function(pid) {

    // show the products for pid

    }



    this.handleEvent = function(args) {

    if (args.event == 'showProducts') {

    this.setProducts(args.value);

    }

    }



    this.load = function () {

    dojo.event.topic.subscribe("/scroller", this, handleEvent);

    }
    function Accordion() {

    function expandRow(target) {

    ...

    var link = document.createElement("a");

    dojo.event.connect(link, "onclick", function(evt){

    this.target = target;

    dojo.event.topic.publish("/scroller", {event: "showProducts", value : target});

    });

    }

    }

    An "onclick" event on the element link will cause an event to be published to the topic name "/scroller" which is shared by both the Accordion and Scroller objects. In the case of this example the "handleEvent" function of the Scroller object will be callsed with the object literal {event: "showProducts", value : target}.

    As can be seen topics can be very useful. When designing widgets or objects that need to interact with widgets or objects consider using publish and subscribe style events.

    Working with Simple Events

    Events in JavaScript or Dojo based applications are essential to making applications work. Connecting an event handler (function) to an element or an object is one of the most common things you will do when developing applications using Dojo. Dojo provides a simple API for connecting events via the dojo.event.connect() function. One important thing to note here is that events can be mapped to any property or object or element. Using this API you can wire your user interfaces together or allow for your objects to communicate. The dojo.event.connnect() API does not require that the objects be Dojo based. In other words, you can use this API with your existing interfaces.

    DOM Events

    dojo.event.connect has multiple function signatures, but one of the simplest is:

    dojo.event.connect(srcObj, "srcFunc", targetFunc);

    The arguments are the source object, the source function (in quotes) and the target function reference or anonymous function.

    Here we have a DOM node called mylink, and whenever that DOM node is clicked myHandler will be called:

    var link = dojo.byId("mylink");

    dojo.event.connect(link, "onclick", myHandler);
    function myHandler(evt) {

    alert("dojo.connect handler");

    }

    Above the "onclick" property of link element is connnected to the function myHandler.

    But what if we don't want to set up a named function for the event handler? No problem:



    var link = dojo.byId("mylink");

    // connect link element 'onclick' property to an anonymous function

    dojo.event.connect(link, "onclick", function(evt) {

    ...

    });

    The example above shows how an anonymous function can be mapped to the "onclick" property of a link element with an existing in-lined DOM 1 style handler connected to using the "onclick" attribute of the element.

    So far, though, we're not doing anything that can't be done by setting the onclick property of the DOM Node. But what about attaching a method of an object to a DOM Node's event handler? Normally, you'd have to do something like:

    var handlerNode = document.getElementById("handler");



    handlerNode.onclick = function(evt){

    object.handler(evt);

    };

    Dojo simplifies it to:

    var handlerNode = document.getElementById("handler");



    dojo.event.connect(handlerNode, "onclick", object, "handler");

    This connect() call ensures that when handlerNode.onclick() is called, object.handler() will be called with the same arguments. Language limitations of JavaScript make it impossible to pass in the object and function name together, however separating them into an object reference and function name isn't difficult.

    Other Events

    So we've seen that connect() can handle DOM events, but what about that more expansive view of events that was mentioned earlier? To demonstrate, lets define a simple object with a couple of methods:

    var exampleObj = {

    counter: 0,

    foo: function(){

    alert("foo");

    this.counter++;

    },

    bar: function(){

    alert("bar");

    this.counter++;

    }

    };

    So lets say that I want exampleObj.bar() to get called whenever exampleObj.foo() is called. We can set this up the same way that we do with DOM events:

    dojo.event.connect(exampleObj, "foo", exampleObj, "bar");

    Now calling foo() will also call bar(), thereby incrementing the counter twice and alerting "foo" and then "bar". Any caller that was counting on getting the return value from foo() won't be disappointed. The source method should behave just as it always has. On the other hand, since there's no explicit caller for bar(), it's return value will be lost since there's no obvious place to put it.

    The Dojo event model

    We've also inadvertently demonstrated that connect() takes variable forms of arguments. So far, it's correctly handled:

    • object, name, name
    • object, name, function pointer
    • object, name, object, name

    This is par for the course when using connect(). Since it is used in so many places, for so many things, and in so many ways, connect() does a lot of checking and normalization of it's arguments. The connect method tries to disambiguate the types of the positional parameters based on usage. Some common usages are:

    • dojo.event.connect(scope1, "functionName1", "globalFunctionName2");
    • dojo.event.connect("globalFunctionName1", scope2, "functionName2");
    • dojo.event.connect(scope1, "functionName1", scope2, "functionName2");
    • dojo.event.connect("after", scope1, "functionName1", scope2, "functionName2");
    • dojo.event.connect("before", scope1, "functionName1", scope2, "functionName2");

    The first paramether is adviceType ("after" and "before") and is optional. If it is not supplied then it defaults to "before". In the above example, adviceType was not provided and so the default, in this case "before" is used.

    srcObj - the scope (scope1) in which to locate/execute the named srcFunc. This is also optional and if it is not supplied then Dojo assumes the global object.

    srcFunc - the name of the function to connect to. In the above examples it is "globalFunctionName2" or "functionName2". This is in conjunction with the srcObj parameter. Dojo will look for a function, srcFunc, in srcObj.

    adviceObj - scope (scope 2) in which to locate/execute the named adviceFunc. Again this parameter is optional and if not supplied Dojo will assume the global object.

    adviceFunc - name of the function ("globalFunctionName1" or "functionName1") being conected to srcObj.srcFunc

    Delaying Execution

    There's one more modifier up the sleeve of connect()/kwConnect(); delayed calling. The delay property in kwConnect (the 9th positional parameter for connect) is a delay in milliseconds for those platforms that support it (all browsers do).

    The last problem worth mentioning is circular connections. Circular connections can occur when (perhaps even indirectly) a listener also calls the function it's listening to. The good news is that in a JavaScript interpreter, this will pretty quickly yield an exception of some sort. "Too much recursion" is a tip off that you've hit this problem. Debugging circular connections can be opaque, but tools like Venkman help.

    I/O

    The Dojo project is working to build a modern, capable, "webish", and easy to use DHTML toolkit. Part of that effort includes smoothing out many of the sharp edges of the DHTML programming and user experience. On the back of such high-profile success stories such as Oddpost, Google Maps, and Google Suggest, the XMLHTTP object has been getting a lot of attention of late. Sadly, in spite of all the coverage, developers have been on their own when it comes down to solving the usability problems that come along for the ride.

    Cross Domain XMLHttpRequest using an IFrame Proxy

    Note: The code for this feature is available in Dojo 0.4 and later. IE 7 Support in Dojo 0.4.1 and later.

    Background

    The browser security model does not allow using XMLHttpRequest (XHR) from one web page domain to contact an URL on another domain. However, there are cases when it would be nice to do cross domain XHR requests. There is a proposal in the W3C's Web API group to address this need (see this Mozilla tracking bug, and the bug comments for a link to the proposal).



    As with most standards, it will take a while for this proper solution to saturate the marketplace. In the meantime, to get something like cross domain XHR requests today, there are the following options:

    • Set up a proxy server on the web page domain and have it forward the requests to the real XHR endpoint (requires server infrastructure).
    • Use Flash (user has to have Flash installed).
    • Use script tags (can do cross domain requests but return type must be JavaScript/JSON, and a callback mechanism needs to be established).
    Another way to allow cross domain requests is to use the technique that is now available via dojo.io.XhrIframeProxy: use iframes that communicate with each other by changing URL fragment identifiers. This has the benefit of being just plain HTML and JavaScript (no additional server infrastructure or Flash), and it should be able to accommodate any asynchronous XHR request. It has been tested and works in IE 6.0, Firefox 1.5, Safari 2.0.3, and Opera 9.



    It also contains a security mechanism that API providers can use to restrict the allowed cross-domain requests.

    IFrames, Fragment Identifiers and XHR Proxying

    Fragment Identifiers are the part of an URL that comes after the # sign:



    http://www.a.com/path/to/file.html#fragmentIdentifier



    A document in an IFrame can change the fragment identifier on its parent document (the document containing the IFrame). Changing the fragment identifier does not cause the page to reload. Similarly, the parent document can change an IFrame's fragment identifier without causing page reloads. Since the pages don't reload, state can be maintained inside the page.



    To communicate between two cross domain documents :

    • A document (the Client document) defines an IFrame that loads the other document (the Server document).
    • Define a protocol to pass information through fragment identifiers.
    • Tell each document about the URL for the other document (so they can set the fragment identifiers correctly -- the browser needs a complete URL when setting a cross domain location).
    • Use a JavaScript timer to check for changes in the fragment identifiers.
    To send an XHR request to another domain:

    • Define a JavaScript object that implements the XHR interface (a Facade).
    • Use that object instead of an actual XHR object.
    • For the Facade's send() method, serialize the request headers, method, URL and data.
    • The browser places a limit on the size of a document's URL, so the Client document breaks this serialized data into a set of fragment identifiers that will fit under the URL limit.
    • The Client document sends each fragment identifier to the Server document. The Server document sends an acknowledgement back to the Client, and the Client sends the next fragment identifier, until all are sent.
    • The Server document assembles the fragment identifier parts into the original serialized data, unpacks it into an object, then uses a real XHR object (now on the Server's domain) to do the final API service call.
    • The Server document then serializes the XHR response, and sends it back to the Client using fragment identifier segments.
    • The Client unpacks the serialized response, and sets the appropriate values on the XHR Facade.

    Trade-Offs

    Pros

    • 100% pure browser. No Flash or additional server infrastructure.
    • It can be dropped in fairly transparently to code that is already using XHR.
    Cons
    • The technique uses IFrames and loads documents into the IFrames, so it takes more browser memory than native XHR. It would be interesting to compare the resource requirements with the amount needed to run Flash.
    • More network traffic to download xip_client.html and xip_server.html (the contents of the IFrames). However, you can configure your web server to tell the browser to cache these files for a very long time.
    • Timers are involved, with message serialization and deserialization.
    • Setting all of those URLs in the IFrames causes MSIE to make lots of those "clicking" sounds (the sound normally to indicate to the user they clicked on a link).

    Security Considerations

    This approach does not allow cross domain access to any XHR-enabled API service. For it to work, the API service must place the Server document (web page) on its server. That web page is given the Client URL and the XHR request in serialized form, so it can restrict who can contact the service and what types of requests are allowed. Note that all request validation happens inside the Server document's JavaScript.



    You should not experiment with this technique unless you are very restrictive on the clients and API URLs that are allowed. Placing the Server document on your web server means opening up the allowed URLs to the world.

    Dojo Implementation/Examples

    As of 7/31/2006, the Dojo tree has support for XHR IFrame Proxying. The relevant files are:

    • src/io/XhrIframeProxy.js: the Dojo package, dojo.io.XhrIframeProxy, that provides the XHR Facade and manages the use of xip_client.html.
    • src/io/xip_client.html: the Client document. Used internally by dojo.io.XhrIframeProxy.
    • src/io/xip_server.html: the Server document. Used by API service providers to enable cross domain XHR requests.
    • tests/io/iframeproxy: test files.
    The test files are running here if you want to try it out (note that the API server for these tests is not a powerful box, so it may seem slower than usual to get the responses).

    For web page developers

    In addition to doing the normal things for dojo.io.bind(), do the following:

    • To enable src/io/xip_client.html, find the commented out script tag under the <!-- Security protection: uncomment the script tag to enable. --> comment and remove the comments from that opening script tag.
    • dojo.require("dojo.io.XhrIframeProxy");
    • Define an iframeProxyUrl parameter to dojo.io.bind(). This will be an URL to the xip_server.html file on the API service server.
    • Only asynchronous XHR requests are supported.
    Example code snippet:



    dojo.require("dojo.io.*");

    dojo.require("dojo.io.XhrIframeProxy");



    dojo.io.bind({

    iframeProxyUrl: "http://some.domain.com/path/to/xip_server.html",

    url: "http:/some.domain.com/path/to/api",

    load: function(type, data, evt, kwArgs){

    /* do stuff with the result here */

    }

    });


    For API service providers

    API service providers will not care about src/io/XhrIframeProxy.js or xip_client.hml. They will be most interested in xip_server.html. For security reasons, xip_server.html will not run "out of the box". The following function needs to be defined:



    function isAllowedRequest(request){

    /* Decide if you want to allow the request. Return true or false */

    }



    By default, it is expecting this to be declared in an isAllowed.js file in the same directory as xip_server.html. See the comments in xip_server.html for more information.

    In addition to defining the isAllowedRequest() function, the script in xip_server.html needs to be enabled. To enable xip_server.html, find the commented out script tag under the <!-- Security protection: uncomment the script tag to enable. --> comment and remove the comments from that opening script tag.

    Reusable Parts for Non-Dojo Implementations

    • src/io/XhrIframeProxy.js: Provides the XHR Facade and manages the use of xip_client.html. It does not have all XHR methods defined, only the ones needed by Dojo's usage of XHR. You can look at the package code to see how it manages the Facade objects and the interaction with xip_client.html.
    • src/io/xip_client.html: Does not depend on any Dojo files, but it makes a call to a Dojo function when it receives a response from the Server document. Just replace the function call to your own function. Used internally by XhrIframeProxy.js.
    • src/io/xip_server.html: Does not depend on any Dojo files. Used for the final XHR request to the API service.

    Introduction to I/O bind

    The dojo.io package provides portable code for XMLHTTP and other, more complicated, transport mechanisms. Additionally, the "transports" that plug into it each provide their own logic to make each of them easier to use. The rest of this article will cover how the XMLHTTP transport from Dojo provides ways around the book-marking and back button problems.

    Most of the magic of the dojo.io package is exposed through the bind() method. dojo.io.bind() is a generic asynchronous request API that wraps multiple transport layers (queues of iframes, XMLHTTP, mod_pubsub, LivePage, etc.). Dojo attempts to pick the best available transport for the request at hand, and in the provided package file, only XMLHTTP will ever be chosen since no other transports are rolled in. The API accepts a single anonymous object with known attributes of that object acting as function arguments. To make a request that returns raw text from a URL, you would call bind() like this:

    dojo.io.bind({

    url: "http://foo.bar.com/sampleData.txt",

    load: function(type, data, evt){ /*do something w/ the data */ },

    mimetype: "text/plain"

    });

    That's all there is to it. You provide the location of the data you want to get and a callback function that you'd like to have called when you actually DO get the data. But what about if something goes wrong with the request? Just register an error handler too:

    dojo.io.bind({

    url: "http://foo.bar.com/sampleData.txt",

    load: function(type, data, evt){ /*do something w/ the data */ },

    error: function(type, error){ /*do something w/ the error*/ },

    mimetype: "text/plain"

    });

    It's possible to also register just a single handler that will figure out what kind of event got passed and react accordingly instead of registering separate load and error handlers:

    dojo.io.bind({

    url: "http://foo.bar.com/sampleData.txt",

    handle: function(type, data, evt){

    if(type == "load"){

    // do something with the data object

    }else if(type == "error"){

    // here, "data" is our error object

    // respond to the error here

    }else{

    // other types of events might get passed, handle them here

    }

    },

    mimetype: "text/plain"

    });

    One common idiom for dynamic content loading is (for performance reasons) to request a JavaScript literal string and then evaluate it. That's also baked into bind, just provide a different expected response type with the mimetype argument:

    dojo.io.bind({

    url: "http://foo.bar.com/sampleData.js",

    load: function(type, evaldObj){ /* do something */ },

    mimetype: "text/javascript"

    });

    And if you want to be DARN SURE you're using the XMLHTTP transport, you can specify that too:

    dojo.io.bind({

    url: "http://foo.bar.com/sampleData.js",

    load: function(type, evaldObj){ /* do something */ },

    mimetype: "text/plain", // get plain text, don't eval()

    transport: "XMLHTTPTransport"

    });

    Being a jack-of-all-trades, bind() also supports the submission of forms via a request (with the single caveat that it won't do file upload over XMLHTTP):

    dojo.io.bind({

    url: "http://foo.bar.com/processForm.cgi",

    load: function(type, evaldObj){ /* do something */ },

    formNode: document.getElementById("formToSubmit")

    });

    Phew. Think that about covers the basics. Good thing you weren't planning on implementing all that stuff yourself, right?

    Remote Procedure Calls (RPC)

    As you have seen, Dojo provides powerful, yet simple, ways of performing a variety of I/O functions through the use of dojo.io.bind. However, during the development of a typical application, a developer will have many I/O calls to make and will typically gravitate towards a common way of making those I/O calls on both the server and the client. This will often include defining functions that take some input and perform the appropriate request, as well as hooking that request to a callback function to process the results. In effect, the developer is required to implement a way of marshaling the request to the server in a way that it expects and then to have the client receive the contents in a way it expects. Dojo's RPC service aims to make this less error prone, easy to do, and require less code.

    Remote Procedure Calls (RPC), also know as Remote Method Invocations, are a mainstay of the client/server development world. Essentially, RPC allows a developer to invoke a method on a remote host. Dojo provides a basic RPC client class that has been extended to provide access to JSON-RPC services and Yahoo services. It was designed so that it is also fairly trivial to implement custom RPC services.

    Let's pretend that we have a little application that we want to make some server calls with. For simplicity's sake, we'll say the methods we want the server to do are add(x,y) and subtract(x,y). Without using anything special, like an RPC client, we might do something like this:

    add = function(x,y) {



    request = {x: x, y: y};



    dojo.io.bind({

    url: "add.php",

    load: onAddResults,

    mimetype: "text/plain",

    content: request

    });

    }



    subtract = function(x,y) {



    request = {x: x, y: y};



    dojo.io.bind({

    url: "subtract",

    load: onSubtractResults,

    mimetype: "text/plain"

    content: request

    });

    }


    As you can see, this isn't particularly difficult. However, this is quite the simple application, despite our every attempt to make it complicated by having the server add or subtract two numbers instead of performing these operations in the client in the first place. What happens if our application is not so simple and has 30 different requests to make? I guess we would have to just write this same code over and over for each different request; each time making a request object, specifying URLs, potentially validating parameter types, and so on. This is simply error prone and boring to write.

    Dojo's RPC clients simplify this whole process by taking a simple definition of the remote methods and application needs and generating client side functions to call these methods. A developer need only write this definition, and initialize a RPC client object and then all of these remote methods are available for the developer to use as normal.

    The definition file, called a Simple Method Description (SMD) file, is a simple JSON string that defines a URL that will process the RPC requests, any methods available at that URL, and the parameters those methods take. The definition for our example above might look like this:

    {

    "serviceType": "JSON-RPC",

    "serviceURL": "rpcProcessor.php",

    "methods":[

    {

    "name": "add",

    "parameters":[

    {"name": "x"},

    {"name": "y"}

    ]

    },

    {

    "name": "subtract",

    "parameters":[

    {"name": "x"},

    {"name": "y"}

    ]

    }

    ]

    }

    Once the definition has been created, the code its pretty simple. The definition can be supplied either as a URL to retrieve it, a JSON string, or a JavaScript object.

    var myObject = new dojo.rpc.JsonService("http://localhost/definition.smd");

    var myObject = new dojo.rpc.JsonService({smdStr: definitionJSON});

    var myObject = new dojo.rpc.JsonService({smdObj: definition});

    Thats it! Now all thats left is to call the method.

    myObject.add(3,5);

    I'll bet you are saying to yourself, "Nice try, but I want to get the results of the add method, not just call it." You are correct, but that is also simple to achieve. Recall that we are making asynchronous calls to the server. While we could make the request synchronous, it would likely provide for a bad user experience because it would block the user interface during the call. Instead, the return value of the myObject.add() call, is a deferred object. The deferred object, something that might be familiar to users of Twisted Python or MochiKit, allows a developer to attach one or more callbacks and errbacks to the resultant data event. Our simple example can be expanded as such:

    var myDeferred = myObject.add(3,5);

    myDeferred.addCallback(myCallbackMethod);

    or more succinctly:

    var myDeferred = myObject.add(3,5).addCallback(myCallbackMethod);

    As you can see, we've added myCallbackMethod as a callback for the deferred object returned from myObject.add(). In this case myCallbackMethod will be called with parameter with a value of 8. Likewise, an errback method can be attached to the deferred object to process an errors returned from the server. We can add as many callbacks and errbacks to our deferred object as we want and they will be called in the order that they were connected to the deferred object.

    This discussion has revolved around using dojo.rpc.JsonService, which is Dojo's JSON-RPC client. In addition to JsonService, Dojo offers an RPC client for connecting to Yahoo services, dojo.rpc.YahooService. The syntax and call structure is identical. While Dojo is currently limited to these two RPC clients, the design of the dojo.rpc.RpcService base class, which is inherited by dojo.rpc.JsonClient and dojo.rpc.YahooService allows a developer to easily customize and extend dojo.rpc.RpcService, to create services that meets their specific needs. These customizations will be discussed later in Part II when we discuss how to get the most out of Dojo.

    Transports

    dojo.io.bind and related functions can communicate with the server using various methods, called transports. Each has certain limitations, so you should pick the transport that works correctly for your situation.

    The default transport is XMLHttp.

    IFrame I/O

    The IFrame I/O transport is useful because it can upload files to the server. Example usage:

    <script type="text/javascript">

    dojo.require("dojo.io.*");

    dojo.require("dojo.io.IframeIO");



    function mySubmit() {

    dojo.io.bind ({

    url: 'server.cfm',

    handler: callBack,

    mimetype: "text/plain",

    formNode: dojo.byId('myForm')

    });

    }
       function callBack(type, data, evt) {

    //The data object will be different

    //depending on the mimetype used in the dojo.io.bind()

    //call. See below for more info.

    dojo.byId('result').innerHTML = data;

    }

    </script>


    The response type from the above URL can be text, html, or JS/JSON.

    IframeIO responses need to be a little different from the ones that are sent back from XMLHttpRequest responses. Because an iframe is used, the only reliable, cross-browser way of knowing when the response is loaded is to use an HTML document as the return type.

    If the return type (specified by the mimetype) is text/plain, text/javascript or text/json, then the server response should be an HTML page that has a <textarea> element. The data that you want returned to the dojo.io.bind() load callback should be the text inside the textarea element. For the text/javascript or text/json return types, the text inside the textarea element will be converted to JavaScript or JSON, repectively, and that will be the data sent to the load callback.

    If the return type is text/html as the return type, then the data parameter will be the complete HTML document that is in the iframe.

    For IframeIO, XML responses are not supported because we can't get a nice cross-browser solution. If you want text/html as the mimetype, what you get back is the document object for the document in the iframe.

    See these tests for more info:

    text/plain: http://archive.dojotoolkit.org/nightly/tests/io/test_IframeIO.text.html

    text/html: http://archive.dojotoolkit.org/nightly/tests/io/test_IframeIO.html.html

    text/javascript: http://archive.dojotoolkit.org/nightly/tests/io/test_IframeIO.html

    ScriptSrcIO

    Due to security restrictions, XMLHttp cannot load data from another domain. The ScriptSrcIO transport is useful for doing this. Yahoo's RPC service is implemented using ScriptSrcIO.

    To use ScriptSrcIO, use the following require statements

    • dojo.require("dojo.io.*");
    • dojo.require("dojo.io.ScriptSrcIO");

    and use the normal dojo.io.bind() method.

    To force a ScriptSrcTransport request, use transport: "ScriptSrcTransport" in the keyword arguments to dojo.io.bind(). The mimetype argument is also required.

    Example:
    dojo.require("dojo.io.*");
    dojo.require("dojo.io.ScriptSrcIO");
    
    dojo.io.bind({
        url: "http://example.com/json.php",
        transport: "ScriptSrcTransport",
        mimetype: “application/json",
        jsonParamName: "callback",
        content: { ... }
    });

    ScriptSrcIO (which provides ScriptSrcTransport) allows for four basic types of requests:

    Each type uses [script src="url"][/script] to accomplish the request.

    Here is a list of bind() keyword arguments that are supported for all types of requests. The four types of transport requests are:

    Simple

    Simply adds a script element with a src. Does not do any polling and does not expect a callback. Also does not support any timeouts. Example:

    dojo.io.bind({
    	url: "http://the.script.url/goes/here",
    	transport: "ScriptSrcTransport",
    	mimetype: “text/javascript"
    });

    Polling

    Adds a script element with a src. It will poll to see if a typeof expression does not equal undefined. When the typeof check succeeds, a load callback is called. Timeout and error callbacks are supported with this type of request.

    Example:

    dojo.io.bind({
    	url: "http://the.script.url/goes/here",
    	transport: "ScriptSrcTransport",
    	mimetype: "text/javascript",
    	checkString: "foo", //This means (typeof(foo) != undefined) indicates that the script loaded.
    	load: function(type, data, event, kwArgs) { /* type will be "load", data and event null, , and kwArgs are the keyword arguments used in the dojo.io.bind call. */ },
    	error: function(type, data, event, kwArgs) { /* type will be "error", data and event will have the error, , and kwArgs are the keyword arguments used in the dojo.io.bind call. */ },
    	timeout: function() { /* Called if there is a timeout */},
    	timeoutSeconds: 10 //The number of seconds to wait until firing timeout callback in case of timeout.
    });
    

    JSONP and JSON Callbacks

    Adds a script element with a src. This sort of usage allows using services that use the JSONP convention to specify the callback that the server will use.  Specify the name of the JSONP callback parameter using jsonParamName. Yahoo! Web Services use a jsonParamName of "callback". Some other services use jsonParamName of "jsonp". Timeouts are supported with this type of request. Example for a data service that uses "callback" as the URL parameter:

    dojo.io.bind({
    	url: "http://the.script.url/goes/here",
    	transport: "ScriptSrcTransport",
    	mimetype: "application/json",
    	jsonParamName: "callback",
    	load: function(type, data, event, kwArgs) { /* type will be "load", data will be response data,  event will null, and kwArgs are the keyword arguments used in the dojo.io.bind call. */ },
    	error: function(type, data, event, kwArgs) { /* type will be "error", data will be response data,  event will null, and kwArgs are the keyword arguments used in the dojo.io.bind call. */ },
    	timeout: function() { /* Called if there is a timeout */},
    	timeoutSeconds: 10 //The number of seconds to wait until firing timeout callback in case of timeout.
    });
    

    Here is a real example of using JSONP to look up the del.icio.us bookmarks.

    <style type="text/css">
    .bookmarks {
      width: 300;
      background: lightGray;
      border-style: solid;
      border-width: 2px;
      border-color: black
    }
    </style>
    <script type="text/javascript" src="dojo.js"></script>
    <script type="text/javascript">
    dojo.require("dojo.io.*"); 
    dojo.require("dojo.io.ScriptSrcIO");
    
    dojo.addOnLoad(getBookmarks);
    
    function getBookmarks() {
        dojo.io.bind({ 
            url: "http://del.icio.us/feeds/json/dojomaster", 
            transport: "ScriptSrcTransport", 
            jsonParamName: "callback",
            load: function(type, data, event, kwArgs){showBookmarks(data);},
            mimetype: "application/json",
            timeout: function() {alert('timeout');},
            timeoutSeconds: 10
        });
    }
    
    // The code for showing the bookmarks is courtesy of del.icio.us
    // http://del.icio.us/help/json
    function showBookmarks(posts) {
         var ul = document.createElement('ul');
         for (var i=0, post; post = posts[i]; i++) {
             var li = document.createElement('li');
             var a = document.createElement('a');
             a.style.marginLeft = '20px';
             var img = document.createElement('img');
             img.style.position = 'absolute';
             img.style.display = 'none';
             img.height = img.width = 16;
             img.src = post.u.split('/').splice(0,3).join('/')+'/favicon.ico'
             img.onload = showImage(img);
             a.setAttribute('href', post.u);
             a.appendChild(document.createTextNode(post.d));
             li.appendChild(img);
             li.appendChild(a);
             ul.appendChild(li);
         }
         document.getElementById("container").appendChild(ul);
    }
    function showImage(img){ return (function(){ img.style.display='inline' }) }
    </script>
    <div id="container" class="bookmarks"></div>

    To customize this scirpt simply change the URL http://del.icio.us/feeds/json/dojomaster to include your del.icio.us user name. This example shows the bookmarks for the user "dojomaster".

    DSR/Multipart

    Adds a script element with a src. Uses the Dynamic Script Request convention to specify the callback that the server will use. Multipart requests (splitting a long request across multiple GET requests) is supported. Timeout and error callbacks are supported with this type of request. Example:

    dojo.io.bind({
    	url: "http://the.script.url/goes/here",
    	transport: "ScriptSrcTransport",
    	mimetype: "application/json",
    	useRequestId: true, //adds the _dsrId to request with a generated ID. If a specific request ID is wanted, use apiId: "myId" instead
    	//optional: forceSingleRequest: true, //Will not segment the request to multipart requests even if it is a long URL.
    	constantParams: "name1=value1&name2=value2" //params to be sent with each request that is part of a multipart request. See spec.
    	load: function(type, data, event, kwArgs) { /* type will be "load", data will be response data, event will be onscriptload event, and kwArgs are the keyword arguments used in the dojo.io.bind call. */ },
    	error: function(type, data, event, kwArgs) { /* type will be "error", data will be response data, event will be onscriptload event, and kwArgs are the keyword arguments used in the dojo.io.bind call. */ },
    	timeout: function() { /* Called if there is a timeout */},
    	timeoutSeconds: 10 //The number of seconds to wait until firing timeout callback in case of timeout.
    });
    

    Common bind() arguments

    ScriptSrcTransport supports the following arguments across all types of requests. In general, all of these arguments have the same meaning and use in XMLHTTPTransport.

    • mimetype: REQUIRED. Tells ScriptSrcTransport how to deal with the response. The only allowed values are "text/javascript", "text/json" or "application/json".
    • transport: RECOMMENDED. Tells dojo.io.bind() which transport you want to use. If you do not specify this transport, there is a chance that the XMLHTTPTransport might try to handle the request.
    • formNode: Uses a form to generate query parameters.
    • backButton, back, forward, forwardButton, changeUrl: used to register a back/forward handler. See dojo.undo.browser for more info.
    • content: a JS object that gets turned into query parameters.
    • postContent: Adds raw name=value parameters to query parameters.
    • sendTransport: Adds dojo.transport=scriptsrc to query parameters.
    • preventCache: Adds dojo.preventCache=[unique ID] to bypass browser cache and force a fresh GET.
    • handle: A function that accepts the following arguments: function(type, data, event) {}. This can be used instead of specifying a separate load, error and timeout handler. The type parameter will be a string that specifies the callback type ("load", "error", "timeout").

    XMLHttp

    The XMLHttp transport is the default transport.

    It works well in most cases, but it cannot transfer files, cannot work across domains (ie, cannot connect to another site than the current page), and doesn't work with the file:// protocol.

    Example usage:

    <script type="text/javascript">
       dojo.require("dojo.io.*");
    
       function mySubmit() {
         dojo.io.bind ({
           url: 'server.cfm',
           handler: callBack,
          formNode: dojo.byId('myForm')
         });
       }
       function callBack(type, data, evt) {
          dojo.byId('result').innerHTML = data;
       }
    </script>
    

    Part 6: "Customizing Dojo Builds for Better Performance"

    When all of your testing is done and you're ready to distribute your application, you don't want to have to use a bulky source, or have the various resource used by your application scattered all over the place. As a web developer, you want a way to lower the number of file requests and the overall download size of your application code. Dojo provides a robust system that not only solves these problems, but also allows incremental loading of infrequently used resources. The following sections will show you how to build a profile that suits your needs and how to create a Dojo release to include in your HTML file.

    Additionally, the build system allows for the creation of a compressed package file that contains all of the script code needed for your app, no more and no less, through an Ant build step.

    The loader system allows you to list a single script include file which will then find and fetch resources as they are needed for your application from the Dojo source tree. This means in the simplest case, no build process is required and yet there is no need to include a script tag for every resource that is to be loaded.

    Additional performance optimizations can be had by adjusting the web server or application code. Included in this section are some tips on both server side and client side changes that can make your application run faster.

    Getting the Source

    Dojo 0.4 - OBSOLETE DOCUMENTATION

    For current svn / build notes: http://dojotoolkit.org/book/dojo-book-0-9/part-4-meta-dojo-0

    The first step to creating a custom build is getting the Dojo source tree. There are several places to get the source as described below.

    Dojo Release

    The latest Dojo releases can be downloaded from the download section of the dojotoolkit.org project's home page. There are several prebuilt Dojo bundles available pre release. Each bundle has been tested and believed to be pretty stable. The names of the bundles should indicate what is contained in that bundle. For example, kitchen_sink contains most of what you need in Dojo except the widgets while core contains just the core features.

    NOTE: the release bundles are NOT suitable to be used for a custom build. For that, you need an original source snapshot. Starting with Dojo 0.4.2, the source snapshots are in the download directory for the release, but have "-src" in their name. For instance:
    http://download.dojotoolkit.org/release-0.4.2/dojo-0.4.2-src.zip
    or
    http://download.dojotoolkit.org/release-0.4.2/dojo-0.4.2-src.tar.gz

    Nightly Builds

    Dojo is built every night and bundled into a tarball hosted at archive.dojotoolkit.org. The most recent build is always available in the nightly directory. Note that this bundle has not undergone much testing and therefore may not be stable. It does however contain the latest bug fixes and feature implementations for the current release of Dojo. There is only one bundle available in the nightly directory and it contains all the code.

    Version Control - SVN

    Dojo source code is hosted in a Subversion repository. The repository contains the latest source code and is used to create the nightly builds. There are a couple of ways to get a copy of the most up-to-date source code.

    Anonymous release checkout (change 0.4.2 to the release you want): "svn co http://http://svn.dojotoolkit.org/dojo/tags/release-0.4.2"

    Anonymous 0.4 branch checkout: "svn co http://svn.dojotoolkit.org/dojo/branches/0.4"

    Anonymous trunk checkout: "svn co http://svn.dojotoolkit.org/dojo/trunk"

    The Dojo Build System

    The Build system reduces the loading time of Javascript library files. It does this by:

    • compacting the source code
      • removal of white space
      • removal of comments
      • replacement of identifiers with short identifiers
      • removal of extra semicolons or other semantically equivalent changes.

    • consolidating multiple files into a single file to remove round-trip delays and to avoid browser limits for parallel file requests.

    Its main benefit is that it encourages developers who are using Dojo to structure their code properly without premature optimisation. The developer can write clean, well laid out and commented code knowing that it will not affect the loading time at deployment.

    When developing or debugging a web site, the original source code is usually used directly. Once the site is ready to be deployed the relevant library files are compacted and packaged together. The Build system will create both compressed and uncompressed packages for distribution. For more information on how compression works see this article.

    Creating a Custom Distribution

    Prerequisites

    You need the following installed on your computer to run Dojo's build system:

    Creating a Custom Profile

    Creating your own Dojo distribution allows you to take advantage of the build system. In the src/buildscripts/profiles directory, you will create a profile build file called foo.profile.js like this:

    var dependencies = [ 

    "dojo.io.*",

    "dojo.event.*",

    "dojo.xml.*",

    "dojo.graphics.*",

    "dojo.io.BrowserIO",

    "dojo.widgets.*",

    "dojo.widgets.Button",

    ];



    load("getDependencyList.js");

    This should list all the resources you are directly using.

    Including Non-Dojo Resources

    If you want to include your own code into a build you need to make

    custom resource for your code, ie,

    dojo.provide("mycompany.widget.SomethingCool"), then define the profile file like this:

    var dependencies = [

    "dojo.widget.*",

    "dojo.io.*",

    "dojo.event.*"

    "mycompany.widget.SomethingCool"

    ];



    dependencies.prefixes = [

    ["mycompany", "../mycompany"]

    ];



    load("getDependencyList.js");

    The "../mycompany" path in the dependencies.prefixes is relative to dojo.js. It operates the same as what you use for dojo.registerModulePath() (or dojo.setModulePrefix() in 0.3.1 or earlier versions).

    Exclude Interning Resources from the Build

    (available since Dojo 0.4.1)

    If you subclass a Dojo widget in your code and don't make use of the original widget standalone (directly), then you may not want to include the original template file for that particular widget at all. Or you have a custom css file for a Dojo widget, and you don't want to include the original one, then you can specify internSkipList in your profile.

    Let's assume you want to have a built Dojo with Editor2 in it, but you do not want to include the default EditorToolbar.html as you already have a customized one, then you can use this profile:

    var dependencies = [

    "dojo.widget.Editor2"

    ];



    dependencies.internSkipList = [

    "dojo.widget:templates/EditorToolbar.html"

    ];



    load("getDependencyList.js");

    Notice the "dojo.widget:" prefix to the actual path for EditorToolbar.html: it is used to avoid confliction of template file paths among different namespaces.

    Running The Build

    After specifying a profile file as shown above that statically specifies the resources you want to include, and saving it as /buildscripts/profiles/foo.profile.js, run Ant and specify the profile name as a parameter. For example, from the buildscripts directory:

    ant -Dprofile=foo clean release

    Some additional build options:

    -DprofileFile=/path/to/myProfile.profile.js

    You can use this instead of -Dprofile to specify a profile file that is not in the dojo/buildscripts/profiles folder.

    intern-strings

    If widgets that are built into dojo.js specify templatePath and/or templateCssPath, the intern-strings ant target will intern the HTML/CSS into dojo.js so that additional XMLHttpRequests are not needed to load the HTML and CSS later.

    Another build example:

    ant -DprofileFile=/path/to/myProfile.profile.js clean release intern-strings
    strip-resource-comments

    Available in Dojo 0.4.2+. This task will remove the comments from the files in src/. This helps the download size of modules that are not part of dojo.js and are loaded via dojo.require().

    Example (for Dojo 0.4.2+):

    ant -Dprofile=ajax clean release intern-strings strip-resource-comments

    For Dojo 0.4.3+, it is possible to run all the files through the Dojo compressor instead of just stripping comments. To do that, specify -Dstrip_and_compress=true in the ant command:

    ant -Dprofile=ajax -Dstrip_and_compress=true clean release intern-strings strip-resource-comments

    After the build

    Running the build generates a release directory and all that is needed to use Dojo with your application. You will distribute the contents of the release directory with your application.

    Including Dojo from HTML source files

    Once your resources are specified, to include Dojo from the source tree, you simply include the following tag in your html source:

    Additionally, you will need to specify the resources to load as you need them loaded. I do this in a separate script file to initialize Dojo. For example, if you have code that required the Dojo event system, you would add the command:

    dojo.require("dojo.event.Event");

    Note however, that you should still distribute the release/dojo/src directory in your distribution. Although the JavaScript?, css, and html files have been merged into dojo.js, there are still image files that are being accessed from the src/ directory.

    Supporting dojo.require("foo.baz.*")

    If you want to say something like dojo.require("foo.baz.*"), to include all the resources in the acme/foo directory, then you will need to create file called

    __package__.js

    in the foo/baz directory, with the following format:

    dojo.kwCompoundRequire({

    common: ["foo.baz.resource1",

    "foo.baz.resource2"] // a generic dependency

    });



    dojo.provide("foo.baz.*");

    Definitions

    kwCompoundRequire
    passes an object of modules, grouped by host environment
    provide
    Conclude with the name of the module that is now considered loaded.

    NOTE: For Dojo 0.3+, dojo.hostenv.conditionalLoadModule() has been replaced with dojo.kwCompoundRequire() and dojo.hostenv.moduleLoaded() has been replaced with dojo.provide(). To use the old methods with Dojo 0.3+, please use a compatibility package.

    A Longer Example

    Here's the contents of dojo/src/foo/__package__.js:

    dojo.kwCompoundRequire({

    common: ["dojo.foo.Foo", "dojo.foo.Bar"],

    browser: ["dojo.foo.Baz"]

    });



    dojo.provide("dojo.foo.*");

    This set of definitions says that when someone writes dojo.require("dojo.foo.*"); from within a browser, Foo.js, Bar.js, and Baz.js will get included from this directory. When run from the command line, only Foo.js and Bar.js will get included.

    Profiling Your Distribution

    Not every application is the same, and Dojo's package and build tools let you mix and match in order to handle these variations. In many cases, you may be able to use one of the "off the shelf" builds in order to test the performance differences.

    If you've done testing and are certain that a custom build is the right soltuion for you, or you are just curious about what is happening, then the first step is to decide what modules to include in your build. All of the files from the modules you specify and their dependencies will be included in the resulting dojo.js file. It's best to start with the modules that you know are used in every page of your application, or at least the majority of them. This will be your base profile, and all of these modules will be compressed into a single file that you will specify on your HTML page.

    Creating this profile is a soft science, because loading files later means that they are less compressed and the total download size of your application increases, as well as the number of files that need to be loaded later on. But this compromise means that if there are infrequently used parts of your application, the average user will have a smaller download. Also, if getting to various stages of your application happens over a long period of time, these modules might be better left out of your profile as well. The package system can be used to opportunistically load as well as optimistically load resources. Here's where tools can help sort out what's best.

    Since Dojo package and resource loading generally happens via XMLHTTP requests, tools like Firebug's network visualization to determine which files are getting requested at runtime from Dojo. See the section on Profiling JavaScript for more tips on how to determine what should be included in your distribution. A poorly constructed build, or no build at all, tends to show a cascade of requests that are very clearly serialized. By noting the namespaces that are being requested and then adding them to the profile, you can construct builds that meet the needs of users and defer work until it's really necessary.

    For example, if a user has to go through a log in screen before he or she is taken to the next page, it might be worth waiting for a successful log in before loading additional modules.

    Performance Optimization

    So you've built an application using Dojo and it "feels slow", now what?

    Dojo's performance tuning centers around four areas:

    1. download
    2. parsing
    3. instantiation
    4. deferred download
    To understand how to make Dojo applications faster, we need to first understand how Dojo systems can contribute to overall latency and what options Dojo makes available at each level to reduce latency.

    If you are a widget developer, you might also be interested in Profiling Javascript.

    Download

    Understanding The Problem

    JavaScript execution is entirely sequential. That means that scripts on your page don't run until the scripts behind them have finished executing, and that means that time spent loading files is time your users spend waiting to use the application. Waiting makes your users grumpy, and you don't want grumpy users. The best bytes are the ones you never send. By default, all dojo.require() statements are synchronous, request "full sized" versions of the code, and most incurr network I/O overhead. We need to address all of these concerns to fully combat latency in a deployment.



    Synchronous network requests from a browser are necessaray for loading external code because it's the only way to block execution of JavaScript while dependencies are satisfied. Once the code is fecthec it is then eval()'d and execution picks up after the dojo.require() statement. Unfortunantly synchronous IO requests also have the effect of "locking" the UI of the browser, preventing other resources that may be coming in from affecting the layout of the page. Additionally, the synchronous nature of the package system also means that each request is serial. The package system doesn't know enough to try to request multiple files at once. Scripts included in a page via the <script> tag also suffer from the same serial behavior. Add to that the latency involved in opening up a (potentially) new network connection for each requested resource and you're staring down some nasty latency in any non-trivial application.



    The plan of attack for reducing page loading latency is to configure the web server sending the JavaScript files to cache them agressively and to not request even status information about them. Servers that don't send adequate cache information may find that UIs are very slow even though the content of the scripts are never sent. Instead, browsers may be checking to see if the file has changed since it was last seen. This "has it changed?" check is synchronous and serial and so we want to eliminate it if possible.

    There are several things you can do to improve the performance of your application relating to the web server that are outside the scope of Dojo. Some things to look at are server side caching, data compression and even application program structure. See the following article Improving performance of Dojo-based web applications which contains useful information on these topics.

    IE repeated image download

    If you find IE is repeatedly downloading the same image, read this article for advice. This also helps get rid of ugly image flickering in IE.

    Creating Custom Dojo builds

    Dojo builds or distributions contain all the files that are part of Dojo and that may seem some what large. Luckily Dojo includes a mechanism which downloads only files that you use. Creating a custom build of Dojo means identifying the files that you use which will improve the download of the page since a separate network request is not made for each module.

    See Creating a Custom Distribution for more information on how to customize your Dojo build.

    Parsing

    Page load time is proportional to the number of nodes on the page because Dojo searches all the nodes to see if they are widgets. You can avoid this in the following ways:

    Specifying which nodes to search for dojoType widget tags

    Add the following line in the

    before you include dojo.js:

        <script> djConfig = { parseWidgets: false, searchIds: [] }; </script>
    This turns off parsing so if you do include widgets in your page you will have to make the widgets known to Dojo yourself. Use searchIds.push on the global object djConfig. In the :
    <div dojoType="Button" id="button1">...</div> 

    <script>djConfig.searchIds.push("button1");</script>

    ...

    <div dojoType="Button" id="button2">...</div>

    <script>djConfig.searchIds.push("button2");</script>

    ...

    You can also make the parser not search underneath a given node by doing this

    <div parseWidgets="false">

    ... no widgets in here, don't waste your time looking! ...

    </div>

    One little known feature about searchIds usage is that if you add at least one searchIds, then the "body" (and so the whole) of the document will not be parsed at all, even when the djConfig.parseWidget=true. (See hostenv_browser.js 's makeWidget() function) I think this is a buggy behaviour, and the whole document should still be parsed if djConfig.parseWidget=true. A use case when this is simply necessary: you have a section where you turn off the parsing using the new parseWidget="false" flag. But if you happen to have some widgets inside, those must be parsed using searchIds. But because you've added one such, the whole page will not be parsed, but those parts may contain other widgets!

    Reduce number of tags on a page

    It sounds obvious, but many web pages have a lot of unnecessary markup. For example, instead of

    <table ...>

    <tr>

    <td>Hello World</td>

    </tr>

    </table>

    just do

    <div class="foo">Hello World</div>

    Instantiation

    Time is proportional to the number of widgets on the page. No advice here.

    Deferred Download

    Instead of downloading your whole page at once, defer portions until the user needs them. For example:

    <div dojoType="Tooltip" href="tooltip.jsp"></div>
    <div dojoType="TabContainer">

    <a dojoType="LinkPane" href="tab1.jsp">Tab #1</a>

    <a dojoType="LinkPane" href="tab2.jsp">Tab #2</a>

    </div>



    FIXME: content below this break is legacy and in need of serious rework




    Will JavaScript Closure Memory Leaks Ruin My App?

    On Sunday 20 November 2005 7:32 am, Bob Ippolito wrote:
    >> On Nov 20, 2005, at 3:54 AM, Christian Boulanger wrote:

    >

    >>> > I am using dojo.io.bind and need a reference to the calling object

    >>> > in the handler code. Not being a javascript geek, I am still

    >>> > confused about the closures stuff and how much it impacts

    >>> > performance. Is the following way an ok way of doing things or is

    >>> > there a better way? Would it make sense to add a parameter to

    >>> > dojo.io.bind which transfers the callingObject to the handler

    >>> > without need to create the closure?

    >

    >>

    >> Closures are fine to use all over the place. The only time it can

    >> matter is the specific instances when they cause circular references

    >> to certain kinds of objects that don't fully participate in the

    >> interpreter's garbage collection scheme (most commonly DOM objects in

    >> IE).





    and if you use dojo.event.connect() to set up your event handlers, you

    don't have to worry about it even then. In short, go nuts with the

    closures, just don't manually set properties on DOM nodes that include

    functions or objects.



    Regards

    Alex Russell

    Visit this link for more information about closures and memory leaks in IE.

    Cross Domain Resource Loading

    Concept

    This feature allows hosting dojo on a separate machine from the application; usually the dojo instance is shared between multiple applications. It's especially useful for internet applications, where dojo will be cached by proxy servers close to the client, thus increasing performance. AOL hosts dojo in this way.

    When the dojo instance is shared between multiple applications, the dojo.js on the shared server typically won't contain all the resources required by an individual application. Thus, when the application issues a dojo.require() call, it will download the resource (such as dojo.html.extras).

    This feature allows dojo to load the resource across domains. The technique uses HTML script elements to load the resources. This technique is also referred to as xdomain loading or xdDojo.

    Benefits

    • You get more connections in MSIE, since you can load from another domain. Faster loading.
    • You get increased cacheability/startup if many of your applications use the same installation.
    • Resource loading does not block the rest of the page from filling in as with Dojo's normal, synchronous loading.
    • With a local install, your ISP may charge you for all of those Dojo bits that you are serving.

    Implications/Limitations

    • Resource search paths are not supported. Only the first path in the search path is supported. So that means if you use something like dojo.require("dojo.dom.*") won't work because there is no dom/__package__.js file. You need to make sure to use the exact require syntax that maps to the first file in the resource search path.
    • Not all external resources can be xdomain loaded, in particular widget images, and things like IFrameScriptTransport? and dojo.undo.browser because they need to load iframe_history.html. Therefore, it is still important that you have your own version of Dojo installed on your server. If you are very careful about what you need, you may be able to get away with not having your own installation. [as of 0.4.2, you do not need a local Dojo copy]
    • Requires a "xdomain" build of Dojo (see below for more info on how to make a xdomain build). For 0.4.2+, you can use Dojo's web build system to make an xdomain dojo.js that includes just Dojo modules. The web built dojo.js is tied to an xdomain Dojo build on a Content Delivery Network (CDN).
    • Asynchronous loading. MUST use dojo.addOnLoad() to register a callback function to get notification of package loading. This can be used even after the initial page load. Just do the dojo.require()s that you need, and then call dojo.addOnLoad() with a callback function, and once those new packages are loaded (or if they are already loaded), then the callback will be called.

    Module-specific notes

    For Dojo 0.4.2+:

    Widget automatic loading

    • xdomain loading does not support automatic loading of Dojo or custom widgets. You must explicitly include widgets you want to use by using dojo.require() statements for widgets you want to use.

    dojo.debugDeep()

    • Save src/debug/deep.html to your local server and set djConfig.dojoDebugDeepHtmlUrl to the location of that file.

    dojo.flash

    • This module uses document.write(), so you must do a custom web build that includes the following modules to use it:
      • dojo.event.*
      • dojo.flash

    dojo.io.createIFrame()

    • Save iframe_history.html to your local server and set djConfig.dojoIframeHistoryUrl to the location of that file.

    dojo.rpc.YahooService

    • Save src/rpc/yahoo.smd to your local server and set djConfig.yahooServiceSmdUrl to the location of that file.

    dojo.storage

    • This module uses document.write(), so you must do a custom web build that includes the following modules to use it:
      • dojo.event.*
      • dojo.storage

    dojo.undo.browser

    • Save iframe_history.html to your local server and set djConfig.dojoIframeHistoryUrl to the location of that file.
    • If you do a custom build using the web tool and want to use dojo.undo.browser, be sure to include it in dojo.js. Do not load it after dojo.js loads, otherwise it will not work.

    dojo.widget.GoogleMap

    • Does not seem to work even if you include dojo.widget.GoogleMap in a custom build.

    dojo.widget.Editor2, dojo.widget.Editor, dojo.widget.RichText

    • Save src/widget/templates/richtextframe.html to your local server and set djConfig.dojoRichTextFrameUrl to the location of that file.

    Usage

    • In djConfig, add useXDomain = true.
    • After the script tag for Dojo, set the following before any dojo.require statements:

      dojo.registerModulePath("dojo", "http://url/to/xdomain/dojo/installation/src");[For 0.4.2+, use djConfig.modulePaths instead (more info below)].
    • Register a callback function to get notification of when the packages are loaded by using dojo.addOnLoad().

    An example is at the end of the Usage section.

    Resource Loading Timeout

    You can optionally set a wait time in milliseconds (djConfig.xdWaitSeconds) that specifies how long the resource loader should wait for a resource to load until returning an error. Since script elements do not give information about failed or long-running requests, this timeout is used to prevent infinite waiting in the browser. An exception via dojo.raise() will be thrown to indicate a load error. The default xdWaitSeconds is 30 for Dojo versions before 0.4.2, and 15 for 0.4.2+.

    djConfig.modulePaths

    In Dojo 0.4.2+, instead of calling dojo.registerModulePath() a bunch of times to register all of your custom module paths, you can use djConfig.modulePaths to set up the module to path mappings:

    var djConfig = {

    xdWaitSeconds: 10,

    modulePaths: {

    "acme": "path/to/acme/module/resources",

    "foo": "
    path/to/foo/module/resources"

    }};

    Complete Example

    This example shows cross-domain loading pieces that should be in the head element of an HTML page:

    <script type="text/javascript">

    var djConfig = {

    baseScriptUri: "../path/to/local/dojo/",

    useXDomain: true,

    xdWaitSeconds: 10

    };

    </script>

    <script type="text/javascript" src="http://some.domain.com/dojo/dojo.js"></script>

    <script language="JavaScript" type="text/javascript">

    dojo.registerModulePath("dojo", "http://some.domain.com/dojo/src");



    //Now any Dojo resources that are referenced via dojo.require()

    //will be xdomain loaded.

    dojo.require("dojo.lang.extras");



    dojo.addOnLoad(function(){
    alert("Resources are loaded!");});

    </script>

    Note that it is important to always follow any block of dojo.require() statements with a dojo.addOnLoad() call to register a function that should be called after the asynchronous package loading finishes. So, if you had this function before using an xdomain build:



    function someFunctionThatIsRunAfterPageIsFirstLoaded(){

    dojo.require("dojo.html");

    dojo.require("dojo.event.*");



    //Use dojo immediately

    dojo.event.connect(....);

    }




    With xdomain asynchronous loading, this becomes:



    function someFunctionThatIsRunAfterPageIsFirstLoaded(){

    dojo.require("dojo.html");

    dojo.require("dojo.event.*");



    dojo.addOnLoad(function(){

    //Use dojo after all the packages are ready.

    dojo.event.connect(....);

    });

    }


    Making a "XDomain" Build

    Specify a dojoLoader=xdomain parameter to the build command. For instance, to build the Ajax custom build with xdomain loading:

    ant -Dprofile=ajax -DdojoLoader=xdomain -Ddocless=true clean release intern-strings

    dojoLoader=xdomain will include loader_xd.js in dojo.js, and generate the *.xd.js files that are needed to enable the build to be loaded across domains. The "intern-strings" part will inject the HTML and CSS used by widgets into the widget JavaScript file so that it can be loaded across domains.

    New options for 0.4.2+

    As of 0.4.2, there are two new options you can add to the ant build. Also, it is no longer necessary to specify -Ddocless=true. The task that uses the "docless" value has been removed for 0.4.2.

    xd-dojo-config

    This ant task will set djConfig.useXDomain = true; inside the built dojo.js, effectively making the dojo.js always want to try to do xdomain loading (you can always load some modules locally by using dojo.registerModulePath()). It will also automatically register the module path for Dojo, so that you do not have to specify dojo.registerModulePath("dojo", "[url goes here]");. In order for the xd-dojo-config task to work, you must define -DxdDojoUrl. Also, this task should be run after the release ant target. Example:

    ant -Dprofile=ajax -DdojoLoader=xdomain -DxdDojoUrl=http://some.domain.com/dojo/release-0.4.2/src clean release intern-strings xd-dojo-config

    If you use this task, then for pages that use this dojo.js, you do not have to explicitly set djConfig.useXDomain or call dojo.registerModulePath() for the Dojo module.

    strip-resource-comments

    This task will remove the comments from the files in src/. This helps the download size of modules that are not part of dojo.js and are loaded via dojo.require(). This ant task is available for regular builds too. It should be the last task in the list. Example:

    ant -Dprofile=ajax -DdojoLoader=xdomain -DxdDojoUrl=http://some.domain.com/dojo/release-0.4.2/src clean release intern-strings xd-dojo-config strip-resource-comments



    Part 7: "Utilities"


    Back Button and Bookmarking

    Dynamic web applications that avoid page refreshes are very powerful, but this normally means that the Back and Forward buttons stop working in the browser. In addition, it can be hard to give the user an URL that can be bookmarked.

    However, Dojo provides a solution to these issues, allowing a web application to capture the Back and Forward button clicks, and set a unique URL in the browser's location field. The solution is to use dojo.undo.browser.

    Using dojo.undo.browser

    0.2.2 Compatibility

    The behavior described in this document is different from the 0.2.2 Dojo release. In that release, it was not very easy to track the state that corresponded to a "page", but it was possible to register callbacks for Back and Forward through a dojo.io.bind() call. For 0.3 and later, the Back/Forward/Bookmarking support was moved to a new module, dojo.undo.browser. As a result of the changes, you may see slightly different behavior as compared with 0.2.2. The main difference is the need to call dojo.undo.browser.setInitialState(state) to set the state for the page as it is first loaded by the browser.

    Theory

    Dynamic web applications that use things like XMLHTTPRequest and DOM updates instead of page refreshes do not update the browser history, and they do not change the URL of the page. That means if the user clicks the Back button, they will likely jump all the way out of the web application, losing any state that they were in. It is also hard to allow a user to bookmark the web application at a certain state.

    Dojo's dojo.undo.browser module will introduce browser history so that it is possible for the user to click Back and Forward without leaving the web application, and the developer can get notification of these Back and Forward events and update the web application appropriately. Browser history is generated by using a hidden IFRAME and/or adding a unique value to the fragment identifier portion of the page URL. The fragment identifier is the #value thing in a URL. For example:

    http://some.domain.com/my/path/to/page.html#fragmentIdentifier

    Since changing the fragment identifier does not cause the page to refresh, it is ideal for maintaining the state of the application. The developer can specify a more meaningful value for the fragment identifier to allow bookmarking.

    dojo.undo.browser allows setting a state object that represents the state of the page. This state object will get callbacks when the Back or Forward button is pressed. In addition to registering state objects with dojo.undo.browser directly, the state object can be passed to dojo.io.bind() and it will be added to dojo.undo.browser for you.

    Implementation

    The following prerequisites are needed to use dojo.undo.browser:

    • Define preventBackButtonFix: false in your djConfig. This allows the hidden iframe to be added to the current page via a document.write(). If you do not do this, dojo.undo.browser will not work correctly.
    • Add the appropriate require statement: dojo.require("dojo.undo.browser");

    Register the initial state of the page by calling:

    dojo.undo.browser.setInitialState(state);

    This state object will be called when the user clicks Back all the way back to the start of the web application. If the user clicks Back once more, they will go back in the browser to wherever they were before loading the web application.

    The state object should have the following functions defined:

    • For receiving Back notifications: back(), backButton() or handle(type), where type will either be the string "back" or "forward".
    • For receiving Forward notifications: forward(), forwardButton() or handle(type), where type will either be the string "back" or "forward".

    Example of the a very simple state object:

    var state = {

    back: function() { alert("Back was clicked!"); },

    forward: function() { alert("Forward was clicked!"); }

    };

    To register a state object that represents the result of a user action, use the following call:

    dojo.undo.browser.addToHistory(state);

    or, if you are using dojo.io.bind(), if the object contains the function back() or backButton() or the property changeUrl, then dojo.io.bind() will call dojo.undo.browser for you (only works with XMLHTTPTransport and ScriptSrcTransport).

    To change the URL in the browser's location bar, include a changeUrl property on the state object. If this property is set to true, dojo.undo.browser will generate a unique value for the fragment identifier. If it is set to any other value (except undefined, null, 0 or empty string), then that value will be used as the fragment identifier. This will allow users to bookmark the page.

    Browser Compatibility and Usage Notes

    • Do not mix dojo.undo.browser.addToHistory() calls that use changeUrl with ones that do not use changeUrl. Always use one or the other. If you are using changeUrl, you don't always have to provide a string for the value, but at least set it to true to get an auto-generated URL hash.
    • Don't test this page using local disk for MSIE. MSIE will not create a history list for iframe_history.html if served from a file: URL. The XML served back from the XHR tests will also not be properly created if served from local disk. Serve the test pages from a web server to test in that browser.
    • Safari 2.0.3+ (and probably 1.3.2+): Only the back button works OK (not the forward button), and only if changeUrl is NOT used. When changeUrl is used, Safari jumps all the way back to whatever page was shown before the page that uses dojo.undo.browser support.
    • Opera 8.5.3: Does not work.
    • Konqueror: Unknown. The latest may have Safari's behavior.

    Complete Example

    There are test pages that shows how to use all of the pieces together:

    http://archive.dojotoolkit.org/dojo-2007-05-15/ajax/tests/undo/test_brow...

    http://archive.dojotoolkit.org/dojo-2007-05-15/ajax/tests/undo/test_brow...

    What Usability Problems?

    What usability problems?

    Consider that most painful of topics for web application developers: the back button. Web developers armed with some sample code, a decent DOM reference, and a lot of perseverance can build a pretty decent dynamic UI in modern browsers. These UIs doesn't jarringly destroy the user's in-page experience for the most trivial of tasks, like adding an item to a list. When larger portions of an application are mediated in this way, the user naturally has more desire to "go back" to some earlier state if things aren't working out the way they had planned or if the action isn't what they expected. An example might be switching between a view and edit mode in a content editing application.

    As high-gloss web applications become the norm, many interactions become intra-page and not inter-page. Programmers looking for creative solutions have chosen XMLHTTP for these scenarios, but unfortunately, XMLHTTP breaks the back button, impairing the user experience. If the back button doesn't function in a way that meets user expectations, it becomes ever easier for the user to lose work or become confused about the state of an application. To assist the user, programmer need a way to capture back-button presses and do something intelligent with them.

    If you've used Google Maps and you've tried to send your directions to a friend, you know that not being able to simply copy the URL out of the address bar is significantly confusing at first. Applications that dynamically construct large sections of the UI (like Google Maps) today resort to a link in an intermediate screen that the user can click to return to their current state and then, perhaps, book mark or send to someone else. And this is if and when they consider the "bookmarkability" problem at all. More common is an application that simply refuses to acknowledge that the user might want to pass around a URL to a friend and instead builds some heavyweight and non-standard state serialization mechanism that is more akin to a desktop application's "save as" feature. "Save-as" on the web is book-marking, and usable applications recognize this (even if they don't have great solutions for it today). Regardless of what serialization mechanism is in use, being able to represent the state of the application in a URL (or a marker for serialized state) is a must. This is a hard problem to be sure, and none of the currently available tools provide simple answers.

    Dojo Data

    What is dojo.data?

    The dojo.data module provides datastore objects which a JavaScript program can use to access a variety of different data sources. A data source could be a simple data file, a web service provided by a site like del.icio.us or Yahoo, or a database like a relational database or an XML database.



    The goal of dojo.data is to have a standard set of data-access APIs, and a large set of datastore implementations that conform to those APIs.

    Simple example

    Here's a simple example from the dojo.data unit tests. This test page creates a dojo.data.CsvStore object, uses the CsvStore to read the contents of a simple .csv format file, and displays the results in the Dojo FilteringTable widget.



    Here's the movies.csv file that the CsvStore reads from, and the test page that displays the movies in a FilteringTable:
    movies.csv ==> CsvStore ==> movies.html

    What sorts of datastores are available?

    As of January 2007, we have five simple datastores, which are included in dojo as example datastore implementations. The four datastores are:

    dojo.data.CsvStorea read-only store that reads tabular data from .csv format files
    dojo.data.OpmlStorea read-only store that reads hierarchical data from .ompl format files
    dojo.data.YahooStorea read-only store that fetches search results from the Yahoo search engine web service
    dojo.data.DeliciousStorea read-only store that fetches bookmarks from the del.icio.us web service
    dojo.data.RdfStorea read-write store that uses SPARQL to talk to RDF data servers including, for example, the Rhizome RDF application server



    We hope to have more datastore implementations bundled with Dojo in future releases. We also hope that people who use Dojo will be able to easily create new custom datastore implementations to talk to their own custom data sources.

    The dojo.data APIs

    All of the dojo datastore implementations conform to a standard set of data-access APIs. Even though dojo.data.CsvStore and dojo.data.YahooStore read data from very different sources, CsvStore and YahooStore offer the exact same set of methods for accessing that data.



    The basic operations in the dojo.data APIs are fairly simple -- create an item, delete an item, set the value of an attribute, get an attribute value, etc. We've split those operations into groups. The first section is the simple read-only access methods, then the simple write-access methods, and then additional methods for less common features like attribution, versioning, update notification, schema inspection, etc.



    As of Dojo release 0.4.1, we have only defined a few basic data-access APIs:

    • dojo.data.core.Read -- 10 basic read-only datastore access methods

    • dojo.data.core.Write -- 9 basic methods for creating data items and updating attribute values
    • dojo.data.core.Identity -- 2 basic methods for datastores that can uniquely identify items
    In future Dojo releases we'll be adding more API definitions, probably including:

    • dojo.data.core.Notification -- update notifications
    • dojo.data.core.Schema or Model
    • dojo.data.core.Attribution -- create/modify timestamps, author, etc.
    • dojo.data.core.Versioning -- for access to old versions of items
    • dojo.data.core.Derivation -- derived attributes and calculated values

    Status as of December 2006

    The initial dojo.data APIs and example datastore implementations will ship in the dojo 0.4.1 release in December 2006. The APIs aren't 100% stable yet, and we don't yet have good documentation, but they should be a foundation for the dojo.data work to come. The entire dojo.data module is marked as experimental, and the APIs are subject to change without notice.

    Terminology

    • data source -- the place that the raw data comes from. In the movies.csv example above, the movies.csv file is the data source. The data source could be a file, a database server, a web service, or something else completely.
    • datastore -- a JavaScript object that reads data from a data source and makes that data available as data items via the dojo.data APIs
    • dojo.data APIs -- the standard set of methods that datastore implements. The dojo.data module includes a set of APIs (Read, Write, etc.), and a datastore may implement one or more of the APIs.
    • internal data representation -- the private data structures that a datastore uses to cache data in local memory (XML DOM nodes, anonymous JSON objects, arrays of arrays, etc.)
    • item -- a data item that has attributes with attribute values. In the movies.csv example above, Alien is an item with three attributes, Title:'Alien', Year:1979, and Producer:'Ridley Scott'.
    • attribute -- one of the fields or properties of an item. In the movies.csv example, Producer is an attribute.
    • value -- the contents of an attribute for a given item. In the movies.csv example, 'Ridley Scott' is a string value.
    • reference -- a value in an item that points to another item
    • identity -- some sort of identifier that can be used to uniquely identify an item within the context of a single datastore.
    • query -- some sort of specification or request that asks a datastore for some subset of the items it knows about. A query will often be a string, but in some datastores it might be a number or a date or a complex object structure.

    Source code organization

    The dojo.data API definition files, like Read.js and Write.js, are located in the /src/data/core directory. The core directory also contains any generally re-usable dojo.data code, including classes that are designed to serve as abstract superclasses for datastore implementations, or files that are designed to be used as mixins for adding functionality to datastore implementations.



    The datastore implementations that ship with dojo are located in the /src/data directory itself.

    API documenation

    The API definition files include in-line comments providing documentation about each of the methods in the API. Those documentation comments should eventually be available in the on-line Dojo API Reference tool, but unfortunately that's not yet working correctly, so for now the methods' signatures and their comments fail to show up in the on-line Dojo API Reference tool. Once that's fixed, the API Reference documentation will be here:

    http://dojotoolkit.org/api/#dojo.data

    You can also look at the API definition files themselves to see the documentation about individual API methods:

    Examples

    In the dojo.data unit-tests, there's an example page that allows you to read data from any of a variety of different data sources and then display data in a few different widgets:

    We also have a number of dojo.data unit-test pages, which may serve as simple examples of how to use the APIs:



    Dependency diagram

    One of the goals for the dojo.data module was to decouple widget code from data provider code. Because we now have standard dojo.data APIs, a widget author can write a widget binding that gets data using the dojo.data APIs and displays that data in the widget.

    The widget code itself can be independent of the dojo.data APIs, and know nothing about how data access is done. The widget binding code depends on both dojo.data APIs and on the widget itself, but the dojo.data APIs are independent of both the widget itself and the widget binding. The widget and the binding are both independent of any particular datastore implementation, which allows a single widget binding to be used with a variety of datastores implementations.

    Once one person has written one binding to display dojo.data items in a FilteringTable, then data items from any datastore can be displayed in a FilteringTable. If Dojo someday has 15 datastore implementations, and has 20 data display widgets, then dojo authors will only need to write 20 bindings in order to connect all the widgets to all the datastores. Without a standard dojo.data API, we would need a different bit of intermediary code for each possible connection between a widget and a datastore -- with 15 datastores and 20 widgets, that would require 300 different pieces of intermediary data translation code.



    +-----------+

    Widgets Bindings |
    dojo.data | Datastores Data Sources

    |
    APIs |

    +------------------+ | |

    | Trees | | |

    | (TreeV3) |-- binding --| |

    +------------------+ | | +------------+ +-----------+

    | |---| CsvStore |---| .csv file |

    +------------------+ | | +------------+ +-----------+

    | Tables & Grids | | |

    | (FilteringTable) |-- binding --| | +------------+ +-------------------+

    +------------------+ | |---| YahooStore |---| Yahoo web service |

    | | +------------+ +-------------------+

    +------------------+ | |

    | Charts & Graphs | | | +------------+ +------------+

    | (Chart) |-- binding --| |---| OpmlStore |---| .opml file |

    +------------------+ | | +------------+ +------------+

    | |

    +------------------+ | | +------------+ +------------+

    | Other widgets | | |---| RdfStore |---| RDF server |

    | (ComboBox) |-- binding --| | +------------+ +------------+

    | (SlideShow) |-- binding --| |

    | (etc.) |-- binding --| | +------------+ +--------------------+

    +------------------+ | |---| Other |---| other file formats |

    +-----------+ | stores | | and web services |

    +------------+ +--------------------+

    Goals

    We are designing the data-access APIs with a wide variety of use cases in mind, and we hope that the APIs will work well with many different kinds of data: RDF, XML, SQL, CSV, OMPL, etc. Here are some of the features we've been designing the APIs to support:

    • sync vs. async -- Some data sources can be read synchronously and will always have data available immediately (for example, if the data is coming from an HTML table on the web page itself). Some datastores may provide data asynchronously (for example, as the result of a search submitted to Yahoo). Some datastores may provide results incrementally -- the first 100 items in one batch, then a pause, then another 100 items, etc.
    • read vs. write -- Some datastores may be read-only, and some may be read-write.
    • hierarchical vs. tabular -- Some sources have hierarchical data and some sources have tabular data.
    • strongly typed vs. loosely typed -- Some datastores may provide "strongly typed" data and some sources may have free-form data.
    • literals vs. references -- Some datastores may only support literal values (for example, a CsvStore), while other datastores support both literals and item references.
    • single-valued vs. multi-valued attributes -- Some datastores may store only single values for an attribute {name:"robert"}, and some datastores may allow multiple values for each attribute {name:["robert","bob"]}.
    • simple identifiers vs. complex identifiers -- Most datastores may use simple serial numbers or key strings as unique identifiers, but relational databases might require compound keys, and other datastores may require XPath expressions, or may not support the notion of identifiers at all.
    • derived attributes -- Some items may have derived attributes as well as stored attribute values (price = base-price + tax).

    • lazy reference building -- Simple data sources will have only literal attribute values, but some data sources will have items with reference values that point to other data items. When the data is read from some serialized format (read from a file or retrieved from a server), all the references will be in the form of foreign keys or unique ids. At some point the datastore needs to notice the id-reference and replace it with a pointer to the referenced object. The datastore could to that reference building step in bulk for all the items immediately after the data is loaded, but the API is designed so as not to force the datastore to be implemented that way.

    • lazy loading: reference faulting -- For a datastore that connects to large database, it won't be possible to load the whole data set into memory at once. For example, imagine a genealogy data set, with data records for hundreds of thousands of people, all interconnected. You might want to load only a few dozen records at first, and then incrementally load more records as the user navigates through the graph. When the user clicks on something in the UI to see who Mark's mother is, the UI code calls get(mark, mother) on the datastore API. The datastore might be able to return a result based on what's already loaded in memory, or it might need to send a request to the server to get more info.

    • lazy loading: partial objects -- For some large data sets, you may want to start by loading only part of each data item. For example, in an e-mail client, you might have a list view that shows just the subject line for a bunch of messages -- when you select one of the messages, then the body of the message appears in the detail pane below. You don't want to load all the message body in bulk -- you want to get them one at a time, as needed.

    Widget bindings

    We hope that the dojo.data APIs will support:

    • Dojo widget bindings -- Dojo includes a number of data display widgets (Trees, Charts, Data Tables, etc.), and we want to be able to "bind" any of those widgets to any datastore instance that conforms to the data-access APIs.
    • third party widget bindings -- We also want the authors of proprietary third-party widgets to be able to bind those widgets to any datastore implementation.
    • read/write bindings, with update notifications -- The bound widget updates when the data model updates, and the data model is updated when the user makes a change via the widget.
    • easy creation of bindings -- support for creating bindings programmatically in JavaScript code or declaratively in HTML
    • different granularities for bindings -- simple attribute bindings that bind one attribute of one data item to one property of one UI control (for example, an input field), as well as full data-set bindings that bind an entire set of data items to a widget like a chart or a grid

    Data sources

    We hope to eventually have different datastore implementations that read data from a wide variety of data sources. Here's a list of some of the kinds of data sources that we've had in mind while designing the APIs:

    Datastore data representations

    We hope that the dojo.data APIs do not impose unnecessary limitations on the ways that datastore implementations can represent data in memory. In particular, we've designed the APIs with a few different data representations in mind:

    • simple anonymous JSON objects: {name:"sales", headcount:38}
    • JavaScript objects that are instances of data classes: Dept.js, Employee.js...
    • XML DOM nodes

    Design notes

    Question: Why aren't the dojo.data APIs more object-oriented, with accessor methods available on the individual data items?

    For example, using the existing dojo.data APIs, a line of code might look like this:

    var value = datastore.get(kermit, color);
    Why weren't the APIs designed so that line of code instead looked like this:

    var value = kermitItem.get(color);

    Answer: Performance -- both memory use and execution speed.

    If we had used an object-oriented API for the item accessor methods, that would have required every datastore implementation to have a different JavaScript object for every data item. By putting the accessor methods on the datastore object rather than on a data item object, we made it possible for different datastore implementations to use different data structures.

    For example, our OpmlStore reads data from an XML text file using XMLHttpRequest via a call to dojo.io.bind. The XML text file is automatically parsed into XML DOM nodes, and the OpmlStore uses those XML DOM nodes themselves as its native data item representation, which avoids the unnecessary step of copying all the data from the DOM nodes into JavaScript objects.

    For more background on different approaches we considered, see the dojo.data design page on the dojo development wiki.

    History

    The dojo.data module was first envisioned back in 2005 or earlier, before Dojo release 0.1 had shipped.

    • The first experimental dojo.data code was written in January 2006, but it was geared only toward semi-structured data, and it had a single data model implementation rather than defining an API which could be implemented differently by different datastore authors.
    • In July 2006 IBM contributed a significant body of experimental data model code, including both code geared toward data from XML data sources and code geared toward data from structured data sources like relational databases.
    • Over the course of the next few months, August to October, Dojo held a series of dojo.data design meetings to try to design a unified set of APIs that would work well for a wide variety of use cases. In November 2006, for the Dojo 0.4.1 release, we checked in the initial dojo.data Read and Write APIs, as well as a handful of example datastore implementations, and a handful of test pages. The old design work is archived on the dojo wiki, on the dojo.data page.

    Future plans

    In 2007 we hope to:

    • extend the dojo.data APIs to handle features like attribution, versioning, and update notification
    • write more datastore implementations
    • write bindings for a number of dojo data display widgets
    • write more unit tests
    • improve the documentation

    How can people volunteer to help?

    If you'd like to help with the dojo.data module, here are some areas where we could especially use some help:

    • Dojo Book entry: add to this page, or improve what's already here
    • API method documentation: improve the documentation in the API definition files
    • unit tests: write more unit tests to help ensure that different datastore implementations are truly interoperable
    • datastore implementations: write new datastore classes that get data from different data sources (different web services, file formats, databases, application server, server frameworks, etc.)

    Language Utilities

    dojo.lang.* contains wrappers for common idioms found in the JavaScript language. It is not a replacement for language constructs but it does provide value added functions that go beyond what the standard JavaScript routines can do such as masking browser incompatibilities, structuring object oriented techniques and speeding up implementation of common procedures.

    The sections below show some of the idioms that deserve special mention. See the reference guide for the full list.

    All about functions

    Hitch

    Often you need to pass a function pointer as an argument (like above), but the function needs to operate within the context of an object. For example, if I have a class Foo that defines bar:

    bar: function() {

    return this.i++;

    }

    and then I instantiate an instance of Foo:

    var fooInstance = new Foo();

    and I want a pointer to fooInstance.bar, I would use:

    dojo.lang.hitch(fooInstance, "bar");

    Curry

    Curry takes hitch one step farther, and embeds parameters into the function. If I declare an object with a function like this:

    var foo = {

    bar: function(arg1, arg2){

    alert(arg1 + " is a " + arg2);

    }

    };

    And then I declare a curried function:

    	var tmp = dojo.lang.curry(foo, “bar�, "Bill");

    Then when I call tmp:

       tmp("baker")

    It will throw up the alert "Bill is a baker".



    Array Iterators

    Dojo provides various functions to Iteration over arrays, rather than the for() loop of traditional procedural languages. A callback function is specified and called for each element of an array.

    For example, this will display three alerts, one for each of the specified words:

    var colors = ["red", "white", "blue"];

    dojo.lang.forEach(colors, alert);

    This will return an array of the squares of the first five positive integers:

    var integers = [1,2,3,4,5];

    var squares = dojo.lang.map(integers, function(x){ return x*x; });

    There are also functions:

    • dojo.lang.filter - filter out matching items from an array into a new array
    • dojo.lang.some - tests if given function returns true for any element in the array
    • dojo.lang.every - tests if function returns true for every array element

    The iteration functions work on arrays, comma separated lists, or other array-like variables.

    Miscellaneous

    Here are a few more areas of dojo's dojo.lang module that deserve special attention.

    dojo.byId()

    Much like the prototype $() operator, dojo.byId represents the short form document.getElementById(). This will map a dom node id to a node, but if passed a node, it will return that same node. More importantly, however, is the fact that document.getElementById() is not available when using Dojo and dojo.byId() is the only way to grab nodes.



    Others

    • dojo.lang.is*
    • dojo.lang.setTimeout
    • dojo.lang.delayThese
    • dojo.lang.curry
    • dojo.lang.assert

    Storage

    dojo.storage -- The Dojo Manual


    Introduction

    Imagine if web applications could store megabytes of data on the client-side, in the browser, both persistently and securely. No server needed.



    Imagine if web applications could work offline with the click of a button. Want to access your web based word processor when you are not on the network, with your private files stored privately, right on your own machine and not on some server? Now you can.



    Even better, imagine if all of this worked across the existing web; 95% of the existing browsers on the web could start using these features right now, with no software installs or funky new browsers.



    What could you build if you had these tools? How about a truly collaborative, web-based word processor with client-side storage for your private documents, as well as offline access? Maybe an Ajax RSS aggregator with client-side caching of the feeds you read and offline access? An offline, web-based book reader using data from the Internet Archive's Open Library would be cool.



    These are the goals of Dojo Storage

    What is Dojo Storage?

    Dojo Storage is a unified API to provide JavaScript applications with storage. It is a generic front-end to be able to provide all JavaScript applications a consistent API for their storage needs, whether this JavaScript is in a browser, a Firefox plugin, an ActiveX control, using Windows Scripting Host, etc. Further, the storage backends can use whatever mechanism is appropriate; dojo.storage automagically detects its environment and available storage options and selects the most appropriate one.



    The dojo.storage architecture is simple; a JavaScript application interacts with the Dojo Storage Manager, which selects the best available Storage Provider and makes it available. Storage Providers implement a generic interface, which makes the underlying storage system look like a simple hash table that can be saved and loaded from. A storage provider can optionally be persistent, and can provide metadata about their capabilities (isPersistent, hasSettingsUI, getMaximumSize, etc.). Under the covers, storage providers can use a wide array of possible ways to store data, from hidden Flash to native browser capabilities.



    Right now the dojo.storage system includes three storage providers, a Flash Storage Provider that uses a hidden Flash applet; a WHAT WG Storage Provider that uses native client-side storage abilities in Firefox 2; and a File Storage Provider that uses the native file system if a web app is loaded from the local file system. Creating other kinds of storage providers is a great way for the community to contribute.



    Please note that the WHAT WG storage provider is available in the 0.4.1 Dojo release, while the FileStorageProvider will be in the upcomming 0.4.2 Dojo release and is available on the Dojo trunk in the Subversion repository. The FlashStorageProvider has been available since Dojo 0.3.



    The Flash Storage Provider uses features available since Flash 6, including Flash's SharedObject's. By default, a web page can store up to 100K without user permission; after 100K the user is prompted for each order of magnitude increase. For example, if you store 101K, the user is prompted on whether to give you permission. Afterwards, the user will not be prompted until the next order of magnitude, which is at 1 megabyte.



    The WHAT WG storage provider uses native client-side storage features inside of Firefox 2. Firefox 2's API for client-side storage is based on the WHAT Working Group's specification. This API opens up at least 5 megabytes of client-side storage, instantly, in a highly-performant way. Dojo Storage will use this API before the Flash one, and fallback if the browser does not support the WHAT WG API.



    The File Storage Provider will use the local file system if a web app that uses Dojo Storage is loaded from a file:// or chrome:// URL; this will happen transparently, without the developer needing to invoke this storage provider. The File Storage Provider uses XPCOM on Firefox to achieve this, while it uses ActiveX on Internet Explorer. Code exists for Safari and Opera support, but is not finished yet. Please note that the FileStorageProvider has been removed from the Dojo Storage system, and is available as a manual patch -- this was to make Dojo Storage smaller, and because very few people need this provider. See ticket 2285 for the FileStorageProvider code.



    Dojo Storage, in combination with Flash and the WHAT WG provider, instantly works across about 95% of the existing web, covering a wide swath of the machines out there. It works with IE 6, Firefox, and Safari.

    Usage & Example

    The example usage we will describe is based on the Moxie demo application, which is a show case for Dojo.Storage; feel free to use the source code in Moxie as you please, since it is under a BSD license.



    At the bottom of the Moxie JS file, we first subscribe to find out when the storage system is ready for us to work with; we can not load or save values until the underlying storage provider is ready. In this case we want to call Moxie.initialize when storage is ready; if by the off chance it is already ready when we get to this code block, then we want to wait until the page is fully loaded before working with it:

    if(dojo.storage.manager.isInitialized() == false){

    dojo.event.connect(dojo.storage.manager,

    "loaded", Moxie,

    Moxie.initialize);

    }else{

    dojo.event.connect(dojo, "loaded",

    Moxie,

    Moxie.initialize);

    }
    It's very important that your code is right for listening to when storage is ready to be used; this is a common source of bugs. Your event listener must be at the top level and executed while the page is loading. Dojo.Storage writes thing out into the page during page load that won't work afterwards (i.e. it uses document.writes). The above code should be top-level and not be inside of a nested function; otherwise it won't execute on page load.



    When we are all loaded up, we can start to play with the storage system. For example, to load a value that was previously saved with some key, we just do the following:

    var results = dojo.storage.get(key);
    To save some value:

    try{

    dojo.storage.put(key, value,

    saveHandler);

    }catch(exp){

    alert(exp);

    }
    'Value' can be a string or even a complicated JS object; we internally JSON everything before storing it as a flat string, and turn it back into an object on later retrieval. Dojo.storage actually does some nifty autodetection if it is working with strings, to avoid the JSON performance hit of evaling() and bypass this, which gives much better performance for storing large strings into storage, like XML files or digital books.



    Notice the 'saveHandler' above; a storage system can optionally ask the user if they want to accept or deny your storage request, so you must be ready for a save request to fail.



    'saveHandler' is a callback function that receives two arguments. The first is the 'status', which can be one of three values:

    • dojo.storage.SUCCESS - Saving was successful
    • dojo.storage.FAILED - User denied storage request
    • dojo.storage.PENDING - The user is being prompted with some UI on whether to approve this storage request
    The second argument is the 'keyName' that is being saved; since saving is asynchronous, it is sometimes useful to know which key is being worked with on the callback.



    The Flash Storage Provider pops up an underlying Flash storage dialog to the user after 100K, and every order of magnitude increase after that:





    The WHAT WG storage provider, used by default when Dojo Storage is loaded into Firefox 2, automatically gives you up to 5 megabytes of storage space, with no prompting like the Flash storage provider.



    Here's an example saveHandler:

    var saveHandler = function(status, keyName){

    if(status == dojo.storage.PENDING){

    // ...

    }else if(status == dojo.storage.FAILED){

    // ...

    }else if(status == dojo.storage.SUCCESS){

    // ...

    }

    }



    try{

    dojo.storage.put(key, value,

    saveHandler);

    }catch(exp){

    alert(exp);

    }
    There is more to the code, but those are the important bits in terms of understanding dojo.storage. It's pretty straightforward to use.

    Demos & References

    API Reference

    See the automatically generated API reference

    Requires

    • dojo.lang
    • dojo.flash
    • dojo.event

    Provides

    dojo.storage
    A local durable cache


    dojo.storage.manager
    Manager class that autodetects available storage and instantiates best one; can be used for lower level control

    Contributors

    Brad Neuberg, bkn3@columbia.edu - Module maintainer and creator

    Alex Russel, alex@dojotoolkit.org - Created early version of Dojo Storage

    JB Boisseau, jb.boisseau@eutech-ssii.com - Created WHAT WG storage provider for Dojo Storage

    FAQ

    Help! Dojo.Storage Isn't Working!

    Some possibilities:

    • You must have the following SWF files in the same directory as your dojo.js file: flash6_gateway.swf, storage_dialog.swf, Storage_version6.swf, Storage_version8.swf
    • Make sure your web server is returning the correct MIME type for SWF files: application/x-shockwave-flash swf
    • You are on an unsupported browser. The following are supported: IE 6+, Firefox, Safari.
    • You have a really old version of Flash, or no Flash, and don't have permission on your computer to upgrade software (perhaps you aren't the administrator account and don't have permission to install Flash?)
    • You're being served from an https domain, where there is a known bug right now that needs to be fixed (I can't fix it because I don't have an SSL/HTTPS environment setup; any volunteers?)
    • Your event listener for the Dojo Storage Manager is incorrect. See below.

    It's very important that your code is right for listening to when storage is ready to be used. Your event listener must be at the top level and executed while the page is loading. Dojo.Storage writes thing out into the page during page load that won't work afterwards (i.e. it uses document.writes). Study the source code for the Moxie example storage application carefully before asking questions; compare your code and see what is different.

    How Do I Write a Storage Provider?

    See the FlashStorageProvider for an example. Basic steps:

    • Find the file that corresponds to your render environment; for example, if your provider will run in the standard web browser environment, then you will want to put your storage provider into src/storage/browser.js. If a file does not exist for your render environment yet, such as a Konfabulator storage provider, then you would create a new file corresponding to your environment, such as src/storage/konfabulator.js or src/storage/xpcom.js. All the providers for a given render environment must be in the same source file. If you have added a new file for a new environment, make sure to add the existence of this environment to src/storage/__package__.js so it is loaded for your new environment.

    • Subclass dojo.storage:

      dojo.inherits(dojo.storage.browser.FlashStorageProvider, dojo.storage);
    • Implement the dojo.storage methods, such as put() and get(). You MUST also implement the isAvailable() method in a solid way, since this method will be called by the Storage Manager to see if your provider is available in the given environment.
    • Tell the Storage Manager about our existence at the bottom of your source file; remember to order these registrations in the order you want storage providers to be chosen. For example, if you have three storage providers, such as a What WG provider, a Flash provider, and a proprietary Internet Explorer storage provider, and you want these to be preferred in this order, you would have the following at the bottom of your source file:
      // register the existence of our storage providers

      // choose What WG provider first

      dojo.storage.manager.register("dojo.storage.browser.WhatWGStorageProvider",

      new dojo.storage.browser.WhatWGStorageProvider());

      // if not available, use Flash

      dojo.storage.manager.register("dojo.storage.browser.FlashStorageProvider",

      new dojo.storage.browser.FlashStorageProvider());

      // if still not available, prefer IE method

      dojo.storage.manager.register("dojo.storage.browser.InternetExplorerStorageProvider",

      new dojo.storage.browser.InternetExplorerStorageProvider());
    • Initialize the Storage Manager:

      // now that we are loaded and registered tell the storage manager to initialize

      // itself

      dojo.storage.manager.initialize();

    How is Dojo Storage Related to AMASS?

    AMASS, or the Ajax Massive Storage system, is an older, proof-of-concept version of Dojo.Storage written by the same author as Dojo Storage, Brad Neuberg. Dojo.Storage replaces AMASS. It is more full featured, better tested, and is supported across more browsers than AMASS. AMASS will no longer be supported.

    I Heard Dojo Storage Can Be Used For Offline Access. How?

    For offline, Julien Couvreur discovered the necessary HTTP headers. The core part of it is to use HTTP caching; your site must have the following kinds of HTTP headers on:

    • Etag
    • Last-Modified
    • Expires
    • Cache-Control
    Etags and Last-Modified are on by default on Apache 2.0; the rest have to be turned on in httpd.conf with mod_expires:

    LoadModule expires_module modules/mod_expires.so





    ExpiresActive On

    ExpiresDefault "access plus 1 month"

    The page is now in the browser cache after the first access. In IE and Firefox, the user has to go to File > Work Offline to work offline, and then can simply just navigate to the app's URL. In Safari, this is not necessary, and you can just go to the URL. I like to provide a link that can be dragged to the toolbar.



    The offline and dojo.storage work together, because whether you are offline or online you can access the same persistent storage, saving data while offline then syncing when online. Expect a dojo.offline and dojo.sync in the future that will provide abstractions for common operations like this. I'm looking for financial sponsors on this if you are interested so that I can finish and open source it (email me).

    The Dojo.Storage presentation goes into offline access as well:

    http://codinginparadise.org/weblog/2006/05/new-slides-from-ajax-experience-talk.html



    You should note that offline support is alpha, since there are issues with cache clearing.

    Who created and supports Dojo Storage?

    Dojo Storage was created by Brad Neuberg and is supported by him. Please refer all questions to the Dojo mailing lists before emailing Brad directly, as the email load is getting large. Brad might charge to answer your question, since he does Dojo Storage for free and makes money through open source consulting.

    How Can I Help With Dojo Storage?

    There are two big ways you can help Dojo Storage. The first is by creating new storage providers; a nice one would be for Internet Explorer's proprietary storage mechanism.



    The other big way to help is with squashing bugs, especially with the https bug that affects Dojo Storage, since I don't have a testing environment for it. See a list of all Dojo Storage bugs.



    Email Brad Neuberg if you are available to help with either of these.

    Why Did You Use Flash?

    Why Flash? Flash now has a greater installed base than Internet Explorer; Flash 6+ has a 97.1% penetration across the installed base of PCs, while IE 5, 6, and 7 have 64.7%. Flash is probably one of the most installed pieces of software on the planet.



    The Flash Storage Provider uses Flash as a hidden runtime to extend the browser, because of its ubiquity and cross-platform/cross-browser qualities. When browsers get native persistence support, your dojo.storage applications will continue to run since you write them against the generic dojo.storage APIs.

    What Is The Security of Dojo Storage?

    The Flash Storage Provider has exactly the same security characteristics as cookie based storage. Data is siloed by domain, so other domains can't access this data. The data is stored on the local disk, usually in the user's home directory.

    When working with Dojo Storage and it's Flash Storage Provider you should take exactly the same care you would take with cookies -- i.e., don't leave yourself open to Cross Site Scripting Attacks (XSS).

    What Is The Maximum Amount of Storage I Can Do?

    Using the Flash Storage Provider, this breaks down into three questions:

    1. How much total data can I store? If the user has given you permission, there should be no limit. I have successfully stored up to 10 megabytes, and just stopped there.
    2. How much data can I send over in one put()? For Internet Explorer on Windows and Firefox on all platforms, I can send over about 700K or 1 megabyte in a single put() and the performance is fine; however, for Safari on Mac, you can really only send over about 300 to 500K in one put() with acceptable performance. There will be a difference between saving a string object, which is much faster, than serializing a large JavaScript tree, which might get very slow if you are working with a set of JavaScript objects that are hundreds of K.
    3. What are the total number of records I can have? What if I have hundreds of thousands of keys? I have never tested this, so am not sure where it will fall over.
    For the WHAT WG provider, there is actually currently no upper-limit on the amount of storage a site can store! The WHAT WG spec recommends 5 megabytes, but Firefox implements no upper-limit. This is obviously a bug and will be fixed by Firefox.

    What About A Query Layer For Dojo Storage?

    In 2005 sent out a call for someone to layer over the very cool Trim Path Java Script SQL layer over AMASS (an earlier version of Dojo Storage), and someone took up the call and created a prototype linking them together. You should check it out; its very cool. No one has done this with Dojo Storage, but it shouldn't be hard. This might be very useful for offline Ajax apps.

    What About Syncing?

    This is a hard problem that is difficult to find general solutions for. I am looking for financial sponsorship to create and open source this. Email me if you are interested.

    I Don't Care About Dojo Storage or Dojo Flash - How Can I Turn Flash Off In My App?

    If you are using a 'kitchen sink' build of Dojo, you will get Dojo Storage and Dojo Flash included by default. This will cause the Dojo Flash material to be written into the page. If you don't want this to happen, you can disable all of Dojo Flash with the following djConfig variable:



    var djConfig = { disableFlashStorage: true; }

    How Does The Underlying Dojo Flash System Work?

    I'll briefly describe dojo.flash here, which is the layer that seperates out JS and Flash communication and which is used by the Flash Storage Provider. It might be useful for folks who are encontering any issues with the storage system due to Flash.



    Cross-browser, fast, reliable JS+Flash communication is really hard and ugly, so I encapsulated this portion out into it's own layer. The great thing is you don't have to know any of this externally; dojo.flash and dojo.storage work together to figure out your Flash capabilities, and use the appropriate mechanism internally. Zero hassle.



    Dojo.flash provides several major services:

    • dojo.flash.Info - Is Flash available + what version of Flash?
    • dojo.flash.Embed - Embeds Flash into page for Flash+JS communication
    • dojo.flash.Communicator - Provides uniform, fast, reliable, JS + Flash communication
    • dojo.flash.Install - Uniform installation and upgrading of Flash
    dojo.flash.Communicator was the real doozy to create; it was a pain in the butt, to put it lightly. This area of the system provides a method abstraction between Flash + JS. For example, JavaScript can call sayHello(), which is some Flash method, while Flash can execute DojoExternalInterface.call("dojo.storage.save", resultsHandler) to run some JS method.



    The heart is something called DojoExternalInterface, which is a backport of Flash 8's ExternalInterface to Flash 6. I didn't want to create this backport, but the complexity of handling all the internal tricks I was doing to make this stuff work required that I wrap this magic in some kind of maintainable API. DojoExternalInterface makes it possible to register Flash methods that can be called from JavaScript:

    DojoExternalInterface.initialize();

    DojoExternalInterface.addCallback("put",

    this, put);

    DojoExternalInterface.addCallback("get",

    this, get);

    DojoExternalInterface.addCallback("remove",

    this,

    remove);

    DojoExternalInterface.loaded();
    There are three ways to do Flash+JS communication:

    1. Using LiveConnect/ActiveX + fscommands - Flash 6

      Pro: Extremely fast, can send very large data, mature

      Con: Only works on IE and Firefox
    2. Using ExternalInterface - Flash 8

      Pro: Easy to use, Works on Safari

      Con: Unbelievably slow, performance degrades O(n^2), serious serialization bugs
    3. getURL/LocalConnection/New Flash object for each call - Flash 7

      Pro: Very cross platform

      Cons: Destroys history, serious data size limitations and performance issues
    Only methods 1 and 2 are acceptable for the Flash Storage Providers needs. It turns out that we use method 1 for IE and Firefox, and method 2, ExternalInterface, for Safari. I found workarounds to fix ExternalInterface's serious bugs, so that it works on Safari and is fast and serializes correctly, but these workarounds only work on Safari.



    The Flash 8 communication support for Safari took 3 months to figure out; pain in the behind. I won't go into detail on the method and workarounds, but here is a bit of info on the workarounds:

    • We chunk method call data into many different small calls through ExternalInterface,

      which makes performance linear rather than O(n^2), which is what the standard ExternalInterface's performance is.
    • I used a debugger to find hidden JS serialization methods used by the Flash plugin, which internally uses XML serialization and made all sorts of mistakes in terms of serializing characters - for example, it doesn't escape characters and also uses eval() so its slow as dirt. I found a way to bypass this internal serialization, do it all manually, and make the method call myself using an undocumented JS function.
    • I now double encode and decode all XML characters on both sides:

      & --> && This is very important for persisting XML, and I did lots of testing around this to catch every single problem character (including nulls, for example)
    With these workarounds, performance and reliability on Safari are great. Unfortunately, these workarounds only work on Safari, so we can't use ExternalInterface cross browser.



    Check out this testing page, which provides a lower level interface to dojo.storage; there are quick links on the left to fully save an entire book into the storage system, in this case Faust by Goethe at 250k courtesy of Project Gutenberg; there is also a quick link to save an example RSS XML feed into the storage, in this case an atom feed from my weblog. Try this on Safari; in the past it took minutes and froze the machine to save the book, now it takes seconds.



    I won't go into the Flash 6 communication necessary for IE and Firefox support; there were lots of fun different things that had to be figured out, like how to center the Flash dialog even if you are across many different kinds of HTML doctypes, browsers, and platforms, or isolating timing issues like the fact that the fscommand infrustructure on some versions of IE comes up after your Flash applet is already running.



    Can I Force a Certain Storage Provider to Be Used? Can I Disable Certain Storage Providers?

    Several djConfig variables exist to help you control what storage provider is used. The first is called forceStorageProvider, and will cause the Dojo Storage system to automatically use the given provider, even if the platform doesn't support it or a better one is available:



    djConfig = {forceStorageProvider: "dojo.storage.browser.WhatWGStorageProvider"};
    Two other djConfig flags exist: disableWhatWGStorage, which can be true or false and which will disable WHAT WG storage and fall back to using a different one; and disableFlashStorage, which can be true or false and which will disable using the Flash Storage Provider. The last flag is useful if you want to make sure that Flash is never used, for example if you don't want to have Flash touch your codebase.



    Note that the forceStorageProvider and disableWhatWGStorage/disableFlashStorage flags are mutually exclusive; you should choose to use EITHER forceStorageProvider or disableWhatWGStorage/disableFlashStorage. Things won't work correctly if you try to mix the 'force' and 'disable' flags.



    I'm Having Wierdness with the FileStorageProvider on Internet Explorer 7!



    On Internet Explorer 7, XMLHTTPRequest does not work on the local file system; this breaks Dojo's package loading, which uses XHR. To use the FileStorageProvider on IE 7 with your code, make sure to use a release build of dojo.js (i.e. the one that packs everything into a single dojo.js file), with preferably the 'storage', 'moxie', or 'kitchen_sink' profiles to make sure everything got packed into there and won't be loaded dynamically at runtime by XHR.



    When loading from file:// URLs, the user is prompted for permission to run secure code; what's up?



    On Firefox, the first time you load a page that uses Dojo Storage (and therefore the FileStorageProvider), you will be prompted on whether to give this page permission to access the local file system. If the user approves, you will not be asked again. On IE 6 and 7, you will also be prompted, but will be prompted each time the page is loaded. After you have given permission the page will work normally.



    Can I Use Dojo Storage With Other Non-Dojo, JavaScript Libraries?



    Dojo Storage can be used with other JavaScript libraries, such as TIBCO or Prototype. This FAQ question will provide pointers to sites where others have integrated Dojo Storage with their own libraries.

    Luke Birdeau has integrated Dojo Storage with the TIBCO GI library. See details on the TIBCO forum.



    About

    Author:Brad Neuberg
    Version:0.5
    Copyright:Dojo Foundation, 2006
    Date:2006/11/18

    UI Utilities

    There are a number of modules in dojo for UI related tasks

    DOM functions

    Dojo provides three modules with basic functions on the document tree:

    • dojo.dom - basic functions such as moving / copying / deleting children. These functions are meant to apply equally to html or svg or vml nodes.
    • dojo.style - functions relating to style attributes, such as calculating the size of elements, setting their opacity, etc.
    • dojo.html - this module contains functions that only apply to HTML nodes, such as appending/removing classes

    All of the functions above are written to either mask browser incompatibilites, or to provide functionality that wasn't provided within the standard javascript API.

    All of the functions can take either a node or an id as their first argument.

    Sizing

    One particular set of functions to note are the sizing functions.

    An HTML block element has the following format:

    		+-------------------------+

    | margin |

    | +---------------------+ |

    | | border | |

    | | +-----------------+ | |

    | | | padding | | |

    | | | +-------------+ | | |

    | | | | content | | | |

    | | | +-------------+ | | |

    | | +-|-------------|-+ | |

    | +-|-|-------------|-|-+ |

    +-|-|-|-------------|-|-|-+

    | | | | | | | |

    | | | |<- content ->| | | |

    | |<------ inner ------>| |

    |<-------- outer -------->|

    +-------------------------+

    There are three sizes associated with this element:

    • outer - size of the outermost box, including margin, border, padding, and content
    • inner - size of the second box, including border, padding, and content
    • content - size of the innermost box

    Depending on browser, and mode, asking for the width/height of an element will return different results; sometimes it will give you the content size and sometimes it will give you the inner size. Therefore, it's important to always use dojo's functions for getting/setting the size;

    • dojo.style.getOuterWidth / dojo.style.getOuterHeight
    • dojo.style.getInnerWidth / dojo.style.getInnerHeight

    Classes

    An HTML element can have multiple classes, such as:

    Dojo provides functions to handle this class string as an ordered set, so you don't need to do string manipulations to add/remove items from the class list:

    • dojo.html.addClass(node, className)
    • dojo.html.prependClass(node, className)
    • dojo.html.removeClass(node, className)
    • dojo.html.replaceClass(node, className, oldClassName)
    There are many more DOM related functions. Please see the reference doc for details.

    Drag and Drop (DnD)

    When Microsoft and Netscape introduced Dynamic HTML (different versions of course), the drag-and-drop demos elicited much applause. You could put a shopping cart on the left and catalog items on the right, and the customer could drag items right to the shopping cart. Neato! It looked just like a client-server application.

    Because of the incompabilities between DHMTL, writing a cross-platform drag-and-drop application was difficult. Dojo makes it easy by layering an easy-to-use API over the top.

    To drag and drop, you need three things:

    • Something to drag, called a DragSource. In Dojo, any HTML element can be one.
    • Some place to drop, called a DropTarget. This too can be any HTML element.
    • Something to do when the item is dropped

    A Simple Example

    (Under construction)

    Here's a simple application of drag and drop to simulate a kitchen. You have three drop targets - Frying Pan, Pot of Boiling Water, and Oven - and two drag sources - Egg and Chicken Leg. A user can drag the word Egg to the box surrounding Pot of Boiling Water to simulate boiling the egg. You can drag the foods from their initial locations, or from one destination to another, as in dragging the Egg from the Pot of Boiling Water to the Frying Pan.

    <script type="text/javascript' src="/path/to/dojo.js"></script>
    <script type='text/javascript">
       dojo.require("dojo.dnd.*");
       dojo.require("dojo.event.*");
    
       function initKtichen() {
          // "pan" matches the id for the <div> tag with id="pan" below
          // The [] around "dest" are required, for reasons we'll see later
          new dojo.dnd.HtmlDropTarget(dojo.byId("pan"), ["dest"]);
          new dojo.dnd.HtmlDropTarget(dojo.byId("pot"), ["dest"]);
          new dojo.dnd.HtmlDropTarget(dojo.byId("oven"), ["dest"]);
    
          // "dest" matches the "dest" in the DropTarget's above.
          new dojo.dnd.HtmlDragSource(dojo.byId("egg"), "dest");      
          new dojo.dnd.HtmlDragSource(dojo.byId("leg"), "dest");
       }
    
       dojo.addOnLoad("initKitchen");
    </script>
    </head>
    
    <body>
       <H1>Food</H1>
       <div id="egg">Egg</div>
       <div id="leg">ChickenLeg</div>
    
       <H1>Destinations</H1>
       // This is the pan, the first drop target
       <div id="pan" style="border:3px soid black;width:200px">
          Frying Pan
       </div>
       <div id="pot" style="border:3px solid black;width:200px">
          Pot of Boiling Water
       </div>
       <div id="oven" style="border:3px soid black;width:200px">
          Oven
       </div>
       <br />
    

    (Vegetarians may substitute soy products with no loss of functionality) Running this example, you notice things happen automagically:

    • The item moves with your mouse pointer as you drag
    • When you drop the item on a drop target, the drop target expands to hold the item
    • If you try to drop an item outside of a drop target, the item snaps back to its original position.
    • A horizontal line appears where the drop will occur. In this example, you'll see the top border of the drop target become "fatter".

    The HTMLDragSource and HTMLDropTarget constructor calls contain two arguments:

    • The component do be dragged or dropped, respectively. dojo.byId() is helpful here, and an easier-to-type shortcut for document.getElementById().
    • A destination code to specify which drag sources can do to which drop targets. We'll see that work in LimitingDragAndDropOptions.
    The example does a lot with a little bit of code. In the next examples, we'll work on making the user interface discoverable, meaning the user can figure it out with visual cues.

    Beautification

    Let's put those events to good use. When a user drags an object, they'd like to know where it's legal to drop. We can help out by visually indicating a legal drop target. Going forward with our kitchen example, when the user drags a food item to a utensil, we'll make the utensil border red. When they drop, or drag out of that utensil, we'll make it black again.

    To accomplish this, the natural events to use are onDragOver and onDragOut. These act a lot like the onMouseOver and onMouseOut attributes of an HTML tag.

        function initKitchen(){

    dojo.declare("dojo.book.dnd.DestDropTarget",dojo.dnd.HtmlDropTarget,{

    onDragOver:
    function(e) {

    // domNode is the drop target we're over

    this.domNode.style.borderColor = "red";

    dojo.dnd.HtmlDropTarget.prototype.onDragOver.apply(this, arguments);

    },

    onDragOut:
    function(e) {

    // this.domNode is the drop target we're leaving

    this.domNode.style.borderColor = "black";

    dojo.dnd.HtmlDropTarget.prototype.onDragOut.apply(this, arguments);

    }

    });

    new dojo.book.dnd.DestDropTarget(dojo.byId("pan"), ["dest"]);

    new dojo.book.dnd.DestDropTarget(dojo.byId("pot"), ["dest"]);

    new dojo.book.dnd.DestDropTarget(dojo.byId("oven"), ["dest"]);



    new dojo.dnd.HtmlDragSource(dojo.byId("egg"), "dest");

    new dojo.dnd.HtmlDragSource(dojo.byId("leg"), "dest");

    }



    Drag and Drop Actions

    OK, we have drag sources and drop targets. But without an action, drag and drop isn't very interesting. We want something to happen when the item is dropped.

    Because Drag and Drop uses Dojo's event model, you can set up actions with very few lines of code. (If you haven't reviewed the Object Oriented Concepts section, now's a good time.) Here's a simple example, which displays an alert box when the item is dropped.

       function initKitchen(){
            dojo.declare(dojo.book.dnd.DestDropTarget",dojo.dnd.HtmlDropTarget,{
                onDrop: function(e) {
                    alert('Ready to cook!');
                    // Call the superclass method to do the actual dropping
                    dojo.dnd.HtmlDropTarget.prototype.onDrop.apply(this, arguments);
                }
            });
            new dojo.book.dnd.DestDropTarget(dojo.byId("pan"), ["dest");
            new dojo.book.dnd.DestDropTarget(dojo.byId("pot"), ["dest"]);
            new dojo.book.dnd.DestDropTarget(dojo.byId("oven", ["dest"]);
    
            new dojo.dnd.HtmlDragSource(dojo.byId("egg"), "dest");
            new dojo.dnd.HtmlDragSource(dojo.byId("leg"), "dest");
        }

    Notice how the drop target object type is no longer HtmlDropTarget, but your new class DestDropTarget. This is simple inheritance. A DestDropTarget is a more specific kind of HtmlDropTarget. Most of the methods are delegated to the superclass, HtmlDropTarget, but our specific functionality for onDrop is added before the superclass call.

    Drop Events

    onDrop is the most common event to override for drop targets. But there are others. To connect an action to one of these events, simply specify the method for that event in your dojo.declare call. All events without an action will be handled by HtmlDropTarget.

    • onDragOver(e) - called when the user begins dragging a source over this drop target. It is called only once when the drag source "flies over".
    • onDragMove(e) - called repeatedly as the drag source is over the drop target. You shouldn't perform any long calculations here.

    • onDragOut(e) - the opposite of onDragOver, this is called when the drag source leaves the drop target area without having been dropped. This is called only once.

    So the events fire like this:

    • For dragging across a drop target without dropping - onDragOver(), onDragMove(), onDragMove(), ... onDragMove(), onDragOut().
    • For dragging into a drop target and dropping - onDragOver(), onDragMove(), onDragMove(), ... onDragMove(), onDrop().

    Like all event handlers, your code must include one parameter for the event information itself. This object is of type dojo.dnd.DragEvent, because Drag events and Drop events have identical event information. DragEvent contains the following readable fields:

    • dragObject - the object being dragged.
    • dragSource - the dragSource being dragged. Note that dragSource.dragObject is the same as dragObject. (Confusing, but remember the HtmlDragSource constructor takes two parameters: the object and the possible destinations).
    • target - the drop target for that drop. Null if the drag source hasn't been dropped yet.
    The onDropEvent also has onDropStart and onDropEnd events before and after, accordingly. onDropStart is a good place to verify the drop target is OK, although destinations do the job easier.

    Drag Events

    Each of these events also has a DragEvent parameter with event information.

    • onSelected(e) - called when a drag source is clicked
    • onDragStart(e) - called once when dragging begins
    • onDragEnd(e) - called once when dragging ends. For drops, this event is called right before onDrop.
    Note that not every drag ends with a drop! The user can drag a source, then leave the browser window entirely and let go of the mouse button. Nothing will be dropped, since a browser doesn't usually interact with other windows. But onDragEnd will be called nonetheless.

    Limiting Drag and Drop Options

    Using default settings as we have, the user can drag items all over the page. They may have to try several potential drop before stumbling on the right one. You can help them out by limiting their choices.

    Disabling Drop Targets for Certain Sources

    Continuing from our example, suppose that chicken legs can go into all destinations: the oven, the pot, or the frying pan. But eggs can go only into the pot or pan, not in the oven. (Yes there is such a thing as baked eggs, but you're not Wolfgang Puck!)

    That's where the "dest" parameter comes in. The second argument for both HtmlDragSource and HtmlDropTarget, this acts much like the name="..." parameter of radio buttons. They tie different drop sources with a common set of rules.

    In the following example, we set up two destination groups: allCookingOptions, and topOfStoveOnly. The HTML portion is exactly like our first example, but we change initKitchen to:

        function initKitchen(){

    // A pan can be a drop target for any drag source of type topOfStoveOnly or

    // allCookingOptions.

    new dojo.dnd.HtmlDropTarget(dojo.byId("pan"), ["allCookingOptions","topOfStoveOnly"]);

    new dojo.dnd.HtmlDropTarget(dojo.byId("pot"), ["allCookingOptions","topOfStoveOnly"]);

    new dojo.dnd.HtmlDropTarget(dojo.byId("oven"), ["allCookingOptions"]);



    // An egg can only be dropped on a target of type topOfStoveOnly

    new dojo.dnd.
    HtmlDragSource(dojo.byId("egg"), "topOfStoveOnly");

    new dojo.dnd.HtmlDragSource(dojo.byId("leg"), "allCookingOptions");

    }

    You may wonder, "Why the brackets around destinations in a drop target?" Drag sources belong to one and only one group, but drop targets can belong to many groups. That's why drop targets always have an array of destinations signified by [...], even when there's only one group.

    Constraining The Drag Area

    To make the User Interface more discoverable, you can put a boundary around all the possible drop targets.  This prevents the drag source from leaving the area, even if the mouse is dragged outside. The technique is especially helpful if you have two or more sets of drag and drop areas. For example, if you had a drop area of domestic cities and a drop area of international cities, you could constrain domestic planes (our drag source) to the domestic cities.ÂÂ

    To constrain the drag sources, you use the constrain() method. That requires first constructing the HtmlDragSource object, as we have in previous examples. Then you call constrain with the id of the bounding box. Note this can be any HTML object, and the boundaries of that object act as the constraining box. But
    is a natural choice for a bounding box.ÂÂ

    Extending our kitchen example, the user will not be able to drag the egg or chicken leg outside an imaginary box containing all three destinations.

      function initKitchen(){
         ... // Construct drop sources
    
         // Keep the egg from leaving the boundaries of the object kitchenDiv
         var eggSource = new dojo.dnd.HtmlDragSource(dojo.byId("egg"), "dest");
         eggSource.constrainTo("kitchenDiv");
    
         // Do the same with the chicken leg
         legSource = new dojo.dnd.HtmlDragSource(dojo.byId("leg"), "dest");
         legSource.constrainTo("kitchenDiv");
      }
    
    ...
    <H1>Destinations </H1>
    
    <div id="kitchenDiv">
       <div id="pan" ...
    

    The user cannot drag either the egg or the chicken leg outside of the bounding box, although they can try to drop it (unsuccessfully) between the destinations. Later we'll see how to give more visual cues to the user as to where they can drop.

    List Rearrangement

    Yes, you can use Drag and Drop outside of the kitchen! One popular application is rearranging lists. You can do this by enabling list items as drag sources and the list itself as a drop target. That seems kind of wierd at first - you do not expect drag sources and drop targets to overlap, much less contain one another. Here, it helps to think of dragging and dropping as a shortcut for cut-and-paste.

    In this example you can drag the elements, Jim Hendrix albums, into whatever order you wish.

    <html><head>
    <script type="text/javascript" src="/path/to/dojo.js"></script>
    <script type="text/javascript">
    dojo.require("dojo.dnd.*");
    
       dojo.require("dojo.dnd.event.*");
    
       function initList() {
          // Loop through all li elements of list, and make them drop targets
          var dl = dojo.byId("listToRearrange");
          var lis = dl.getElementsByTagName("li");
          for (var i=0; i<lis.length; i++)
             new dojo.dnd.HtmlDragSource(lis[i], "dest");
    
          new dojo.dnd.HtmlDropTarget(dl,"dest");
       }
    
       dojo.addOnLoad(initList);
    </script>
    </head>
    <body>
    <H1>Jimi Hendrix Albums</H1>
    <p>Arrange in order of your own preference.</p>
    <ul id="listToRearrange">
       <li>Electric Ladyland</li>
       <li>Are You Experienced?</li>
       <li>Axis, Bold as Love</li>
    </ul>
    </body>
    </html>
    

    Once this is done, you can use getElementsByTagName to loop through the elements in their new order, then send them by dojo.io or a page submission.

    LFX

    The dojo.lfx.* module is dojo's animation system. It includes many “canned� effects:

    • fadeIn, fadeShow, fadeOut, fadeHide,
    • wipeIn, wipeOut
    • slideTo
    • explode, implode
    • highlight, unhighlight
    In addition, it has a powerful system for chaining together primitives:

    // wipe two elements out, one after

    // the other, following a 300ms delay
    var anim1 = dojo.lfx.wipeOut(�foo�, 300);

    var anim2 = dojo.lfx.wipeOut(�bar�, 500);

    var composed = dojo.lfx.chain(anim1, anim2);

    composed.play(300);
    // fade out three nodes together, using

    // acceleration
    dojo.lfx.fadeOut(

    [�foo�, “bar�, “baz�],

    300,

    dojo.lfx.easeInOut

    ).play();

    Part 8: "Internationalization and Accessiblity"

    Needs and intro and subsections.

    Accessibility

    Introduction

    It is important that all users can access applications built using Dojo. People with disabilities access the Web in many manners. Some people rely on the keyboard only to navigate and make selections.Others use custom font and color settings, custom style sheets, screen magnification or screen readers to interact with the computer.With the addition of multimedia to the Web, captioning is important for folks with hearing loss. Those with cognitive disabilities may use a variety of assistive technology to adapt the content to a particular learning style.

    In addition, many countries require that Web Content is accessible to all.Section 508 of the Disabilities Act requires the US Government to purchase accessible technology.Australia, Japan, Great Britain and the European Union all have accessibility regulations.Thus, it is important for the Dojo Toolkit to provide a way to build accessible applications.





    Web Accessibility Issues

    Web Accessibility Issues

    The main accessibility concern for the Dojo Toolkit is the widget set.The widgets need to be usable by all users in order to create accessible applications using Dojo.The main issues are with color, device independent interactions and providing semantic information about these user interface (UI) components.

    Many widgets were initially implemented with support only for mouse interactions and need keyboard support added.In order to create high performance, easily styled and visually appealing widgets, the toolkit makes use of CSS to create a desktop-like look and feel.These widgets may not work when CSS is turned off.

    Color/CSS

    There are several issues with color.Some people have vision problems which prevent them from seeing certain colors or may need a high contrast between foreground and background colors. These people may adjust the operating system colors to meet their needs or use the high contrast settings provided by the operating system.In some cases, when a person can not distinguish the colors in an image, he may turn off images in the browser and rely on the description of the image provided via a text alternative. In HTML the text alternative is provided via the alt attribute of the img element.

    In order to create high performance, easily styled and visually appealing widgets, the toolkit makes use of CSS to create a desktop-like look and feel.These widgets may not work in high contrast mode.Selecting high contrast mode in the Windows operating system forces all color and image related CSS information to be turned off in the browser.All positioning information is retained but the colors and background images are turned off.Dojo Widgets that create the visual effects using CSS background-images will not be visible in high contrast mode.In order to accommodate high contrast mode, the widgets need to use real img elements to create the visual look. Images are still displayed when high contrast mode is enabled.However, some users still may not be able to distinguish the image due to the colors used. These users may also disable images in the browser and rely on the text alternative to provide the necessary information.

    More info about High Contrast Mode and Widgets

    Device Independent Interactions

    Not all users are able to interact with the computer using a mouse, thus, device independent interaction is important.This means that, at a minimum, keyboard interactions must be supported.The Dojo widgets must not work solely via the mouse.For HTML links and form controls, the keyboard is automatically supported. But, since Dojo is creating custom widgets via DHTML and scripting of elements other than links and form controls, keyboard event handling must be added.Dojo widgets created using technologies other than HTML such as SVG, must also support the keyboard or provide an alternative interface that works with the keyboard.

    Detailed information about role and state

    Accessibility application programming interfaces (APIs) for desktop graphical user interface (GUI) frameworks define a standard contract between an application component and an assistive technology (AT). The information about the type of component and its current state is provided to the AT via the accessibility programming interface. Examples of accessibility APIs are Java Accessibility API , Microsoft Active Accessibility (MSAA), Apple Accessibility for COCOA, and the Gnome Accessibility Toolkit (ATK). In the browser environment, certain HTML elements have well defined roles and states. Examples include lists, links and form elements. The browser communicates the information about these elements and the current state such as checked, unchecked, readonly, disabled, visited, etc. to assistive technology via the accessibility APIs. Now that user interface components are being created via scripted HTML elements such as div and span, the assistive technology needs additional information about the created component and its behaviors.

    The W3C Web Accessibility Initiative Accessible Rich Internet Applications (WAI-ARIA) Roadmap provides specifications which describe how to provide this additional information to the assistive technology.These specifications define a set of roles and states which can be added to the created DHTML user interface components.With the addition of this information and support by the browser and assistive technology, a user of AT can get detailed information about the user interface components create for the Web.For example, a tree component will be identified as a tree and each tree item, its level in the tree, expanded and collapsed state and number of children is now available to a screen reader user.The current ARIA specifications are supported in Firefox 1.5 and later versions as well as the by recent versions of the Window-Eyes and JAWS screen readers. Support from other Windows browsers and assistive technology vendors are expected in the future.To provide the most comprehensible accessibility solution for Dojo, the ARIA techniques are being applied to the widgets.

    Dojo Accessibility Strategy

    Dojo Accessibility Strategy

    Support High Contrast /Images Off

    NOTE:As of February, 2007 the High contrast mode accessibility strategy has changed. Dojo will NOT be using background images to convey information. Since Dojo will use real img elements there is not a high contrast issue and separate a11y versions of the widgets will not be necessary. This documentation will be updated when the strategy for creating multiple themes is in place. -Becky

    Customizing the Dojo Widgets look and feel is very important, as is performance. The Dojo team uses background images in the creation of the widgets because it allows the look and feel of the widgets to be easily customized by modifying the CSS.In addition, using CSS allows several images to be combined into one file and then the proper subset of the image to displayed using positioning. Thus all of the images for a particular widget element can be retrieved via one HTTP request rather than a separate request for each separate image file.For these reasons, Dojo did not want to require the use of image elements when creating widgets. This presents a problem for users with high contrast mode settings because in this mode, background images are not displayed. The solution is to create an accessible version of each widget which will work in high contrast mode.

    The accessible version of each widget will be derived from the original widget.Then, only the necessary methods need to be overridden and the general functionality of the widget is maintained. Dojo already has a mechanism to load a particular widget class based on the technology in use so this has been extended to accessible widgets as well. When high contrast or images mode is detected, the accessible version of a widget will be loaded if it exists.

    The accessible version of a widget may use HTML img elements to replace the CSS background images used in the widget, or in some cases, it may fall back to the HTML default version of the control.The checkbox widget uses CSS background widgets to create a graphical look and feel for the Dojo checkbox.Since that graphical look and feel and coloring will be lost in high contrast mode, the solution for the checkbox is to fall back to the default HTML input type=checkbox element in high contrast or images off mode.The dojo.widget.checkbox.a11y class is defined within checkbox.js and is a subclass of dojo.widget.checkbox. It overrides only the necessary methods to implement the accessible version of the checkbox.

    The TabContainer widget uses background images to create the close icon on tabs that can be closed.Since there is no HTML alternative for the TabContainer, the accessible version uses img elements in place of the CSS background images.The TabContainer widget uses a TabButton widget to create the tabs.A dojo.widget.a11y.TabButton widget creates the tabs using an img element rather than a background image.The img element will display when the system is in high contrast mode. There is still a possibility that the user may not be able to discern the colors in the close image and will turn images off.With images turned off, the alt attribute of the image element will display and allow the user to properly interpret and interact with the tab.

    The main functionality of the TabContainer is maintained in that module.The section of the TabContainer that needs modification to support high contrast and images off mode, the tabs, have been separated out into a separate widget.Thus the minimal adjustments needed to support high contrast and images off mode accessibility are provided in the helper a11y.TabButton class.

    Support Device Independent Interaction

    One way of providing keyboard support in HTML is to use form and list elements which can accept keyboard focus by default.The user can use the tab key to navigate to these types of elements.The problem is that building sophisticated widgets using these elements in not practical. And, navigating via only the tab key can be very tedious.The solution is to provide full keyboard support within the widgets using additional keystrokes such as the arrow keys to provide more intuitive navigation.

    One of the keys to supporting the keyboard is to allow focus to be set to any element.The tabindex attribute can be used to include additional elements in the tab order and to set programmatic focus to them.This was a feature implemented in Internet Explorer that has been extended to Firefox and Mozilla.The following table outlines the use of the tabindex attribute:

    Tabindex Attribute ValueFocusable via mouse or scripting via element.focus()Tab Navigation
    not presentFollow default behavior of element (only form controls and anchors receive focus)Follows default behavior of element
    zero - tabindex="0"YesIn tab order relative to element's position in document
    positive - tabindex="x" (where x is a positive integer between 1 and 32768)Yestabindex value directly specifies where this element is positioned in the tab order
    negative - tabindex="-1"YesNot in tab order, author must focus it with element.focus() as result of a key press.

    Adding a tabindex of -1 to an element allows the element to receive focus via JavaScript using the element.focus() method.This is used to allow arrow key navigation to elements.Each element that can be navigated to via arrow keys must have a tabindex of -1 to allow it to receive focus.A keydown event handler can determine the next object to receive focus and call that element's focus() method.In addition, the style of the element may need to be updated in order to show the focus as brower's are inconsistent in displaying focus for items that receive focus programmatically.

    In order to assist with key event handling, an onkey event has been added to Dojo to normalize key events. The appropriate key event, either onkeydown or onkeypress, will be used depending upon the browser.The key codes have been normalized as well. See dojo.event.browser class in dojo.event.browser.js.

    Device Independent behavior means more than just supporting the keyboard. Where ever possible use the most generic event handler possible. For example, consider a widget where the down arrow key selects an element in the widget.The selection needs to be distinguished with a specific style.Rather than modifying the style of the element when processing the down arrow key event , focus the item from the down arrow key event handler and change the style via a focus event handler. This way, if focus is set from a means other than the keyboard such as a voice input system, the styling is properly set and does not depend solely on keyboard actions.

    When implementing keyboard navigation, the ideal solution is to mimic the behavior of the operating system..For example, the right and left arrow keys are used to expand and collapse nodes in a Windows tree control and the up and down arrow keys move between nodes in the control.Unfortunately it is not always possible to mimic the operating system or browser behavior because the widgets may not be able to capture the necessary keys. A group of industry representatives are working to create a style guide to describe the navigation and behaviors of Web widgets. When completed, this Style Guide will be provided to open source and Dojo plans to implement the recommendations.The Style Guide will attempt to normalize the differences between operating systems and provide a generalized solution for Web components

    Implement ARIA Specification

    ARIA techniques allow creating sophisticated UI components using scripting which can be identified to assistive technology. In the future, user agents can also make use of this information to provide additional visual clues about components as well. For example, client side validation of a text entry component that was marked using the ARIA invalid attribute could be visually identified by the browser rather than requiring the developer to provide a specific style or text identification on the component.



    The ARIA information will be added into the base widget modules rather than the accessibility extension to insure that the ARIA information is updated whenever behavior changes are made to the widget. Methods have been added to Dojo to enable setting the ARIA information. The roles and states for a widget can be set via the widget template or within the widget scripting code. The details of these methods are discussed later in this document. In addition to providing the roles and states for each component, there are some architectural considerations as well. For components that represent a hierarchy, such as a tree or menu, it is important to identify parent and child relationships. For items where position or count are important it may be necessary to hierarchically group elements or identify a set of related elements as a group. In some cases there are specific ARIA roles for grouping items such as treegroup for tree items within the same level. If no specific grouping role is provided the generic group role can be used.

    Dojo Accessibility Resources

    Dojo Accessibility Resources

    a11y.js

    NOTE: As of February, 2007 the High contrast mode accessibility strategy has changed. Dojo will NOT be using background images to convey information. Since Dojo will use real img elements there is not a high contrast issue and high contrast detection may not be necessary. These a11y apis are very likely to change for the 0.9 release. This documentation will be updated with the final apis are determined and the changes to a11y.js have been completed. -Becky

    The dojo.a11y module contains methods to detect high contrast mode and to instruct Dojo to load the accessible versions of widgets (when an accessible version exists).



    dojo.a11y.accessible is a private variable that stores the current accessibility state. The value is initialized to null when the a11y module is instantiated. This is used to prevent running the accessibility test more than once during widget instantiation or to force a particular mode. A value of true indicates that accessible versions of the widgets will be loaded. A value of false indicates that the accessible version of widgets will not be loaded. The value of this variable is normally set after running the check for high contrast mode or images off but can also be forced via the dojo.a11y.setAccessible() api.



    dojo.a11y.doAccessibleCheck determines whether or not the accessibility check is performed. The default value is true – the check for high contrast mode or images off will be performed for each page load. The dojo.a11y.setCheckAccessible() method is provided to set this variable to false and prevent the accessibility check from being performed. The ability to turn off the check is provided for applications that may provide a mechanism for the user to request the accessible mode for widgets.



    dojo.a11y.testAccessible always performs the check for accessibility mode. The check is currently only made for the Internet Explorer and Mozilla browsers as others do not yet support high contrast mode or do not support it in a detectable manner. Additional work is required for operating systems other than Windows. The test is performed by creating a div and assigning it a background-image. A very small image is used for the background image so that the download time is short and performance is not greatly affected. In both IE and Firefox, checking the current / computed style of this div after it has been added to the Document Object Model (DOM) will reveal whether or not the background image is visible. Both browsers will return "none" as the computed value for the background-image when high contrast mode is turned on at the operating system level. In Firefox the value returned is "url(invalid-url:)" when images are turned on. Currently images off mode is not detected in IE but further research is being conducted to find a method for determining images off in IE that does not require waiting for the image's onload or onerror events to fire.



    dojo.a11y.checkAccessible will perform the accessibility check if the check has not already been performed and testing for accessibility has not been turned off via dojo.a11y.setCheckAccessible(false).



    dojo.a11y.setCheckAccessible provides a mechanism to turn off checking for high contrast mode or images off. A value of true turns on accessibility checking and a value of false turns checking off. Generally an application should not turn off checking for the need to display accessible versions of the widgets as a developer can never be certain that the audience does not contain people with disabilities who rely on accessibility accommodations. However, an application may provide another mechanism for the user to request the accessible versions of the widgets and set accessible mode via the dojo.a11y.setAccessible method. In this case the accessibility check would not be required.



    dojo.a11y.setAccessibleMode is used to perform the check for accessibility if it has not yet been performed and based on the results set Dojo to load the accessible version of the widgets if necessary. This method is called from the dojo.widget.buildWidgetFromParseTree method. For performance reasons this method checks whether or not the accessibility mode has been set before making any additional calls to avoid performing the accessibility test for each widget.

    dojo.a11y.setAccessible is used to force accessibiliy mode on or off and prevent a check for accessibility. It sets the value of dojo.a11y.accessible variable to the value of the boolean parameter passed into the setAccessible method. Passing in true will force accessible mode on and a value of false will force it off.

    dom.widget.wai

    dom.widget.wainames encapsulates the type of ARIA information being provided, roles and states, which are represented via waiRole and waiState respectively. The wai prefix is used to represent the W3C Web Accessibility Initiative which is hosting the ARIA specification.



    The dom.widget.wai module is provided to normalize setting the roles and states. The ARIA techniques are designed to be used with XHTML via namespaces. Since a content-type of application/xhtml+xml is required to fully support namespaces an alternate solution is needed for the most commonly supported content-type of text/html. The roles and states can be manipulated using the DOM namespace apis: getAttributeNS, setAttributeNS, and removeAttributeNS. In browsers which do not support the namespace apis, the generic attribute apis, getAttribute, setAttribute, removeAttribute, are used and namespaces are simulated.



    The dom.widget.wai module provides the necessary mapping of namespace information and attribute apis for each of the dom.widget.wainames. It contains two submodules, waiRole and waiState, each with the following variables.

    • name represents the ARIA type being set, waiRole or waiState.
    • namespace contains the actual namespace for the role or state information.
    • alias is the pseudo namespace to be used when true namespaces are not supported.
    • prefix will be added to the beginning of the value being set.



    The dom.widget.wai methods getAttr(), setAttr(), and removeAttr() are wrappers to the appropriate attribute apis for the browser in use. These apis are called with the following parameters:

    • node – the DOM node on which to make the appropriate attribute api call
    • ns – this selects the appropriate dom.widget.wai module to use; waiRole or waiState.
    • attr– the attribute name. This will be "role" when setting a role value and it will be one of the possible ARIA state attributes when specifying a state.
    • value – the actual value to be set; either an ARIA role value (tree, treeitem, checkbox, tab, etc) or the value for the previously specified state name (true, false, mixed, etc).



    The following will set a role of treeitem onto a DOM node:

    dijit.util.wai.setAttr(nodeObj, "waiRole", "role", "treeitem");

    This example sets the state of the treeitem to expanded:

    dijit.util.wai.setAttr(nodeObj, "waiState", "expanded","true");



    The role and state can also be set via the widget template using the waiRole or waiState prefix. Setting the role in the template is that same as setting it via scripting – the dijit.util.wai.setAttr() method will be called during widget instantiation. Simply add the waiRole="actualrole" or waiState="state-value" parameters into the template markup for the element. The element will be passed as the nodeObj into the dijit.util.wai.setaAttr() method. The state is specified as a state name and value pair, the state is separated from the value using the hyphen character (-): state-value. The state becomes the attribute name when dijit.util.wai.setAttr() is called. This mechanism is useful when templates are used to create the objects requiring the role value or when the state is known at creation time.



    Here is an example of setting the role in the tab container template. The containerNode is given the "tabpanel" role.

    <div id="${this.widgetId}" class="dojoTabContainer">
          <div dojoAttachPoint="tablistNode"></div>
          <div class="dojoTabPaneWrapper" dojoAttachPoint="containerNode" 
    dojoAttachEvent="onKey" waiRole="tabpanel"></div>
    </div>



    The role or state can also be specified via variables. This example shows an excerpt from the slider template which sets the tabindex, role and state for the slider component. Note that currently only one waiRole and waiState value is allowed per element (trac #2163).

    <div class="sliderMain" dojoAttachPoint="focusNode" waiRole="slider"  
    waiState="valuenow-${this.initialValue}"  dojoAttachEvent="onmousedown:_setFocus; 
    onkey:_handleKeyEvents; onkeyup:_buttonReleased; onmouseup:_buttonReleased; 
    onmousewheel:_mouseWheeled;"

    tabindex="0" cols=3 cellpadding=0 cellspacing=0 style="">



    Creating Accessible Widgets

    Creating Accessible Widgets

    Add device independent interaction

    This is a fairly high level description of how to support device independent interaction. An understanding of general Dojo widget development and event handling within scripting and the Dojo Toolkit is assumed and not described.

    Device Independent behavior means more than just supporting the keyboard. Where ever possible use the most generic event handler available. For example, consider a widget where the down arrow key selects an element in the widget. The selection needs to be distinguished with a specific style. Rather than modifying the style of the element when processing the down arrow key event, focus the item from the down arrow key event handler and change the style via a focus event handler. This way, if focus is set from a means other than the keyboard such as a voice input system, the styling is properly set and does not depend solely on keyboard actions.

    Determining Key Behavior

    When implementing keyboard navigation, the ideal solution is to mimic the behavior of the operating system. For example, the right and left arrow keys are used to expand and collapse nodes in a Windows tree control and the up and down arrow keys move between nodes in the control. Unfortunately it is not always possible to mimic the operating system or browser behavior because the widgets may not be able to capture the necessary keys. A group of industry representatives are working to create a style guide to describe the navigation and behaviors of Web widgets. When completed, this Style Guide will be provided to open source and Dojo plans to implement the recommendations. The Style Guide will attempt to normalize the differences between operating systems and provide a generalized solution for Web components

    Within all widgets interaction with both the keyboard and the mouse is important. Users may switch between using the mouse and using the keyboard at any time. A widget author can not assume only keyboard or only mouse interaction. Thus, the widget component will generally need to store information about the current item with focus.

    This can also be useful when the keyboard event handler is placed on an owning object in the component hierarchy rather than the actual element generating the event, for example on the table element rather than on each td element. Even though the event handler provides information on exactly what element generated the event, it is often necessary or easier to use the stored point of reference

    In order to support both the mouse and the keyboard a component will usually have both onclick and onkey events. The onclick handler must include steps to update the point of regard so that any keyboard actions after the onclick will continue to work. In addition, the point of regard is often needed in order to update the style on the element losing focus before updating the new item irregardless or whether the mouse or the keyboard generated the event that results in a focus change.

    Trapping Key Events

    When implementing keyboard navigation, first determine where in the hierarchy to trap the key events. It is generally best to trap the key events at as high a level as possible and use the event object to determine that actual source of the event and perform the necessary action. This method prevents having to add a key handler to each individual element thus conserving the amount of markup to be generated. However, there may be cases where the event needs to be trapped at the level of each individual element. The actual source of the event is needed in order to determine how to process the keystroke received.

    Once the component handles an event, it will usually stop that event from being propagated to other elements. For example, if the down arrow key is captured and moves focus to the next item in a tree control, the event should not propagate up to the browser where it might be interpreted as a command to scroll the page. Use the dojo.event.browser.stopEvent(event) method to stop the event.

    In order to assist with key event handling, an onkey event has been added to Dojo to normalize key events. The appropriate key event, either onkeydown or onkeypress, will be used depending upon the browser. The key codes have been normalized as well. See dojo.event.browser class in dojo.event.browser.js.

    Add the dojo onKey event into the widget template or via scripting using one of the dojo event connection apis.

    Example using dojo.event.connect to connect the object represented by the node variable to the onKey handler function in the current object:

    dojo.event.connect(node, "onkey", this, "onKey");

    Example from tab container template to demonstrate adding the onkey handler and waiRole within the template:

    <div id="${this.widgetId}" class="dojoTabContainer">
        <div dojoAttachPoint="tablistNode"></div>
        <div class="dojoTabPaneWrapper" dojoAttachPoint="containerNode"  
    dojoAttachEvent="onKey" waiRole="tabpanel"></div>
    </div>
    
    Tabindex and Focus

    When navigating via the keyboard is it essential that the element that is navigated to receives focus. The focus should NOT be simulated via CSS - call the focus() method on the element. Styling can be used to enhance the visual focus or selection but should not replace actually setting focus on an element. A screen reader will only speak information about the element when it receives focus. Screen magnifiers rely on focus to move the zoomed viewport on the screen.

    Use the tabindex value to provide direct or programmatic keyboard focus to an element. The tabindex attribute can be used to include additional elements in the tab order and to set programmatic focus to them.This was a feature implemented in Internet Explorer that has been extended to Firefox and Mozilla.The following table outlines the use of the tabindex attribute:

    Tabindex Attribute ValueFocusable via mouse or scripting via element.focus()Tab Navigation
    not presentFollow default behavior of element (only form controls and anchors receive focus)Follows default behavior of element
    zero - tabindex="0"YesIn tab order relative to element's position in document
    positive - tabindex="x" (where x is a positive integer between 1 and 32768)Yestabindex value directly specifies where this element is positioned in the tab order
    negative - tabindex="-1"YesNot in tab order, author must focus it with element.focus() as result of a key press.

    When adding support for keyboard navigation, consider the widget as a component. The tab key can be used to navigate from component to component on a page and then the arrow and other keys should be used to navigate within the component. Only one element in a given component should have a tabindex equal to zero at any one time. This allows the user to navigate into and set focus within in the component using the tab key. Then, trap the onkey events and use the arrow keys to navigate within the elements of the component. All of the elements within the component which can receive focus must have a tabindex equal to -1. When an element is programmatically given focus, its tabindex value is changed from -1 to 0 and the tabindex of the previous element with focus will be changed from 0 to -1. This will insure that only one element within the component is in the tab order of the page and that the element with tabindex = 0 is the most recently focused element in the component. For example, when creating a tree control, each tree item is represented by an element. The first tree item in the control will be given a tabindex of 0. All of the other elements which represent tree items and can receive focus programmatically will have a tabindex value of -1. An onkey handler will trap the keyboard events for the tree control. When a tree item element is given focus via element.focus(), the element’s tabindex will be changed from -1 to 0. and it will be put into the tab order. Now if the user moves focus out of the tree control (either via a mouse click or by tabbing to the next component on the page), when the user sets focus back into the tree control using the tab key, the last focused tree item, which was given a tabindex of 0, will receive focus.

    Add ARIA information

    The Accessible Rich Internet Applications Roadmap is being developed by the W3C Web Accessibility Initiative (WAI) Protocols and Formats working group. The group is creating specifications for role and state information which can be added to markup to provide semantic information about user interface components.The browsers will translate this role and state information into the accessibility api for the platform in use.Currently Firefox 1.5 and later supports this additional semantic information on the Windows platform where it converts the information into the Microsoft Active Accessibility (MSAA) api.When recent versions of the Window-Eyes and JAWS screen readers are used with Firefox, this additional information is spoken to the user.

    Some HTML elements such as links and form elements have well defined roles and behaviors. Interactive controls created from generic elements can now also be identified with roles and states.When an element receives focus the role and state information provided by the developer will be made available to assistive technologies. For example, as a screen reader traverses through a Dojo Tree control using the arrow keys, as each tree item receives focus the title of the tree item will be spoken as well as its expanded or collapsed state if it has children. Likewise, a Dojo checkbox created using <div>and <pan> tags can be identified as a checkbox and its checked or unchecked state can be reported. When creating a new Dojo widget, the role of the widget must be identifed and the state of the widget must be set and updated as it changes.

    As of January, 2007, the ARIA specifications are still under development; however a significant portion of the specification has been implemented in Firefox 1.5 and 2.0.

    The public drafts of the specifications can be found at http://www.w3.org/wai/pf.

    Accessible DHML in the Mozilla Developer Center provides additional information about using the ARIA specifications and includes a table of roles and states supported in current versions of Firefox.

    Assigning Roles

    Use the tabindex to provide keyboard focus or to allow programmatic focus to an object. By adding a tabindex to an element, the element will now be included in the accessibility hierarchy of the Firefox browser.Information about elements in the accessibility hierarchy will be provided to assistive technologies. If you use a tabindex attribute on a DIV, SPAN, IMG or any element which has no natural role of its own then you need to provide a role. Any element that can receive focus must have a role, either implied, such as input elements and anchors, or specified via a role attribute. For things with an implied role such as input fields and anchors, you can use tabindex=-1 to remove them from the tab order. You can also specify a different role for elements which already have an implied role.

    The role and states are added to Dojo widgets within the widget template or via the dojo.widget.wai APIs as described in the Dojo Accessibility Strategy section.

    Providing Hierarchical Information

    In order for Firefox to determine the correct parent child relationships between objects, and to communicate this via an accessibility API to assistive technologies, it is best to create components in a hierarchical fashion. For example, when creating a menubar it is best to have the components that make up the menus and menuitems of the menubar be children of the menubar. Likewise, menuitems should be children of the owning menus. This hierarchy allows Firefox to provide menu information to the assistive technologies, and for a screen reader to speak more information about the menu such as, menu open, File, item 1 of X when the user opens a menu. Here is a simple pseudo code example demonstrating a hierarchical layout of elements for a menu control. This exmaple only shows the addition of role attributes and does not represent a complete menu widget.(Note: Attributes are not quoted in pseudo-code examples to help improve the readability):

    <div role=menubar>
     <div role=menuitem>A</div>
      <div role=menu><divole=menuitem>A.1</div>
        <div role=menuitem<A.2</div>
      </div>
      <div role=menuitem>B</div>
      <div role=menu>           
        <div role=menuitem>B.2</div>                
        <div role=menuitem>B.2.1</div>  </div>
    </div>
    

    It may not always be practical to create items via HTML in a hierarchical fashion. In that case the group role can help to associate the items properly. This is illustrated in the following simple pseudo code example of a tree hierarchy.

    <div role=tree >
      <div role=treeitem>Top>/div>
      <div role=group>
         <div role=treeitem>1>div>
         <div role=group>
           <div role=treeitem>1.1 </div>
           <div role=treeitem<1.2>/div>
           <div role=treeitem>1.3</div>
           <div role=group>
             <div role=treeitem>1.3.1<div>
             <div role=treeitem>1.3.2>/div>
             <div role=treeitem>1.3.3</div>
             <div role=treeitem>1.3.4</div>
           </div>
           <div role=treeitem>1.4</div>
         </div>
         <div role=treeitem>2>/div>
         <div role=treeitem>3</div>
         <div role=group>
           <div role=treeitem>3.1</div>
           <div role=treeitem>3.2</div>
         >/div>
      </div>
    </div>
    

    The tree items at the same level in the hierarchy are grouped together within a DIV element identified with role=group. With this organization, the assistive technologies can be provided with the information about what level and item number a particular treeitem represents. For example, in the above tree example, with focus on item 1.3.3 a screen reader might speak, "one dot three dot three item three of four, level four" or something similar.

    Other items included in the hierarchy may not be essential to the component. These items can be marked with a role of presentation to eliminate them from consideration when determining information about the component.

    Using the Presentation Role

    While it is preferable to use CSS for layout, tables are still used to quickly and easily arrange elements on a page. This is especially true of existing widgets which were originally created to work in older browsers. Putting information in tables can easily confuse the hierarchy of the component. If tables must be used, they can be marked with a role of presentation to eliminate them from the hierarchy. Here is a pseudo code example where the presentation role was used on tables within a tree component:

    <div role=tree>
      <table role=presentation>           
        <tr><td><div role=treeitem>Top</div>&;lt;/tdgt;</tr>
      </table>
      <div role=group>
        <table role=presentation>
          <tr><td><span role=treeitem>1</span></td></tr>
        </table>
        <div role=group>
          <table role=presentation>
            <tr><td><span role=treeitem>1.1</span&;gt;</td></tr>
          </table>
          <table role=presentation>
            <tr><td><span role=treeitem>1.2</span></td></tr>
          </table>
        </div>
        <table role=presentation>
          <tr><td><span role=treeitem>2</span></td></tr>
        </table>
      </div>
    </div>
    

    Since the table is only used for layout it is identified with a role of presentation to remove if from the accessibility hierarchy so that information about the table is not provided to assistive technology.

    Other elements may need to be removed from the accessibility hierarchy as well. For example, when creating a DHTML checkbox, an image may be contained within a span element that is marked with a role of checkbox and an appropriate state of checked equals true or false. The image which represents the checkbox is contained within the span and should not contain any alt text since the role and state are managed by the surrounding span. Images are considered important elements and are normally included with the accessibility hierarchy of the browser. In order to ignore this image in the accessibility hierarchy, it is marked with a role of presentation:





    Assigning States

    In addition to identifying the role of a widget, the state of the widget must be identified and updated. The initial state can be set within the widget template or via scripting when the widget is created. As the state changes during user interaction with the widget, the state must be updated using the dijit.util.wai apis:

    dijit.util.wai.setAttr(/*DomNode*/node, /*String*/ ns, /*String*/ attr, 
    /*String|Boolean*/value)
    dijit.util.wai.getAttr(/*DomNode*/ node, /*String*/ ns, /*String|Boolean*/ attr)
    dijit.util.wai.removeAttr(/*DomNode*/ node, /*String*/ ns, /*String|Boolean*/ attr)

    The ns value passed into these function is either "waiState" or "waiRole". The dijit.util.wai functions above are wrappers to the DOM apis to set, get and remove attributes. In browsers where namespaces are supported the setAttributeNS, getAttributeNS, and removeAttributeNS, apis are called. In other browsers the setAttribute, getAttribute and removeAttribute apis are called and the namespace is simulated. The namespace information is stored in the dijit.util.wai class.



    It is important to update the state information as it changes so assistive technology users can be made aware of the change. For example, when a treeitem is expanded, the state for the element that has been assigned role=treeitem, must be set to expanded=true. Likewise, when a treeitem is collapsed, the state for the element with the role=treeitem must be updated to expanded=false. Be aware that some of the boolean states imply more than just a dual state. For the state attributes checked, selected and expanded a value of false indicates that the widget is capable of being checked, selected or expanded while no attribute indicates that the element is not capable of that state. For example, a tree node with children will have either a state of expanded=true or expanded=false depending upon whether the child nodes are visible or not. An end node, with no children will have no expanded state value set.



    Generally only items which have a role can have a state value. The role may be explicitly set by the author such as a treeitem or may be implicitly defined such as a form element or link. Items which have been added into the accessibility hierarchy via a tabindex attribute may also have states such as desribedby or labelled by.

    Create a11y subclass to support high contrast / images off mode

    NOTE:As of February, 2007 the High contrast mode accessibility strategy has changed. Dojo will NOT be using background images to convey information. Since Dojo will use real img elements there is not a high contrast issue and separate a11y versions of the widgets will not be necessary. For now, when creating new widgets do NOT use CSS backgound images to convey information - use real img elements. This documentation will be updated when the strategy for creating multiple themes is in place. -Becky

    All widgets must be visible and work when the operating system is set to high contrast mode. In this mode, colors and background images set via CSS are turned off. The CSS positioning attributes are still used but any color information is overridden by the colors selected for high contrast mode. The most common high contrast modes are black on white or white on black but any color combination can be set by the user. In addition to high contrast mode, the widgets should generally be usable with the display of images turned off in the browser. When images are turned off, the alt attribute for any images in the widget should provide the same information. There may be cases, such as an analog clock, where the alt attribute information can not substitute for the actual image. In these cases there should be an alternative method of obtaining the same information. In the case of an analog clock, a text display of the time would be appropriate, since it conveys that same information, the current time.

    To avoid problems with high contrast mode it is best to create HTML widgets using the img element rather than relying on CSS background images. Since the use of img elements can have a negative affect on performance, many developers prefer to use background images even though they do not display in high contrast mode. In this case, an accessible version of the widget must be created that will work in high contrast mode.

    The widget author must create an additional widget that derives from the original widget to support accessibility. This widget will be named with the a11y prefix. If the original widget name is dojo.widget.MyCoolWidget, the accessible version will be named, dojo.widget.a11y.MyCoolWidget. When using namespaces other than dojo, the same naming convention must be followed of appending a11y to the actual widget name. For example, the accessible version of mynamespace.MyCoolWidget becomes mynamespace.a11y.MyCoolWidget. The preferred location for this new class is within the same file as the original widget. This will make it easier to keep the two files in sync. Although, the reason for extending the original widget class to create the accessible version is to reduce the need to make changes in two places. The majority of the behavior and logic for the widget should remain in the original class. All of the keyboard support and role and state information belong in the original widget. The only modifications for the accessible version should be to support high contrast and images off mode.

    The appropriate version of the widget will be loaded and used based on a check for accessibility that is performed in dojo.widget.buildWidgetFromParseTree():

    // test for accessibility mode

    dojo.a11y.setAccessibleMode();

    If a check has not already been performed, dojo.a11y.setAccessibleMode will test to determine if high contrast mode is set or images are turned off in the browser. This test is only performed once and the results cached to improve performance. When images are turned off or high contrast mode is enabled, the dojo.a11y.accessible variable is set to true. In addition, the a11y prefix is added to the beginning of the dojo.render.html.prefixes array. This will cause dojo to load the a11y version of a widget if it exists rather than the default version. This is the same mechanism that is used to load an svg version of a widget if the browser supports svg and an svg version of the widget exists.

    If the Dojo widget has an HTML equivalent, such button or checkbox, the accessible version can use the basic HTML type. This strategy will likely involving creating a new template for the accessible version of the widget. The new template name should include "a11" in the name to distinguish it from the standard template. For example the templates for the checkbox widget are named Checkbox.html and CheckboxA11y.html. The accessible version of the widget will refer to the accessible template via the templatePath variable.

    If there is not an equivalent HTML element, the accessible version of the widget can replace the use of CSS background images with img elements. When using image elements the look and feel of the widget can not easily be changed since changes to the image source are likely to occur within the scripting code and can no longer be changed via style sheets. Since the purpose of the accessible widget is to support high contrast mode and high contrast mode does not support a color theme, the image set used is not critical. Certainly, there are some high contrast uses who may not be able to interpret the images due to the colors used within the images. But, there is no universal color scheme that can be used in the images that will suit all users. Users which can not interpret the images due to color or size will turn the images off and use the alt text. Thus, it is essential that alternative text is provided for all images using the alt attribute of the img elements.

    Testing Widgets for Accessibility

    Currently the Windows operating system provides the most resources and functionality for testing for accessibility in more than one browser.Also, the majority of assistive technologies run under the Windows operating system.Test with both Firefox and Internet Explorer on at least the Windows operating system to assure at least a minimal level of accessibility.

    Test for full keyboard support

    All widgets must be tested for use with the keyboard only.The most stringent way to test for keyboard support is to remove the mouse from the system and interact with the widget. This assures that only the keyboard can be used for navigation and interaction. Test that all functionality of the widget can be accomplished using the keyboard only.The functionality does not necessarily have to be performed in exactly the same manner as with the mouse. For example, it is preferred that a slider can be adjusted by dragging with the mouse or focusing the slider and adjusting the value using the arrow keys or plus and minus characters on the keyboard. But if the value and position of a slider can be adjusted by entering a new value in an associated text field, the slider is keyboard accessible.Drag and drop operations are another example of behavior which may need to be supported via an alternative mechanism such as a menu bar or context menu.

    After determining that the widget is accessible using only the keyboard, test using both the keyboard and the mouse.There is no guarantee that a user will interact only using the keyboard or only using the mouse.Make certain that both types of interaction can be used within the widget and that the focus and styles are updated appropriately.

    Since not all browsers support adding the tabindex to elements to make them focusable and thus keyboard accessible, it may not be possible to provide full keyboard support in all instances.As of January 2007, Opera and Safari do not support adding tabindex to additional elements.

    Resources for Navigating via the keyboard

    Test for focus and role and state

    It is important that when an element is clicked on with the mouse or navigated to via the keyboard that the element receives actual focus.Focus must not be simulated via styles since assistive technology relies on the focus event to inform the user about the element and the role and state.Focus can be visually tested since browsers will indicate focus, however the focus border may be difficult to see or have been modified with a different style.

    Testing using Microsoft Inspect Objects

    The Microsoft Inspect Objects application from the Microsoft Active Accessibility SDK can be used on Windows to test for focus and also to verify the correct ARIA role and state has been set on the element. Test using Firefox version 1.5 or later since Firefox has the most comprehensive support for the ARIA role and state information.

    Download Inspect32 from Active Accessibility 2.0 SDK Tools.Basic information on using the tool is provided at Using Inspect Objects.

    Run Inspect Objects and test the widget for focus and role and state information. Turn on Highlight Focus tracking via the Inspect Objects Options menu. This mode will display a yellow rectangle around the currently focused element.Use this rectangle to verify that the expected element is actually receiving focus. The Show Information Tooltip option may also be helpful.As you set focus to an item, Inspect Objects will display information about the role and state of the object.You can verify that the object receives focus and that the role and state information is as expected.

    Figure 1 is a picture of the Dojo checkbox with Inspect Objects running and the Show Information Tooltip and Turn On Highlight Focus options set.With focus on a checked Dojo checkbox, note the focus rectangle and role and state information displayed within the tooltip. The role is "checkbox" and the state is "focused, checked, marqueed, focusable". This verifies that the role of checkbox has been properly set in the Dojo checkbox code. The state information verifies that the element is focused and the checked state is set. With focus on an unchecked checkbox the state would display, "focused, marqueed, focusable", since the checked state is not set. Note that when the state of an element changes, focus must be removed from the element and then returned in order for Inspect Objects to update the information. Or, rather than change focus use the Inspect Objects Action Refresh command to update the information about the currently focused object.

    view of checkbox via MS Inspect Objects described above

    Figure 1

    Testing using a screen reader

    For best results, testing with a screen reader is recommended. This section provides a brief introduction to using screen readers. Currently Window-Eyes 5.5 and JAWS 7.10 or later versions of both running with Firefox 1.5 or later will recognize ARIA role and state information. Unfortunately it takes some time and experience to become proficient using a screen reader and these programs are expensive. There are demo versions available which will run for a limited amount of time (usually 30 minutes) before a reboot of the system is required). But, note that often the demo versions are not to be used for commercial purposes.

    When testing with a screen reader be careful not to use the mouse when navigating through the widget. Often a tester will miss problems with keyboard navigation or focus by using the mouse to initiate or force interaction within a widget. Not all screen reader users are blind – some may have low vision or use the screen reader to assist with cognitive disabilities, but, in order to catch all problems it is important to interact with the widget in the same manner as someone with no vision.

    Screen readers work by storing the contents of the entire page into a virtual buffer. The user can move through this buffer and have the contents spoken by the screen reader. There are many mechanisms to move through the virtual buffer, by character, word, line, or from object to object. Both JAWS and Window-Eyes have different terminology for this buffer.Window-Eyes refers to it as "browse mode" and JAWS as "forms mode&quo; or via the navigation mode, "virtual PC cursor on/off". When interacting with a Web page, the screen reader can no longer rely on the virtual buffer. In Window-Eyes the user must turn browse mode off to interact with form elements and interactive widgets. In JAWS the user must enter forms mode on or virtual PC cursor mode off tointeract with form controls and interactive widgets.

    When testing fully keyboard accessible Dojo widgets the screen readers must be in the "interactive mode" to announce the role and state of an element as the element receives focus. In Window-Eyes this is "browse mode off" which is toggled using the ctrl-shift-a keys.< In JAWS this is "forms mode on" which is turned on by pressing enter on a focusable element and is turned off by pressing the numpad plus key. You can also turn off the virtual PC cursor in JAWS. This setting is toggled via the ins–z key combination.

    If the role and state information have been applied correctly the screen reader will hear information about the role and stated of widget elements as well as information about child objects.Figure 2 is a simple tree control with three main nodes with titles Node1 , Node 2, and Node 3. Node 1 has no children. Node 2 is expanded and has two childe nodes titled Node 2.1 and Node 2.2. Node 3 has children but is not expanded. Here is a brief example of what is spoken by Window-Eyes with focus on the Node 2 tree item within the tree control displayed in figure 2, "Node 2 expanded two items, 2 of 3"



    tree control with three top level nodes

    Figure 2

    The user knows which node currently has focus, Node 2; that the state of the node is expanded, it has two children and that Node 2 is the second node in a group of three nodes at the same level in the tree hierarchy. This type of information is critical for screen reader users to interact and navigate with complex widgets. The title of the Node is spoken when it receives focus. Because the tree control developer has properly set the state of Node 2 to expanded=true, that information is spoken by the screen reader. All of the nodes in the tree control have been properly organized into hierarchies and identified using the role="group" so the browser is able to provide the screen reader with information about the number of children and the position of the current node within its siblings.

    When testing widgets with a screen reader interact with each element and verify that the proper role and state and grouping information is spoken by the screen reader and all functionality is accessible via only the keyboard. In addition, make certain that elements which do not provide information to the screen reader user are marked with a role="presentation" so they are ignored. In Figure 2, each tree item node which has children is preceded by an image of a plus or minus to indicate the expanded state of the item. This plus or minus image can not receive focus and it does not need focus since the keyboard user can expand and collapse the tree item using the arrow keys and the screen reader will speak the expanded or collapsed information based on the state value.

    See the videos linked from Adventures in Accessibility - What is a screen reader? for more in depth information about screen readers.

    Test for high contrast mode support

    Windows comes configured with default high contrast mode settings. Turn on high contrast mode in Windows XP via the Accessibility Options dialog available from the Control Panel. From the Diplay panel check the high contrast checkbox. Press the settings button to modify the display colors and font sizes. Checking the Use shortcut checkbox from the settings dialog allows toggling high contrast mode on and off using the shift-alt-printscreen key combination. Press OK to confirm the settings and then OK again to close the dialog and turn on high contrast mode. Note, putting your system in high contrast mode will likely rearrange the desktop icons on the system due to the changes in font size.

    After turning on high contrast mode, test the widget in Firefox or Internet Explorer. If the widget test page was already loaded in the browser, you may need to refresh the page for the high contrast mode to take affect. Verify that all components within the widget are visible. Any components or visual effects which are improperly created via background images or color will no longer be visible in high contrast mode. Verify that the user is able to determine where current focus is within the widget. If focus is not visually evident the widget developer may have used a background color change to indicate focus (or the browser provided focus rectangle may be hard to distinguish). Fix this by using a different styled border or other mechanism to indicate focus if the browser differentiation is not sufficient. When testing in high contrast mode it is fairly evident where the problems occur since any visual effects which rely on color or images will no longer be visible.

    Test for usable operation with images off

    In addition to testing for support of high contrast mode, the widgets should also work with images are turned off in the browser. Test in Firefox by turning images off via the Tools Options dialog. Note that only images loaded via http will no longer display. Changing this settig in Firefox does not prevent loading of images from the file system. Test in Internet Explorer by turning off images in the Advanced tab of the Tools Internet Options dialog.

    With images turned off the widgets should still be usable. Since no images are loaded, bythe browser the alt attribute of the image elements will be displayed. Verify that the alt attribute provides sufficient information for the user to interact with the widget.

    Internationalization

    Internationalization (i18n)




    Internationalization is the process of making an application available in different locales. A locale is the combination of regional and language data which includes things such as date and time format, text formatting, and providing localized specifc content in a specific encoding. Localization (also referred to as l10n) is the process of making application functionality available in one or more locales. This section will cover the basic concepts of internationalization and show how these features are implemented in Dojo. This section willl cover internationalization and locales, localized Strings, and Date and Time Formatting, Bi-directional text and encoding.



    Locales

    Localization is driven by a locale, a short string, defined by the host environment, which conforms to RFC 3066 used in the HTML specification. It consists of short identifiers, typically two characters long which are case-insensitive. Note that Dojo uses dash separators, not underscores like Java (e.g. "en-us", not "en_US"). Typically country codes are used in the optional second identifier, and additional variants may be specified. For example, Japanese is "ja"; Japanese in Japan is "ja-jp". Notice that the lower case is intentional -- while Dojo will often convert all locales to lowercase to normalize them, it is the lowercase that must be used when defining your resources.

    The locale in the browser is typically set during install and is not easily configurable. Note that this is not the same locale in the preferences dialog which can be used to accompany HTTP requests; there is unfortunately no way to access that locale from the client without a server round-trip.

    The dojo.locale property is used to specify the locale of the host environment.



    The locale Dojo uses on a page may be overridden by setting djConfig.locale. This may be done to accomodate applications with a known user profile or server pages which do manual assembly and assume a certain locale. You may also set djConfig.extraLocale to load localizations in addition to your own, in case you want to specify a particular translation or have multiple languages appear on your page.

    Localizing Strings

    dojo.requireLocalization() / dojo.i18n.getLocalization()

    The DojoPackageSystem was leveraged to create a mechanism to load locale-specific resources. Resources are associated with Javascript packages and structured as a Javascript object (like JSON notation) with identifiers as indices. The property values may be strings or any other type. Resources may be located within any Javascript package beneath a specially named "nls" directory, with translations made available in directories named by their locale.

    dojo.requireLocalization() is used to declare usage of these resources and load them in the same way that dojo.requires() pulls in Javascript packages. An optional locale argument can specify a particular translation to load; otherwise the one best matching the user agent's locale will be used. If djConfig.extraLocale is set, the localizations in that list will be loaded also. A "root" bundle is provided in the case where the requested localization or less-specific variants are not available.

    Use dojo.i18n.getLocalization() to get the object (hash) representing the resources. An optional locale may be specified, otherwise the user's environment will specify the locale and the best match loaded by the "requireLocalization" step will be used. The localized values will be available as properties on the returned object. For example:

     dojo.require("dojo.i18n.common");

    dojo.requireLocalization("dojo.widget", "validate", "ja");
    var validate = dojo.i18n.getLocalization("dojo.widget", "validate", "ja");

     dojo.debug(validate.invalidMessage);

    The example above will display a invalidMessage message from the validate bundle in package dojo.widget.

    DEBUG: * 入力��データ�該当�るも���り��ん。



    All localized messages and localized content are provided in a "nls" directory (short for native language support) under the specific package. The name of the file containg the localized resources is specified as the second argument. In the example above the default messages would be in a file called src/widget/nls/validate.js, and the Japanese translation would be located at src/widget/nls/ja/validate.js.

    Date and Time

    Dojo is capable of formatting and parsing date formats for many locales, using a repository based on the work at unicode.org. For example, look at the following date formatted using the default locale (in this case, English/US) and also with a specific locale override of China:

    dojo.require("dojo.date.format");

    var d = new Date(2006,9,29,4,30);

    dojo.date.format(d, {selector:'dateOnly'});

    => "Monday, October 30, 2006"

    dojo.date.format(d, {selector:'dateOnly', locale:'zh-cn'})

    => "2006年10月30日星期一"

    Other options may be specified to use a different format "length" -- a choice of "short", "medium", "long", or "full" -- or to print only the time portion of the Date object:

    dojo.date.format(d, {selector:'dateOnly', formatLength:'short'});

    => "10/30/06"

    dojo.date.format(d, {selector:'timeOnly', formatLength:'long', locale:'zh-cn'})

    => "上�04时30分00秒"

    Also, it is possible to reverse the process and parse String objects into Dates. Running in a Dutch locale like "nl-nl", the following would produce a valid Date object:

    dojo.date.parse("maandag 30 oktober 2006");

    Special patterns may be specified, or additional resource bundles may be used to provide sets of localizations for custom formats. The patterns used

    follow the specification and are similar to those used by the Java dateformat class (e.g. MMddyyyy).

    Numbers

    dojo.i18n.number is EXPERIMENTAL. The code is not complete, may not work, and APIs are likely to change.

    The formatting of numbers, such as the separator and the handling of decimal places, differs among locales. Dojo will provide the facilities to properly format numbers on a localized basis.

    Currency

    dojo.i18n.currency is EXPERIMENTAL. The code is not complete, may not work, and APIs are likely to change.

    To use these APIs to format a currency in a locale spec manner with the correct number of decimal places and the correct currency symbols. The currency package contains the necessary APIs to format currencies.

    dojo.require("dojo.i18n.currency.common");
    The following code will be used to format the number 1234.567.

    dojo.i18n.currency.format("1234.567", "USD");

    dojo.debug(dojo.i18n.currency.format("1234.567", "USD"));



    Will result in the following:



    DEBUG: USD: $1,234.57

    The following example shows how to display currency in Euros.

    dojo.debug(dojo.i18n.currency.format("1234.567", "EUR"));



    Will result in:

    DEBUG:  EUR: € 1,234.57

    Bi-directional Text (BiDi)

    Some languages, mostly middle-eastern in origin, have text flow from the right to the left (e.g. Hebrew and Arabic) Again, the web browser generally takes care of this for us, provided the appropriate HTML attributes are set. However, sometimes the logic of an application or widget must change to accomodate bidi. For example, menu widgets drop from the upper-right hand corner of the screen and are right-justified. Some icons may also have different orientation. isLTR() can be checked to provide alternate logic for true and false results.

    Dojo provides the dojo.i18n.isLTR() function to specify wether a language is left to right. Most complex widgets, such as Menu are not BiDi enabled yet.

    Encoding

    The two most important aspects of internationalization are insuring that the inputs and the outputs are in the proper encoding. Thankfully, UTF-8 encoding can be used exclusively on the wire in modern web applications to make this interaction extremely simple. Once read into memory, Javascript treats all strings as a series of double-byte characters and encoding is irrelevant.

    Other encodings should be used with great care. A user agent such as one of the current generation browsers infers a page's encoding using the content-type header provided from a server or it may be picked up from a meta tag in the head of a document.



    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />



    The most common means of specifying the encoding in a page is to use the meta tag. UTF-8 is the detault encoding used by XML documents exchanged by the XmlHttpRequest object and is encoding that is used internally by Dojo APIs such as dojo.io.bind. We recommend using UTF-8 as the encoding with your internationalized applications.



    It is important to properly specify the encoding of the page when forms or form widgets are present as the page's encoding is used to properly encode data that is sent back to a server.

    Usage

    TODO: show examples of declaring/overriding locale; explain that locales will only function if part of the operating set.

    Builds

    TBD

    Author: Peller

    Part 9: "Dojo Community"

    Looking at the nuts and bolts of Dojo, and encouraging activity within the Dojo community

    A Little History

    A group of JavaScript developers starting talking about the future of DHTML. Alex Russell (original creator of NetWindows), in looking for assistance at Informatica, started talking to many of the members of the DHTML community, culminating in the April 25, 2004 email titled "Selling the future of DHTML". David Schontzler (Stilleye) spent a summer working at Informatica, and Dylan Schiemann (currently at SitePen) also joined Informatica. The first lines of code contributed to Dojo were done by Alex and Dylan with the support of Informatica. There were many other community members that were active participants in shaping the direction of Dojo, including Joyce Park (mod_pubsub), Tom Trenka (creator of f(m)), Mark Anderson (bustlib), Leonard Lin (who suggested the name Dojo), Aaron Boodman (YoungPup, before GreaseMonkey and Google), Simon Willison (before Django, now at Flickr), Cal Henderson (Flickr), and Dan Pupius (animation, now at Google).



    After several months of discussions on the ng-dhtml (now dojo-developer) mailing list about licensing, choosing a name, coding conventions, build tools, server configuration, and requirements (what would this Dojo thing actually become), we began writing code and setting up a foundation to own the copyright to the Dojo code. After writing some initial specs, the first Dojo code was written and/or ported into Dojo in early September, 2004, by Alex Russell and Dylan Schiemann. In addition to new code, early contributions and inspirations were made from NetWindows, burstlib, and f(m). In March, 2005, we started to receive contribtions from other Dojo members, and by the end of 2006, we have completed four major releases, with over 300,000 downloads to date, and contributions from more than 60 developers and companies.



    The Dojo Foundation was established in 2005 as a 501c(6) organization. Currently, Alex Russell serves as the president, and Dylan Schiemann is the Secretary/Treasurer. Voting of issues is done by +1/-1 by the contributor mailing list.



    Over the first two years since that initial e-mail discussion, Dojo has grown in a manner that we could have only dreamed of. Through several major releases, and extensive contributions by the community, Dojo has become a much more diverse and flexible toolkit in a shorter amount of time than any of its predecessors. We were originally just hoping to build a toolkit that would learn from the limitations of toolkits of the day, make real use of DOM-capable browsers, and build something with real capabilities and a real software engineering approach. Our goal was to avoid reinventing the wheel so that we could move on and make new mistakes.

    Developing a Module

    All the magic that is the dojo module system takes place in the src directory. Take a look in your distribution at the various files an directories. You'll notice that there are many directories and many js files. Now look inside the directories. Most of the directories have a file named __package__.js along with many other js files.

    There are several ways to develop a module. Basically, it all has to do with how you plan to divide up your code. If you're writing a small module which needs the full source code to work, you'll want to put a js file in the base of the src directory with the same name as your module and a .js on the end. This can be called by putting the following in your source code:

    dojo.require("dojo.packagename");

    If you want to split your module into several unrelated files (called "resources"), you'd create a directory an put each part into a seperate js file. The only way you could call this is by using a deeper require statement, as follows:

    dojo.require("dojo.packagename.subpackage");

    If you want to split your module into smaller pieces that sometimes rely on each other, then you'll want a __package__.js file. What this file does is specifies default resources to load depending on the environment. Open up some of your files and see how it looks. There are two important functions: kwCompoundRequire and provide.ÂÂ

    The first function specified which packages to load in an object with keys consisting of the different environments. An example is as follows:

    dojo.kwCompoundRequire({

    common: ["dojo.package.first", "dojo.package.second"],

    browser: ["dojo.package.browser"]

    });

    The second function tells dojo that your module was loaded properly.

    dojo.provide("dojo.package.*");

    In all the other js files, you'll need to have a require statement to inform dojo that the package pieces have been loaded. This is also true of the files in the base of the src directory:

    dojo.provide("dojo.package");

    Developing For Dojo

    The Dojo team is always looking for new code contributions. Please see the resources below for more info:

    Filing Bugs And Patches

    The Dojo bug database is located at http://trac.dojotoolkit.org. You need to login with your dojotoolkit.org user name and password before filing bugs. If you don't already have an account, you can sign up for one here.

    You don't need to sign a Contributor License Agreementto submit bugs, although you do need to sign it before submitting patches. See this post for a fuller explanation of why we insist on CLAs for patches.

    A good bug report should include:

    • Version of Dojo and build name if applicable
    • Browser versions and platforms where the bug occurs
    • Error messages displayed (if any)
    • A contact address so we can follow-up with you
    • Describe the exact situation in which the bug occurs as precisely as possible. Is it reproducible? Did it break in a certain release? Is it still an issue with nightly or subversion head? etc.
    • Testcases and patchces are even better.
    • Version is the version of Dojo in which you have the issue (usually the current release), and milestone is the version in which it will be fixed. Unless you are a Dojo commiter, we prefer that you leave milestone blank so we can triage it as appropriate.

    Getting Involved

    Dojo is a community effort, and we need your help!

    It's easy to get involved and there are lots of ways you can make a difference:

    • Helping other users on IRC and mailing lists
    • Writing documentation (like this manual)
    • Filing good bugs (and test cases) to help developers solve issues quickly
    • Working on widget visual design and HTML/CSS
    • Writing translations for i18n support
    • Writing patches to fix bugs
    • Submitting code for new features

    The Road To Becoming A Committer

    Inside the Dojo community, there are two "levels" of participants. The word "contributors" refers to anyone who submits documentation, bug fixes, or even bug reports and test cases. This large group of designers and developers who are using the Toolkit to improve user experience are the lifeblood of the project. You can become a contributor by sending in a Contributors License Agreement and starting to pitch in in any of the ways noted above.

    "Committers" are a subset of the contributor community who have been granted the ability to make changes directly to Dojo. Committers can also vote in Toolkit and Foundation matters. With that power comes significant responsibility. Committers are required to act as the front line of defense against IP pollution, follow community development standards, merge or reject patches sent in by contributors, vote, participate in weekly IRC meetings, and work on criticial release issues. Becoming a Dojo committer is considered to be an honor as it means you've impressed a discerning group of hackers with the quality of your contributions and feels that they can get along with you.

    So how do you go from "contributor" to "committer"?

    The first step is to file a Contributors License Agreement (outlined below). Once it's been received, start contributing! There's no hard-and-fast rule for how many contributions it takes before you are considered for committer status, but in general you'll need to close important bugs, demonstrate that you are willing to participate in the community (mailing lists, IRC, etc.), and show that you respect community norms and standards. Committers make recommendations to the Foundation Board regarding people they think should be considered for commiter status, so working with a committer to assist him/her with issues that are important for an upcoming release is a great way to ensure that your contributions are integrated and that you will be considered for committer status in a timely fashion.

    The Contributors License Agreement

    Most forms of contribution aside from providing support to other users requires that you sign and submit a Contributors License Agreement (or "CLA" for short) with the Dojo Foundation. All additions to this manual, for instance, require signed CLAs.

    There are two versions of the agreement:

    1. The Individual CLA
      • use this version if you're working on Dojo in your spare time or can clearly claim ownership of copyright in what you'll be submitting
    2. The Corporate CLA
      • have your corporate lawyer review and submit this if your company is going to be contributing to Foundation projects

    Summarized, the CLA asserts that when you donate fixes or documentation, you both own the code that you're submitting and that the Dojo Foundation can in turn license that code to other people. While the text on the agreement suggests that you fax it in, you can submit a valid CLA by:

    • snail-mail to the address on the form
    • fax to the number of the form
    • or an email'd digital photograph of the signed document to <carrie at dojotoolkit dot org>

    Sending in a CLA is a one-time thing, and once it's done, you're in the clear to start contributing to all Dojo Foundation projects! To be effective, though, you need to know a little bit about how contributors coordinate their work.

    Mailing Lists

    Most decisions, support, and coordination happens on the project's mailing lists. The dojo-interest mailing list is our most active list, with over a hundred messages sent to subscribers on some days. Everything from how best to theme widgets to complex debugging of applications happens here. There are several web interfaces onto this list, including the searchable gmane archive and the Nabble interface which allows for participation via the web UI.

    Project mailing lists are broken up more-or-less by function. The full list:

    • dojo-interest

      "The big one", this list is ground zero for the Dojo community. While very high volume, it's the best place (aside from this site) to find answers to your Dojo-related questions. Remember to search the archives, though! With over 15-- subscribers, someone has probably already run into (and solved) the same problem before.
    • dojo-accessibility

      Developers and users alike use this list to discuss strategies for making Dojo widgets easier to use with assistive technologies such as screen-readers, high-contrast-mode displays, and magnification systems.
    • dojo-jobs

      JavaScript devleopers are in short supply, and as always, so are great employers. The dojo-jobs mailing list tries to help Dojo-clued employers and job seekers alike find each other. There's no charge to post a resume or job listing.
    • dojo-checkins

      Notifications of every change to the code of the toolkit are sent to this list along with notifications of changes to bug tickets in the project's bug tracking system.
    • dojo-soc

      Dojo sponsors students that participate in Google's Summer of Code program. Discussions on this list center around project status updates, how to best tackle problems