Programmatically Trigger an Application Cache Download

By Rob Gravelle

Although there are a lot of good things that can be said about the new Application Cache (AppCache) specification, there is still room for improvement. One criticism that comes up a lot is its implementation is too inflexible. The crux of the issue lies in the way that the manifest file is declared as an attribute of the HTML tag. That makes it difficult to control the downloading of the manifest file so that you can give visitors a heads up as to what's about to happen. As it stands, a warning dialog is the only tip-off that resources are being downloaded. Having said that, there is a way to kick off an AppCache download via a script. Read on to learn all about it!

The IFrame & JavaScript Solution

The most satisfactory dynamic AppCache download implementation that I have tried is to load a second page within an iframe. As an extra precaution, the content of the iFrame is not loaded until the download event is triggered via a user action. The serving of cross-site content in iframes has given them a bit of a bad name, but what we're doing here is a perfectly legitimate use of an iframe because we are loading content from the same server. Even the most restrictive browser will allow same-site content in an iframe:

The HTML Markup

Just about any user action or event can be utilized to kick off the loading of the iframe document, but the simplest is of course a button. A couple of other elements worth noting in the code snippet below are the results, which is a DIV that presents messages to the user, replacing annoying alert boxes. Normally, the element styling would be defined in the CSS file, but I included it here for the sake of simplicity. The content-area DIV will hold other page contents unrelated to the AppCache. However, I included it here because it is referenced by the code that appends the iframe to the document:

<input type="button" id="downloadAppCache" class="mobile_button" value="Download App Cache" />
<div id="results" style="color: white; font-size: 1.2rem; margin: 10px; height: 100px;"></div>
<div id="content-area"></div>

The Button Click Event Handler

In the JavaScript file, the button handler is bound to the click event using the unobtrusive jQuery click() function. In the anonymous handler function, a reference to the iframe object is obtained using the get() with an index of zero. That unwraps the iframe element from the jQuery holder object so that we can call its native update() function on subsequent button clicks. An if statement checks whether or not the iframe exists and either updates it or creates it if it does not:

$(document).ready(function() { 
  $('#downloadAppCache').click(function() {
    var appCacheFrame = $('#offlineAppDownloadFrame').get(0);
    results.html('');
    if (appCacheFrame) {
        appCacheFrame.contentWindow.applicationCache.update();
    }
    else {
        $('#content-area').after('<iframe id="appCacheDownloadFrame" src="/wp-includes/offline.html" style="position:absolute;"></iframe>');
    }
 });
});

Making the IFrame Invisible

Assigning a position of "absolute" ensures that the iframe doesn't consume any screen real estate. Moreover, its offline.html content contains no HTML within the BODY tag, making the iframe essentially invisible. Nonetheless, their inclusion in the containing document still causes the browser to download the manifest and listed resources:

<!DOCTYPE HTML>
<html manifest="GoodFoodTalksManifest.php">
<head>
<title>offline.html</title>
</head>
<body>
</body>
</html>

The <html manifest="GoodFoodTalksManifest.php"> is what causes the manifest and associated resources to be downloaded.

The ApplicationCache Event Hanlder

In the HEAD section of the offline.html file, there is a script that monitors the results of the manifest downloads. There are three events that we are interested in: the cached, error, and noupdate. JQuery's on() function accepts multiple events, separated by a space, so that one handler may server numerous events. A switch within the handler can select an action based on the event type. Regarding the error event, AppCache errors don't provide any information, so a generic message is displayed, along with a link to dismiss the message. This is accomplished by calling a JavaScript function as the href target. The results.html(\'\') is not a page, but a call to the jQuery html() function with an empty string, which is applied to the results DIV. The quotes must be escaped because the entire line is itself a string. Enclosing the call within the void() function cancelled navigation to the link so that the browser doesn't navigate away from the page.

Since the offline.html page has no direct reference to jQuery, we must employ the parent's using parent.$. The $ object is passed to the ready() event so that it may be accessed by enclosed functions:

<head>
<title>offline.html</title>

<script type="text/javascript">
parent.$(document).ready(function($) {
  $(window.applicationCache).on('cached error noupdate', function(e) {
     var message = '';
     switch (e.type) { 
       case 'error':
         message = "The AppCache did not download."      + "<br />"
                 + "We'll try again next time you visit." + "<br />"
                 + '<a href="javascript:void(results.html(\'\'))">Click here</a> to dismiss this message and continue.';
         break;
      case 'cached': 
         message = "The AppCache has been successfully installed.";
         break;
      case 'noupdate':
         message = "No changes have been made to the cached files since you last downloaded the AppCache.";
         break;    
     }
     $(parent.results).html(message);
  });
});
</script>
</head>

Conclusion

Despite some limitations, I have found the AppCache to be an extremely useful addition to the HTML5 toolset. Limitations that I'm sure will be stomped out within a few releases as the AppCache specification matures. Until then, we can manage just fine.


If you enjoyed this article, please contribute to Rob's rock star aspirations by purchasing one of Rob's cover or original songs from iTunes.com for only 0.99 cents each.

Rob Gravelle resides in Ottawa, Canada, and is the founder of GravelleWebDesign.com. Rob has built systems for Intelligence-related organizations such as Canada Border Services, CSIS as well as for numerous commercial businesses. Email Rob to receive a free estimate on your software project.

In his spare time, Rob has become an accomplished guitar player, and has released several CDs. His band, Ivory Knight, was rated as one Canada's top hard rock and metal groups by Brave Words magazine (issue #92).

Rob uses and recommends MochaHost, which provides Web Hosting at $1.95 per month, 2 LifeTime Free Domains, and 6 Months Free!



Make a Comment

Loading Comments...

  • Web Development Newsletter Signup

    Invalid email
    You have successfuly registered to our newsletter.
  •