C H A P T E R  12

Creating HTML5 Offline Web Applications

In this chapter, we will explore what you can do with offline HTML5 applications. HTML5 applications do not necessarily require constant access to the network, and loading cached resources can now be more flexibly controlled by developers.

Overview of HTML5 Offline Web Applications

The first, and most obvious, reason to use the application cache is offline support. In the age of universal connectivity, offline applications are still desirable. What do you do when you do not have a network connection? Before you say the days of intermittent connectivity are over, consider the following:

  • Do all of the flights you take have onboard Wi-Fi?
  • Do you have perfect signal coverage on your mobile Internet device (when was the last time you saw zero bars)?
  • Can you count on having an Internet connection when you give presentations?

As more applications move to the Web, it is tempting to assume 24/7 uninterrupted connectivity for all users, but the reality of the Internet is that interruptions happen and, in situations like air travel, can happen predictably for several hours at a time.

Intermittent connectivity has been the Achilles' heel of network computing systems. If your applications depend on communication with remote hosts, and those hosts are unreachable, you're out of luck. However, when you do have an Internet connection, web applications can always be up-to-date, because the code loads from a remote location on each use.

If your applications require only occasional communication, they can still be useful as long as the application resources are stored locally. With the advent of browser-only devices, web applications that continue to function without continuous connectivity will only grow more important. Desktop applications that do not require continuous connectivity have historically held that advantage over web applications.

HTML5 exposes control over application caching to get the best of both worlds: applications built with web technology that run in the browser and update when they are online but can also be used offline. However, this new offline application feature must be used explicitly, because current web servers do not provide any default caching behavior for offline applications.

The HTML5 offline application cache makes it possible to augment an application to run without a network connection. You do not need a connection to the Internet just to draft an e-mail. HTML5 introduces the offline application cache that allows a Web application to run without network connectivity.

An application developer can specify specific additional resources comprising an HTML5 application (HTML, CSS, JavaScript, and images) to make an application available for offline use. There are many use cases for this, for example:

  • Read and compose e-mail
  • Edit documents
  • Edit and display presentations
  • Create to-do lists

Using offline storage can avoid the normal network requests needed to load an application. If the cache manifest is up to date, the browser knows it does not need to check if the other resources are also up to date, and most of the application can load very quickly out of the local application cache. Additionally, loading resources out of a cache (instead of making multiple HTTP requests to see if resources have been updated) saves bandwidth, which can be especially important for mobile web applications. Currently, slower loading is one way that web applications suffer in comparison with desktop applications. Caching can offset that.

The application cache gives developers explicit control over caching. The cache manifest file allows you to group related resources into a logical application. This is a powerful concept that can give web applications some of the characteristics of desktop applications. You can use this additional power in new, creative ways.

Resources identified in the cache manifest file create what is known as an application cache, which is the place where browsers store the resources persistently, typically on disk. Some browsers give users a way to view the data in the application cache. For example, the Offline cache device section in the internal about:cache page in Firefox shows you details about the application cache and a way to view individual files in the cache, as shown in Figure 12-1.

Image

Figure 12-1. Viewing application cache entries in Firefox

Similarly, the internal page chrome://appcache-internals/ provides details about the contents of the different application caches stored on your system. It also provides a way to view the contents and remove these caches entirely as shown in Figure 12-2.

Image

Figure 12-2. Viewing application cache entries in Chrome

Browser Support for HTML5 Offline Web Applications

For a complete overview of the current browser support, including mobile support, refer to http://caniuse.com and search for Offline Web Applications or Application Cache. If you have to support older browsers, it's always a good idea to first see whether Application Cache is supported before you use the API. The section “Checking for Browser Support” later in this chapter will show you how you can programmatically check for browser support.

Using the HTML5 Application Cache API

In this section, we will explore the specifics of how you can use the Offline Web Applications API.

Checking for Browser Support

Before you try to use the Offline Web Applications API, it is a good idea to check for browser support. Listing 12-1 shows how you can do that.

Listing 12-1. Checking Browser Support for the Offline Web Applications API

if(window.applicationCache) {
  // this browser supports offline applications
}

Creating a Simple Offline Application

Let's say that you want to create a one-page application that consists of an HTML document, a style sheet, and a JavaScript file. To add offline support to your HTML5 application, you include a manifest attribute on the html element as shown in the Listing 12-2.

Listing 12-2. The manifest Attribute on the HTML Element

<!DOCTYPE html>
<html manifest="application.appcache">
  .
  .
  .
</html>

Alongside the HTML document, provide a manifest file with the *.appcache extension) specifying which resources to cache. Listing 12-3 shows the contents of an example cache manifest file.

Listing 12-3. Contents of an Example Cache Manifest File

CACHE MANIFEST
example.html
example.js
example.css
example.gif

Going Offline

To make applications aware of intermittent connectivity, there are additional events exposed by HTML5 browsers. Your applications may have different modes for online and offline behavior. Some additions to the window.navigator object make that easier. First, navigator.onLine is a Boolean property that indicates whether the browser believes it is online. Of course, a true value of onLine is not a definite assurance that the servers that your web application must communicate with are reachable from the user's machine. On the other hand, a false value means the browser will not even attempt to connect out over the network. Listing 12-4 shows how you can check to see if your page is online or offline.

Listing 12-4. Checking Online Status

// When the page loads, set the status to online or offline
function loadDemo() {
  if (navigator.onLine) {
    log("Online");
  } else {
    log("Offline");
  }
}

// Now add event listeners to notify a change in online status
window.addEventListener("online", function(e) {
  log("Online");
}, true);

window.addEventListener("offline", function(e) {
  log("Offline");
}, true);

Manifest Files

Offline applications consist of a manifest listing one or more resources that browser will cache for offline use. Manifest files have the MIME type text/cache-manifest. The SimpleHTTPServer module in the Python standard library will serve files with the .manifest extension with the header Content-type: text/cache-manifest. To configure settings, open the file PYTHON_HOME/Lib/mimetypes.py, and add the following line:

'.appcache'    : 'text/cache-manifest manifest',

Other web servers may require additional configuration. For example, for Apache HTTP Server, you can update the mime.types file in the conf folder by adding the following line:

text/cache-manifest appcache

If you are using Microsoft IIS, in your website's home, double-click the MIME Types icon, then add the .appcache extension with MIME type text/cache-manifest in the Add MIME Type dialog.

The manifest syntax is simple line separated text that starts with CACHE MANIFEST (as the first line). Lines can end in CR, LF, or CRLF—the format is flexible—but the text must be UTF-8 encoded, which is the typical output for most text editors. Comments begin with the hash symbol and must be on their own lines; you cannot append a comment to other non-comment lines in the file.

Listing 12-5. Example Manifest File with All Possible Sections

CACHE MANIFEST
# files to cache
about.html
html5.css
index.html
happy-trails-rc.gif
lake-tahoe.JPG

#do not cache signup page
NETWORK
signup.html

FALLBACK
signup.html     offline.html
/app/ajax/      default.html

Let's look at the different sections.

If no CACHE: heading is specified, the files that are listed will be treated as files to be cached (caching is the default behavior). The following simple manifest specifies that three files (index.html, application.js, and style.css) must be cached:

CACHE MANIFEST
index.html
application.js
style.css

Similarly, the following section would do the same (you can use the same CACHE, NETWORK, and FALLBACK headers multiple times in a manifest file if you want to):

CACHE MANIFEST

# Cache section
CACHE:
Index.html
application.js
style.css

By listing a file in the CACHE section, you instruct the browser to serve the file from the application cache, even if the application is online. It is unnecessary to specify the application's main HTML resource. The HTML document that initially pointed to the manifest file is implicitly included (this is called a Master entry). However, if you want to cache multiple HTML documents or if you would like multiple HTML documents to act as possible entry points for the cacheable application, they should all be explicitly listed in the cache manifest file.

FALLBACK entries allow you to give alternate paths to replace resources that cannot be fetched. The manifest in Listing 12-5 would cause requests to /app/ajax/ or subpaths beginning with /app/ajax/ to fall back to default.html when /app/ajax/* is unreachable.

NETWORK specifies resources that are always fetched using the network. The difference with simply omitting these files from the manifest is that master entries are cached without being explicitly listed in the manifest file. To ensure that the application requests the file from the server even if the cached resource is cached in the application cache, you can place that file in the NETWORK: section.

The ApplicationCache API

The ApplicationCache API is an interface for working with the application cache. A new window.applicationCache object fires several events related to the state of the cache. The object has a numerical property, window.applicationCache.status, which indicates the state of the cache. The six states a cache can have are shown in Table 12-1.

Image

Most pages on the Web today do not specify cache manifests and are uncached. Idle is the typical state for an application with a cache manifest. An application in the idle state has all its resources stored by the browser with no updates in progress. A cache enters the obsolete state if there was at one point a valid cache but the manifest is now missing. There are events (and callback attributes) in the API that correspond to some of these states. For instance, when the cache enters the idle state after an update, the cached event fires. At that time, an application might notify the user that they can disconnect from the network and still expect the application to be available in offline mode. Table 12-2 shows some common events and their associated caches states.

Image

Additionally, there are events indicating update progress, when no update is available, or when an error has occurred:

  • onerror
  • onnoupdate
  • onprogress

window.applicationCache has an update() method. Calling update() requests that the browser update the cache. This includes checking for a new version of the manifest file and downloading new resources if necessary. If there is no cache or if the cache is obsolete, an error will be thrown.

Application Cache in Action

Although creating the manifest file and using it in an application is relatively simple, what happens when you update pages on the server is not as intuitive as you might think. The main thing to keep in mind is that once the browser has successfully cached the application's resources in the application cache, it will always serve those pages from the cache first. After that, the browser will do only one more thing: check if the manifest file has been changed on the server.

To better understand how the process works, let's step through an example scenario, using the manifest file shown in Listing 12-5.

  1. When you access the index.html page for the very first time (while online), say on http://www.example.com, the browser loads the page and its subresources (CSS, JavaScript, and image files).
  2. While parsing the page, the browser comes across the manifest attribute in the html element and proceeds to load all the files listed in the CACHE (default) and FALLBACK sections in the application cache for the example.com site (browsers allow about 5 MB of storage space).
  3. From now on, when you navigate to http://www.example.com, the browser will always load the site from the application cache, and it will then attempt to check if the manifest file has been updated (it can only do the latter when you are online). This means that if you now go offline (voluntarily or otherwise) and point your browser at http://www.example.com, the browser will load the site from the application cache—yes, you can still use the site in its entirety in offline mode.
  4. If you try to access a cached resource while you're offline, it will load from the application cache. When you try to access a NETWORK resource (signup.html), FALLBACK content (offline.html) will be served. NETWORK files will be available again only if you go back online.
  5. So far so good. Everything works as expected. We will now try to step you through the digital minefield that you must cross when you change content on the server. When you change, for example, the about.html page on the server and access that page while you're in online mode by reloading the page in the browser, it would be reasonable to expect the updated page to show up. After all, you're online and have direct access to the server. However, you will just be looking at the same old page as before, possibly with a puzzled look on your face. The reason for this is that the browser will always load the page from the application cache, and after that it checks only one thing: whether the manifest file has been updated. Therefore, if you want updated resources to be downloaded you must make a change to the manifest file as well (do not just “touch” the file, because that will not be considered a change—it must be a byte-for-byte change). A common way to make this change is to add a version comment at the top of the file as shown in Listing 12.5. The browser does not actually understand the version comment, but it is a good best practice to follow. Because of this and because it is easy to overlook a new or removed file, it is recommended that you use some sort of build script to maintain the manifest file. HTML5 Boilerplate 2.0 (http://html5boilerplate.com) ships with a build file that can be used to automatically build and version the appcache file, a great addition to that already great resource.
  6. When you make a change to both the about.html page and the manifest file and subsequently refresh the page in your browser while you're online you will, once again, be disappointed to see the same old page. What happened? Well, even though the browser has now found that the manifest has been updated and downloaded all of the files again into a new version of the cache, the page was already loaded from the application cache before the server check was performed, and the browser does not automatically reload the page for you in the browser. You can compare this process to how a new version of a software program (for example, the Firefox browser) can be downloaded in the background but require a restart of the program to take effect. If you can't wait for the next page refresh, you can programmatically add an event listener for the onupdateready event and prompt the user to refresh the page. A little confusing at first, but it all actually makes sense when you think about it.

Using Application Cache to Boost Performance

Building an Application with HTML5 Offline Web Applications

In this example application, we will track a runner's location while out on the trail (with intermittent or no connectivity). For example, Peter goes running, and he will have his new Geolocation–enabled phone and HTML5 web browser with him, but there is not always a great signal out in the woods around his house. He wants to use this application to track and record his location even when he cannot use the Internet.

When offline, the Geolocation API should continue to work on devices with hardware geolocation (such as GPS) but obviously not on devices that use IP geolocation. IP geolocation requires network connectivity to map the client's IP address to coordinates. In addition, offline applications can always access persistent storage on the local machine through APIs such as local storage or Indexed Database.

The example files for this application are located on the book's page at www.apress.com and at the book‘s website in the offline code folder, and you can start the demo by navigating to the code/offline folder and issuing the command:

Python –m SimpleHTTPServer 9999.

Prior to starting the web server, make sure you have configured Python to serve the manifest files (files with the *.appcache extension) with the correct mime type as described earlier. This is the most common cause of failure for offline web applications. If it does not work as expected, check the console in Chrome Developer tools for possible descriptive error messages.

This starts Python's HTTP server module on port 9999 (you can start it on any port, but you may need admin privileges to bind to ports lower than 1024. After starting the HTTP server, you can navigate to http://localhost:9999/tracker.html to see the application in action.

Figure 12-3 shows what happens in Firefox when you access the site for the first time: you are prompted to opt in to storing data on your computer (note, however, that not all browsers will prompt you before storing data).

Image

Figure 12-3. Firefox prompting to store data for the web application

After allowing the application to store data, the application cache process starts and the browser starts downloading the files referenced in the application cache manifest file (this happens after the page is loaded, and, therefore, it has minimal impact on the responsiveness of the page. Figure 12-4 shows how Chrome Developer Tools provide a detailed overview of what is cached for the localhost origin in the Resources pane. It also provides information in the console about the application cache events that fire while the page and the manifest were processed.

Image

Figure 12-4. The Offline Page in Chrome with details about the application cache in Chrome Developer Tools

To run this application, you will need a web server serving these static resources. Remember that the manifest file must be served with the content type text/cache-manifest. If your browser supports the application cache, but the file is served with the incorrect content type, you will receive a cache error. An easy way to test this is to view the events that fire in the Chrome Developer Tools console as shown in Figure 12-4; it will tell you if the appcache file is served with the wrong mime type.

To run this application with complete functionality, you will need a server that can receive geolocation data. The server-side complement to this example would presumably store, analyze, and make available this data. It may or may not be served from the same origin as the static application. Figure 12-5 shows the example application running in offline mode in Firefox. You can use File Image Work Offline to turn this mode on in Firefox and Opera. Other browsers do not have this convenience function, but you can disconnect from the network. Note, however, that disconnecting from the network does not interrupt the connection to a Python server running on localhost.

Image

Figure 12-5. The application in offline mode

Creating a Manifest File for the Application Resources

First, in a text editor, create the tracker.appcache file as follows. This manifest file will list the files that are part of this application:

CACHE MANIFEST
# JavaScript
./offline.js
#./tracker.js
./log.js

# stylesheets
./html5.css

# images

Creating the HTML Structure and CSS for the UI

This is the basic UI structure of the example. Both tracker.html and html5.css will be cached, so the application will be served from the application cache.

<!DOCTYPE html>
<html lang="en" manifest="tracker.appcache">
<head>
    <title>HTML5 Offline Application</title>
    <script src="log.js"></script>
    <script src="offline.js"></script>
    <script src="tracker.js"></script>
    <link rel="stylesheet" href="html5.css">
</head>

<body>
    <header>
      <h1>Offline Example</h1>
    </header>
      
    <section>
      <article>
        <button id="installButton">Check for Updates</button>
        <h3>Log</h3>
        <div id="info">
        </div>
      </article>
    </section>
</body>
</html>

There are a couple of things to note in this HTML that pertain to this application's offline capabilities. The first is the manifest attribute on the HTML element. Most of the HTML examples in this book omit the <html> element because it is optional in HTML5. However, the ability to cache offline depends on specifying the manifest file there.

The second thing to note is the button. That will give the user control over configuring this application for offline use.

Creating the Offline JavaScript

For this example, the JavaScript is contained in multiple .js files included with <script> tags. These scripts are cached along with the HTML and CSS.

<offline.js>
/*
 * log each of the events fired by window.applicationCache
 */
window.applicationCache.onchecking = function(e) {
    log("Checking for application update");
}

window.applicationCache.onnoupdate = function(e) {
    log("No application update found");
}

window.applicationCache.onupdateready = function(e) {
    log("Application update ready");
}

window.applicationCache.onobsolete = function(e) {
    log("Application obsolete");
}

window.applicationCache.ondownloading = function(e) {
    log("Downloading application update");
}

window.applicationCache.oncached = function(e) {
    log("Application cached");
}

window.applicationCache.onerror = function(e) {
    log("Application cache error");
}

window.addEventListener("online", function(e) {
    log("Online");
}, true);

window.addEventListener("offline", function(e) {
    log("Offline");
}, true);


/*
 * Convert applicationCache status codes into messages
 */
showCacheStatus = function(n) {
    statusMessages = ["Uncached","Idle","Checking","Downloading","Update Ready","Obsolete"];
    return statusMessages[n];
}

install = function() {
    log("Checking for updates");
    try {
        window.applicationCache.update();
    } catch (e) {
        applicationCache.onerror();
    }
}

onload = function(e) {
    // Check for required browser features
    if (!window.applicationCache) {
        log("HTML5 Offline Applications are not supported in your browser.");
        return;
    }

    if (!navigator.geolocation) {
        log("HTML5 Geolocation is not supported in your browser.");
        return;
    }

    if (!window.localStorage) {
        log("HTML5 Local Storage not supported in your browser.");
        return;
    }

    log("Initial cache status: " + showCacheStatus(window.applicationCache.status));
    document.getElementById("installButton").onclick = checkFor;
}

<log.js>
log = function() {
    var p = document.createElement("p");
    var message = Array.prototype.join.call(arguments, " ");
    p.innerHTML = message;
    document.getElementById("info").appendChild(p);
}

Check for ApplicationCache Support

In addition to the offline application cache, this example uses geolocation and local storage. We ensure that the browser supports all of these features when the page loads.

onload = function(e) {
    // Check for required browser features
    if (!window.applicationCache) {
        log("HTML5 Offline Applications are not supported in your browser.");
        return;
    }

    if (!navigator.geolocation) {
        log("HTML5 Geolocation is not supported in your browser.");
        return;
    }

    if (!window.localStorage) {
        log("HTML5 Local Storage is not supported in your browser.");
        return;
    }

    if (!window.WebSocket) {
        log("HTML5 WebSocket is not supported in your browser.");
        return;
    }
    log("Initial cache status: " + showCacheStatus(window.applicationCache.status));
    document.getElementById("installButton").onclick = install;
}

Adding the Update Button Handler

Next, add an update handler that updates the application cache as follows:

install = function() {
    log("Checking for updates");
    try {
        window.applicationCache.update();
    } catch (e) {
        applicationCache.onerror();
    }
}

Clicking this button will explicitly start the cache check that will cause all cache resources to be downloaded if necessary. When available updates have completely downloaded, a message is logged in the UI. At this point, the user knows that the application has successfully installed and can be run in offline mode.

Add Geolocation Tracking Code

This code is based on the geolocation code from Chapter 4. It is contained in the tracker.js JavaScript file.

/*
 * Track and report the current location
 */
var handlePositionUpdate = function(e) {
    var latitude = e.coords.latitude;
    var longitude = e.coords.longitude;
    log("Position update:", latitude, longitude);
    if(navigator.onLine) {
        uploadLocations(latitude, longitude);
    }
    storeLocation(latitude, longitude);
}

var handlePositionError = function(e) {
    log("Position error");
}

var uploadLocations = function(latitude, longitude) {
    var request = new XMLHttpRequest();
    request.open("POST", "http://geodata.example.net:8000/geoupload", true);
    request.send(localStorage.locations);
}

var geolocationConfig = {"maximumAge":20000};

navigator.geolocation.watchPosition(handlePositionUpdate,
                                    handlePositionError,
                                    geolocationConfig);

Adding Storage Code

Next, add the code that writes updates to localStorage when the application is in offline mode.

var storeLocation = function(latitude, longitude) {
    // load stored location list
    var locations = JSON.parse(localStorage.locations || "[]");
    // add location
    locations.push({"latitude" : latitude, "longitude" : longitude});
    // save new location list
    localStorage.locations = JSON.stringify(locations);
}

This application stores coordinates using HTML5 local storage as described in Chapter 9. Local storage is a natural fit for offline-capable applications, because it provides a way to persist data locally in the browser. The data will be available in future sessions. When network connectivity is restored, the application can synchronize with a remote server.

Using storage here has the added benefit of allowing recovery from failed upload requests. If the application experiences a network error for any reason, or if the application is closed (by user action, browser or operating system crash, or page navigation) the data is stored for future transmission.

Adding Offline Event Handling

Every time the location update handler runs, it checks the connectivity status. If the application is online, it will store and upload the coordinates. If the application is offline, it will merely store the coordinates. When the application comes back online, it can update the UI to show the online status and upload any data is stored while online.

window.addEventListener("online", function(e) {
    log("Online");
}, true);

window.addEventListener("offline", function(e) {
    log("Offline");
}, true);

The connectivity status may change while the application is not actively running. For instance, the user may have closed the browser, refreshed, or navigated to a different site. To handle these cases, our offline application checks to see if it has come back online on each page load. If it has, it will attempt to synchronize with the remote server.

// Synchronize with the server if the browser is now online
if(navigator.onLine) {
    uploadLocations();
}

Summary

In this chapter, you have seen how HTML5 Offline Web Applications can be used to create compelling applications that can be used even when there is no Internet connection. You can ensure that all your files are cached by specifying the files that are part of the web application in the cache manifest file and then referencing the files from the main HTML page of the application. Then, by adding event listeners for online and offline status changes, you can make your site behave differently based on whether an Internet connection is available or not.

In the final chapter, we will discuss the future of HTML5 programming.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.239.48