Build a Location-Based Mobile App With HTML5 and Javascript: Part 3

Web & App Developer
This entry is part 3 of 6 in the series Build a Location-Based Mobile App With HTML5 and Javascript

Build a Location-Based Mobile App With HTML5 and Javascript

In the previous part of the series, I showed you the code of all the HTML pages of “Where I parked my car”. However, so far they are useless because we’ve not written the business logic behind them. In this article, we will start adding the few configurations lines of jQuery Mobile to the project, and then I’ll continue explaining some of the JavaScript files that power the application, as well as describing several Cordova APIs.

jQuery Mobile Configuration

jQuery Mobile does a lot for you just by adding it to your project without any special settings. However, there will be times when you want to modify or to take control of some default behavior. You can achieve this writing a configuration file. As stated in the first part of the series, the app has a file called jquery.mobile.config.js that, as you might guess, contains a configuration for jQuery Mobile. The most observant of you might have noticed that in the index.html file, the configuration is loaded before the library. You have to do so because when jQuery Mobile starts, it fires an event called mobileinit, and this is also the event you have to bind to override the default settings. Hence, you have to attach the handler before jQuery Mobile starts, but after the jQuery library.

You can override the default configuration in two ways. The first is using the jQuery extend() method as you can see in the following example.

$(document).bind('mobileinit', function() {
  $.extend($.mobile , {
    property1: value1,
    property2: value2
    // and so on...
  });
});

The second way is to set the properties using the $.mobile object.

$(document).bind('mobileinit', function() {
  $.mobile.property1 = value1;
  $.mobile.property2 = value2;
  // and so on...
});

Explaining all the jQuery Mobile properties is outside the scope of this article, however you can study them in deep detail by reading the jQuery Mobile configuration docs.

In the app configuration file, I won’t change a lot of properties. I’ll focus on changing the pages’ transition, the option to show the text when showing the page loader widget, and the theme. The full source of the file (that uses the second method to set jQuery Mobile properties) is listed below.

$(document).on(
  'mobileinit',
  function()
  {
    // Default pages' transition effect
    $.mobile.defaultPageTransition = 'slide';

    // Page Loader Widget
    $.mobile.loader.prototype.options.textVisible = true;

    // Theme
    $.mobile.page.prototype.options.theme  = 'b';
    $.mobile.page.prototype.options.headerTheme = 'b';
    $.mobile.page.prototype.options.contentTheme = 'b';
    $.mobile.page.prototype.options.footerTheme = 'b';
    $.mobile.page.prototype.options.backBtnTheme = 'b';
  }
);

The APIs

From this section on, I’ll start describing the APIs used throughout the project. Apart from the Google Maps API, the other two—that is the Geolocation API and the Web Storage API—are W3C specifications. We’ll use this APIs through the Cordova framework, however some devices may already provide an implementation of these APIs. For those devices, Cordova will use their built-in support instead of the Cordova’s implementation, but for those that don’t have storage support, the Cordova implementation has been built to remain compatible with the W3C specs.

Managing the Locations

In this section I’ll show you the class called Position, which is responsible for managing the locations. It’s aim is to create, delete, and load the locations using the Web Storage API that is composed by two areas: the Session and the Local. Cordova uses the local storage because the storage is always at app level. All the locations will be stored in an item called “positions.”

Before going on, I want to highlight two facts. The first is that to store complex data (like objects and arrays) using the Web Storage API, you have to use the JSON format. So, in the Position class methods, you’ll see the use of the JSON class and its parse() and stringify() methods. The second is that Windows Phone 7 doesn’t support the dot notation so, you must use the setItem() and getItem() methods to ensure the compatibility for all devices. This is just the first of a lot of quirks that you have to face while developing with Cordova. Since with “Where I parked my car” we’re not targeting a specific platform, you have to take into account the different support and the quirks of the Cordova API. Luckily, the first one we encountered wasn’t so hard to deal with.

For the purpose of the app, we need to save the user’s and car’s latitude and longitude, but we also need the accuracy of the measurement to give the user an idea of how precise our locations are. I’ll wrap the first three data into a class called Coords.

Reading coordinates isn’t very user-friendly, so to enhance the user experience, we’ll use the Google Maps API to retrieve the address of the current location. The app will also store the date and time associated with that GPS location. Remember that the app will store up to 50 locations, so if the limits is reached, the extra positions (the older ones) will be eliminated using the JavaScript slice() method.

For what discussed so far, the starting point of the Position class is implemented by the following code.

function Position(position, address, datetime)
{
  var _db = window.localStorage;
  var MAX_POSITIONS = 50;

  this.getMaxPositions = function()
  {
    return MAX_POSITIONS;
  }

  this.position = position;
  this.address = address;
  this.datetime = datetime;
}

function Coords(latitude, longitude, accuracy)
{
  this.latitude = latitude;
  this.longitude = longitude;
  this.accuracy = accuracy;
}

As you may have guessed, in the JavaScript sense of the object-oriented approach, the _db property is private and MAX_POSITIONS is a constant. With the given skeleton we can’t do much, in fact, we need methods to let the application interface with the Web Storage API. These methods are

  • savePosition() to store the car’s position
  • updatePosition() to update the last position retrieved with the address if the callback to the Google Maps API succeed
  • deletePosition() to allow the user to remove a previously saved position
  • getPositions() to load all the stored locations

In all the cited methods, I’ll test if the database var (_db) is null and if it is, I’ll show an error message to the user. I test for this state because I like to try to anticipate and manage unexpected interface problems. To show the message, I’ll take advantage of the alert() method of the Cordova Notification API. Most of the supported platforms use a native dialog box, however, others (like Bada 2.X) use the well-known browser’s alert() function’s look and feel, which is less customizable. The Cordova alert() function accepts up to four parameters:

  1. message: The string that contains the message to show
  2. alertCallback: A callback to invoke when the alert dialog is dismissed
  3. title: The title of the dialog (by default is “Alert”)
  4. buttonName: The text of the button included in the dialog (by default is “OK”)

There’s another quirk that we have to consider while developing with PhoneGap. Windows Phone 7 ignores the button name and always uses the default. And, there isn’t a built-in browser alert, so if you want to use alert('message'); you have to assign window.alert = navigator.notification.alert. For a complete list of the differences between mobile operating systems, refer to the Cordova Notification alert() documentation.

If you’ve gotten this far, you deserve a prize: the full source of the described methods. You can copy and paste the code inside the Position class after the getMaxPositions() methods or before, it doesn’t matter.

this.savePosition = function(position, address)
{
  if (!_db)
  {
    console.log('The database is null. Unable to save position');
    navigator.notification.alert(
      'Unable to save position',
      function(){},
      'Error'
    );
  }

  var positions = this.getPositions();
  if (positions == null)
    positions = [];

  positions.unshift(new Position(position, address, new Date()));
  // Only the top MAX_POSITIONS results are needed
  if (positions.length > this.MAX_POSITIONS)
    positions = positions.slice(0, this.MAX_POSITIONS);

  _db.setItem('positions', JSON.stringify(positions));

  return positions;
}

this.updatePosition = function(index, position, address)
{
  if (!_db)
  {
    console.log('The database is null. Unable to update position');
    navigator.notification.alert(
      'Unable to update position',
      function(){},
      'Error'
    );
  }

  var positions = this.getPositions();
  if (positions != null && positions[index] != undefined)
  {
    positions[index].coords = position;
    positions[index].address = address;
  }

  _db.setItem('positions', JSON.stringify(positions));

  return positions;
}

this.deletePosition = function(index)
{
  if (!_db)
  {
    console.log('The database is null. Unable to delete position');
    navigator.notification.alert(
      'Unable to delete position',
      function(){},
      'Error'
    );
  }

  var positions = this.getPositions();
  if (positions != null && positions[index] != undefined)
    positions.splice(index, 1);

  _db.setItem('positions', JSON.stringify(positions));

  return positions;
}

this.getPositions = function()
{
  if (!_db)
  {
    console.log('The database is null. Unable to retrieve positions');
    navigator.notification.alert(
      'Unable to retrieve positions',
      function(){},
      'Error'
    );
  }

  var positions = JSON.parse(_db.getItem('positions'));
  if (positions == null)
    positions = [];

  return positions;
}

Conclusion

In this third part, you’ve seen how to configure “Where I parked my car” to change the pages’ transition, the option to show the text when showing the page loader widget, and the theme. You also learned how to store the car’s positions using the Web Storage API. In the next article, I’ll show you the last two JavaScript files: maps.js, and functions.js. The former contains the functions to initialize the application and some other utility functions, while the latter has the functions that use the Google Maps API to draw the map and the route to the car.

Build a Location-Based Mobile App With HTML5 and Javascript

<< Build a Location-Based Mobile App With HTML5 and Javascript: Part 2Build a Location-Based Mobile App With HTML5 and Javascript: Part 4 >>

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Christian

    Is there a way to load cordova.js with a CDN?

  • http://www.audero.it/ Aurelio De Rosa

    Hi Christian and thank you for the comment. The method to use a CDN, as if it was JQuery or similar, doesn’t apply in this case because Cordova isn’t a simple JavaScript. In fact, as you may know, you have also to attach a Jar file when you create an app. Moreover, the files are different for every platform and let it would be possible, what will be happen if you use a CDN and the user will be offline?

  • http://bambicorro.com Bambi Corro III

    Hi Aurelio, I tried searching for this app on the Apps Store but to no avail. Dropped it?

    • http://www.audero.it/ Aurelio De Rosa

      Hi and thank you for your interest. This app isn’t and on a market place and probably will never be. However, in the final article of the series, I’ll give the link to its public repository so anyone can use it.

  • http://www.davconnect.com Abhishek

    When is the last part going to come out?

  • pmcdonnell

    Great stuff………..

    waiting for the last part

  • http://www.audero.it/ Aurelio De Rosa

    I’m very glad that so many people are interested in this series. However, the fourth part won’t be the last because the code to explain was too much.

  • Ari

    Great stuff… Thankin’ you, kind Sir!

    P.S.
    A typo has this installment listed as “… part 2 of 3 in the series”.
    You also have “… part 1 of 3 in the series” — twice

    P.P.S.
    Having some problem obtaining/extracting the following :
    [1] ajax-loader.png
    [2] cordova-2.0.0.js

    Wonder if it is just me…

    • http://www.audero.it/ Aurelio De Rosa

      Hi Ari. Thank you for having pointed out the series error, I fixed it. About the file cordova-2.0.0.js you can obtain it downloading the version 2.0.0 of Cordova (PhoneGap) through their download page at http://phonegap.com/download. About the file ajax-loader.png you can find it in the zip file of jQuery Mobile.

  • Ari

    Hi Aurelio: It is just a typo — nothing serious.

    For the benefit of those who may encounter the same problems I did, please note that ajax-loader.png file can be found in the version 1.1.1 of jQuery Mobile zip file. (Version 1.2.0 contains only ajax-loader.gif)

    When I downloaded Cordova (PhoneGap) through their download page at http://phonegap.com/download, I found multiple instances cordova-2.0.0.js. I am not sure which instance to use.

    • http://www.audero.it/ Aurelio De Rosa

      You’re right about the image, I fixed it in the first part of the series. About the cordova-2.0.0.js file, you can find the explanation in the first article (“Remember that Cordova uses a different JavaScript file [...] and I’ll include the Android JavaScript file”).

      • Ari

        Aurelio:

        I clearly missed that… Thanks!

        • http://www.audero.it/ Aurelio De Rosa

          Don’t worry mate and thank you for your interest in my article.

  • Jonathan

    Hi, I see part 3 of 3, but cannot find the public link for the project download as mentioned. Am I overlooking it? or should manually build the pages from the info. Great article

    • http://www.audero.it/ Aurelio De Rosa

      Hi Jonathan. The parts are actually 6 but only 3 have already been published (the remainings will be published in few days). This is the reason you don’t see the repository.