Let's dive in and start using Dojo Offline.
First download the Dojo SDK and unzip it.
When you unzip the SDK, you will find the following directories:
Dojo Offline is an optional Dojo extension, and is therefore located in the dojox directory.
If you are looking to track down Dojo Offline's source code, most of it is in dojox/off/. The Dojo SQL layer is in dojox/_sql/, while Dojo Storage is in dojox/storage/. An autogenerated, JavaDoc-like API is available. When looking at the API docs or source code, many advanced options are available to deeply customize Dojo Offline; you can almost always safely ignore these unless you are an advanced user, and they usually say "For advanced usage; most developers can ignore this" in their documentation.
Dojo Offline has three main demos, a Hello World example, a more complicated web-based editor named Moxie that includes an example server-side written in Java, and a demo of Dojo Offline's SQL cryptography. You can play with hosted versions of the Hello World example here; a hosted version of the Moxie editor here; and the SQL cryptography demo here.
If you want to study the demo examples' source code, the Hello World example
is located in
dojox/off/demos/helloworld/,
while the Moxie editor is located in
dojox/off/demos/editor/.
You can see the SQL cryptography demo source in
dojox/_sql/demos/customers/customers.html.
The Hello World example has no server-side requirement; Moxie, however,
includes a full Java server-side that you can use as a template and
scaffolding. Running the server-side of Moxie is simple. Make sure you have
Java 1.5+ installed, and then just type:
java -jar editor.jar
while inside the directory
dojox/off/demos/editor/server/,
and the Moxie server-side will start running, with an embedded web-server
(Jetty) and relational database (Derby) already set up for you.
In a web-browser, you can now go to the following URL:
http://localhost:8000/dojox/off/demos/editor/editor.html
to run Moxie against the local server you just started.
For more details on the server-side portion of Moxie and how to build see the README file at dojox/off/demos/editor/server/README.
Now that you have awareness of the SDK, it's file layout, and the provided
demos, let's get down to illustrating what you need to do to create an
offline-aware application using Dojo Offline.
For our example source code we will pretend that you are creating your
application with the Dojo Offline SDK in a subdirectory named offline-sdk.
First, bring in Dojo and Dojo Offline: /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}
isDebug is a useful flag that when set to true will print out debug messages, and
when set to false will hide them. In your own code you can add console.debug("some message"); to have
these printed out to help with debugging. If you are using Firefox in
conjunction with Firebug then these messages will print out to the Firebug
console; otherwise they will print on to the web page itself, such as in
Internet Explorer.
Notice that we do not bring in Dijit; Dojo Offline has no dependencies on Dijit, the Dojo Widget system, helping to keep your code size smaller. We only include it in the SDK because Moxie, the example editor, uses the Dijit rich text editor.
Next, bring in Dojo and Dojo Offline's style sheets:
/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */
.geshifilter {font-family: monospace;}
.geshifilter .imp {font-weight: bold; color: red;}
.geshifilter .kw1 {color: #b1b100;}
.geshifilter .kw2 {color: #000000; font-weight: bold;}
.geshifilter .kw3 {color: #000066;}
.geshifilter .coMULTI {color: #808080; font-style: italic;}
.geshifilter .es0 {color: #000099; font-weight: bold;}
.geshifilter .br0 {color: #66cc66;}
.geshifilter .st0 {color: #ff0000;}
.geshifilter .nu0 {color: #cc66cc;}
.geshifilter .sc0 {color: #00bbdd;}
.geshifilter .sc1 {color: #ddbb00;}
.geshifilter .sc2 {color: #009900;}
Dojo Offline includes a default offline widget that does much of the hard work of providing a good offline UI to your end-users. It updates the user with online and offline feedback; provides sync messages; and delivers help and instructions on using your application offline. If Dojo Offline did not provide these you would have to roll them yourself, so providing a default UI makes developing offline applications easier.
Getting this widget is as easy as adding a bit of HTML to your page, with a
special, predefined ID:
/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */
.geshifilter {font-family: monospace;}
.geshifilter .imp {font-weight: bold; color: red;}
.geshifilter .kw1 {color: #b1b100;}
.geshifilter .kw2 {color: #000000; font-weight: bold;}
.geshifilter .kw3 {color: #000066;}
.geshifilter .coMULTI {color: #808080; font-style: italic;}
.geshifilter .es0 {color: #000099; font-weight: bold;}
.geshifilter .br0 {color: #66cc66;}
.geshifilter .st0 {color: #ff0000;}
.geshifilter .nu0 {color: #cc66cc;}
.geshifilter .sc0 {color: #00bbdd;}
.geshifilter .sc1 {color: #ddbb00;}
.geshifilter .sc2 {color: #009900;}
The offline widget is a small, self-contained unit that provides feedback to the user, giving you a bunch of great functionality for free.
Here is a screenshot of Moxie, with the offline widget circled:
For more screenshots of the offline widget and a full description of what it gives you for free, see here. It is recommended that you use the offline widget in your own offline applications unless you are an advanced developer; information on customizing its look and feel, including dropping it, can be found here.
Let's take a look at our JavaScript now. All you have to do is set your
application name:
/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */
.geshifilter {font-family: monospace;}
.geshifilter .imp {font-weight: bold; color: red;}
.geshifilter .kw1 {color: #000066; font-weight: bold;}
.geshifilter .kw2 {color: #003366; font-weight: bold;}
.geshifilter .kw3 {color: #000066;}
.geshifilter .co1 {color: #009900; font-style: italic;}
.geshifilter .coMULTI {color: #009900; font-style: italic;}
.geshifilter .es0 {color: #000099; font-weight: bold;}
.geshifilter .br0 {color: #66cc66;}
.geshifilter .st0 {color: #3366CC;}
.geshifilter .nu0 {color: #CC0000;}
.geshifilter .me1 {color: #006600;}
.geshifilter .re0 {color: #0066FF;}
The Offline Widget will use your application name to customize it's user-interface; that is how the offline widget, for example, can insert "Learn How to Use Hello World Offline" into its instructions without you having to manually edit the offline widget's HTML.
slurp(), that will slurp
the page and automatically figure out all of the resources you need to have
available offline:slurp() method is awesome; it will automatically scan your page and quickly
figure out all your JavaScript and CSS files; grab any IMG tags in your source;
and even grab any background URLs you might have in your attached CSS. The only
thing it doesn't do is look at inline styles or scan your JavaScript for dynamic
additions.slurp() has a nice debug method if you want to see all the files that have been
slurped; make sure that Dojo's isDebug is true, and call dojox.off.files.printURLs() after the page and
Dojo Offline are finished loading (more details on how to know when Dojo Offline
is done loading in the next section).
During development, as you hit your offline application, it will always be pulled from the Google Gears file cache first, even if you are online. This can sometimes make development tricky and tedious. If you just made a change locally, hitting your web application, even if you are offline, will cause the older version to be pulled in first.
To make this process easier, I have created a bookmarklet that you can drag to your links toolbar in your browser. View this page to get the bookmarklets for Firefox and Internet Explorer.
These bookmarklets clear the Google Gears cache, removing all of the files that
slurp() added offline. Press it during development when you have made a new change
that you want to show up; you must press it when you are at your web application
(i.e. it won't work if you just press it when you are at a different or blank
page). Then, refresh the page to see your change.
Dojo Offline can't be used until the page is finished loading and Dojo Offline itself is finished initializing (such as the offline widget finishing being placed into the page). You should wait until the page and Dojo Offline are finished loading, and then your application can start doing its own custom thing:
/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
myApp is some object that will hold all of the methods for our application;
myApp.initialize() is the method in particular that would initialize our application on page
and Dojo Offline load in some way. We use dojo.connect to connect to the dojox.off.ui.onLoad event;
when this fires, myApp.initialize() is called. At this point we could begin to manipulate the
DOM on the page, since it is loaded, or do further actions specific to your
application; in this case we just print an alert message.
The final call to dojox.off.initialize() is there to tell Dojo Offline that we are done
configuring it, and that it can now initialize itself; we don't want Dojo
Offline to initialize itself before we have set our appName and called slurp(), for
example, so this final call tells Dojo Offline that we are finished doing
configuration.
Let's look at all of our code so far in this tutorial:
/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}Important: I've noticed over and over in the frameworks I have created, such as Dojo Storage and the Really Simple History library, that developers get confused about an important point. Notice that configuring Dojo Offline must be done before the page loads:
/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}
Notice that our calls to things like dojox.off.ui.appName and the dojo.connect to dojox.off.ui are at
the top-level of the script tag done before the page loads; if you were to
put these into a function and call that after the page has loaded, Dojo
Offline will not work:
// set our application name dojox.off.ui.appName = "Example Application"; // automatically "slurp" the page and // capture the resources we need offline dojox.off.files.slurp(); // etc. } </script>
At this point we have the shell of an offline application using Dojo Offline and Google Gears. Let's delve into common issues that come up with offline applications now, such as toggling your user-interface in different ways if you are on- or off-line; knowing the network status; syncing; and more.
In the background Dojo Offline is checking to make sure that your web application is available on the network (for technical details on what it is doing see here). If your web application disappears or appears within a few seconds (five to thirty seconds), Dojo Offline will detect this and automatically inform your application so that you can move on- or offline, such as enabling or disabling UI elements that might not be available offline. Dojo Offline fires an event when the network status changes, which you can easily subscribe to using Dojo Connect:
/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}By default, the Dojo Offline UI widget also subscribes to these events, handling most of the work of informing the user when they are on- or off-line so you don't have to:
Another useful property is dojox.off.isOnline. At any time in your code you can check dojox.off.isOnline
to see if you are on- or off-line and change your applications behavior
appropriately. This will be true if we are online and the application is
available on the network, and false otherwise.
A common pattern in offline applications is the need to have a UI element do
different things when you are on- or off-line. For example, you could imagine a
SEARCH button that when you are online will shoot off a network request to the
server to do a search, while when offline will do a SQL search on the local
Google Gears datastore. A common way to handle this is to use Dojo Connect and the
dojox.off.isOnline property:
This can be a nice pattern to separate on- and off-line code and easily toggle
between them without littering lower level code with dojox.off.isOnline network checks; just
do it at the dojo.connect level instead, which is cleaner.