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

This entry is part 5 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 our series, I explained how to use the Google Maps API to display stored locations on a map and to trace a route from the user’s current position to his car. I also demonstrated several utility functions that will be useful during the development of the application. In this penultimate article, I’ll describe the final utility function and those that complete “Where I parked my car”. In the final part of the series, we’ll demonstrate how to run our finished Cordova application optimally.

Utility Functions

As described in the introduction, there is still one more utility function to explain, and that is the one to retrieve the value of a parameter inside a URL.

Retrieving a URL Parameter’s Value

The HTTP protocol has several request methods, but the two most common are GET and POST. As you most likely know, the GET method sends the data through the URL appending variables and values after a question mark (www.website.com?variableone=build&variable2=mobile). Unfortunately, jQuery Mobile doesn’t offer a native method to retrieve a single parameter of the URL. In fact, the only method that does something similar is $.mobile.path.parseUrl(). However, this method returns an object having a property called search that gives you all the parameters, not just the one desired.

To meet our development needs, I created the following function.

function urlParam(name)
{
   var results = new RegExp('[?&]' + name + '=([^&#]*)').exec(window.location.href);
   if (results != null && typeof results[1] !== 'undefined')
      return results[1];
   else
      return null;
}

Initializing the Application

Now that you’ve seen all of the utility functions, I’ll explain the two initialization functions that power “Where I parked my car”. The first function we’ll look at, called initApplication(), determines the behavior of application elements like homepage buttons (index.html). The positions’ history page (positions.html). initApplication() is responsible for managing several other elements, which you’ll see in greater detail in the following sections.

Disable Buttons Conditionally

In part 4 of our series, I illustrated the requirements of our application, showing you the function that tests to see if they all are satisfied (checkRequirements()). When the user runs “Where I parked my car”, if the test fails, the buttons to set the position and to find the car will be disabled, and the user will be notified of the connectivity issues. (In this scenario, they lack the Internet access needed to use the Google Maps API.) This is done using the following code:

$('#set-car-position, #find-car').click(function() {
   if (checkRequirements() === false)
   {
      $(this).removeClass('ui-btn-active');
      return false;
   }
});

Be Responsive

To enhance the application interface and make it responsive, I created the function updateIcons() that I’ll use as a handler for the pagebeforecreate and the orientationchange events.

$(document).on('pagebeforecreate orientationchange', updateIcons);

Get or Set the Position

Th next piece of code, one of the most important parts of the whole app, is the callback function attached to the pageshow event of the map-page element. It’s responsible for checking if the page has been required to set or to get the car’s position and act accordingly using the urlParam() function. If the value of the variable requestType is set, we’ll use the getCurrentPosition() method of the Geolocation API to retrieve the position and then store it in the app database using the Web Storage API. After saving the position, I’ll use both requestLocation() and displayMap() methods of our Map class to retrieve the address of the car’s position and to display the map indicating the user position with a marker.

On the other hand, if the value of requestType is get, we’ll use the watchPosition() method of the Geolocation API to poll the user location to constantly refresh the route to his car.

The necessary code is below.

$('#map-page').live(
   'pageshow',
   function()
   {
      var requestType = urlParam('requestType');
      var positionIndex = urlParam('index');
      var geolocationOptions = {
         timeout: 15 * 1000, // 15 seconds
         maximumAge: 10 * 1000, // 10 seconds
         enableHighAccuracy: true
      };
      var position = new Position();

      $.mobile.loading('show');
      // If the parameter requestType is 'set', the user wants to set
      // his car position else he want to retrieve the position
      if (requestType == 'set')
      {
         navigator.geolocation.getCurrentPosition(
            function(location)
            {
               // Save the position in the history log
               position.savePosition(
                  new Coords(
                     location.coords.latitude,
                     location.coords.longitude,
                     location.coords.accuracy
                  )
               );
               // Update the saved position to set the address name
               Map.requestLocation(location);
               Map.displayMap(location, null);
               navigator.notification.alert(
                  'Your position has been saved',
                  function(){},
                  'Info'
               );
            },
            function(error)
            {
               navigator.notification.alert(
                  'Unable to retrieve your position. Is your GPS enabled?',
                  function(){
                     alert("Unable to retrieve the position: " + error.message);
                  },
                  'Error'
               );
               $.mobile.changePage('index.html');
            },
            geolocationOptions
         );
      }
      else
      {
         if (position.getPositions().length == 0)
         {
            navigator.notification.alert(
               'You have not set a position',
               function(){},
               'Error'
            );
            $.mobile.changePage('index.html');
            return false;
         }
         else
         {
            navigator.geolocation.watchPosition(
               function(location)
               {
                  // If positionIndex parameter isn't set, the user wants to retrieve
                  // the last saved position. Otherwise he accessed the map page
                  // from the history page, so he wants to see an old position
                  if (positionIndex == undefined)
                     Map.displayMap(location, position.getPositions()[0]);
                  else
                     Map.displayMap(location, position.getPositions()[positionIndex]);
               },
               function(error)
               {
                  console.log("Unable to retrieve the position: " + error.message);
               },
               geolocationOptions
            );
         }
      }
   }
);

Initialize the Positions’ History Page

As soon as the user requires the file positions.html, jQuery Mobile will start enhancing the components of the page. After the jQuery framework did its job, we need to retrieve the user’s previous locations and show them as a list. So, I’ll set a callback function to the pageinit event of the position-page element. The callback, will simply call the createPositionsHistoryList() function that will be illustrated in a few moments.

$('#positions-page').live(
   'pageinit',
   function()
   {
      createPositionsHistoryList('positions-list', (new Position()).getPositions());
   }
);

Create the Positions’ History List

As stated before, createPositionsHistoryList() creates a list, item by item, using the stored position history and the Web Storage API. Each entry in the list has two actions that the user can run. The first action executes once the user touches the text of a previous location’s address, which will show it on the map in the same way that it displays the current car location when “Find car” is clicked. This is achieved by sending the same value (get) to the map file (map.html) for the requestType parameter and by adding an additional one, called index, that indicates which item of the locations’ history must be processed. If the connection isn’t available and a location is selected, the user will be notified of the connectivity problem and no other action will take place.

The second action is executed by touching the delete icon that is shown on the right side of the address for each item in the list. This, of course, will delete the selected item both from the list and the database. If there are any problems with deleting the item, the user will be warned.

The full source of createPositionsHistoryList() is listed below.

/**
 * Create the positions' history list
 */
function createPositionsHistoryList(idElement, positions)
{
   if (positions == null || positions.length == 0)
      return;

   $('#' + idElement).empty();
   var $listElement, $linkElement, dateTime;
   for(var i = 0; i < positions.length; i++)
   {
      $listElement = $('<li>');
      $linkElement = $('<a>');
      $linkElement
      .attr('href', '#')
      .click(
         function()
         {
            if (checkRequirements() === false)
               return false;

            $.mobile.changePage(
               'map.html',
               {
                  data: {
                     requestType: 'get',
                     index: $(this).closest('li').index()
                  }
               }
            );
         }
      );

      if (positions[i].address == '' || positions[i].address == null)
         $linkElement.text('Address not found');
      else
         $linkElement.text(positions[i].address);

      dateTime = new Date(positions[i].datetime);
      $linkElement.text(
         $linkElement.text() + ' @ ' +
         dateTime.toLocaleDateString() + ' ' +
         dateTime.toLocaleTimeString()
      );

      // Append the link to the <li> element
      $listElement.append($linkElement);

      $linkElement = $('<a>');
      $linkElement.attr('href', '#')
      .text('Delete')
      .click(
         function()
         {
            var position = new Position();
            var oldLenght = position.getPositions().length;
            var $parentUl = $(this).closest('ul');

            position.deletePosition($(this).closest('li').index());
            if (oldLenght == position.getPositions().length + 1)
            {
               $(this).closest('li').remove();
               $parentUl.listview('refresh');
            }
            else
            {
               navigator.notification.alert(
                  'Position not deleted. Something gone wrong so please try again.',
                  function(){},
                  'Error'
               );
            }

         }
      );
      // Append the link to the <li> element
      $listElement.append($linkElement);

      // Append the <li> element to the <ul> element
      $('#' + idElement).append($listElement);
   }
   $('#' + idElement).listview('refresh');
}

Running the Application

In the last section, all the pieces of the application were built and all the HTML, CSS, and JavaScript files were put in their place. So, now you’re ready to build and deploy “Where I parked my car”. But, you have to set the entry functions for the whole application. As you might have guessed, the function will be initApplication() and it will run once Cordova is fully loaded. In this way, you can safely calls the Cordova APIs.

To achieve this goal we’ve set initApplication() as a callback function for the deviceready event. This is done by adding the following code to the index.html file as you’ve already in its source listed in the second part of the series.

<script>
   $(document).one('deviceready', initApplication);
</script>

Conclusion

In this article, I explained the last functions of the file function.js. In the next and last part of this series, you’ll see how to create the configuration file (config.xml), described in the first article for use with Adobe PhoneGap Build. The configuration file will help us to specify certain properties of the application like the author, the loading screens, the permissions, and so on. As you may expect, I’ll also publish the link to the repository where you can download the full source so that you can play with it. I’ll also share some final thoughts on what features you can add to further improve the application and what I hope the Cordova team will implement in the next releases that could take our app to the next level.

Build a Location-Based Mobile App With HTML5 and Javascript

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

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.

  • http://www.arttitude360.com/ Tolu

    Great series you have here, Aurelio. Well-written!

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

    Glad that you like it.

  • dino

    Is there a place where we can preview how this is supposed to look like?

    I dont see it anywhere?

    Thanks,

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

      Currently there aren’t screenshots but you can easily install the app and use it.