Friday, February 28, 2014

Microsoft 70-480: Implement HTML5 APIs

Exam Objectives


Implement storage APIs, AppCache API, and Geolocation API


Quick Overview of Training Materials


Programming in HTML5 with JavaScript and CSS3 - Training Guide - Chapters 14, 15, 16*
MSDN Application Cache API ("AppCache")
MSDN Storage API blog - specific to Windows Apps, not too useful...
MSDN Storage and State reference
MSDN - Quickstart: detecting location using HTML5

*the chart in the back of the book includes Chapter 10, which covers the WebSockets API. However, another set of objectives covers WebSockets, so Chapter 10 really belongs there.



Storage API


HTML5 includes several options for local storage of data.  Where this was once handled by cookies or plug-ins, local storage is now a first class citizen.  Several storage methods are at our disposal:

  • Web storage: this uses the localStorage and sessionStorage objects for storage. It is most widely implemented of these four methods and both the training guide and  Dive Into HTML5 devote most of their discussion of storage on this technology.
  • Web SQL database: basically a wrapper for an SQLite database.  Allows for full relational database capabilities, including SQL queries, but limited browser support.
  • IndexedDB: NoSQL database. No structured query language available, you access information as you would with a collection of objects.
  • Filesystem API: The training guide indicates almost no support and DiveIntoHTML5 doesn't even mention it. There is a File API for Windows 8 apps but I don't think it's the same thing
With any of these storage mechanisms, only the URL that created local data may access it. This means that the host, port, and protocol must all match for a URL to access local data. This can be a probablem, however, if your site lives in a shared domain, as every site on the domain would have access to your data.

Since web storage is the most widely supported, we'll look at it in more depth.  I created the following JSFiddle to demo the technology:


Using localStorage is pretty simple, since all it consists of is a list of key/value pairs.  You create a new data item using the localStorage.setItem(key,value) method, you retrieve data using localStorage.getItem(key), delete an item using localStorage.removeItem(key), remove everything with localStorage.clear(), check the number of stored items with localStorage.length, and get a key for a given index with localStorage.key(index).  In the demo above, I use a for() loop to iterate through localStorage (i=0; i < localStorage.length; i++), and for each iteration retreive the key by calling localStorage.key(i). Then I use this key to get the value using .getItem(), drop them both in a <span> and use .appendChild() to attach them to the <div> holding the results. Piece of cake.

To sync up multiple instances of the same site on multiple tabs or windows, localStorage has an event that fires any time localStorage is changed. The storageEvent only fires on OTHER windows, not the current page, so to see it in action you have to open the demo twice. Changing a value will fire the event in the other tab, which looks something like this:

Changing stuff here...


...fires an event here.
The storageEvent object includes the key, oldValue, newValue, url, and storageArea. For the most part it is subscribed to like other events, however it can not be canceled, and it does not bubble (events are covered in more depth elsewhere if that doesn't make sense).

The training guide points out potential pitfalls of using localStorage include potential slow search times (because it doesn't support indexing), and lack of transactions can cause wonkiness if more than one tab is open.  While localStorage only holds data as key/value pairs, it is posible to store complex data by useing stringified JSON.  However, because all values are stored as strings, it is necessary to use .parseInt or .parseFloat if you want to use retreived numeric values.

Web storage has a few key advantages over cookies that are worth noting.  Whereas cookies transmit their content to the server with every HTTPRequest, local storage data is never retransmit to the server. As a result, the performance penalty that can accrue with cookies is avoided. Also, web storage has significantly greater capacity (w3c recommends 5MB, vs about 4KB for cookies).  Finally, when cookies are sent back to the server, they are unencrypted unless the connection itself is encrypted (https).

AppCache API


The AppCache API allows you to design websites in such a way that they will still work when users are offline (as long as they've visited before).  The key piece to the API is the cache manifest, which is pointed to by the "manifest" attribute in the <html> tag.  Because it's impossible to modify this tag on JSFiddle, building a working demo would have been WAAAY more involved than it was worth, however I did find a demo site built already at http://appcachefacts.info/demo/. When you load the site and get into developer mode (inspect elements on Chrome), you can see that the Application Cache data under Resources is populated:


There are a few important points to keep in mind with the manifest file:

  • attribute on the <html> tag, like this: <html manifest="clock.appcache">
  • the manifest file must be served with a MIME type of text/cache-manifest, which may require server configuration or modifying web.config (IIS) or .htaccess (Apache) files.
  • .appcache is the file extension used by w3.org and the training guide. DiveInto and the IE10 Dev Cenbter use .manifest.
  • the manifest is essentially a text file, and the first line is always "CACHE MANIFEST"
  • you can include line comments using the hash # symbol, like: #this is a comment
  • there are three sections:
    • cache (explicit) - these are the URLs, either relative or fully qualified, of files to cache. Anything listed after "CACHE MANIFEST" is implicitly explicit. 
    • network - whilelist of URL NOT to cache because they require a connection, for instance a dynamically loaded tracking script.
    • fallback - substitute files to use. If you don't want to cache images, you could do something like: /img/  /img/offline.png
  • The cache is updated when the manifest is updated, NOT when resources are updated. Including a comment with a version and timestamp is one method to ensure that the cache is up to date.
  • resources in the manifest can be relative or absolute URL, or relative or absolute paths.

When the cache is updating, it fires a number of events, including (these are mostly verbatim from the spec):
  • checking - The browser is checking for an update, or attempting to download the manifest for the first time. This is always the first event in the sequence.
  • noupdate - The manifest is unchanged.
  • downloading - The browser is downloading the resources listed by the manifest. Could be an update or the first time.
  • progress - Fired periodically as the browser downloads resources
  • cached - The resources listed in the manifest have been downloaded, and the application is now cached.
  • updateReady - The resources listed in the manifest have been newly redownloaded, and the script can use swapCache() to switch to the new cache.
  • obsolete - The manifest was found to have become a 404 (missing) or 410 (permanently removed) page, so the application cache is being deleted.
  • error - The manifest was obsolete (404 or 410), the page requesting the manifest didn't load properly, a fatal error occured while fetching resources from the manifest, or the manifest was updated. If the manifest was updated, the browser will try again, otherwise the cache process is aborted.
DiveIntoHTML5 offers an interesting example (from the spec) of how a cache for a site might be built dynamically. Consider the following manifest file:

CACHE MANIFEST
FALLBACK
/ /offline.html
NETWORK
*

Attaching this manifest to every pages <html> tag would mean that whenever you visit a page, that page (and just that page) would be cached (a page referencing a manifest is implicitly included in the CACHE section).  The FALLBACK section specifies an error page offline.html to display if you navigate to a page offline that you have not visited online.  The NETWORK section includes the asterisk which is the online whitelist wildcard flag. This ensures that anything not explicitily listed in the cache can still be downloaded when you are online, and ensures that online behavior is not affected.  This isn't a complete example, since images, css, javascript, and other resources would need to be listed explicitly to be cached.

The MSDN article Building Offline Experiences with HTML5 AppCache and IndexedDB covers AppCache as well as IndexedDB and Web storage.  Microsoft also built a web application demo site using appcache and IndexedDB together.  When the site is first visited, data is loaded into a IndexedDB, while the basic site pages are cached via an application cache manifest.



It is possible to download the manifest for this app directly here. It looks like this:

CACHE MANIFEST
#Version 1

CACHE:
images/breakfast.JPG
images/Clear.png
images/CommentsLabel.png
images/dessert.JPG
images/DetailsBkg.jpg
images/dinner.JPG
images/IngredientsLabel.png
images/InstructionsLabel.png
images/LeftArrow.png
images/ListBkg.jpg
images/lunch.JPG
images/MainBkg.jpg
images/MealImg.JPG
images/Populate.png
images/RightArrow.png
images/Search.png
images/ServesImg.JPG
images/Start.png
images/TitleIntro.png
JScript.js
StyleSheet.css
Default.html

NETWORK:
*

Geolocation API


The geolocation API can make your web application location aware.  Interaction with this API is done through the use of the navigator.geolocation object.  The following JSFiddle demo (which is really just a mashup of the DiveIntoHTML5, MSDN-Quickstart, and Google Maps API exampls) gives a crude example of using the "one-shot" position update:



Because location information can be considered sensitive, the user will have to give your site permission to use it when you try to access it:


In the case of the JSFiddle, selecting "Deny" will result in the callback function geoError() being called, which displays the error message "Could not find you!".  If you select "Allow", the geoSuccess() callback function is called, which will display your Lat, Long, Accuracy, and a Google Map of your location.

The callback function called on successfully calling .getCurrentPosition is called with a single parameter which represents the position (this is called "position" or "p" in most of the examples).  This "position" parameter is an object with two properties: coords and timestamp. Timestamp is self explanatory; coords is an object implementing the Coordinates interface and has the following properties (from the spec):
  • coords.latitude & coords.longitude:  geographic coordinates specified in decimal degrees.
  • coords.altitude: denotes the height of the position, specified in meters above the [WGS84] ellipsoid. If the implementation cannot provide altitude information, the value of this attribute must be null.
  • coords.accuracy: denotes the accuracy level of the latitude and longitude coordinates. It is specified in meters. The value of the accuracy attribute is a non-negative real number. Should correspond to a 95% confidence level.
  • coords.altitudeAccuracy: specified in meters. If the implementation cannot provide altitude information, the value of this attribute must be null. Otherwise, the value of the altitudeAccuracy attribute is a non-negative real number. Should also correspond to a 95% confidence level.
  • coords.heading: denotes the direction of travel of the hosting device and is specified in degrees, where 0° ≤ heading < 360°, counting clockwise relative to the true north. If the implementation cannot provide heading information, the value of this attribute must be null. If the hosting device is stationary (i.e. the value of the speed attribute is 0), then the value of the heading attribute must be NaN.
  • coords.speed: denotes the magnitude of the horizontal component of the hosting device's current velocity and is specified in meters per second. If the implementation cannot provide speed information, the value of this attribute must be null. Otherwise, the value of the speed attribute is a non-negative real number.
Only latitude, longitude, and accuracy are required by the specification, so altitude, heading, and speed may all return null.  

The .getCurrentPosition() function can take multiple callback functions as parameters. The minimum required is the callback for successful getting the position, which is the first parameter. The second parameter is an error handling callback function.  The error handling callback is passed a single enum parameter that contains the error code.  Error code (1) is where the user denied permission to the application to use geolocation, error code (2) occurs when the location information is unavailable, and error code (3) occurs when the network timed out.

The third argument to the getCurrentPosition functions is a PositionOptions object. This object allows you to enable high accuracy location (using GPS), which is disabled by default. It also allows you to set the timeout (in milliseconds), which determines how long your application will wait on the network for position information (if it runs out, will through a TIMEOUT error). Finally, you can also control the maximum age (in milliseconds) of cached results. If your application may request the users location multiple times, but doesn't need new position information every time, using this setting may avoid making extra unnecessary position calculations.

If you need constant position updates (e.g. you are making a GPS navigation app), then getCurrentPosition is not the appropriate function to call; instead use watchPosition(). It functions almost exactly like getCurrentPosition, except for two differences: it calls the success callback function every time the users location changes, and it returns an id token that can be used by clearWatch() to stop watching the users position.  

DiveIntoHTML5 covers a JavaScript library called geoPosition.js that provides backward compatibility for browsers that do not support HTML5 geolocation.  I used this library in the above demo.  The training guide includes an exercise using the Google API that does pretty much exactly what the above JS Fiddle accomplishes.



No comments:

Post a Comment