QR Codes, Geolocation, Google Maps API, and HTML5 Video

Exclusive offer: get 50% off this eBook here
Creating Mobile Apps with jQuery Mobile

Creating Mobile Apps with jQuery Mobile — Save 50%

Learn to make practical, unique, real-world sites that span a variety of industries and technologies with the world's most popular mobile development library book and ebook.

$26.99    $13.50
by Shane Gliser | June 2013 | Open Source

We have discussed many of the core concerns of small and big business. Let's turn our eyes now to other concepts that would concern media companies. In this article by Shane Gliser from the book Creating Mobile Apps with jQuery Mobile, we'll look at a movie theater chain, but really, these concepts could be applied to any business that has multiple physical locations.

In this article, we'll cover:

  • QR Codes
  • Basic geolocation
  • Integrating Google Maps API
  • Linking and embedding video

(For more resources related to this topic, see here.)

QR codes

We love our smartphones. We love showing off what our smartphones can do. So, when those cryptic squares, as shown in the following figure, started showing up all over the place and befuddling the masses, smartphone users quickly stepped up and started showing people what it's all about in the same overly-enthusiastic manner that we whip them out to answer even the most trivial question heard in passing. And, since it looks like NFC isn't taking off anytime soon, we'd better be familiar with QR codes and how to leverage them.

The data shows that knowledge and usage of QR codes is very high according to surveys:(http://researchaccess.com/2012/01/new-data-on-qrcode-adoption/)

  • More than two-thirds of smartphone users have scanned a code
  • More than 70 percent of the users say they'd do it again (especially for a discount)

Wait, what does this have to do with jQuery Mobile? Traffic. Big-time successful traffic. A banner ad is considered successful if only two percent of people lick through (http://en.wikipedia.org/wiki/Clickthrough_rate). QR codes get more than 66 percent! I'd say it's a pretty good way to get people to our reations and, thus, should be of concern. But QR codes are for more than just URLs. Here we have a URL, a block of text, a phone number, and an SMS in the following QR codes:

There are many ways to generate QR codes (http://www.the-qrcode-generator.com/, http://www.qrstuff.com/). Really, just search for QR Code Generator on Google and you'll have numerous options.

Let us consider a local movie theater chain. Dickinson Theatres (dtmovies.com) has been around since the 1920s and is considering throwing its hat into the mobile ring. Perhaps they will invest in a mobile website, and go all-out in placing posters and ads in bus stops and other outdoor locations. Naturally, people are going to start scanning, and this is valuable to us because they're going to tell us exactly which locations are paying off. This is really a first in the advertising industry. We have a medium that seems to spur people to interact on devices that will tell us exactly where they were when they scanned it. Geolocation matters and this can help us find the right locations.

Geolocation

When GPS first came out on phones, it was pretty useless for anything other than police tracking in case of emergencies. Today, it is making the devices that we hold in our hands even more personal than our personal computers. For now, we can get a latitude, longitude, and timestamp very dependably. The geolocation API specification from the W3C can be found at http://dev.w3.org/geo/api/spec-source.html. For now, we'll pretend that we have a poster prompting the user to scan a QR code to find the nearest theater and show the timings. It would bring the user to a page like this:

Since there's no better first date than dinner and a movie, the movie going crowd tends to skew a bit to the younger side. Unfortunately, that group does not tend to have a lot of money. They may have more feature phones than smartphones. Some might only have very basic browsers. Maybe they have JavaScript, but we can't count on it. If they do, they might have geolocation. Regardless, given the audience, progressive enhancement is going to be the key.

The first thing we'll do is create a base level page with a simple form that will submit a zip code to a server. Since we're using our template from before, we'll add validation to the form for anyone who has JavaScript using the validateMe class. If they have JavaScript and geolocation, we'll replace the form with a message saying that we're trying to find their location. For now, don't worry about creating this file. The source code is incomplete at this stage. This page will evolve and the final version will be in the source package for the article in the file called qrresponse. php as shown in the following code:

<?php $documentTitle = "Dickinson Theatres"; $headerLeftHref = "/"; $headerLeftLinkText = "Home"; $headerLeftIcon = "home"; $headerTitle = ""; $headerRightHref = "tel:8165555555"; $headerRightLinkText = "Call"; $headerRightIcon = "grid"; $fullSiteLinkHref = "/"; ?> <!DOCTYPE html> <html> <head> <?php include("includes/meta.php"); ?> </head> <body> <div id="qrfindclosest" data-role="page"> <div class="logoContainer ui-shadow"></div> <div data-role="content"> <div id="latLong> <form id="findTheaterForm" action="fullshowtimes.php" method="get" class="validateMe"> <p> <label for="zip">Enter Zip Code</label> <input type="tel" name="zip" id="zip" class="required number"/> </p> <p><input type="submit" value="Go"></p> </form> </div> <p> <ul id="showing" data-role="listview" class="movieListings" data-dividertheme="g"> </ul> </p> </div> <?php include("includes/footer.php"); ?> </div> <script type="text/javascript"> //We'll put our page specific code here soon </script> </body> </html>

For anyone who does not have JavaScript, this is what they will see, nothing special. We could spruce it up with a little CSS but what would be the point? If they're on a browser that doesn't have JavaScript, there's pretty good chance their browser is also miserable at rendering CSS. That's fine really. After all, progressive enhancement doesn't necessarily mean making it wonderful for everyone, it just means being sure it works for everyone. Most will never see this but if they do, it will work just fine

For everyone else, we'll need to start working with JavaScript to get our theater data in a format we can digest programmatically. JSON is perfectly suited for this task. If you are already familiar with the concept of JSON, skip to the next paragraph now. If you're not familiar with it, basically, it's another way of shipping data across the Interwebs. It's like XML but more useful. It's less verbose and can be directly interacted with and manipulated using JavaScript because it's actually written in JavaScript. JSON is an acronym for JavaScript Object Notation. A special thank you goes out to Douglas Crockford (the father of JSON). XML still has its place on the server. It has no business in the browser as a data format if you can get JSON. This is such a widespread view that at the last developer conference I went to, one of the speakers chuckled as he asked, "Is anyone still actually using XML?"

{ "theaters":[ { "id":161, "name":"Chenal 9 IMAX Theatre", "address":"17825 Chenal Parkway", "city":"Little Rock", "state":"AR", "zip":"72223", "distance":9999, "geo":{"lat":34.7684775,"long":-92.4599322}, "phone":"501-821-2616" }, { "id":158, "name":"Gateway 12 IMAX Theatre", "address":"1935 S. Signal Butte", "city":"Mesa", "state":"AZ", "zip":"85209", "distance":9999, "geo":{"lat":33.3788674,"long":-111.6016081}, "phone":"480-354-8030" }, { "id":135, "name":"Northglen 14 Theatre", "address":"4900 N.E. 80th Street", "city":"Kansas City", "state":"MO", "zip":"64119", "distance":9999, "geo":{"lat":39.240027,"long":-94.5226432}, "phone":"816-468-1100" } ] }

Now that we have data to work with, we can prepare the on-page scripts. Let's put the following chunks of JavaScript in a script tag at the bottom of the HTML where we had the comment: We'll put our page specific code here soon

//declare our global variables var theaterData = null; var timestamp = null; var latitude = null; var longitude = null; var closestTheater = null; //Once the page is initialized, hide the manual zip code form //and place a message saying that we're attempting to find //their location. $(document).on("pageinit", "#qrfindclosest", function(){ if(navigator.geolocation){ $("#findTheaterForm").hide(); $("#latLong").append("<p id='finding'>Finding your location...</ p>"); } }); //Once the page is showing, go grab the theater data and find out which one is closest. $(document).on("pageshow", "#qrfindclosest", function(){ theaterData = $.getJSON("js/theaters.js", function(data){ theaterData = data; selectClosestTheater(); }); }); function selectClosestTheater(){ navigator.geolocation.getCurrentPosition( function(position) { //success latitude = position.coords.latitude; longitude = position.coords.longitude; timestamp = position.timestamp; for(var x = 0; x < theaterData.theaters.length; x++) { var theater = theaterData.theaters[x]; var distance = getDistance(latitude, longitude, theater.geo.lat, theater.geo.long); theaterData.theaters[x].distance = distance; }} theaterData.theaters.sort(compareDistances); closestTheater = theaterData.theaters[0]; _gaq.push(['_trackEvent', "qr", "ad_scan", (""+latitude+","+longitude) ]); var dt = new Date(); dt.setTime(timestamp); $("#latLong").html("<div class='theaterName'>" +closestTheater.name+"</div><strong>" +closestTheater.distance.toFixed(2) +"miles</strong><br/>" +closestTheater.address+"<br/>" +closestTheater.city+", "+closestTheater.state+" " +closestTheater.zip+"<br/><a href='tel:" +closestTheater.phone+"'>" +closestTheater.phone+"</a>"); $("#showing").load("showtimes.php", function(){ $("#showing").listview('refresh'); }); }, function(error){ //error switch(error.code) { case error.TIMEOUT: $("#latLong").prepend("<div class='ui-bar-e'> Unable to get your position: Timeout</div>"); break; case error.POSITION_UNAVAILABLE: $("#latLong").prepend("<div class='ui-bar-e'> Unable to get your position: Position unavailable</div>"); break; case error.PERMISSION_DENIED: $("#latLong").prepend("<div class='ui-bar-e'> Unable to get your position: Permission denied. You may want to check your settings.</div>"); break; case error.UNKNOWN_ERROR: $("#latLong").prepend("<div class='ui-bar-e'> Unknown error while trying to access your position.</div>"); break; } $("#finding").hide(); $("#findTheaterForm").show(); }, {maximumAge:600000}); //nothing too stale }

The key here is the function geolocation.getCurrentPosition, which will prompt the user to allow us access to their location data, as shown here on iPhone

If somebody is a privacy advocate, they may have turned off all location services. In this case, we'll need to inform the user that their choice has impacted our ability to help them. That's what the error function is all about. In such a case, we'll display an error message and show the standard form again.

Creating Mobile Apps with jQuery Mobile Learn to make practical, unique, real-world sites that span a variety of industries and technologies with the world's most popular mobile development library book and ebook.
Published: April 2013
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

Once we have our user's position and the list of theaters, it's time to sort the theaters by distance and show the closest one. The following is a pretty generic code that we may want to use on more than one page. So we'll put this into our global.js file:

function getDistance(lat1, lon1, lat2, lon2){ //great-circle distances between the two points //because the earth isn't flat var R = 6371; // km var dLat = (lat2-lat1).toRad(); var dLon = (lon2-lon1).toRad(); var lat1 = lat1.toRad(); var lat2 = lat2.toRad(); var a = Math.sin(dLat/2) * Math.sin(dLat/2) + QR Codes, Geolocation, Google Maps API, and HTML5 Video [ 84 ] Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2); var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); var d = R * c; //distance in km var m = d * 0.621371; //distance in miles return m; } if (typeof(Number.prototype.toRad) === "undefined") { Number.prototype.toRad = function() { return this * Math.PI / 180; } } function compareDistances(a,b) { if (a.distanceb.distance) return 1; return 0; }

With all of these pieces in place, it is now simple enough to get the user's position and find the closest theater. It will be the first in the array, as well as stored directly in the global variable, closestTheater. If they have JavaScript turned off, we'll have to use some server-side algorithms or APIs to figure out which is closest. Regardless, we are keeping every theater's show times as a set of list items in a flat file (showtimes.php). In a real world situation, this would be database driven and we would call the page with a URL that has the ID of the correct theater. For now, the following code is all we need:

<li data-role="list-divider">Opening This Week</li> <li> <a href="movie.php?id=193818"> <img src = "images/darkknightrises.jpeg"> <h3>Dark Knight Rises</h3> <p>PG-13 - 2h 20m<br/> <strong>Showtimes:</strong> 12:00 - 12:30 - 1:00 - 1:30 - 3:30 - 4:00 - 4:30 – 7:00 - 7:15 - 7:30 - 7:45 - 8:00 - 10:30 - 10:45 </p> </a> </li> <li> <a href="moviedetails.php?id=193812"> <img src = "images/iceagecontinentaldrift.jpeg"> <h3>Ice Age 4: Continental Drift</h3> <p>PG - 1h 56m<br/> <strong>Showtimes:</strong> 10:20 AM - 10:50 AM – 12:40 - 1:15 - 3:00 - 7:00 - 7:30 - 9:30 </p> </a> </li> <li data-role="list-divider">Also in Theaters</li> <li> <a href="moviedetails.php?id=194103"> <img src = "images/savages.jpeg"> <h3>Savages</h3> <p>R - 7/6/2012<br/><strong>Showtimes:</strong> 10:05 AM - 1:05 - 4:05 - 7:05 - 10:15 </p> </a> </li> <li> <a href="moviedetails.php?id=194226"> <img src = "images/katyperrypartofme.jpeg"> <h3>Katy Perry: Part of Me</h3> <p>PG - 7/5/2012<br/> <strong>Showtimes:</strong> 10:05 AM - 1:05 – 4:05 - 7:05 - 10:15 </p> </a> </li> <li> <a href="moviedetails.php?id=193807"> <img src = "images/amazingspiderman.jpeg"> <h3>Amazing Spider-Man</h3> <p>PG-13 - 7/5/2012<br/> <strong>Showtimes:</strong> 10:00 AM - 1:00 – 4:00 - 7:00 - 10:00 </p> </a> </li>

We pull in this page fragment using the following on-page scripts:

$("#showing").load("showtimes.php", function(){ $("#showing").listview('refresh'); });

In this case we have the showtimes.php file containing only the listview items, and we are injecting them directly into the listview before refreshing. Another way to accomplish the same thing would be to have another file, like fullshowtimes.php, be a fully rendered page with headers, footers, and everything. This would be perfect for the situations where JavaScript or geolocation is not available and we have to revert back to standard page submissions

<?php $documentTitle = "Showtimes | Northglen 16 Theatre"; $headerLeftHref = "/"; $headerLeftLinkText = "Home"; $headerLeftIcon = "home"; $headerTitle = ""; $headerRightHref = "tel:8165555555"; $headerRightLinkText = "Call"; $headerRightIcon = "grid"; $fullSiteLinkHref = "/"; ?> <!DOCTYPE html> <html> <head> <?php include("includes/meta.php"); ?> </head> <body> <div id="qrfindclosest" data-role="page"> <div class="logoContainer ui-shadow"></div> <div data-role="content"> <h3>Northglen 14 Theatre</h3> <p><a href="https://maps.google.com/maps?q=Northglen+14+Theatre, +Northeast+80th+Street,+Kansas+City,+MO&hl=en&sll=38.304661, -92.437099&sspn=7.971484,8.470459&oq=northglen+&t=h&hq=Northglen+ 14+Theatre,&hnear=NE+80th+St,+Kansas+City,+Clay, +Missouri&z=15">4900 N.E. 80th Street<br> Kansas City, MO 64119</a> </p> <p><a href="tel:8164681100">816-468-1100</a></p> <p> <ul id="showing" data-role="listview" class="movieListings" data-dividertheme="g"> <?php include("includes/showtimes.php"); ?> </ul> </p> </div> <?php include("includes/footer.php");?> </div> </body> </html>

Then, instead of calling the load function with simply a page, we could load the entire page and then select the elements in the page we want to inject by using the following code:

$("#showing").load("fullshowtimes.php #showing li", function(){ $("#showing").listview('refresh'); });

Certainly, this would be a less efficient way of doing things, but it's worth noting that such a thing can be done. It almost certainly will come in handy in the future.

Creating Mobile Apps with jQuery Mobile Learn to make practical, unique, real-world sites that span a variety of industries and technologies with the world's most popular mobile development library book and ebook.
Published: April 2013
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

Integrating the Google Maps API

We've done well up to this point on our own. We can tell which theater is closest and how far it is as the crow flies. Sadly though, despite all it's promise, the 21st century has not led to us all having private jet packs. Therefore, it is probably best that we not display that distance. Most likely, they're going to drive, ride a bus, bike, or walk.

Let's leverage the Google Maps API (https://developers.google.com/maps/documentation/javascript/). If your site is going to have a lot of API hits, you might have to pay for the business pricing. For us, while we are in development, there's no need.

Here's a look at what we're about to build:

First, we'll need another page to show a map and directions, as well as the script that will actually load the maps from Google Maps API, using the following code:

<div id="directions" data-role="page"> <div data-role="header"> <h3>Directions</h3> </div> <div data-role="footer"> <div data-role="navbar" class="directionsBar"> <ul> <li> <a href="#" id="drivingButton" onClick="showDirections('DRIVING')"> <div class="icon driving"></div> </a> </li> <li> <a href="#" id="transitButton" onClick="showDirections('TRANSIT')"> <div class="icon transit"></div> </a> </li> <li> <a href="#" id="bicycleButton" onClick="showDirections('BICYCLING')"> <div class="icon bicycle"></div> </a> </li> <li> <a href="#" id="walkingButton" onClick="showDirections('WALKING')"> <div class="icon walking"></div> </a> </li> </ul> </div> </div> <div id="map_canvas"></div> <div data-role="content" id="directions-panel"> </div> </div> <script src = "https://maps.googleapis.com/maps/api/js?sensor=true"> </script>

We have several important parts of this page. The first is the navbar attribute within a footer attribute for directions to the theater. What you may not realize is that footers don't actually have to be at the bottom of the page. When you use a navbar attribute within a footer attribute, the link that you clicked on will retain its active state. Without the footer surrounding it, the link will only blink the active state once and then go back to normal. The map_canvas and directions-panel attributes will be filled in by the Google Maps API.

Now, we need to update the CSS code for the extra icons and map constraints. As before, we're keeping them in the location /css/custom.css.

.directionsBar .icon{ height:28px; width:34px; margin:auto; background-repeat:no-repeat; background-position:center center; } .directionsBar .driving{ background-image:url(../icons/xtras-white/16-car.png); background-size:34px 19px; } .directionsBar .transit{ background-image:url(../icons/xtras-white/15-bus.png); background-size:22px 28px; } .directionsBar .bicycle{ background-image:url(../icons/xtras-white/13-bicycle.png); background-size:34px 21px; } .directionsBar .walking{ background-image:url(../icons/icons-white/102-walk.png); background-size:14px 27px; } .theaterAddress{ padding-left:35px; background-image:url(../icons/icons-gray/193-location-arrow.png); background-size:24px 24px; background-repeat:no-repeat; } .theaterPhone{ padding-left:35px; background-image:url(../icons/icons-gray/75-phone.png); background-size:24px 24px; background-repeat:no-repeat; height: 24px; } #map_canvas { height: 150px; } @media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min--moz-device-pixel-ratio: 1.5), only screen and (min-resolution: 240dpi) { .directionsBar .driving{ background-image:url(../icons/xtras-white/16-car@2x.png); } .directionsBar .transit{ background-image:url(../icons/xtras-white/15-bus@2x.png); } .directionsBar .bicycle{ background-image:url(../icons/xtras-white/13 bicycle@2x.png); } .directionsBar .walking{ background-image:url(../icons/icons-white/102-walk@2x.png); } .theaterAddress{ background-image:url(../icons/icons-gray/ 193-location-arrow@2x.png); } .theaterPhone{ background-image:url(../icons/icons-gray/75-phone@2x.png); } }

Next, we'll add a few more global variables and functions to our current on-page scripts.

var directionData = null; var directionDisplay; var directionsService = new google.maps.DirectionsService(); var map; function showDirections(travelMode){ var request = { origin:latitude+","+longitude, destination:closestTheater.geo.lat+"," +closestTheater.geo.long, travelMode: travelMode }; directionsService.route(request, function(response, status){ if (status == google.maps.DirectionsStatus.OK){ directionsDisplay.setDirections(response); } }); $("#directions").live("pageshow", function(){ directionsDisplay = new google.maps.DirectionsRenderer(); var userLocation = new google.maps.LatLng(latitude, longitude); var mapOptions = { zoom:14, mapTypeId: google.maps.MapTypeId.ROADMAP, center: userLocation } map = new google.maps.Map( document.getElementById('map_canvas'), mapOptions); directionsDisplay.setMap(map); directionsDisplay.setPanel( document.getElementById('directions-panel') ); showDirections( google.maps.DirectionsTravelMode.DRIVING ); $("#drivingButton").click(); });

Here, we see the global variables for holding the Google objects. The showDirections method is made to take a string representing one of four different travel modes: 'DRIVING', 'TRANSIT', 'BICYCLING', and 'WALKING'.

We could populate the map and directions at the same time we figure out which theater is closest. It would actually make for a great user experience. However, without analytics to show that the majority of people actually want directions, it makes no sense to incur the costs. Ultimately, that is a business decision, but a company with a customer base of any size could get hammered with API costs. For now, it seems best to trigger the loading of maps and directions when the users go to the directions page.

Geek-out moment—GPS monitoring

So, let's geek-out for a minute. What we've done is probably good enough for most circumstances. We show a map and turn-by-turn directions. Let's take it a step further. The geolocation API does more than just determine your current location. It includes a timestamp (no biggie) and can allow you to continuously monitor the user's position using the method navigator.geolocation.watchPosition (http://dev.w3.org/geo/api/spec-source.html#watch-position). This means that with only a little bit of effort, we can turn our previous direction page into a continuously-updating directions page. In the example code, this is all contained within the file qrresponse2.php.

Again, updating too often could get expensive. So we should really limit how often we redraw the map and directions. For each transportation mode, there is a difference in the amount of meaningful time needed between updates. While we're at it, let's re-do the buttons to contain these options. Here is the entire page's code:

<?php $documentTitle = "Dickinson Theatres"; $headerLeftHref = "/"; $headerLeftLinkText = "Home"; $headerLeftIcon = "home"; $headerTitle = ""; $headerRightHref = "tel:8165555555"; $headerRightLinkText = "Call"; $headerRightIcon = "grid"; $fullSiteLinkHref = "/"; ?> <!DOCTYPE html> <html> <head> <?php include("includes/meta.php"); ?> <style type="text/css"> .logoContainer{ display:block; height:84px; background-image:url(images/header.png); background-position:top center; background-size:885px 84px; background-repeat:no-repeat; } </style> <script type="text/javascript" src = "http://maps.googleapis.com/maps/api/js? key=asdfafefaewfacaevaeaceebvaewaewbk&sensor=true"></script> </head> <body> <div id="qrfindclosest" data-role="page"> <div class="logoContainer ui-shadow"></div> <div data-role="content"> <div id="latLong"> <form id="findTheaterForm" action="fullshowtimes.php" method="get" class="validateMe"> <p> <label for="zip">Enter Zip Code</label> <input type="tel" name="zip" id="zip" class="required number"/> </p> <p><input type="submit" value="Go"></p> </form> </div> <p> <ul id="showing" data-role="listview" class="movieListings" data-dividertheme="g"> </ul> </p> </div> <?php include("includes/footer.php"); ?> </div> <div id="directions" data-role="page"> <div data-role="header"> <h3>Directions</h3> </div> <div data-role="footer"> <div data-role="navbar" class="directionsBar"> <ul> <li> <a href="#" id="drivingButton" data-transMode="DRIVING" data-interval="10000"> <div class="icon driving"></div> </a> </li> <li> <a href="#" id="transitButton" data-transMode="TRANSIT" data-interval="10000"> <div class="icon transit"></div> </a> </li> <li> <a href="#" id="bicycleButton" data-transMode="BICYCLING" data-interval="30000"> <div class="icon bicycle"></div> </a> </li> <li> <a href="#" id="walkingButton" data-transMode="WALKING" data-interval="60000"> <div class="icon walking"></div> </a> </li> </ul> </div> </div> <div id="map_canvas"></div> <div data-role="content" id="directions-panel"></div> </div>

So, now let's look at the on-page scripts for this GPS monitoring edition:

<script type="text/javascript"> //declare our global variables var theaterData = null; var timestamp = null; var latitude = null; var longitude = null; var closestTheater = null; var directionData = null; var directionDisplay; var directionsService = new google.maps.DirectionsService(); var map; var positionUpdateInterval = null; var transporationMethod = null; //Once the page is initialized, hide the manual zip form //and place a message saying that we're attempting to find their location. $(document).on("pageinit", "#qrfindclosest", function(){ if(navigator.geolocation){ $("#findTheaterForm").hide(); $("#latLong").append("<p id='finding'>Finding your location...</p>"); } }); $(document).on("pageshow", "#qrfindclosest", function(){ theaterData = $.getJSON("js/theaters.js", function(data){ theaterData = data; selectClosestTheater(); }); $("div.directionsBar a").click(function(){ if(positionUpdateInterval != null){ clearInterval(positionUpdateInterval); } var $link = $(this); transporationMethod = $link.attr("data-transMode"); showDirections(); setInterval(function(){ showDirections(); },Number($link.attr("data-interval"))); }); function showDirections(){ var request = { origin:latitude+","+longitude, destination:closestTheater.geo.lat+"," +closestTheater.geo.long, travelMode: transportationMethod } directionsService.route(request, function(response, status) { if (status == google.maps.DirectionsStatus.OK) { directionsDisplay.setDirections(response); } }); } $(document).on("pageshow", "#directions", function(){ directionsDisplay = new google.maps.DirectionsRenderer(); var userLocation = new google.maps.LatLng(latitude, longitude); var mapOptions = { zoom:14, mapTypeId: google.maps.MapTypeId.ROADMAP, center: userLocation } map = new google.maps.Map(document.getElementById('map_canvas'), mapOptions); directionsDisplay.setMap(map); directionsDisplay.setPanel( document.getElementById('directions-panel')); if(positionUpdateInterval == null) { transportationMethod = "DRIVING"; positionUpdateInterval = setInterval(function(){ showDirections(); },(10000)); } $("#drivingButton").click(); }); function selectClosestTheater(){ var watchId=navigator.geolocation.watchPosition( function(position){ //success latitude = position.coords.latitude; longitude = position.coords.longitude; timestamp = position.timestamp; var dt = new Date(); dt.setTime(timestamp); for(var x = 0; x < theaterData.theaters.length; x++){ var theater = theaterData.theaters[x]; var distance = getDistance(latitude, longitude, theater.geo.lat, theater.geo.long); theaterData.theaters[x].distance = distance; } theaterData.theaters.sort(compareDistances); closestTheater = theaterData.theaters[0]; $("#latLong").html("<div class='theaterName'>" +closestTheater.name +"</div><p class='theaterAddress'> <a href='#directions'>" +closestTheater.address+"<br/>" +closestTheater.city+", " +closestTheater.state +" "+closestTheater.zip +"</a></p><p class='theaterPhone'><a href='tel:" +closestTheater.phone+"'>" +closestTheater.phone+"</a></p>" ); $("#showing").load("fullshowtimes.php #showing li", function(){ $("#showing").listview('refresh'); }); } }, function(error){ //error $("#findTheaterForm").show(); $("#finding").hide(); switch(error.code) { case error.TIMEOUT: $("#latLong").prepend("<div class='ui-bar-e'> Unable to get your position: Timeout</div>"); break; case error.POSITION_UNAVAILABLE: $("#latLong").prepend("<div class='ui-bar-e'> Unable to get your position: Position unavailable</div>"); break; case error.PERMISSION_DENIED: $("#latLong").prepend("<div class='ui-bar-e'> Unable to get your position: Permission denied. You may want to check your settings.</div>"); break; case error.UNKNOWN_ERROR: $("#latLong").prepend("<div class='ui-bar-e'> Unknown error while trying to access your position.</div>"); break; } }); } </script> </body> </html>

Linking and embedding video

Previews are a staple in the movie industry. We could simply link directly to the previews on YouTube as many do. Here's a simple way to do it:

<p><a data-role="button" href="http://www.youtube.com/watch?v=J9DlV9qwtF0"> Watch Preview</a></p>

That will work but the problem is that it takes the user away from your site. While that may not be the end of the world from a user's perspective, it's a big e-commerce no-no.

So, in order to improve the experience and keep the user on our own site, let's directly embed the HTML5 video and use the universal image for movie previews as we have depicted here.

Despite the fact that it looks like this will play in a teeny-tiny segment of the page, on smartphones, the video will play in fullscreen landscape mode. The story is a little different on the iPad where it will play inline at the embedded side.

Ultimately, we'd like to push the right-sized video back to the user for their device using the following code. Smartphones without high-resolution displays aren't exactly going to benefit from a 720p video.

<video id="preview" width="100%" controls poster="images/preview.gif"> <source src = "previews/batmanTrailer-2_720.mp4" type="video/mp4" media="only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min--moz-device-pixel-ratio: 1.5), only screen and (min-resolution: 240dpi)"/> <source src = "previews/batmanTrailer-1_480.mov" type="video/mov" /> <a data-role="button" href="http://www.youtube.com/watch?v=J9DlV9qwtF0"> Watch Preview</a> </video>

If the browser recognizes the HTML5 video tag, the player will start at the top and look through each source tag until it finds one that it knows how to play and matches the right media query (if media queries have been specified). If the browser does not support HTML5 video, it will not know what to do with the video and source tags, and simply consider them to be valid XML elements. They will be treated like extraneous div tags and the link button will be displayed.

As you can see, we've added media queries here to different sources. If it's a highresolution screen, we'll load a prettier video. You could really geek out here by adding lots of different sources: a 480p video for the average smartphone, a 720p video for the iPhone and early iPads, and a 1080p video for the 3rd generation iPad. The only word of caution here is that even though the Apple Retina Display is capable of showing a much more beautiful video, it still has to come over the same pipes. Loading a smaller video might still be better because it will play sooner and cost the customer less bandwidth.

Let's add a little more CSS to this picture. We're leaving the width at 100 percent of whatever is containing it. On smartphones, the picture ratio will scale properly as the width increases. The iPad, not so much. So, let's detect its screen resolutions using media queries and give it an explicit height that will take better advantage of the real estate.

/* iPad ----------------*/ @media only screen and (min-device-width: 768px) and (max-device-width: 1024px) { #preview{ height:380px;} }

Summary

We've explored the boundaries of modern media on smartphones. You can now brainstorm on the uses and take advantage of QR codes, find out where the user is, monitor the user's position, get directions and maps from Google, and feed responsive videos to the user.

Think about all you have just learned. How hard would it be to create a socially connected website that would allow users to get maps to each other's positions that continue to update as they move closer or further apart. It could be valuable if packaged and marketed properly.

Resources for Article :


Further resources on this subject:


About the Author :


Shane Gliser

Shane Gliser graduated from Washburn University in 2001, specializing in Java development. Over the next several years, he developed a love of web development and taught himself HTML, CSS, and JavaScript. Having shifted his focus again, Shane's primary passions are user experience and the mobile web.

Shane began working with jQuery Mobile while it was still in the Alpha 2 phase and deployed American Century Investments' mobile site while the framework was still in Beta 2. Since then, he has rebranded and re-launched his own personal business, Roughly Brilliant Digital Studios (http://roughlybrilliant.com), as a place where he could start blogging tips about using jQuery Mobile.

Books From Packt


 HTML5 Video How-To [Instant]
HTML5 Video How-To [Instant]

jQuery 1.4 Reference Guide
jQuery 1.4 Reference Guide

 jQuery Plugin Development Beginner's Guide
jQuery Plugin Development Beginner's Guide

 jQuery Tools UI Library
jQuery Tools UI Library

jQuery Mobile First Look
jQuery Mobile First Look

jQuery Mobile Web Development Essentials
jQuery Mobile Web Development Essentials

Learning jQuery 1.3
Learning jQuery 1.3

jQuery UI 1.8: The User Interface Library for jQuery
jQuery UI 1.8: The User Interface Library for jQuery


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software