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

Aurelio De Rosa
Share

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.

CSS Master, 3rd Edition