- •Contents at a Glance
- •Contents
- •Foreword
- •About the Authors
- •About the Technical Reviewer
- •Acknowledgments
- •Introduction
- •Who This Book Is For
- •An Overview of This Book
- •Example Code and Companion Web Site
- •Contacting the Authors
- •Overview of HTML5
- •The Story So Far—The History of HTML5
- •The Myth of 2022 and Why It Doesn’t Matter
- •Who Is Developing HTML5?
- •A New Vision
- •Compatibility and Paving the Cow Paths
- •Utility and the Priority of Constituencies
- •Interoperability Simplification
- •Universal Access
- •A Plugin–Free Paradigm
- •What’s In and What’s Out?
- •What’s New in HTML5?
- •New DOCTYPE and Character Set
- •New and Deprecated Elements
- •Semantic Markup
- •Simplifying Selection Using the Selectors API
- •JavaScript Logging and Debugging
- •window.JSON
- •DOM Level 3
- •Monkeys, Squirrelfish, and Other Speedy Oddities
- •Summary
- •Using the Canvas API
- •Overview of HTML5 Canvas
- •History
- •What Is a Canvas?
- •Canvas Coordinates
- •When Not to Use Canvas
- •Fallback Content
- •CSS and Canvas
- •Browser Support for HTML5 Canvas
- •Using the HTML5 Canvas APIs
- •Checking for Browser Support
- •Adding a Canvas to a Page
- •Applying Transformations to Drawings
- •Working with Paths
- •Working with Stroke Styles
- •Working with Fill Styles
- •Filling Rectangular Content
- •Drawing Curves
- •Inserting Images into a Canvas
- •Using Gradients
- •Using Background Patterns
- •Scaling Canvas Objects
- •Using Canvas Transforms
- •Using Canvas Text
- •Applying Shadows
- •Working with Pixel Data
- •Implementing Canvas Security
- •Building an Application with HTML5 Canvas
- •Practical Extra: Full Page Glass Pane
- •Practical Extra: Timing Your Canvas Animation
- •Summary
- •Working with Scalable Vector Graphics
- •Overview of SVG
- •History
- •Understanding SVG
- •Scalable Graphics
- •Creating 2D Graphics with SVG
- •Adding SVG to a Page
- •Simple Shapes
- •Transforming SVG Elements
- •Reusing Content
- •Patterns and Gradients
- •SVG Paths
- •Using SVG Text
- •Putting the Scene Together
- •Building an Interactive Application with SVG
- •Adding Trees
- •Adding the updateTrees Function
- •Adding the removeTree Function
- •Adding the CSS Styles
- •The Final Code
- •Summary
- •Working with Audio and Video
- •Overview of Audio and Video
- •Video Containers
- •Audio and Video Codecs
- •Audio and Video Restrictions
- •Browser Support for Audio and Video
- •Using the Audio and Video API
- •Checking for Browser Support
- •Accessibility
- •Understanding Media Elements
- •Working with Audio
- •Working with Video
- •Practical Extras
- •Summary
- •Using the Geolocation API
- •About Location Information
- •Latitude and Longitude Coordinates
- •Where Does Location Information Come From?
- •IP Address Geolocation Data
- •GPS Geolocation Data
- •Wi-Fi Geolocation Data
- •Cell Phone Geolocation Data
- •User–Defined Geolocation Data
- •Browser Support for Geolocation
- •Privacy
- •Triggering the Privacy Protection Mechanism
- •Dealing with Location Information
- •Using the Geolocation API
- •Checking for Browser Support
- •Position Requests
- •Building an Application with Geolocation
- •Writing the HTML Display
- •Processing the Geolocation Data
- •The Final Code
- •Practical Extras
- •What’s My Status?
- •Show Me on a Google Map
- •Summary
- •Using the Communication APIs
- •Cross Document Messaging
- •Understanding Origin Security
- •Browser Support for Cross Document Messaging
- •Using the postMessage API
- •Building an Application Using the postMessage API
- •XMLHttpRequest Level 2
- •Cross-Origin XMLHttpRequest
- •Progress Events
- •Browser Support for HTML5 XMLHttpRequest Level 2
- •Using the XMLHttpRequest API
- •Building an Application Using XMLHttpRequest
- •Practical Extras
- •Structured Data
- •Framebusting
- •Summary
- •Using the WebSocket API
- •Overview of WebSocket
- •Real-Time and HTTP
- •Understanding WebSocket
- •Writing a Simple Echo WebSocket Server
- •Using the WebSocket API
- •Checking for Browser Support
- •Basic API Usage
- •Building a WebSocket Application
- •Coding the HTML File
- •Adding the WebSocket Code
- •Adding the Geolocation Code
- •Putting It All Together
- •The Final Code
- •Summary
- •Using the Forms API
- •Overview of HTML5 Forms
- •HTML Forms Versus XForms
- •Functional Forms
- •Browser Support for HTML5 Forms
- •An Input Catalog
- •Using the HTML5 Forms APIs
- •New Form Attributes and Functions
- •Checking Forms with Validation
- •Validation Feedback
- •Building an Application with HTML5 Forms
- •Practical Extras
- •Summary
- •Working with Drag-and-Drop
- •Web Drag-and-Drop: The Story So Far
- •Overview of HTML5 Drag-and-Drop
- •The Big Picture
- •Events to Remember
- •Drag Participation
- •Transfer and Control
- •Building an Application with Drag-and-Drop
- •Getting Into the dropzone
- •Handling Drag-and-Drop for Files
- •Practical Extras
- •Customizing the Drag Display
- •Summary
- •Using the Web Workers API
- •Browser Support for Web Workers
- •Using the Web Workers API
- •Checking for Browser Support
- •Creating Web Workers
- •Loading and Executing Additional JavaScript
- •Communicating with Web Workers
- •Coding the Main Page
- •Handling Errors
- •Stopping Web Workers
- •Using Web Workers within Web Workers
- •Using Timers
- •Example Code
- •Building an Application with Web Workers
- •Coding the blur.js Helper Script
- •Coding the blur.html Application Page
- •Coding the blurWorker.js Web Worker Script
- •Communicating with the Web Workers
- •The Application in Action
- •Example Code
- •Summary
- •Using the Storage APIs
- •Overview of Web Storage
- •Browser Support for Web Storage
- •Using the Web Storage API
- •Checking for Browser Support
- •Setting and Retrieving Values
- •Plugging Data Leaks
- •Local Versus Session Storage
- •Other Web Storage API Attributes and Functions
- •Communicating Web Storage Updates
- •Exploring Web Storage
- •Building an Application with Web Storage
- •The Future of Browser Database Storage
- •The Web SQL Database
- •The Indexed Database API
- •Practical Extras
- •JSON Object Storage
- •A Window into Sharing
- •Summary
- •Overview of HTML5 Offline Web Applications
- •Browser Support for HTML5 Offline Web Applications
- •Using the HTML5 Application Cache API
- •Checking for Browser Support
- •Creating a Simple Offline Application
- •Going Offline
- •Manifest Files
- •The ApplicationCache API
- •Application Cache in Action
- •Building an Application with HTML5 Offline Web Applications
- •Creating a Manifest File for the Application Resources
- •Creating the HTML Structure and CSS for the UI
- •Creating the Offline JavaScript
- •Check for ApplicationCache Support
- •Adding the Update Button Handler
- •Add Geolocation Tracking Code
- •Adding Storage Code
- •Adding Offline Event Handling
- •Summary
- •The Future of HTML5
- •Browser Support for HTML5
- •HTML Evolves
- •WebGL
- •Devices
- •Audio Data API
- •Touchscreen Device Events
- •Peer-to-Peer Networking
- •Ultimate Direction
- •Summary
- •Index
CHAPTER 7 USING THE WEBSOCKET API
updateGeolocationStatus("HTML5 Geolocation is supported in your browser.");
}
// register for position updates using the Geolocation API geolocation.watchPosition(updateLocation,
handleLocationError,
{maximumAge:20000});
}
As before, we watch our current location for changes and register that we want the updateLocation function called when they occur. Errors are sent to the handleLocationError function, and the location data is set to expire every twenty seconds.
The next section of code is the handler which is called by the browser whenever a new location is available.
function updateLocation(position) {
var latitude = position.coords.latitude; var longitude = position.coords.longitude; var timestamp = position.timestamp;
updateGeolocationStatus("Location updated at " + timestamp);
// Send my location via WebSocket
var toSend = JSON.stringify([myId, latitude, longitude]); sendMyLocation(toSend);
}
This section is similar to, but simpler than, the same handler in Chapter 5. Here, we grab the latitude, longitude, and timestamp from the position provided by the browser. Then, we update the status message to indicate that a new value has arrived.
Putting It All Together
The final section calculates a message string to send to the remote broadcast WebSocket server. The string here will be JSON encoded:
"[<id>, <latitude>, <longitude>]"
The ID will be the randomly calculated value already created to identify this user. The latitude and longitude are provided by the geolocation position object. We send the message directly to the server as a JSON encoded string.
The actual code to send the position to the server resides in the sendMyLocation() function.
function sendMyLocation(newLocation) { if (socket) {
socket.send(newLocation);
}
}
If a socket was successfully created—and stored for later access—then it is safe to send the message string passed into this function to the server. Once it arrives, the WebSocket message broadcast server will distribute the location string to every browser currently connected and listening for messages. Everyone will know where you are. Or, at least, a largely anonymous “you” identified only by a random number.
186
CHAPTER 7 USING THE WEBSOCKET API
Now that we’re sending messages, let’s see how those same messages should be processed when they arrive at the browser. Recall that we registered an onmessage handler on the socket to pass any incoming data to a dataReturned() function. Next, we will look at that final function in more detail.
function dataReturned(locationData) {
// break the data into ID, latitude, and longitude var allData = JSON.parse(locationData);
var incomingId = allData[1]; var incomingLat = allData[2]; var incomingLong = allData[3];
The dataReturned function serves two purposes. It will create (or update) a display element in the page showing the position reflected in the incoming message string, and it will return a text representation of the user this message originated from. The user name will be used in the status message at the top of the page by the calling function, the socket.onmessage handler.
The first step taken by this data handler function is to break the incoming message back down into its component parts using JSON.parse. Although a more robust application would need to check for unexpected formatting, we will assume that all messages to our server are valid, and therefore our string separates cleanly into a random ID, a latitude, and a longitude.
//locate the HTML element for this ID
//if one doesn't exist, create it
var incomingRow = document.getElementById(incomingId); if (!incomingRow) {
incomingRow = document.createElement('div'); incomingRow.setAttribute('id', incomingId);
Our demonstration user interface will create a visible <div> for every random ID for which it receives a message. This includes the user’s ID itself; in other words, the user’s own data will also be displayed only after it is sent and returned from the WebSocket broadcast server.
Accordingly, the first thing we do with the ID from our message string is use it to locate the display row element matching it. If one does not exist, we create one and set its id attribute to be the id returned from our socket server for future retrieval.
incomingRow.userText = (incomingId == |
myId) ? |
'Me' |
: |
'User ' + |
rowCount; |
rowCount++;
The user text to be displayed in the data row is easy to calculate. If the ID matches the user’s ID, it is simply ‘me’. Otherwise, the username is a combination of a common string and a count of rows, which we will increment.
document.body.appendChild(incomingRow);
}
Once the new display element is ready, it is inserted into the end of the page. Regardless of whether the display element is newly created or if it already existed—due to the fact that a location update was not the first for that particular user—the display row needs to be updated with the current text information.
// update the row text with the new values incomingRow.innerHTML = incomingRow.userText + " \\ Lat: " +
incomingLat + " \\ Lon: " +
187
CHAPTER 7 USING THE WEBSOCKET API
incomingLong;
return incomingRow.userText;
}
In our case, we will separate the user text name from the latitude and longitude values using a backslash (properly escaped, of course). Finally, the display name is returned to the calling function for updating the status row.
Our simple WebSocket and Geolocation mashup is now complete. Try it out, but keep in mind that unless there are multiple browsers accessing the application at the same time, you won’t see many updates. As an exercise to the reader, consider updating this example to display the incoming locations on a global Google Map to get an idea of where HTML5 interest is flourishing at this very moment.
The Final Code
For completeness, the Listing 7-9 provides the entire tracker.html file.
Listing 7-9. The tracker.html Code
<!DOCTYPE html> <html lang="en">
<head>
<title>HTML5 WebSocket / Geolocation Tracker</title> <link rel="stylesheet" href="styles.css">
</head>
<body onload="loadDemo()">
<h1>HTML5 WebSocket / Geolocation Tracker</h1>
<div><strong>Geolocation</strong>: <p id="geoStatus">HTML5 Geolocation is <strong>not</strong> supported in your browser.</p></div>
<div><strong>WebSocket</strong>: <p id="socketStatus">WebSocket is <strong>not</strong> supported in your browser.</p></div>
<script>
//reference to the WebSocket var socket;
//a semi-unique random ID for this session var myId = Math.floor(100000*Math.random());
//number of rows of data presently displayed var rowCount = 0;
function updateSocketStatus(message) { document.getElementById("socketStatus").innerHTML = message;
}
function updateGeolocationStatus(message) {
188
CHAPTER 7 USING THE WEBSOCKET API
document.getElementById("geoStatus").innerHTML = message;
}
function handleLocationError(error) { switch(error.code)
{
case 0:
updateGeolocationStatus("There was an error while retrieving your location: " + error.message);
break; case 1:
updateGeolocationStatus("The user prevented this page from retrieving a location.");
break; case 2:
updateGeolocationStatus("The browser was unable to determine your location: " + error.message);
break; case 3:
updateGeolocationStatus("The browser timed out before retrieving the location."); break;
}
}
function loadDemo() {
// test to make sure that sockets are supported if (window.WebSocket) {
// the location where our broadcast WebSocket server is located url = "ws://localhost:8080";
socket = new WebSocket(url); socket.onopen = function() {
updateSocketStatus("Connected to WebSocket tracker server");
}
socket.onmessage = function(e) {
updateSocketStatus("Updated location from " + dataReturned(e.data));
}
}
var geolocation; if(navigator.geolocation) {
geolocation = navigator.geolocation;
updateGeolocationStatus("HTML5 Geolocation is supported in your browser.");
// register for position updates using the Geolocation API geolocation.watchPosition(updateLocation,
handleLocationError,
{maximumAge:20000});
}
}
function updateLocation(position) {
189
CHAPTER 7 USING THE WEBSOCKET API
var latitude = position.coords.latitude; var longitude = position.coords.longitude; var timestamp = position.timestamp;
updateGeolocationStatus("Location updated at " + timestamp);
// Send my location via WebSocket
var toSend = JSON.stringify([myId, latitude, longitude]); sendMyLocation(toSend);
}
function sendMyLocation(newLocation) { if (socket) {
socket.send(newLocation);
}
}
function dataReturned(locationData) {
// break the data into ID, latitude, and longitude var allData = JSON.parse(locationData)
var incomingId = allData[1]; var incomingLat = allData[2]; var incomingLong = allData[3];
//locate the HTML element for this ID
//if one doesn't exist, create it
var incomingRow = document.getElementById(incomingId); if (!incomingRow) {
incomingRow = document.createElement('div'); incomingRow.setAttribute('id', incomingId);
incomingRow.userText = (incomingId == |
myId) ? |
'Me' |
: |
'User ' + |
rowCount; |
rowCount++; |
|
document.body.appendChild(incomingRow);
}
// update the row text with the new values incomingRow.innerHTML = incomingRow.userText + " \\ Lat: " +
incomingLat + " \\ Lon: " + incomingLong;
return incomingRow.userText;
}
</script>
</body>
</html>
190