Recommend this
on Google+

Recommend this
on Google+

Tuesday, September 14, 2010

Talking about HTML5 - Offline Application Caching

 

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. Infact, 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 came up with GMail, and gave inspiration for this feature of HTML5. Thanks to Google!

Technically speaking...

Lets explore technical details, and it's 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, PHP blah..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 atleast 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 almost ready.

Please refer to the specs document by W3C reference API, and documentation.

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

       Phone: +91-9538856655

-->

<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="Test content for HTML5-ApplicationCache" />

    <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 parameter

        */

        {

            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 on this browser yet.</td></tr>";

                }

            }

            catch (UnknownError) {

 

                window.alert('Internet Explorer does not support Application Caching yet.\nPlease run me on Safari or Firefox 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="text-align: center; font-family: Consolas; font-size: large; font-variant: small-caps;"

        href="index.html">Go to Index page</a>

    <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>