Showing posts with label html5. Show all posts
Showing posts with label html5. Show all posts

Using AngularJs in Worklight/PhoneGap application

Angular.js is a JavaScript MVC framework, that makes development of HTML applications easy. I wanted to figure out how to use it for developing Worklight application so i followed these steps to build simple Hello AngularJs application
  • First create a WorkLight application using WorkLight wizard, make sure that it works
  • Next make changes in the index.html or entry page of your application to include angular.js from Google CDN and also add <p>Hello {{'World'.length}}</p>
  • to test if AngularJs template is working
    
    <!DOCTYPE HTML>
    <html>
    <head>
     <meta charset="UTF-8">
     <title>HelloWorld</title>
     <meta name="viewport" content="width=device-width, 
    initial-scale=1.0, maximum-scale=1.0, 
    minimum-scale=1.0, user-scalable=0">
     <link rel="shortcut icon" href="images/favicon.png">
     <link rel="apple-touch-icon" href="images/apple-touch-icon.png">
     <link rel="stylesheet" href="css/HelloWorld.css">
     <script>window.$ = window.jQuery = WLJQ;</script>
    </head>
    <body id="content" style="display: none;" ng-app>
     <h1>Hello Angularjs</h1> 
     <p>Hello {{'World'.length}}</p>
     <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.min.js" />
     <script src="js/initOptions.js"></script>
     <script src="js/HelloWorld.js"></script>
     <script src="js/messages.js"></script>
    </body>
    </html
    
After deployment you will notice that it prints Hello + length of 'world' which is 5 characters like this

Use Worklight to encrypt the data stored in window.localStorage

In the Reading data stored in localStroage by Android Device or Google Chrome Browser entry i talked about how easy it is to read data stored by web application in the window.localStorage object. WOrklight provides alternative which is to use a Encrypted cache that still uses window.localStorage object to store the data but encrypts the actual data to make it harder for someone else to read that data, even if they get access to your mobile or desktop. I wanted to try this feature out so i built this simple application which lets me store and read data from encrypted cache
First i did build a simple HTML file like this

<!DOCTYPE html>    
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, 
    initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" />
    <title>HelloEncryptedCache</title>
    <link rel="shortcut icon" href="images/favicon.png" />
    <link rel="apple-touch-icon" href="images/apple-touch-icon.png" />
    <link rel="stylesheet" href="css/reset.css" />
    <link rel="stylesheet" href="css/HelloEncryptedCache.css" />
  </head>
  <body onload="WL.Client.init({})" id="content" style="display: none">
    <h1>Encrypted Cache</h1>
    <table>
      <tr>
        <td>Encryption Key</td>
        <td><input type="text" name='encryptionKey' id="encryptionKey" /></td>
      </tr>
      <tr>
        <td><button id="openCache">Open Cache</button></td>
        <td><button id="closeCache">Close Cache</button></td>
      </tr>
      <tr>
        <td><button id="destroyCache">Destroy Cache</button></td>
      </tr>
      <tr>
        <td>Key</td>
        <td><input type="text" name='key' id="key" /></td>
      </tr>
      <tr>
        <td>value</td>
        <td><input type="text" name='value' id="value" /></td>
      </tr>
      <tr>
        <td><button id="encryptKey">Encrypt Key/Value</button></td>
        <td><button id="decryptKey">Decrypt Key</button></td>
      </tr>
      <tr>
        <td><button id="removeKey">Remove key</button></td>
      </tr>
    </table>
    <script src="js/HelloEncryptedCache.js"></script>
    <script src="js/messages.js"></script>
    <script src="js/auth.js"></script>
  </body>
</html>
This is how my JavaScript on the page looks like

window.$ = WLJQ;

function wlCommonInit(){
  $("#openCache").click(function(){
    console.log('The openCache button is clicked ' +$("#encryptionKey").val());
    WL.EncryptedCache.open($("#encryptionKey").val(), true, function(){
      console.log('The cache key opened successfully');
    },onOpenError);
  });
  $("#closeCache").click(function(){
    console.log('The closeCache button is clicked');
    WL.EncryptedCache.close(function(){
      console.log('The cache is closed successfully');
    });
  });
  $("#destroyCache").click(function(){
    console.log('The destroyCache button is clicked');
    WL.EncryptedCache.destroy(function(){
      console.log('Successfully destroyed the encrypted cache');
    });
  });
  $("#encryptKey").click(function(){
    console.log('The encryptKey button is clicked');
    WL.EncryptedCache.write($("#key").val(), $("#value").val(), function() {
      console.log('The entry written successfully');
    }, function(status){
      console.log('There was error in encryptingKey ' + status);
      switch(status){
      case WL.EncryptedCache.ERROR_KEY_CREATION_IN_PROGRESS:
        console.log('Error in key creation process');
        break;
      case WL.EncryptedCache.ERROR_LOCAL_STORAGE_NOT_SUPPORTED:
        console.log('Local storage is not supported');
        break;
      case WL.EncryptedCache.ERROR_NO_EOC:
        console.log('No EOC');
        break;
      case WL.EncryptedCache.ERROR_COULD_NOT_GENERATE_KEY:
        console.log('Could not generate key');
        break;
      case WL.EncryptedCache.ERROR_CREDENTIALS_MISMATCH:
        console.log('Credentials mismatch');
        break;
      }
    }); 
  });
  $("#decryptKey").click(function(){
    console.log('The decryptKey button is clicked');
    WL.EncryptedCache.read($('#key').val(), function(value) {
      console.log('Value from the encrypted cache is ' + value);
      alert('Encrypted value for the key -> ' + value);
    }, function(status){
      console.log('There was error in encryptingKey ' + status);
      switch(status){
      case WL.EncryptedCache.ERROR_KEY_CREATION_IN_PROGRESS:
        console.log('Error in key creation process');
        break;
      case WL.EncryptedCache.ERROR_LOCAL_STORAGE_NOT_SUPPORTED:
        console.log('Local storage is not supported');
        break;
      case WL.EncryptedCache.ERROR_NO_EOC:
        console.log('No EOC');
        break;
      case WL.EncryptedCache.ERROR_COULD_NOT_GENERATE_KEY:
        console.log('Could not generate key');
        break;
      case WL.EncryptedCache.ERROR_CREDENTIALS_MISMATCH:
        console.log('Credentials mismatch');
        break;
      }
    });
  });
  $("#removeKey").click(function(){
    console.log('The removeKey button is clicked');
    WL.EncryptedCache.remove($('#key').val(), function(){
      console.log('The encrypted key removed successfully ->' + $('#key').val() );
    })
  });
}
function onOpenError(status) {
  console.log("Inside onOpenError " + status);
  switch (status) {
  case WL.EncryptedCache.ERROR_KEY_CREATION_IN_PROGRESS:
    console.log("Error key creation in progress");
    break;
  case WL.EncryptedCache.ERROR_LOCAL_STORAGE_NOT_SUPPORTED:
    console.log("Error local storage not supported");
    break;
  case WL.EncryptedCache.ERROR_CREDENTIALS_MISMATCH:
    console.log("Error credentials mismatch");
    break;
  case WL.EncryptedCache.ERROR_SECURE_RANDOM_GENERATOR_UNAVAILABLE:
    console.log("Error secure random generator unavailable");
    break;
  case WL.EncryptedCache.ERROR_NO_EOC:
    console.log("Error no eoc");
    break;
  }
}
The JavaScript has one event handler for each of the button and when you click on the button it makes use of the WL.EncryptedCache API to read/write cache entries. While working with encrypted cache first you have to open the cache before you can write any entry and once your done writing cache entries you will have to close the cache. I noticed one strange thing is if i dont attach error handling functions then my code works in normal browser but it throws undefined error in ANdroid emulator. It seems that the Worklight API makes use of some native device functionality to get encrypted cache working. One thing i noticed is accessing encrypted cache (specially opening it is really slow, so you should use it only if you really need to encrypt the data) After storing data using the Encrypted Cache API i tried to access the file_0.localstorage file from the device but i could not download it due to some file access level restrictions that android is putting on it.
Also in the local browser i can not read the values stored in the encryptedCache

Reading data stored in localStroage by Android Device or Google Chrome Browser

While working on my Using localStorage/sessionStorage in PhoneGap application example i was trying to debug a issue on the Android device where the data was not getting stored properly (Due to my programming mistake) and i wanted to figure out what data is actually getting stored so i used these steps.
  1. First i used the DDMS view in Eclipse to get access to the local file system on Andorid. All the data for an application is stored in andorid in data/data/<apppackage> folder, In my case application package name is com.HelloLocalStorage
    Whatever you store in window.localStorage object is actually stored in the data/data/<apppackage>/app_database/file_0.localstorage file, first download it to your computer. This file is actually SQLLite database, so if you open it in normal notepad you wont be able to read it. When you store data in localStroage object in the Google chrome it gets stored in C:\Users\<username>\AppData\Local\Google\Chrome\User Data\Default\Local Storage directory.
  2. If you want to open SQLLite database you will have to use one of the admin tools, i used the MikeTS SQLLite Management tool to open the file_0.localstorage file and it seems that Google Chrome stores the localStorage data in ItemTable, that table has 2 columns one is key and other is value, when i queried the ItemTable i could see the data stored in local preferences like this

Using localStorage/sessionStorage in PhoneGap application

The HTML 5 specification introduced 2 objects that can be used for storing data in key-value format on the client side, i wanted to try this feature out so i did create this simple Todo list web page that can be used to create a TO DO list. This is how my Todo list application looks like
This is the HTML page that i created for working with

 <!DOCTYPE html>
<html manifest="storage.appcache">
  <head>
    <title>Offline Application Example</title>
    <script type="text/javascript" charset="utf-8" 
 src="jquery.js"></script>
    <script type="text/javascript" charset="utf-8">
    var storage = window.localStorage;
    $(document).ready(function(){
      console.log('Inside document.ready');
      initTodoList();
      $("#clearStorage").click(function(){
        console.log('Entering clearstorage');
        storage.clear();
        $('li').remove();
        console.log('Exiting clearstorage');
      });
    });
    function remove_item(key){
      console.log('Entering remove_item');
      storage.removeItem(key);
      console.log('Find and remove element with id = ' + key)
      $('#'+key).remove();
      console.log('Exiting remove_item');
    }
    function add_item() {
      console.log('Entering add_item');
      var d = new Date();
      var key = d.getTime();
      var value = $('#new_item').val();
      storage.setItem(key,value);
      createToDoListItem(key,value);
      $("#new_item").val('');
      console.log('Exiting add_item');
    }
    function initTodoList(){
      console.log("Entering initTodoList " + storage.length);
      for(var i = 0; i < storage.length; i++){
        var key = storage.key(i);
        var value = storage.getItem(key);
        createToDoListItem(key,value);
      }
    }  
    function createToDoListItem(key, value){
      var html = '<li  data-key="'+key+'" id="'+key+'">'
      +value+'<button onclick="javascript:remove_item(\''+ key+ '\')" 
   >Delete</button></li>';
      console.log('Appending html ' + html)
      $("#todo_list").append(html);
    }
    </script>
  </head>
  <body>
    <input type="text" id="new_item">
  <button onclick="add_item()">
  Add
  </button>
  <ul id="todo_list">
  </ul>
    <br/>
  <button id="clearStorage">Clear storage</button>
  </body>
</html>
You can use either localStorage, in which case the data is storage across browser restarts or you can use the sessionStorage in which case you will loose the TODO list once the browser is closed. All you have to do is change the value of var storage = either window.localStorage or window.sessionStorage Chrome developer tools allows us to look at the data stored in the localStorage as well as remove that data.

Google Map in PhoneGap/Cordova application

I wanted to figure out how to use Google Maps API in the Phone GAP application so i built this sample application that lets you enter address and it displays that address on the Map, you can download the sample application from here This is a screen shot of how my application looks like
This is how the index.html page for my application looks like

<!DOCTYPE html>
<html>
  <head>
    <title>Google Map Example</title>
 <style type="text/css">
      html { height: 100% }
      body { height: 100%; margin: 0; padding: 0 }
      #map_canvas { height: 100% }
    </style>
    <script type="text/javascript" charset="utf-8" src="cordova-1.7.0.js"></script>
    <script type="text/javascript" charset="utf-8" src="jquery.js"></script>
    <script src="http://maps.google.com/maps/api/js?sensor=true"></script>
    <script type="text/javascript" charset="utf-8">
    function displayCurrentLocation(){
     console.log("Entering displayCurrentLocation()");
  try{
   var currentLocationLatAndLong = new google.maps.LatLng(37.422006,-122.084095);
   var mapOptions ={
    zoom:8,
    center:currentLocationLatAndLong,
    mapTypeId: google.maps.MapTypeId.ROADMAP
   };
   var mapDiv = document.getElementById("map");
   map = new google.maps.Map(mapDiv,mapOptions);
  }catch(e){
   console.log("Error occured in ConsultantLocator.displayMap() " + e);
  }
     console.log("Exiting displayCurrentLocation()");
    }
    function addMarker(latLng,title,contentString){
  console.log("Entering addMarker()");
  var markerOptions = new google.maps.Marker({
    map: map,
    position: latLng,
    title:title,
    clickable:true
   });
  var marker = new google.maps.Marker(markerOptions);
  var infoWindowOptions = {
   content: contentString,
   position: latLng
  };
  var infoWindow = new google.maps.InfoWindow(infoWindowOptions);
  google.maps.event.addListener(marker, "click", function(){
   infoWindow.open(map);
  });
  console.log("Exiting addMarker()");
 }
    function getLatLangFromAddress(address){
     console.log("Entering getLatLangFromAddress()");
     var geocoder = new google.maps.Geocoder();
     geocoder.geocode( { 'address': address}, function(results, status) {

      if (status == google.maps.GeocoderStatus.OK) {
    var returnedValue =results[0].geometry.location;
       console.log("Address found is " + returnedValue);
       addMarker(returnedValue);
      }else{
       alert("Geocode was not successful for the following reason: " + status);
      }
        });
     console.log("Exiting getLatLangFromAddress()");
    }
    function addMarkerForAddress(){

     console.log("Entering addMarkerForAddress()");
     var address = $("#address").val();
     console.log($("#address"));
     var latLangForLocation = getLatLangFromAddress(address);
     console.log("Value returned by getLatLangFromAddress " +latLangForLocation);
     addMarker(latLangForLocation,address,address);
     console.log("Exiting addMarkerForAddress()");
    }
 document.addEventListener("deviceready", displayCurrentLocation, false);
    </script>
  </head>
  <body >
   <p>
    <input type="text" name="address" id="address" />
    <input type="button" id="getLocation" onclick="addMarkerForAddress()" value="Get Location"/>
   </p>
    <div id="map" style="width:100%; height:100%"></div>
  </body>
</html>
This application loads the google map during startup. When user enters address and clicks on get location it takes the address and uses it to find the latitude and longitude for that address and then users addMarker() method to display maker for that location

Using ripple mobile browser emulator for testing PhoneGap

One of the biggest pain point for testing PhoneGap application is deploying it on emulator and testing it, that process takes long time. So i started using the Ripple which is Google Chrome extension and it makes testing PhoneGap application really easy.
  1. Install Ripple extension in Chrome
  2. Start the Google Chrome browser with its access to local file system by executing chrome.exe -–allow-file-access-from-files
  3. Then Right click on the Ripple symbol and say Manage Google Extensions, on the next screen check Allow access to file URLs check box
  4. Now open index.html from the phoneGap application using file URL and enable Ripple for it
  5. Now you can test the geolocation application like this. With Ripple advantage is you can directly open the HTML in browser and then set geolocation directly using Ripple

Using Geolocation API in Android Emulator

In the Getting address of the current location using GeoLocation API and Google MAP api entry i talked about how to use the GeoLocation API provided as part of HTML 5 to get the current address of the user. Now Phone Gap also provides support for geolocation which means it checks if the browser on the device has support for geolocation if yes it lets it work if not it will call the native API of the underlying browser to get the the location and return it to browser. I wanted to try that so i took the content of geolocation.html and copied it in the index.html file that my phonegap application for android is using, you can download that app from here I followed these steps to build Android PhoneGap application
  1. I followed the instructions in Getting Started with Android to build application that points to index.html inside the application, i tried it once to make sure that it works
  2. Then i copied the content of Geolocation.html in the index.html page
  3. Change the AndroidManifest.xml file to allow application to use the mock location
    
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.webspherenotes.phonegap"
        android:versionCode="1"
        android:versionName="1.0" >
    
        <uses-sdk android:minSdkVersion="8" />
        <uses-permission android:name="android.permission.CAMERA" />
      <uses-permission android:name="android.permission.VIBRATE" />
      
      <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
      <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
      <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
      <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
     
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
      <uses-permission android:name="android.permission.INTERNET" />
      <uses-permission android:name="android.permission.RECEIVE_SMS" />
      <uses-permission android:name="android.permission.RECORD_AUDIO" />
      <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
      <uses-permission android:name="android.permission.READ_CONTACTS" />
      <uses-permission android:name="android.permission.WRITE_CONTACTS" />
      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
      <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> 
      <uses-permission android:name="android.permission.GET_ACCOUNTS" />
      <uses-permission android:name="android.permission.BROADCAST_STICKY" />
        <application
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name" >
            <activity
                android:name=".HelloPhoneGapActivity"
                android:label="@string/app_name" 
                android:configChanges="orientation|keyboardHidden" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    </manifest>
    
  4. The android emulator does not know how to find out current location of the user, so we have to setup mock location i.e. hard code a location for the user. For that you have two options either use the DDMS perspective in your Eclipse and after your emulator is started select it and enter value of longitude and latitude and click on send like this.
  5. Or open a Telnet session to the emulator by executing telnet localhost 5554 (you can get port number 5554 from the emulator window it would be in title bar) and then in the telnet window use geo fix -121.9754144 37.5669038 command to set the longitude and latitude like this
Now when i run this application in the Android emulator i can see it displaying the address for location that i sent to the emulator like this

Getting address of the current location using GeoLocation API and Google MAP api

The HTML 5 has concept of Geo Location that lets you read current address of the user, So if your using a mobile device it will give you current address using GPS but if your using normal Laptop or Desktop it still gives you current address with less accuracy. In my case it gives address of AT&T office which is my internet provider. I wanted to figure out how GeoLocation API's work so i build this sample application that displays my current address. You can download the Geolocation.html file from here This is how my Geolocation.html page looks like in browser
My Geolocation.html page has one Get Location button when you click on that button it will show you address of the current location. Now the address is not always accurate. Also i tested this code in Firefox 12.0 and Internet Explorer 9, the Geolocation code does not work in Chrome if your accessing the file directly from file system i mean using file:// URL. This is the source code for the Geolocation.html

<!DOCTYPE html>
<html>
  <head>
    <title>GeoLocation</title>
    <script src="http://maps.google.com/maps/api/js?sensor=true">
 </script>
    <script type="text/javascript" charset="utf-8">
   function getLocation(){
      console.log("Entering getLocation()");
      if(navigator.geolocation){
      navigator.geolocation.getCurrentPosition(
      displayCurrentLocation,
      displayError,
      { 
        maximumAge: 3000, 
        timeout: 5000, 
        enableHighAccuracy: true 
      });
    }else{
      console.log("Oops, no geolocation support");
    } 
      console.log("Exiting getLocation()");
    };
    function displayCurrentLocation(position){
      console.log("Entering displayCurrentLocation");
      var latitude = position.coords.latitude;
    var longitude = position.coords.longitude;
    console.log("Latitude " + latitude +" Longitude " + longitude);
    getAddressFromLatLang(latitude,longitude);
      console.log("Exiting displayCurrentLocation");
    }
   function  displayError(error){
    console.log("Entering ConsultantLocator.displayError()");
    var errorType = {
      0: "Unknown error",
      1: "Permission denied by user",
      2: "Position is not available",
      3: "Request time out"
    };
    var errorMessage = errorType[error.code];
    if(error.code == 0  || error.code == 2){
      errorMessage = errorMessage + "  " + error.message;
    }
    alert("Error Message " + errorMessage);
    console.log("Exiting ConsultantLocator.displayError()");
  }
    function getAddressFromLatLang(lat,lng){
      console.log("Entering getAddressFromLatLang()");
      var geocoder = new google.maps.Geocoder();
        var latLng = new google.maps.LatLng(lat, lng);
        geocoder.geocode( { 'latLng': latLng}, function(results, status) {
        console.log("After getting address");
        console.log(results);
        if (status == google.maps.GeocoderStatus.OK) {
          if (results[1]) {
            console.log(results[1]);
            alert(results[1].formatted_address);
          }
        }else{
          alert("Geocode was not successful 
    for the following reason: " + status);
        }
        });
      console.log("Entering getAddressFromLatLang()");
    }
    </script>
  </head>
  <body>
    <h1>Display the map here</h1>
    <input type="button" id="getLocation"
 onclick="getLocation()" value="Get Location"/>
    <div id="map"></div>
  </body>
</html>
When you click on Get Location button the control goes to getLocation() function which first checks if the browser supports GeoLocation API by checking navigator.geolocation object, if that object is not null that means browser supports GeoLocation and we ask browser for current location by calling navigator.geolocation.getCurrentPosition() with displayCurrentLocation() as a call back function if the current location lookup was successful. The browser calls displayCurrentLocation() function with current location using position object, which has longitude and latitude as properties. The latitude and longitude would have values like Latitude 37.5668988 Longitude -121.9753273, so we have to use the Google MAP API to get street address from the longitude and latitude values, for that we call getAddressFromLatLang function. Inside the getAddressFromLatLang() function i am creating object of google.maps.Geocoder and calling its geocode() method with latitude and longitude and it returns array of addresses for that location. Once i have the address i can print it using alert.

Chaning pages dynamically using JQuery Mobile - use different .htmls

In the Changing pages dynamically in JQuery Mobile Worklight application i blogged about how you can use the JQuery Mobile JavaScript framework to create multi-page web application. In that example i built a web application which has only single HTML file that contains HTML fragments for different pages and i used following JavaScript to change page dynamically

function switchToPage2(){
  jq.mobile.changePage ("#page2");
}
One of the reader raised a very good question that it might not be good idea to have all the pages in same html and can we break the html's into different pages and answer is yes. I wanted to try that so first i did create a listcontact.html in the same directory as that of ManageContact.hml that has markup of only listcontact.html like this

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport"
  content="width=device-width, initial-scale=1.0, 
  maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" />
<title>ManageContact</title>
<link rel="shortcut icon" href="images/favicon.png" />
<link rel="apple-touch-icon" href="images/apple-touch-icon.png" />
<link rel="stylesheet" href="css/reset.css" />
<link rel="stylesheet" href="css/ManageContact.css" />
<link rel="stylesheet" href="css/jquery.mobile-1.0.min.css" />
<script src="js/jquery-1.7.1.min.js"></script>
<script>
  var jq = jQuery.noConflict();
</script>
<script src="js/jquery.mobile-1.0.min.js"></script>
</head>
<body onload="WL.Client.init({})" id="content" style="display: none">
  <div data-role="page" id="home">
    <div data-theme="a" data-role="header">
      <h3>Contact List</h3>
    </div>
    <div data-role="content" id="pagePort">
      <ul data-role="listview" data-inset="true" data-filter="true"
        id="displayContact">
      </ul>
       <a data-role="button"
            data-transition="fade" href="#home"
            id="searchContact"> Home </a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>
  <script src="js/ManageContact.js"></script>
  <script src="js/messages.js"></script>
  <script src="js/auth.js"></script>
</body>
The listcontact.html has the same structure as ManageContact.html (The worklight server does add more resources to the ManageContact.html as part of build process). Then i had to use following JavaScript to switch page

function switchToPage2(){
  jq.mobile.changePage ("listcontact.html");
}
and when i click on switchPage it does make forward control to listcontact.html, that works. But since we refreshed the whole page we loose the JavaScript context.

Changing pages dynamically in JQuery Mobile Worklight application

In the Creating multi page application with JQuery Mobile entry i talked about how to build a multi-page application with JQuery Mobile, in that i talked about how to switch page using static href's but most of time you would want to change page name dynamically after executing some JavaScript, so i changed my html like this

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport"
  content="width=device-width, initial-scale=1.0, maximum-scale=1.0,
  minimum-scale=1.0, user-scalable=0" />
<title>HelloWorkLightPhoneGap</title>
<link rel="shortcut icon" href="images/favicon.png" />
<link rel="apple-touch-icon" href="images/apple-touch-icon.png" />
<link rel="stylesheet" href="css/reset.css" />
<link rel="stylesheet" href="css/HelloWorkLightPhoneGap.css" />
<link rel="stylesheet" href="css/jquery.mobile-1.0.min.css" />
<script src="js/jquery-1.7.1.min.js"></script>
<script>
  var jq = jQuery.noConflict();
</script>
<script src="js/jquery.mobile-1.0.min.js"></script>
<script type="text/javascript"
  src="http://maps.google.com/maps/api/js?sensor=false"></script>
</head>
<body onload="WL.Client.init({})" id="content" style="display: none">
  <div data-role="page" id="page1">
    <div data-theme="a" data-role="header">
      <h3>First Page</h3>
    </div>
    <div data-role="content" id="pagePort">
      <a href="javascript:switchToPage2()">Go to second page</a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>

  <div data-role="page" id="page2" >
    <div data-theme="a" data-role="header">
      <h3>Second Page</h3>
    </div>
    <div data-role="content" id="pagePort">
      <a href="javascript:switchToPage1()">Go to third page</a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>
   <script src="js/HelloWorkLightPhoneGap.js"></script>
  <script src="js/messages.js"></script>
  <script src="js/auth.js"></script>
</body>
</html>
In this the anchor for change page is changed to point to JavaScript function that looks like this, you can use the jq.mobile.changePage("<pageid>")

function switchToPage2(){
  jq.mobile.changePage ("#page2");
}

function switchToPage1(){
  jq.mobile.changePage ("#page1");
}

Worklight Phone Gap Android application

I just created my first Worklight Phone Gap application for Android. The process is pretty easy you just add support for Android environment and then WorkLight creates/generates Android project for you. Once your project is ready you just right click on it and say Run as Android application to get a view like this
I wanted to see what is going on so i looked at the generated project, but before that this is how my Worklight application is structured
When you add support for Android, WorkLight generates PhoneGap Android project that is similar to Getting Started with PhoneGap on Android project, the Generated Java class looks like this

package com.HelloWorkLightPhoneGap;

import android.os.Bundle;

import com.worklight.androidgap.WLDroidGap;

public class HelloWorkLightPhoneGap extends WLDroidGap {
 @Override
 public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    super.loadUrl(getWebMainFilePath());
 }  
}
So basically this class is forwarding control to the HTML page but the HTML file name is not hard coded here instead it comes from wlclient.properties file which looks like this

wlServerProtocol = http
wlServerHost = 192.168.94.131
wlServerPort = 8080
wlAppId = HelloWorkLightPhoneGap
wlAppVersion = 1.0
wlC2DMEmailSender = ${wlC2DMEmailSender}
wlMainFilePath = HelloWorkLightPhoneGap.html
enableSettings = true
So the HelloWorkLightGap.html file is the main file for my WorkLight application that becomes a main file here. Also if you take a closer look at the content of the project you will notice that the Android has assets/www/default folder which has all the files from your worklight application

Creating multi page application with JQuery Mobile

JQuery Mobile makes creating multi-page application very easy, I think its much better than using Worklight for creating multi-page application Take a look at this simple html page that i built which has 3 pages page1, page2 and page3, every page has link to the next page.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport"
  content="width=device-width, initial-scale=1.0, maximum-scale=1.0,
  minimum-scale=1.0, user-scalable=0" />
<title>HelloWorkLightPhoneGap</title>
<link rel="shortcut icon" href="images/favicon.png" />
<link rel="apple-touch-icon" href="images/apple-touch-icon.png" />
<link rel="stylesheet" href="css/reset.css" />
<link rel="stylesheet" href="css/HelloWorkLightPhoneGap.css" />
<link rel="stylesheet" href="css/jquery.mobile-1.0.min.css" />
<script src="js/jquery-1.7.1.min.js"></script>
<script>
  var jq = jQuery.noConflict();
</script>
<script src="js/jquery.mobile-1.0.min.js"></script>
<script type="text/javascript"
  src="http://maps.google.com/maps/api/js?sensor=false"></script>
</head>
<body onload="WL.Client.init({})" id="content" style="display: none">
  <div data-role="page" id="page1">
    <div data-theme="a" data-role="header">
      <h3>First Page</h3>
    </div>
    <div data-role="content" id="pagePort">
      <a href="#page2">Go to second page</a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>

  <div data-role="page" id="page2" >
    <div data-theme="a" data-role="header">
      <h3>Second Page</h3>
    </div>
    <div data-role="content" id="pagePort">
      <a href="#page3">Go to third page</a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>
  
  <div data-role="page" id="page3">
    <div data-theme="a" data-role="header">
      <h3>Third page</h3>
    </div>
    <div data-role="content" id="pagePort">
      <a href="#page1">Go to first page</a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>

  <script src="js/HelloWorkLightPhoneGap.js"></script>
  <script src="js/messages.js"></script>
  <script src="js/auth.js"></script>
</body>
</html>
When you want to move to the next page you use the link with value of href equal to the id of the next page

Setting up Android PhoneGap development environment

Before couple of days i was trying to setup Android Phone Gap environment on my machine, by following instructions at Getting started guide for Android environment on PhoneGap site, I was using PhoneGap 1.5 version and i had a problem of not being able to start the application. The solution was to use Android 2.2 SDK using the latest Android SDK some how does not work.

Invoking REST service that returns JSON from worklight

In the Invoking REST service from WorkLight i built a simple WorkLight application that makes REST call but the REST service in that case returns XML, To me if your building a REST service for access from Browser it would be much better idea to return JSON instead of XML that avoids one conversion. So with that goal in mind i wanted to change my application and i followed these steps for that
  • First thing that i had to do was change the REST service so that it returns JSON and also i made JSON as the default return type for service, you can get more information and download the sample application by using entry
  • Next i changed the ContactRESTService-impl.js, the worklight REST adapter that i am using for making the actual call,
    
    function getContactList() {
      var input = {
          method : 'get',
          returnedContentType : 'json',
          path : '/ManageContact/rest/contact'
      };
      return WL.Server.invokeHttp(input);
    }
    
    function searchContact(lastName){
      var input = {
            method : 'get',
            returnedContentType : 'json',
            path : '/ManageContact/rest/contact/search?lastName='+lastName
        };
        return WL.Server.invokeHttp(input);
    }
    
    Only change in this file is to let the WorkLight adapter know that i am expecting json as response type instead of xml
  • After that i had to change the way i am parsing the response and using it to display results to the user, the reason being now the structure of the response is different so the callback that reads the response and displays the result has to know about how to handle the changed structure.
    
    function loadContactSuccess(result) {
      console.log("Inside loadContactSuccess " + result);
      var html = '';
      try {
        if (result.status == 200) {
          var contactList = result.invocationResult.contact; 
          var i = 0;
          for (i = 0; i < contactList.length; i++) {
            var currentContact = contactList[i];
            html = html + '<li><a href="javascript:showContactDetail('
                + currentContact.contactId + ')">'
                + currentContact.firstName + ' '
                + currentContact.lastName + '</a></li>';
          }
        }
        jq("#displayContact").html(html);
        jq("#displayContact").listview('refresh');
        busyIndicator.hide();
      } catch (e) {
        busyIndicator.hide();
        displayError(e.toString());
      }
    }
    
    The highlighted code displays how i am getting the actual contact list from the response. This is how the response of the REST service looks like when i hit it directly
    This is how the response that WorkLight adapter is returning to the JavaScript client looks like

Session Storage API in HTML 5

In the Local Storage API in HTML 5 , i blogged about localStorage JavaScript object that you can use for storing data on the client side. The data stored using localStorage API would be available across browser restart and all the windows/tabs opened from that domain. The HTML5 specification also provides sessionStorage object that has same interface as the localStorage object with the difference that the data stored using the sessionStorage API stays only during lifetime of browser session, so you will loose it when you close the browser window or even if you open a new browser window. I wanted to try this out so i changed the localStorage code to use sessionStorage instead of localStorage object, my code looks like this

<!doctype html>
<html>
  <head>
    <script>
 window.onload =function(){
      if(window["sessionStorage"]){
      // Inserting or updating a key/value
   var currentTime = new Date();
      sessionStorage.setItem("key1","First test value" + currentTime.getTime() );  
   if(sessionStorage.getItem("key2") == null)
  sessionStorage.setItem("key2","Second test value"+ currentTime.getTime());  

   document.getElementById("key2Value").innerHTML = sessionStorage.getItem("key2");
      // Getting value of key from session storage
      console.log(sessionStorage.getItem("key1"));
      
      //Removing a key from the session storage
      sessionStorage.removeItem("key1");
      console.log(sessionStorage.getItem("key1"));
      
      //Iterate through all the keys and print them in key=value format
      for( var key in sessionStorage)
        console.log(key + " = "+sessionStorage[key]);
      
      //Iterate through all the keys and print them in key=value format
      for (var i = 0 ; i < sessionStorage.length;i++){
        var keyName = sessionStorage.key(i);
        console.log(keyName +" = "+ sessionStorage[key]);
      }  
      
      //Removing everything
    // sessionStorage.clear();
      }else{
        alert("Browser does not support sessionStorage");
      }
   }
    </script>
  </head>
  <body>
 <div id="key2Value"></div>
  </body>
  </html>
I made a change to check value of key2 if it exists do not set the value and display it on page. Then i opened two different tabs in same browser and keep refreshing page i could see that two different tabs got different values for the same key.

Local Storage API in HTML 5

The HTML 5 specification allows web applications to store up to 5 MB of data per domain on the client side and the API that you can for it is is called localStorage, this is a sample web page that performs different localStorage API related operations.

<!doctype html>
<html>
  <head>
    <script>
      if(window["localStorage"]){
      // Inserting or updating a key/value
      localStorage.setItem("key1","First test value");  
      localStorage.setItem("key2","Second test value");  
      
      // Getting value of key from local storage
      console.log(localStorage.getItem("key1"));
      
      //Removing a key from the local storage
      localStorage.removeItem("key1");
      console.log(localStorage.getItem("key1"));
      
      //Iterate through all the keys and print them in key=value format
      for( var key in localStorage)
        console.log(key + " = "+localStorage[key]);
      
      //Iterate through all the keys and print them in key=value format
      for (var i = 0 ; i < localStorage.length;i++){
        var keyName = localStorage.key(i);
        console.log(keyName +" = "+ localStorage[key]);
      }  
      
      //Removing everything from localStorage
     localStorage.clear();
      }else{
        alert("Browser does not support localStorage");
      }
    </script>
  </head>
  <body>
   Hello from Local Storage API
  </body>

</html>
This is screen shot of how the keys stored in local Storage look like in Chrome.

Creating multi page application in WorkLight

WorkLight allows you to create multi-page application, i wanted to try this feature, so i changed the Contact Search application that i created in Using BusyIndicator common control entry, so that, when executes a search for contact, it returns list of contact names, user can click on one of the contact name to go to the Contact Details page and that page has button that allows user to go back to Summary page. This is how my summary page looks like
When you click on the arrow button next to any user name it takes you to the Details page for that contact which looks like this
You can click on the Summary button on the Details page to go back to the Contact Summary page. I followed these steps to create this sample application
  1. First i changed the main or landing page of my app to mark the div where the new page should be inserted, i did that by setting value of id element to pagePort like this
    
    !DOCTYPE html>
    <html>
    <head>
    ....
    </head>
    <body onload="WL.Client.init({showLogger:true})" id="content"
      style="display: none">
    
      <div data-role="page" id="page1">
        <div data-theme="a" data-role="header">
          <h3>Contact DB App</h3>
        </div>
        <div data-role="content" id="pagePort" >
          <div data-role="fieldcontain">
            <fieldset data-role="controlgroup">
              <label for="textinput1"> First Name </label> <input
                id="contactName" placeholder="" value="" type="text" />
            </fieldset>
          </div>
          <a data-role="button" data-transition="fade"
            href="javascript:getContact()" id="searchContact"> Search
            Contact </a>
          <ul data-role="listview" data-inset="true" data-filter="true"
            id="displayContact">
          </ul>
        </div>
        <div data-theme="a" data-role="footer">
          <h3>Copyright stuff</h3>
        </div>
      </div>
      <script src="js/HelloDatabase.js"></script>
      <script src="js/messages.js"></script>
      <script src="js/auth.js"></script>
    </body>
    </html>
    
  2. Then i did create this new contactDetail.html page in the same directory as that of my first/landing page
    
    <div data-role="content" id="mainContent">
      <div data-role="fieldcontain">
        <fieldset data-role="controlgroup">
          <label for="firstName"> First Name </label> 
          <input id="firstName" placeholder="" value="" type="text" readonly="readonly" />
          <label for="lastName"> Last Name </label> 
          <input id="lastName" placeholder="" value="" type="text" readonly="readonly"/>
          <label for="email"> Email </label> 
          <input id="email" placeholder="" value="" type="text" readonly="readonly"/>
          
          <a data-role="button" data-transition="fade"
            href="javascript:showSummary()" id="searchContact"> Summary </a>
        </fieldset>
      </div>
    </div>
    
    The Details page shows the form with contact details and it has a Summary button that allows user to go back to the summary page, when you click on that button it will pass control to showSummary() method
  3. I had to make changes to my main JavaScript function to make it look like this
    
    var busyIndicator;
    function wlCommonInit() {
      busyIndicator = new WL.BusyIndicator('page1');
    }
    
    function getContact() {
      console.log("Entering getContact() REST service based version");
      var contactName = $('contactName').getValue();
      var invocationData = {
        adapter : "ContactWSService",
        procedure : "searchContact",
        parameters : [ contactName ]
      }
      var options = {
        onSuccess : loadContactSuccess,
        onFailure : loadContactFailure
      }
      busyIndicator.show();
      WL.Client.invokeProcedure(invocationData, options);
    }
    
    function loadContactSuccess(result) {
      console.log("Inside loadContactSuccess " + result);
      var html = '';
      try {
        if (result.status == 200) {
          var contactList = result.invocationResult.Envelope.Body.searchContactResponse.contactList;
          var i = 0;
          for (i = 0; i < contactList.length; i++) {
            var currentContact = contactList[i];
            html = html + '<li><a href="javascript:showContactDetail('
                + currentContact.contactId + ')">'
                + currentContact.firstName + ' '
                + currentContact.lastName + '</a></li>';
          }
        }
        jq("#displayContact").html(html);
        jq("#displayContact").listview('refresh');
        busyIndicator.hide();
      } catch (e) {
        busyIndicator.hide();
      }
    }
    function showContactDetail(contactId) {
      console.log("Show Contact Detail is clicked " + contactId);
      WL.Page.load("contactDetail.html", {
        onComplete : function() {
          console.log("After fragment is loadded ");
          jq('#mainContent').trigger("create");
          getContactDetails(contactId);
        },
        onUnload : function() {
          console.log("After fragment is unloadded ");
        }
      });
    }
    
    function showSummary() {
      WL.Page.load("contactSummary.html", {
        onComplete : function() {
          console.log("After fragment is loadded ");
          jq('#mainContent').trigger("create");
          
        },
        onUnload : function() {
          console.log("After fragment is unloadded ");
        }
      }); 
    }
    
    function getContactDetails(contactId) {
      console.log("Entering getContactDetails()");
      var invocationData = {
        adapter : "ContactWSService",
        procedure : "getContact",
        parameters : [ contactId ]
      }
      var options = {
        onSuccess : getContactDetailsSuccess,
        onFailure : getContactDetailsFailure
      }
      busyIndicator.show();
      WL.Client.invokeProcedure(invocationData, options);
    }
    
    function getContactDetailsSuccess(result) {
      console.log("Entering getContactDetailsSuccess");
      try {
        if (result.status == 200) {
          var displayContact = result.invocationResult.Envelope.Body.getContactResponse.contact;
          $('firstName').value=displayContact.firstName;
          $('lastName').value=displayContact.lastName;
          $('email').value=displayContact.email;
        }
        busyIndicator.hide();
      } catch (e) {
        busyIndicator.hide();
      }
    }
    function getContactDetailsFailure(result) {
      console.log("Entering getContactDetailsFailure");
    }
    
    I had to make quite a few changes in my JavaScript they are as follows
    • loadContactSuccess: The loadContactSuccess method gets called when you execute search and it gets back the result, this method generates one row each for the result. I changed this method so that when it was generating the list i did attach getContactDetails(contactId) method to each row
    • showContactDetail: The showContactDetail method will get called when user clicks on any of the user name, when that happens i am calling WL.Page.load("contactDetails.html" method which replaces the markup inside the page with contactDetail.html, this method also calls the getContactDetails which calls SOAP service with contactId to get details of the contact.
    • showSummary: The showSummary method will be called when user clicks on the Summary button on the details page, it again calls WL.Page.load("contactSummary.html" to replace the markup in the current page with the contact details

Forcing jQuery Mobile to re-evaluate styles/theme on dynamically inserted content

Today i was trying to build a JQuery Mobile application that has multiple pages. When user clicks on the page switch button i take the markup on the new page and insert it in the old page. One problem that i noticed is when you insert HTML into a page and that HTML has jQuery UI widgets then those widgets do not get evaluated, instead jQuery Displays them without any styles. You can solve this problem by calling the $('changedDiv').trigger("create") method with value of the div equal to id where the new markup got inserted


<div id='changedDiv'>
//Insert jQuery widgets here
</div>

Using WL.SimpleDialog() to display error

I wanted to figure out how to use WL.SimpleDialog() so i used it to change the contact application that i developed in Using BusyIndicator common control so that if there is JavaScript Exception during searching of contact it displays that error in Dialog Box like this
This is how my JavaScript looks like after the changes

var busyIndicator;
function wlCommonInit(){
  busyIndicator = new WL.BusyIndicator('page1');
}
function getContact(){
  console.log("Entering getContact() REST service based version");
  var contactName = $('contactName').getValue();
  var invocationData = {
      adapter:"ContactWSService",
      procedure:"searchContact",
      parameters:[contactName]
  }
  var options ={
      onSuccess:loadContactSuccess,
      onFailure:loadContactFailure
  }
  busyIndicator.show();
  WL.Client.invokeProcedure(invocationData, options);
}

function loadContactSuccess(result){
  console.log("Inside loadContactSuccess " + result);
  var html = '';
  try{
  if(result.status == 200){
    var contactList = result.invocationResult.Envelope.Body.
 searchContactResponse.contactList;
    var i = 0;
    for(i =0 ; i < contactList.length ; i++){
      var currentContact = contactList[i];
      html =  html + '<li><a href="#">'+currentContact.firstName 
   +' ' +currentContact.lastName +'</a></li>';
    }   
  }
  jq("#displayContact").html(html);
  jq("#displayContact").listview('refresh');
  busyIndicator.hide();
  }catch(e){
    busyIndicator.hide();
    displayError(e.toString());
  }
}

function loadContactFailure(result){
  console.log("Inside loadContactError " + result);
  busyIndicator.hide();
  displayError(result);
}

function displayError(errorString) {
  var dialogTitle = "Error";
  WL.SimpleDialog.show(dialogTitle, errorString, [ {
    text : 'OK',
    handler : simpleDialogButton1Click
  }
  ]);
}
function simpleDialogButton1Click() {
}

First i did create a displayError() method that takes error message as input and displays it in Modal dialog that has only one button. The simpleDialogButton1Click() would get called when user clicks on OK in the Error dialog but it does not do anything. The displayError() method is getting called from the exception handler class as well as the loadContactFailure() class.

Using BusyIndicator common control

I wanted to try using the BusyIndicator so i decided to change the Contact Search application that i developed in Using JQuery Mobile in WorkLight application entry, so that as soon as user clicks on Search Contact it starts showing the busy indicator and that indicator stays still the results are updated. I made following changes in my JavaScript file.


var busyIndicator;
function wlCommonInit(){
  busyIndicator = new WL.BusyIndicator('page1');
}
function getContact(){
  var contactName = $('contactName').getValue();
  var invocationData = {
      adapter:"ContactWSService",
      procedure:"searchContact",
      parameters:[contactName]
  }
  var options ={
      onSuccess:loadContactSuccess,
      onFailure:loadContactFailure
  }
  busyIndicator.show();
  
  WL.Client.invokeProcedure(invocationData, options);
}

function loadContactSuccess(result){
  console.log("Inside loadContactSuccess " + result);
  var html = '';
  try{
  if(result.status == 200){
    var contactList = result.invocationResult.Envelope.Body.searchContactResponse.contactList;
    var i = 0;
    for(i =0 ; i < contactList.length ; i++){
      var currentContact = contactList[i];
      html =  html + '<li><a href="#">'+currentContact.firstName +' ' 
   +currentContact.lastName +'</a></li>';
    }   
  }
  jq("#displayContact").html(html);
  jq("#displayContact").listview('refresh');
  
  busyIndicator.hide();
  
  }catch(e){
    busyIndicator.hide();
  }
}

function loadContactFailure(result){
  console.log("Inside loadContactError " + result);
  busyIndicator.hide();
}
First i had to create object of WL.BusyIndicator with value of page1 as input, page1 is the value of id attribute on enclosing div in my page. Without that i was getting JavaScript initialization error. Then the getContact() which is responsible for initiating the contact search call invokes the show() method to start the BusyIndicator. I am calling the hide() method of BusyIndicator in the loadContactSuccess() method which is a callback method that gets called once the results are ready and the UI is updated.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport"
  content="width=device-width, initial-scale=1.0, maximum-scale=1.0, 
  minimum-scale=1.0, user-scalable=0" />
<title>HelloDatabase</title>
<link rel="shortcut icon" href="images/favicon.png" />
<link rel="apple-touch-icon" href="images/apple-touch-icon.png" />
<link rel="stylesheet" href="css/reset.css" />
<link rel="stylesheet" href="css/HelloDatabase.css" />
<link rel="stylesheet" href="css/jquery.mobile-1.0.min.css" />
<script src="js/jquery-1.7.1.min.js"></script>
<script>
  var jq = jQuery.noConflict();
</script>

<script src="js/jquery.mobile-1.0.min.js"></script>
</head>
<body onload="WL.Client.init({showLogger:true})" id="content" style="display: none">
  <div data-role="page" id="page1">
    <div data-theme="a" data-role="header">
      <h3>Contact DB App</h3>
    </div>
    <div data-role="content">
      <div data-role="fieldcontain">
                    <fieldset data-role="controlgroup">
                        <label for="textinput1">
                           First Name
                        </label>
                        <input id="contactName" placeholder="" value="" type="text" />
                    </fieldset>
                </div>
                <a data-role="button" data-transition="fade" 
    href="javascript:getContact()" id="searchContact">
                    Search Contact
                </a>
      <ul data-role="listview" data-inset="true" data-filter="true" id="displayContact">
      </ul>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>
  <script src="js/HelloDatabase.js"></script>
  <script src="js/messages.js"></script>
  <script src="js/auth.js"></script>
</body>
</html>