mike chambers | about

Implementing Offline Web Content with Gears LocalServer API

I spent the afternoon playing around with Google Gears in order to get a basic feel for how it works. Specifically, I wanted to see how much work it would take to add support for offline viewing for tostring.org (an online book site).

I used the Gears LocalServer API which “allows a web application to cache and serve its HTTP resources locally, without a network connection”. The implementation can be though of as a small web server that intercepts requests for remote resources and serves them (seamlessly) from a local cache. This has a couple of advantages:

The main disadvantages are related to #2. Since content is pulled from the local cache, even when online, users may view stale content. This can also make it a bit of a hassle to develop, as you have to remember to clear the Gears cache of the content when trying to view changes.

Gears will periodically check for updated content and sync anything that is updated. It also provides an API to allow developers to force checks for new content, as well as fine grained control over what content is cached / synced locally.

It took me most of the afternoon to implement the functionality, but the vast majority of that time was spent writing python and Django code (which I was a little rusty on), and learning some of the jQuery JavaScript library which I used to provide visual feedback while the syncing was occurring. The actual Gears implementation was pretty trivial and took maybe a total of 30 minutes (which included learning the APIs and implementing them).

Here is the complete JavaScript code (half of which provides feedback to the user while the pages are being synced).

function hasGears() {
    return window.google && google.gears;
}

function updateProgressField(msg) {
    $("#progress").text(msg);
}

function onSyncProgress(event) {
    updateProgressField(
        Math.ceil((event.filesComplete / event.filesTotal) * 100) + "%"
    );
}

function onSyncComplete() {
    updateProgressField("Sync Complete.");
}

function onSyncError(event) {
    updateProgressField("Error Syncing.");
}

function storeForOffline() {
    var localServer = google.gears.factory.create("beta.localserver");
    var store = localServer.createManagedStore("tostring-store");
    store.manifestUrl = "/gearsmanifest";
    store.onerror = onSyncError;
    store.oncomplete = onSyncComplete;
    store.onprogress = onSyncProgress;
    store.checkForUpdate();
}

function onStoreClick(event) {
    event.preventDefault();
    storeForOffline();
}

function onReady() {
    if (hasGears()) {
        $("#offline_span").css("visibility", "visible");
        $("#offline_span").click(onStoreClick);
    }
}

$(document).ready(onReady);

When a user who has Gears installed visits the site they are be presented with a link titled “Save Offline” (top right). Users who do not have Gears will not see this link. If the user clicks the link, they are presented with a Gears dialog asking for permission (I can customize this, but did not). If they allow the action, then the Gears library loads a JSON based manifest file with information on how the site should be synced (including which specific resources should be included).

You can view the full manifest for tostring.org at:

http://www.tostring.org/gearsmanifest

Here is a snippet:

{
    "betaManifestVersion": 1,
    "version": "2008-08-27 14:18:04",
    "entries": [
        { "url": "/" },
        { "url": "/translating/" },
        { "url": "/about/" },
        { "url": "/dmedia/books/scripts/grids-min.css" },
        { "url": "/dmedia/books/css/styles.css" },
        { "url": "/dmedia/books/scripts/jquery.min.js" },
        {
            "url": "/books/adobe-air-for-javascript-developers-pocketguide/1.0/sv/introduktion-till-adobe-air/"
        }
    ]
}

Gears remembers the version string, and periodically checks to see if it has changed. If it has, it will resync the content (you can do some more advanced stuff with server responses to tell Gears that a particular piece of content has not been updated).

While the content is syncing, I present a small progress indicator to the user.

Once the sync is complete, the content is viewed from the cache, regardless of whether the user is online or offline. You can test this in Firefox via File > Work Offline.

All in all, it was very easy to implement and I am impressed with the results.

Couple of issues:

It seems that calling any of the APIs will result in a security / permission dialog being raised (even though some of the docs suggest otherwise). Which ist the best experience when using multiple APIs.

One interesting thing about the API is that it can be used for more than just making web content available offline. You could also use to to cache frequently used assets to:

Indeed, WordPress is using Gears to help make their admin application more responsive.

The next step will be to add a desktop shortcut for the application using the Desktop API. This also looks to be pretty trivial although I am concerned that the user might be presented with two permission dialogs.

If you have Gears installed, you can see this example in action at tostring.org.

If you have any questions or suggestions, or find any errors in my code (likely), then post them in the comments.

twitter github flickr behance