An Overview of HTML5 Offline Web Applications
Introduction
Offline-Application-Cache (or, simply AppCache) is not a new thing. It’s the same caching we always heard about in web-apps development to facilitate client side data storage. Yet, the way it’s done is new J . All the traditional ways of storing data on client include Cookies, Sessions & HTML headers to instruct browser to cache. In fact, there was no standard to make a web page available offline, so that only those features that require connection can be disabled, keeping the rest still available to the client. Also, those features require connection, can be substituted with suitable content, to notify the user gracefully, instead of some error page saying “Page cannot be displayed”.
What it does?
For example, consider a webpage that displays some various types of content such as, quarterly business reports, stock market share price etc. Suppose, while browsing this application, the user lost his network connection, after which the user clicks on the “Next” to see the other part of the report. Obviously, an error page comes in place of the webpage, to notify user that the connection was lost.
Now, let’s analyze the page. Quarterly reports is something that changes once in 3 months, while Stock ticker changes every second. Hence, it would have been sensible to make the report data available, even when offline as it has nothing to do with the server. Instead the webpage has been replaced with an error message??? It is exactly where AppCache provides a seamless browsing experience very gracefully. However, the stock ticker cannot be made available offline as the data changes constantly. Yet, the user can be notified meaningfully, and enhance his browsing experience by keeping past data available till the connection to the server restores.
This is coherent with the work of GMail, that works even while offline. If you hadn't observed this, try now. Login to your GMail from Safari/Chrome and disconnect from your network. You can continue browsing emails, and prepare drafts, before getting back online. It's because of Google Gears, a technology which Google experimented with GMail, and gave inspiration for this feature of HTML5. Thanks to Google!
Technically speaking...
Lets explore technical details, and its implementation now. HTML5, adds new classes to the DOM for making this possible. JavaScript, is used to deal with the API. Don't worry if you didn't understand this jargon like DOM, API...so on.... If you have some experience with HTML & JavaScript, you may ignore this section.
Basically, however different & new web-application-development technologies like ASP.NET, JSP, PHPblah..blah... existed, the sole aim is to produce some HTML to show as output. HTML is the only language of web browsers. HTML uses a set of tags, to instruct the browser, how-&-what to show. Based upon our inputs & interactions, browser submits requests on your behalf for processing. It's the server responsibility to do necessary processing, and respond to the client with suitable output again in the form of HTML.
Imagine a website being used by billions, at once. All the requests, responses make huge amounts of data to flow, contributing network congestion. Hence, it would be ridiculous & costly to send a request to the server for a very minor operation. For example, take an online ticket booking server, which is used for planning trips, booking all tickets. Suppose there is a phone-number, and email ID field that is required at the time of booking tickets, and you forgot to fill it before submitting. It then becomes the server's responsibility to ensure valid data is being processed, apart from various other business transactions. For such a busy server, validating input data would surely become an overhead, and has considerable impact on the performance. Hence, it should be ensured before submitting, that all inputs are fed properly. So, JavaScript comes into the picture here. This script is executed suitably, at the client end to ensure that all data adheres to specifications. Script then submits data after thorough validation. Doesn't it reduce at least half of the requests being made to the server? Obviously, the amount of traffic in the network gets reduced drastically.
Apart from this, JavaScript also enhances the browsing experience by facilitating stunning user-interfaces, dynamic updates, user alerts etc. For JavaScript to run, the browser must provide necessary infrastructure to understand and access various components on the page. For example, to display a message in the form of alert, window.alert("MY MESSAGE") is used. Here, browser provides a "window" object, with "alert()" functionality which is used through JavaScript.
Similarly, to support new HTML5, browser needs to provide support for all new features. Hence, ensure that you develop HTML5 related stuff to run on most recent versions. I'd suggest to use Safari 4.0.5 that provides excellent support as per the W3C Specs.
It's ready.
Please refer to the specs document by W3C
Manifest File
Manifests must be served using the text/cache-manifest MIME type. All resources served using the text/cache-manifest MIME type must follow the syntax of application cache manifests, as described in this section. The first line of an application cache manifest must consist of the string "CACHE".
Please be advised that Comments must be on a line on their own. If they were to be included on a line with a URL, the "#" would be mistaken for part of a fragment identifier.
There are four possible section headers:
1. CACHE: Switches to the explicit section.
2. FALLBACK: Switches to the fallback section.
3. NETWORK: Switches to the online whitelist section.
4. SETTINGS: Switches to the settings section
The resource that declares the manifest (with the manifest attribute in the <HTML manifest=”Sample.manifest”>) will always get taken from the cache, whether it is listed in the cache or not, even if it is listed in an online whitelist namespace.
If a resource is listed in the explicit section or as a fallback entry in the fallback section, the resource will always be taken from the cache, regardless of any other matching entries in the fallback namespaces or online whitelist namespaces.
When a fallback namespace and an online whitelist namespace overlap, the online whitelist namespace has priority.
The online whitelist wildcard flag is applied last, only for URLs that match neither the online whitelist namespace nor the fallback namespace and that are not listed in the explicit section.
Events
Listed event handlers (and their corresponding event handler event types) are supported, as events suitably when Cache undergoes various states.
Event Name |
Handler in code template present above |
checking |
onChecking() |
error |
onError() |
update |
onUpdate() |
downloading |
onDownloading() |
progress |
onProgress() |
Updateready |
onUpdateReady() |
Cached |
onCached() |
obsolete |
onObsolete() |
Tools & Examples
I've prepared a template kind of code, that can be used without much effort. Just edit the parts of this code and add your custom logic, to run it.
<!DOCTYPE html>
<html manifest="FullFeatured.manifest">
<!--
Author: K Chandrasekhar Omkar
E-Mail: kcomkar@gmail.com
-->
<head>
<title>HTML5: Application Caching</title>
<meta http-equiv="Author" content="K Chandrasekhar Omkar" />
<meta http-equiv="E-Mail" content="kcomkar@gmail.com" />
<meta http-equiv="Context" content="Template for HTML5-Offline Web Applications" />
<style type="text/css">
.style1
{
font-family: Consolas;
font-size: LARGE;
text-align: CENTER;
margin-left: 80px;
text-weight: BOLD;
}
</style>
<script type="text/javascript">
function onCacheChecking(e)
/** DOMApplicationCache: CHECKING event's handler
* Argument e is the event parameter
*/
{
printOutput("CHECKING", "Contents of the manifest are being checked.", 0);
}
function onCacheCached(e)
/** DOMApplicationCache: CACHED event's handler
* Argument e is the event parameter
*/
{
printOutput("<tr><td align='right'><strong>CACHED</strong></td><td>All the resources mentioned in the manifest have been downloaded</td></tr>", 0);
}
function onCacheNoUpdate(e)
/** DOMApplicationCache: NOUPDATE event's handler
* Argument e is the event parameter
*/
{
printOutput("<tr><td align='right'><strong>NOUPDATE</strong></td><td>Manifest file has not been changed. No updates took place.</td></tr>", 0);
}
function onCacheUpdateReady(e)
/** DOMApplicationCache: UPDATEREADY event's handler
* Argument e is the event parameter
**/
{
printOutput("<tr><td align='right'><strong>UPDATEREADY</strong></td><td>Changes have been made to manifest file, and were downloaded.</td></tr>", 0);
}
function onCacheError(e)
/** DOMApplicationCache: ERROR event's handler
* Argument e is the event paramet
*/
{
printOutput("<tr><td align='right'><strong>ERROR</strong></td><td>An error occured while trying to process manifest file.</td></tr>", 0);
}
function onCacheObselete(e)
/** DOMApplicationCache: OBSELETE event's handler
* Argument e is the event parameter
*/
{
printOutput("<tr><td align='right'><strong>OBSOLETE</strong></td><td>Either the manifest file has been deleted or renamed at the source</td></tr>", 0);
}
function onCacheDownloading(e)
/** DOMApplicationCache: DOWNLOADING event's handler
* Argument e is the event parameter
*/
{
printOutput("<tr><td align='right'><strong>DOWNLOADING</strong></td><td>Downloading resources into local cache.</td></tr>", 0);
}
function onCacheProgress(e)
/** DOMApplicationCache: PROGRESS event's handler
* Argument e is the event parameter
*/
{
printOutput("<tr><td align='right'><strong>PROGRESS</strong></td><td>Download in process.</td></tr>", 0);
}
function printOutput(eventName, statusMessages, howToTell)
/**
* Outputs information about an event with its description
* @param {String} eventName Should be one of the event being processed by appCache when this message is sent
* @param {String} statusMessages The message string to be displayed that describes the event
* @param {Integer} howToTell Specifies if the output is to be written onto document(0) or alert(1) or both(2)
*/
{
try {
if (howToTell == 2) {
document.getElementById("stat").innerHTML += formatComment(eventName, statusMessages);
window.alert(statusMessages);
}
else if (howToTell == 0) {
document.getElementById("stat").innerHTML += formatComment(eventName, statusMessages);
}
else if (howToTell == 1) {
window.alert(statusMessages);
}
}
catch (IOExceptionOutput) {
window.alert(IOExceptionOutput);
}
}
function formatComment(eventName, statusMessage) {
statusMessage = "<tr><td align='right'><strong>" + eventName + "</strong></td><td>" + statusMessage + "</td></tr>";
return statusMessage;
}
function initiateCaching()
/** This method initiates all the required variables, events and related members of the document.
* It contains the following members:
* S.No. VARIABLE NAME SCOPE DESCRIPTION
* ----- ------------- -------- ----------------------------------------------------
* 1 ONLY_DOC GLOBAL This is a CONSTANT used to specify
* the mode for printOutput() method.
* Indicates that output should be printed on HTML page.
*
* 2 ONLY_ALERT GLOBAL This is a CONSTANT used to specify
* the mode for printOutput() method.
* Indicates that output should be displayed in alert
* dialog window, only.
*
* 3 BOTH_DOC_ALERT GLOBAL This is a CONSTANT used to specify
* the mode for printOutput() method.
* Indicates that output should be printed on HTML page
* & displayed in alert as well.
**/
{
var ONLY_DOC = 0;
var ONLY_ALERT = 1;
var BOTH_DOC_ALERT = 2;
try {
if (window.applicationCache) {
var appcache = window.applicationCache;
printOutput("<tr><td>BROWSER COMPATIBILITY</td><td>SUCCESS!! AppCache works on this browser.</td></tr>", 0);
appcache.addEventListener('checking', onCacheChecking, false);
appcache.addEventListener('cached', onCacheCached, false);
appcache.addEventListener('noupdate', onCacheNoUpdate, false);
appcache.addEventListener('downloading', onCacheDownloading, false);
appcache.addEventListener('progress', onCacheProgress, false);
appcache.addEventListener('updateready', onCacheUpdateReady, false);
appcache.addEventListener('error', onCacheError, false);
appcache.addEventListener('obsolete', onCacheObselete, false);
}
else {
document.getElementById("stat").innerHTML = "<tr><td colspan=2>Failure! AppCaching is not supported by this browser yet.</td></tr>";
}
}
catch (UnknownError) {
window.alert('Your browser does not support Application Caching yet.\nPlease run me on latest browsers\n\n');
stat.innerHTML = "<tr><td colspan='2'>Failure! I cant work.</td></tr>";
}
}
</script>
</head>
<body onload="initiateCaching()">
<table id="stat" name="stat" style="text-transform: capitalize; color: #000000; font-size: medium;
font-weight: bold; font-style: normal; font-variant: small-caps; font-family: Consolas;
table-layout: auto; border: thin dotted #000000;" cellspacing="10">
</table>
<hr />
<a style="font-family: Consolas; font: Consolas; font-size: large; font-variant: small-caps"
href="javascript:var sURL = unescape(window.location.pathname);window.location.href=sURL;">
Reload this page</a>
<hr />
</body>
</html>