Merge branch 'master' of https://github.com/guillaumedev/cordova-plugin-geolocation
diff --git a/.gitignore b/.gitignore
index 52b558e..6964ea0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,7 +12,7 @@
 *.swp
 *.user
 
-
+node_modules
 
 
 
diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 0000000..df32482
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,17 @@
+{
+    "browser": true
+  , "devel": true
+  , "bitwise": true
+  , "undef": true
+  , "trailing": true
+  , "quotmark": false
+  , "indent": 4
+  , "unused": "vars"
+  , "latedef": "nofunc"
+  , "globals": {
+        "module": false,
+        "exports": false,
+        "require": false,
+        "cordova": true
+    }
+}
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..b9af4c5
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,4 @@
+language: node_js
+sudo: false
+node_js:
+  - "4.2"
diff --git a/README.md b/README.md
index 74695ea..81b5bcf 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,7 @@
+---
+title: Geolocation
+description: Access GPS data.
+---
 <!--
 # license: Licensed to the Apache Software Foundation (ASF) under one
 #         or more contributor license agreements.  See the NOTICE file
@@ -17,6 +21,8 @@
 #         under the License.
 -->
 
+[![Build Status](https://travis-ci.org/apache/cordova-plugin-geolocation.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-geolocation)
+
 # cordova-plugin-geolocation
 
 This plugin provides information about the device's location, such as
@@ -43,7 +49,7 @@
 so already).  That notice should provide the same information noted
 above, as well as obtaining the user's permission (e.g., by presenting
 choices for __OK__ and __No Thanks__).  For more information, please
-see the Privacy Guide.
+see the [Privacy Guide](http://cordova.apache.org/docs/en/latest/guide/appdev/privacy/index.html).
 
 This plugin defines a global `navigator.geolocation` object (for platforms
 where it is otherwise missing).
@@ -51,11 +57,15 @@
 Although the object is in the global scope, features provided by this plugin
 are not available until after the `deviceready` event.
 
+```javascript
+
     document.addEventListener("deviceready", onDeviceReady, false);
     function onDeviceReady() {
         console.log("navigator.geolocation works well");
     }
 
+```
+
 ## Installation
 
 This requires cordova 5.0+ ( current stable 1.0.0 )
@@ -79,7 +89,6 @@
 - iOS
 - Tizen
 - Windows Phone 7 and 8
-- Windows 8
 - Windows
 
 ## Methods
@@ -116,6 +125,8 @@
 
 ### Example
 
+```javascript
+
     // onSuccess Callback
     // This method accepts a Position object, which contains the
     // current GPS coordinates
@@ -140,6 +151,8 @@
 
     navigator.geolocation.getCurrentPosition(onSuccess, onError);
 
+```
+
 ### Android Quirks
 
 If Geolocation service is turned off the `onError` callback is invoked after `timeout` interval (if specified).
@@ -171,6 +184,8 @@
 
 ### Example
 
+```javascript
+
     // onSuccess Callback
     //   This method accepts a `Position` object, which contains
     //   the current GPS coordinates
@@ -193,6 +208,7 @@
     //
     var watchID = navigator.geolocation.watchPosition(onSuccess, onError, { timeout: 30000 });
 
+```
 
 ## geolocationOptions
 
@@ -227,6 +243,8 @@
 
 ### Example
 
+```javascript
+
     // Options: watch for changes in position, and use the most
     // accurate position acquisition method available.
     //
@@ -236,6 +254,8 @@
 
     navigator.geolocation.clearWatch(watchID);
 
+```
+
 ## Position
 
 Contains `Position` coordinates and timestamp, created by the geolocation API.
@@ -295,3 +315,438 @@
   - Returned when the device is unable to retrieve a position. In general, this means the device is not connected to a network or can't get a satellite fix.
 - `PositionError.TIMEOUT`
   - Returned when the device is unable to retrieve a position within the time specified by the `timeout` included in `geolocationOptions`. When used with `navigator.geolocation.watchPosition`, this error could be repeatedly passed to the `geolocationError` callback every `timeout` milliseconds.
+
+
+## <a id="sample"></a>Sample: Get the weather, find stores, and see photos of things nearby with Geolocation ##
+
+Use this plugin to help users find things near them such as Groupon deals, houses for sale, movies playing, sports and entertainment events and more.
+
+Here's a "cookbook" of ideas to get you started. In the snippets below, we'll show you some basic ways to add these features to your app.
+
+* [Get your coordinates](#coords).
+* [Get the weather forecast](#weather).
+* [Receive updated weather forecasts as you drive around](#receive).
+* [See where you are on a map](#see).
+* [Find stores near you](#find).
+* [See pictures of things around you](#see).
+
+## <a id="coord"></a>Get your geolocation coordinates
+
+```javascript
+
+function getWeatherLocation() {
+
+    navigator.geolocation.getCurrentPosition
+    (onWeatherSuccess, onWeatherError, { enableHighAccuracy: true });
+}
+
+```
+## <a id="weather"></a>Get the weather forecast
+
+```javascript
+
+// Success callback for get geo coordinates
+
+var onWeatherSuccess = function (position) {
+
+    Latitude = position.coords.latitude;
+    Longitude = position.coords.longitude;
+
+    getWeather(Latitude, Longitude);
+}
+
+// Get weather by using coordinates
+
+function getWeather(latitude, longitude) {
+
+    // Get a free key at http://openweathermap.org/. Replace the "Your_Key_Here" string with that key.
+    var OpenWeatherAppKey = "Your_Key_Here";
+
+    var queryString =
+      'http://api.openweathermap.org/data/2.5/weather?lat='
+      + latitude + '&lon=' + longitude + '&appid=' + OpenWeatherAppKey + '&units=imperial';
+
+    $.getJSON(queryString, function (results) {
+
+        if (results.weather.length) {
+
+            $.getJSON(queryString, function (results) {
+
+                if (results.weather.length) {
+
+                    $('#description').text(results.name);
+                    $('#temp').text(results.main.temp);
+                    $('#wind').text(results.wind.speed);
+                    $('#humidity').text(results.main.humidity);
+                    $('#visibility').text(results.weather[0].main);
+
+                    var sunriseDate = new Date(results.sys.sunrise);
+                    $('#sunrise').text(sunriseDate.toLocaleTimeString());
+
+                    var sunsetDate = new Date(results.sys.sunrise);
+                    $('#sunset').text(sunsetDate.toLocaleTimeString());
+                }
+
+            });
+        }
+    }).fail(function () {
+        console.log("error getting location");
+    });
+}
+
+// Error callback
+
+function onWeatherError(error) {
+    console.log('code: ' + error.code + '\n' +
+        'message: ' + error.message + '\n');
+}
+
+```
+
+## <a id="receive"></a>Receive updated weather forecasts as you drive around
+
+```javascript
+
+// Watch your changing position
+
+function watchWeatherPosition() {
+
+    return navigator.geolocation.watchPosition
+    (onWeatherWatchSuccess, onWeatherError, { enableHighAccuracy: true });
+}
+
+// Success callback for watching your changing position
+
+var onWeatherWatchSuccess = function (position) {
+
+    var updatedLatitude = position.coords.latitude;
+    var updatedLongitude = position.coords.longitude;
+
+    if (updatedLatitude != Latitude && updatedLongitude != Longitude) {
+
+        Latitude = updatedLatitude;
+        Longitude = updatedLongitude;
+
+        // Calls function we defined earlier.
+        getWeather(updatedLatitude, updatedLongitude);
+    }
+}
+
+```
+
+## <a id="see"></a>See where you are on a map
+
+Both Bing and Google have map services. We'll use Google's. You'll need a key but it's free if you're just trying things out.
+
+Add a reference to the **maps** service.
+
+```HTML
+
+ <script src="https://maps.googleapis.com/maps/api/js?key=Your_API_Key"></script>
+
+```
+Then, add code to use it.
+
+```javascript
+
+var Latitude = undefined;
+var Longitude = undefined;
+
+// Get geo coordinates
+
+function getMapLocation() {
+
+    navigator.geolocation.getCurrentPosition
+    (onMapSuccess, onMapError, { enableHighAccuracy: true });
+}
+
+// Success callback for get geo coordinates
+
+var onMapSuccess = function (position) {
+
+    Latitude = position.coords.latitude;
+    Longitude = position.coords.longitude;
+
+    getMap(Latitude, Longitude);
+
+}
+
+// Get map by using coordinates
+
+function getMap(latitude, longitude) {
+
+    var mapOptions = {
+        center: new google.maps.LatLng(0, 0),
+        zoom: 1,
+        mapTypeId: google.maps.MapTypeId.ROADMAP
+    };
+
+    map = new google.maps.Map
+    (document.getElementById("map"), mapOptions);
+
+
+    var latLong = new google.maps.LatLng(latitude, longitude);
+
+    var marker = new google.maps.Marker({
+        position: latLong
+    });
+
+    marker.setMap(map);
+    map.setZoom(15);
+    map.setCenter(marker.getPosition());
+}
+
+// Success callback for watching your changing position
+
+var onMapWatchSuccess = function (position) {
+
+    var updatedLatitude = position.coords.latitude;
+    var updatedLongitude = position.coords.longitude;
+
+    if (updatedLatitude != Latitude && updatedLongitude != Longitude) {
+
+        Latitude = updatedLatitude;
+        Longitude = updatedLongitude;
+
+        getMap(updatedLatitude, updatedLongitude);
+    }
+}
+
+// Error callback
+
+function onMapError(error) {
+    console.log('code: ' + error.code + '\n' +
+        'message: ' + error.message + '\n');
+}
+
+// Watch your changing position
+
+function watchMapPosition() {
+
+    return navigator.geolocation.watchPosition
+    (onMapWatchSuccess, onMapError, { enableHighAccuracy: true });  
+}
+
+```
+
+## <a id="find"></a>Find stores near you
+
+You can use the same Google key for this.
+
+Add a reference to the **places** service.
+
+```HTML
+
+<script src=
+"https://maps.googleapis.com/maps/api/js?key=Your_API_Key&libraries=places">
+</script>
+
+```
+
+Then, add code to use it.
+
+```javascript
+
+var Map;
+var Infowindow;
+var Latitude = undefined;
+var Longitude = undefined;
+
+// Get geo coordinates
+
+function getPlacesLocation() {
+    navigator.geolocation.getCurrentPosition
+    (onPlacesSuccess, onPlacesError, { enableHighAccuracy: true });
+}
+
+// Success callback for get geo coordinates
+
+var onPlacesSuccess = function (position) {
+
+    Latitude = position.coords.latitude;
+    Longitude = position.coords.longitude;
+
+    getPlaces(Latitude, Longitude);
+
+}
+
+// Get places by using coordinates
+
+function getPlaces(latitude, longitude) {
+
+    var latLong = new google.maps.LatLng(latitude, longitude);
+
+    var mapOptions = {
+
+        center: new google.maps.LatLng(latitude, longitude),
+        zoom: 15,
+        mapTypeId: google.maps.MapTypeId.ROADMAP
+
+    };
+
+    Map = new google.maps.Map(document.getElementById("places"), mapOptions);
+
+    Infowindow = new google.maps.InfoWindow();
+
+    var service = new google.maps.places.PlacesService(Map);
+    service.nearbySearch({
+
+        location: latLong,
+        radius: 500,
+        type: ['store']
+    }, foundStoresCallback);
+
+}
+
+// Success callback for watching your changing position
+
+var onPlacesWatchSuccess = function (position) {
+
+    var updatedLatitude = position.coords.latitude;
+    var updatedLongitude = position.coords.longitude;
+
+    if (updatedLatitude != Latitude && updatedLongitude != Longitude) {
+
+        Latitude = updatedLatitude;
+        Longitude = updatedLongitude;
+
+        getPlaces(updatedLatitude, updatedLongitude);
+    }
+}
+
+// Success callback for locating stores in the area
+
+function foundStoresCallback(results, status) {
+
+    if (status === google.maps.places.PlacesServiceStatus.OK) {
+
+        for (var i = 0; i < results.length; i++) {
+
+            createMarker(results[i]);
+
+        }
+    }
+}
+
+// Place a pin for each store on the map
+
+function createMarker(place) {
+
+    var placeLoc = place.geometry.location;
+
+    var marker = new google.maps.Marker({
+        map: Map,
+        position: place.geometry.location
+    });
+
+    google.maps.event.addListener(marker, 'click', function () {
+
+        Infowindow.setContent(place.name);
+        Infowindow.open(Map, this);
+
+    });
+}
+
+// Error callback
+
+function onPlacesError(error) {
+    console.log('code: ' + error.code + '\n' +
+        'message: ' + error.message + '\n');
+}
+
+// Watch your changing position
+
+function watchPlacesPosition() {
+
+    return navigator.geolocation.watchPosition
+    (onPlacesWatchSuccess, onPlacesError, { enableHighAccuracy: true });
+}
+
+```
+
+## <a id="pictures"></a>See pictures of things around you
+
+Digital photos can contain geo coordinates that identify where the picture was taken.
+
+Use Flickr API's to find pictures that folks have taken near you. Like Google services, you'll need a key, but it's free if you just want to try things out.
+
+```javascript
+
+var Latitude = undefined;
+var Longitude = undefined;
+
+// Get geo coordinates
+
+function getPicturesLocation() {
+
+    navigator.geolocation.getCurrentPosition
+    (onPicturesSuccess, onPicturesError, { enableHighAccuracy: true });
+
+}
+
+// Success callback for get geo coordinates
+
+var onPicturesSuccess = function (position) {
+
+    Latitude = position.coords.latitude;
+    Longitude = position.coords.longitude;
+
+    getPictures(Latitude, Longitude);
+}
+
+// Get pictures by using coordinates
+
+function getPictures(latitude, longitude) {
+
+    $('#pictures').empty();
+
+    var queryString =
+    "https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=Your_API_Key&lat="
+    + latitude + "&lon=" + longitude + "&format=json&jsoncallback=?";
+
+    $.getJSON(queryString, function (results) {
+        $.each(results.photos.photo, function (index, item) {
+
+            var photoURL = "http://farm" + item.farm + ".static.flickr.com/" +
+                item.server + "/" + item.id + "_" + item.secret + "_m.jpg";
+
+            $('#pictures').append($("<img />").attr("src", photoURL));                            
+
+           });
+        }
+    );
+}
+
+// Success callback for watching your changing position
+
+var onPicturesWatchSuccess = function (position) {
+
+    var updatedLatitude = position.coords.latitude;
+    var updatedLongitude = position.coords.longitude;
+
+    if (updatedLatitude != Latitude && updatedLongitude != Longitude) {
+
+        Latitude = updatedLatitude;
+        Longitude = updatedLongitude;
+
+        getPictures(updatedLatitude, updatedLongitude);
+    }
+}
+
+// Error callback
+
+function onPicturesError(error) {
+
+    console.log('code: ' + error.code + '\n' +
+        'message: ' + error.message + '\n');
+}
+
+// Watch your changing position
+
+function watchPicturePosition() {
+
+    return navigator.geolocation.watchPosition
+    (onPicturesWatchSuccess, onPicturesError, { enableHighAccuracy: true });
+}
+
+```
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index d291197..8acb569 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -20,6 +20,13 @@
 -->
 # Release Notes
 
+### 2.2.0 (Apr 15, 2016)
+* Replace `PermissionHelper.java` with `cordova-plugin-compat`
+* CB-10691 Check the context to avoid null errors
+* CB-10636 Add `JSHint` for plugins
+* Using a fallback epsilon in case `Number.EPSILON` is not defined.
+* CB-10574 MobileSpec can't get results for **WP8.1** Builds
+
 ### 2.1.0 (Jan 15, 2016)
 * CB-10319 **Android** Adding reflective helper methods for permission requests
 * CB-8523 Fixed accuracy when `enableHighAccuracy: false` on **iOS**.
diff --git a/package.json b/package.json
index ba6e4f7..347d2dc 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "cordova-plugin-geolocation",
-  "version": "2.1.1-dev",
+  "version": "2.2.1-dev",
   "description": "Cordova Geolocation Plugin",
   "cordova": {
     "id": "cordova-plugin-geolocation",
@@ -36,6 +36,13 @@
     "cordova-windows",
     "cordova-firefoxos"
   ],
+  "scripts": {
+    "test": "npm run jshint",
+    "jshint": "node node_modules/jshint/bin/jshint www && node node_modules/jshint/bin/jshint src && node node_modules/jshint/bin/jshint tests"
+  },
   "author": "Apache Software Foundation",
-  "license": "Apache 2.0"
+  "license": "Apache-2.0",
+  "devDependencies": {
+    "jshint": "^2.6.0"
+  }
 }
diff --git a/plugin.xml b/plugin.xml
index b5e1ad7..c312813 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -22,7 +22,7 @@
 xmlns:rim="http://www.blackberry.com/ns/widgets"
 xmlns:android="http://schemas.android.com/apk/res/android"
            id="cordova-plugin-geolocation"
-      version="2.1.1-dev">
+      version="2.2.1-dev">
 
     <name>Geolocation</name>
     <description>Cordova Geolocation Plugin</description>
@@ -31,6 +31,8 @@
     <repo>https://git-wip-us.apache.org/repos/asf/cordova-plugin-geolocation.git</repo>
     <issue>https://issues.apache.org/jira/browse/CB/component/12320638</issue>
 
+    <dependency id="cordova-plugin-compat" version="^1.0.0" />
+
     <!-- android -->
     <platform name="android">
 
@@ -46,7 +48,6 @@
         </config-file>
 
         <source-file src="src/android/Geolocation.java" target-dir="src/org/apache/cordova/geolocation/" />
-        <source-file src="src/android/PermissionHelper.java" target-dir="src/org/apache/cordova/geolocation/" />
 
         <js-module src="www/android/geolocation.js" name="geolocation">
             <clobbers target="navigator.geolocation" />
diff --git a/src/android/Geolocation.java b/src/android/Geolocation.java
index 79f0684..3e0bd8a 100644
--- a/src/android/Geolocation.java
+++ b/src/android/Geolocation.java
@@ -21,10 +21,12 @@
 import android.content.pm.PackageManager;
 import android.Manifest;
 import android.os.Build;
+import android.util.Log;
 
 import org.apache.cordova.CallbackContext;
 import org.apache.cordova.CordovaArgs;
 import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.PermissionHelper;
 import org.apache.cordova.PluginResult;
 import org.apache.cordova.LOG;
 import org.json.JSONArray;
@@ -41,6 +43,7 @@
 
 
     public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
+        Log.d(TAG, "We are entering execute");
         context = callbackContext;
         if(action.equals("getPermission"))
         {
@@ -63,18 +66,20 @@
                                           int[] grantResults) throws JSONException
     {
         PluginResult result;
-        for(int r:grantResults)
-        {
-            if(r == PackageManager.PERMISSION_DENIED)
-            {
-                LOG.d(TAG, "Permission Denied!");
-                result = new PluginResult(PluginResult.Status.ILLEGAL_ACCESS_EXCEPTION);
-                context.sendPluginResult(result);
-                return;
+        //This is important if we're using Cordova without using Cordova, but we have the geolocation plugin installed
+        if(context != null) {
+            for (int r : grantResults) {
+                if (r == PackageManager.PERMISSION_DENIED) {
+                    LOG.d(TAG, "Permission Denied!");
+                    result = new PluginResult(PluginResult.Status.ILLEGAL_ACCESS_EXCEPTION);
+                    context.sendPluginResult(result);
+                    return;
+                }
+
             }
+            result = new PluginResult(PluginResult.Status.OK);
+            context.sendPluginResult(result);
         }
-        result = new PluginResult(PluginResult.Status.OK);
-        context.sendPluginResult(result);
     }
 
     public boolean hasPermisssion() {
diff --git a/src/android/PermissionHelper.java b/src/android/PermissionHelper.java
deleted file mode 100644
index 4a3ca2d..0000000
--- a/src/android/PermissionHelper.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*

-       Licensed to the Apache Software Foundation (ASF) under one

-       or more contributor license agreements.  See the NOTICE file

-       distributed with this work for additional information

-       regarding copyright ownership.  The ASF licenses this file

-       to you under the Apache License, Version 2.0 (the

-       "License"); you may not use this file except in compliance

-       with the License.  You may obtain a copy of the License at

-

-         http://www.apache.org/licenses/LICENSE-2.0

-

-       Unless required by applicable law or agreed to in writing,

-       software distributed under the License is distributed on an

-       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

-       KIND, either express or implied.  See the License for the

-       specific language governing permissions and limitations

-       under the License.

-*/

-package org.apache.cordova.geolocation;

-

-import java.lang.reflect.InvocationTargetException;

-import java.lang.reflect.Method;

-import java.util.Arrays;

-

-import org.apache.cordova.CordovaInterface;

-import org.apache.cordova.CordovaPlugin;

-import org.apache.cordova.LOG;

-

-import android.content.pm.PackageManager;

-

-/**

- * This class provides reflective methods for permission requesting and checking so that plugins

- * written for cordova-android 5.0.0+ can still compile with earlier cordova-android versions.

- */

-public class PermissionHelper {

-    private static final String LOG_TAG = "CordovaPermissionHelper";

-

-    /**

-     * Requests a "dangerous" permission for the application at runtime. This is a helper method

-     * alternative to cordovaInterface.requestPermission() that does not require the project to be

-     * built with cordova-android 5.0.0+

-     *

-     * @param plugin        The plugin the permission is being requested for

-     * @param requestCode   A requestCode to be passed to the plugin's onRequestPermissionResult()

-     *                      along with the result of the permission request

-     * @param permission    The permission to be requested

-     */

-    public static void requestPermission(CordovaPlugin plugin, int requestCode, String permission) {

-        PermissionHelper.requestPermissions(plugin, requestCode, new String[] {permission});

-    }

-

-    /**

-     * Requests "dangerous" permissions for the application at runtime. This is a helper method

-     * alternative to cordovaInterface.requestPermissions() that does not require the project to be

-     * built with cordova-android 5.0.0+

-     *

-     * @param plugin        The plugin the permissions are being requested for

-     * @param requestCode   A requestCode to be passed to the plugin's onRequestPermissionResult()

-     *                      along with the result of the permissions request

-     * @param permissions   The permissions to be requested

-     */

-    public static void requestPermissions(CordovaPlugin plugin, int requestCode, String[] permissions) {

-        try {

-            Method requestPermission = CordovaInterface.class.getDeclaredMethod(

-                    "requestPermissions", CordovaPlugin.class, int.class, String[].class);

-

-            // If there is no exception, then this is cordova-android 5.0.0+

-            requestPermission.invoke(plugin.cordova, plugin, requestCode, permissions);

-        } catch (NoSuchMethodException noSuchMethodException) {

-            // cordova-android version is less than 5.0.0, so permission is implicitly granted

-            LOG.d(LOG_TAG, "No need to request permissions " + Arrays.toString(permissions));

-

-            // Notify the plugin that all were granted by using more reflection

-            deliverPermissionResult(plugin, requestCode, permissions);

-        } catch (IllegalAccessException illegalAccessException) {

-            // Should never be caught; this is a public method

-            LOG.e(LOG_TAG, "IllegalAccessException when requesting permissions " + Arrays.toString(permissions), illegalAccessException);

-        } catch(InvocationTargetException invocationTargetException) {

-            // This method does not throw any exceptions, so this should never be caught

-            LOG.e(LOG_TAG, "invocationTargetException when requesting permissions " + Arrays.toString(permissions), invocationTargetException);

-        }

-    }

-

-    /**

-     * Checks at runtime to see if the application has been granted a permission. This is a helper

-     * method alternative to cordovaInterface.hasPermission() that does not require the project to

-     * be built with cordova-android 5.0.0+

-     *

-     * @param plugin        The plugin the permission is being checked against

-     * @param permission    The permission to be checked

-     *

-     * @return              True if the permission has already been granted and false otherwise

-     */

-    public static boolean hasPermission(CordovaPlugin plugin, String permission) {

-        try {

-            Method hasPermission = CordovaInterface.class.getDeclaredMethod("hasPermission", String.class);

-

-            // If there is no exception, then this is cordova-android 5.0.0+

-            return (Boolean) hasPermission.invoke(plugin.cordova, permission);

-        } catch (NoSuchMethodException noSuchMethodException) {

-            // cordova-android version is less than 5.0.0, so permission is implicitly granted

-            LOG.d(LOG_TAG, "No need to check for permission " + permission);

-            return true;

-        } catch (IllegalAccessException illegalAccessException) {

-            // Should never be caught; this is a public method

-            LOG.e(LOG_TAG, "IllegalAccessException when checking permission " + permission, illegalAccessException);

-        } catch(InvocationTargetException invocationTargetException) {

-            // This method does not throw any exceptions, so this should never be caught

-            LOG.e(LOG_TAG, "invocationTargetException when checking permission " + permission, invocationTargetException);

-        }

-        return false;

-    }

-

-    private static void deliverPermissionResult(CordovaPlugin plugin, int requestCode, String[] permissions) {

-        // Generate the request results

-        int[] requestResults = new int[permissions.length];

-        Arrays.fill(requestResults, PackageManager.PERMISSION_GRANTED);

-

-        try {

-            Method onRequestPermissionResult = CordovaPlugin.class.getDeclaredMethod(

-                    "onRequestPermissionResult", int.class, String[].class, int[].class);

-

-            onRequestPermissionResult.invoke(plugin, requestCode, permissions, requestResults);

-        } catch (NoSuchMethodException noSuchMethodException) {

-            // Should never be caught since the plugin must be written for cordova-android 5.0.0+ if it

-            // made it to this point

-            LOG.e(LOG_TAG, "NoSuchMethodException when delivering permissions results", noSuchMethodException);

-        } catch (IllegalAccessException illegalAccessException) {

-            // Should never be caught; this is a public method

-            LOG.e(LOG_TAG, "IllegalAccessException when delivering permissions results", illegalAccessException);

-        } catch(InvocationTargetException invocationTargetException) {

-            // This method may throw a JSONException. We are just duplicating cordova-android's

-            // exception handling behavior here; all it does is log the exception in CordovaActivity,

-            // print the stacktrace, and ignore it

-            LOG.e(LOG_TAG, "InvocationTargetException when delivering permissions results", invocationTargetException);

-        }

-    }

-}

diff --git a/src/windows/GeolocationProxy.js b/src/windows/GeolocationProxy.js
index f6188bc..03ed495 100644
--- a/src/windows/GeolocationProxy.js
+++ b/src/windows/GeolocationProxy.js
@@ -14,47 +14,48 @@
  * limitations under the License.
  */
 
-var PositionError = require('./PositionError'),
-    ids = {},
-    loc;
+/* global Windows, WinJS */
 
-function ensureLocator() {
+var PositionError   = require('./PositionError');
+var callbacks       = {};
+var locs            = {};
+
+// constants
+var FALLBACK_EPSILON = 0.001;
+
+function ensureAndCreateLocator() {
     var deferral;
 
-    if (!loc) {
-        loc = new Windows.Devices.Geolocation.Geolocator();
+    var loc = new Windows.Devices.Geolocation.Geolocator();
 
-        if (typeof Windows.Devices.Geolocation.Geolocator.requestAccessAsync === 'function') {
-            deferral = Windows.Devices.Geolocation.Geolocator.requestAccessAsync().then(function (result) {
-                if (result === Windows.Devices.Geolocation.GeolocationAccessStatus.allowed) {
-                    return loc;
-                }
+    if (typeof Windows.Devices.Geolocation.Geolocator.requestAccessAsync === 'function') {
+        deferral = Windows.Devices.Geolocation.Geolocator.requestAccessAsync().then(function (result) {
+            if (result === Windows.Devices.Geolocation.GeolocationAccessStatus.allowed) {
+                return loc;
+            }
 
-                return WinJS.Promise.wrapError({
-                    code: PositionError.PERMISSION_DENIED,
-                    message: 'Geolocation access has not been allowed by user.'
-                });
+            return WinJS.Promise.wrapError({
+                code: PositionError.PERMISSION_DENIED,
+                message: 'Geolocation access has not been allowed by user.'
             });
-        } else {
-            deferral = WinJS.Promise.wrap(loc);
-        }
+        });
     } else {
         deferral = WinJS.Promise.wrap(loc);
-    }    
+    }
 
     return deferral;
 }
 
-function createErrorCode() {
+function createErrorCode(loc) {
     switch (loc.locationStatus) {
         case Windows.Devices.Geolocation.PositionStatus.initializing:
             // This status indicates that a location device is still initializing
         case Windows.Devices.Geolocation.PositionStatus.noData:
-            // No location data is currently available 
+            // No location data is currently available
         case Windows.Devices.Geolocation.PositionStatus.notInitialized:
             // This status indicates that the app has not yet requested
-            // location data by calling GetGeolocationAsync() or 
-            // registering an event handler for the positionChanged event. 
+            // location data by calling GetGeolocationAsync() or
+            // registering an event handler for the positionChanged event.
         case Windows.Devices.Geolocation.PositionStatus.notAvailable:
             // Location is not available on this version of Windows
             return PositionError.POSITION_UNAVAILABLE;
@@ -76,7 +77,7 @@
         altitudeAccuracy: pos.coordinate.altitudeAccuracy,
         timestamp: pos.coordinate.timestamp
     };
-    
+
     if (pos.coordinate.point) {
         res.latitude = pos.coordinate.point.position.latitude;
         res.longitude = pos.coordinate.point.position.longitude;
@@ -86,13 +87,13 @@
         res.longitude = pos.coordinate.longitude;
         res.altitude = pos.coordinate.altitude;
     }
-    
+
     return res;
 }
 
 module.exports = {
     getLocation: function (success, fail, args, env) {
-        ensureLocator().done(function () {
+        ensureAndCreateLocator().done(function (loc) {
             if (loc) {
                 var highAccuracy = args[0],
                     maxAge = args[1];
@@ -109,7 +110,7 @@
                     },
                     function (err) {
                         fail({
-                            code: createErrorCode(),
+                            code: createErrorCode(loc),
                             message: err.message
                         });
                     }
@@ -125,15 +126,15 @@
     },
 
     addWatch: function (success, fail, args, env) {
-        ensureLocator().done(function () {
-            var clientId = args[0],
-            highAccuracy = args[1],
+        ensureAndCreateLocator().done(function (loc) {
+            var clientId = args[0];
+            var highAccuracy = args[1];
 
-            onPositionChanged = function (e) {
+            var onPositionChanged = function (e) {
                 success(createResult(e.position), { keepCallback: true });
-            },
+            };
 
-            onStatusChanged = function (e) {
+            var onStatusChanged = function (e) {
                 switch (e.status) {
                     case Windows.Devices.Geolocation.PositionStatus.noData:
                     case Windows.Devices.Geolocation.PositionStatus.notAvailable:
@@ -166,29 +167,36 @@
                 // JavaScript runtime error: Operation aborted
                 // You must set the MovementThreshold property or the ReportInterval property before adding event handlers.
                 // WinRT information: You must set the MovementThreshold property or the ReportInterval property before adding event handlers
-                loc.movementThreshold = Number.EPSILON;
+                if (Number.EPSILON) {
+                    loc.movementThreshold = Number.EPSILON;
+                } else {
+                    loc.movementThreshold = FALLBACK_EPSILON;
+                }
             }
 
             loc.addEventListener("positionchanged", onPositionChanged);
             loc.addEventListener("statuschanged", onStatusChanged);
 
-            ids[clientId] = { pos: onPositionChanged, status: onStatusChanged };
-        }, fail);        
+            callbacks[clientId] = { pos: onPositionChanged, status: onStatusChanged };
+            locs[clientId] = loc;
+        }, fail);
     },
 
     clearWatch: function (success, fail, args, env) {
-        var clientId = args[0],
-            callbacks = ids[clientId];
+        var clientId = args[0];
+        var callback = callbacks[clientId];
+        var loc      = locs[clientId];
 
-        if (callbacks) {
-            loc.removeEventListener("positionchanged", callbacks.pos);
-            loc.removeEventListener("statuschanged", callbacks.status);
+        if (callback && loc) {
+            loc.removeEventListener("positionchanged", callback.pos);
+            loc.removeEventListener("statuschanged", callback.status);
 
-            delete ids[clientId];
+            delete callbacks[clientId];
+            delete locs[clientId];
         }
 
         success();
     }
 };
 
-require("cordova/exec/proxy").add("Geolocation", module.exports);
\ No newline at end of file
+require("cordova/exec/proxy").add("Geolocation", module.exports);
diff --git a/tests/plugin.xml b/tests/plugin.xml
index 255a939..0a5a009 100644
--- a/tests/plugin.xml
+++ b/tests/plugin.xml
@@ -22,7 +22,7 @@
     xmlns:rim="http://www.blackberry.com/ns/widgets"
     xmlns:android="http://schemas.android.com/apk/res/android"
     id="cordova-plugin-geolocation-tests"
-    version="2.1.1-dev">
+    version="2.2.1-dev">
     <name>Cordova Geolocation Plugin Tests</name>
     <license>Apache 2.0</license>
 
diff --git a/tests/tests.js b/tests/tests.js
index 65a9c69..50c5bfb 100644
--- a/tests/tests.js
+++ b/tests/tests.js
@@ -18,6 +18,10 @@
  * under the License.
  *
 */
+
+/* jshint jasmine: true */
+/* global WinJS, device */
+
 exports.defineAutoTests = function () {
     var fail = function (done, context, message) {
         // prevents done() to be called several times
@@ -267,6 +271,40 @@
     var watchLocationId = null;
 
     /**
+     * Set location status
+     */
+    function setLocationStatus(status) {
+        document.getElementById('location_status').innerHTML = status;
+    }
+    function setLocationDetails(p) {
+        var date = (new Date(p.timestamp));
+        document.getElementById('latitude').innerHTML = p.coords.latitude;
+        document.getElementById('longitude').innerHTML = p.coords.longitude;
+        document.getElementById('altitude').innerHTML = p.coords.altitude;
+        document.getElementById('accuracy').innerHTML = p.coords.accuracy;
+        document.getElementById('heading').innerHTML = p.coords.heading;
+        document.getElementById('speed').innerHTML = p.coords.speed;
+        document.getElementById('altitude_accuracy').innerHTML = p.coords.altitudeAccuracy;
+        document.getElementById('timestamp').innerHTML = date.toDateString() + " " + date.toTimeString();
+    }
+
+    /**
+     * Stop watching the location
+     */
+    function stopLocation() {
+        var geo = navigator.geolocation;
+        if (!geo) {
+            alert('navigator.geolocation object is missing.');
+            return;
+        }
+        setLocationStatus("Stopped");
+        if (watchLocationId) {
+            geo.clearWatch(watchLocationId);
+            watchLocationId = null;
+        }
+    }
+
+    /**
      * Start watching location
      */
     var watchLocation = function () {
@@ -293,22 +331,6 @@
     };
 
     /**
-     * Stop watching the location
-     */
-    var stopLocation = function () {
-        var geo = navigator.geolocation;
-        if (!geo) {
-            alert('navigator.geolocation object is missing.');
-            return;
-        }
-        setLocationStatus("Stopped");
-        if (watchLocationId) {
-            geo.clearWatch(watchLocationId);
-            watchLocationId = null;
-        }
-    };
-
-    /**
      * Get current location
      */
     var getLocation = function (opts) {
@@ -340,24 +362,6 @@
 
     };
 
-    /**
-     * Set location status
-     */
-    var setLocationStatus = function (status) {
-        document.getElementById('location_status').innerHTML = status;
-    };
-    var setLocationDetails = function (p) {
-        var date = (new Date(p.timestamp));
-        document.getElementById('latitude').innerHTML = p.coords.latitude;
-        document.getElementById('longitude').innerHTML = p.coords.longitude;
-        document.getElementById('altitude').innerHTML = p.coords.altitude;
-        document.getElementById('accuracy').innerHTML = p.coords.accuracy;
-        document.getElementById('heading').innerHTML = p.coords.heading;
-        document.getElementById('speed').innerHTML = p.coords.speed;
-        document.getElementById('altitude_accuracy').innerHTML = p.coords.altitudeAccuracy;
-        document.getElementById('timestamp').innerHTML = date.toDateString() + " " + date.toTimeString();
-    };
-
     /******************************************************************************/
 
     var location_div = '<div id="info">' +
@@ -423,8 +427,8 @@
         note = 
             '<h3>Allow use of current location, if prompted</h3>';
 
-    contentEl.innerHTML = values_info + location_div + latitude + longitude + altitude + accuracy + heading + speed
-        + altitude_accuracy + time + note + actions;
+    contentEl.innerHTML = values_info + location_div + latitude + longitude + altitude + accuracy + heading + speed +
+        altitude_accuracy + time + note + actions;
 
     createActionButton('Get Location', function () {
         getLocation();
diff --git a/www/android/geolocation.js b/www/android/geolocation.js
index 57b8122..7265bec 100644
--- a/www/android/geolocation.js
+++ b/www/android/geolocation.js
@@ -19,13 +19,15 @@
  *
 */
 
-
 var exec = cordova.require('cordova/exec');
 var utils = require('cordova/utils');
 var PositionError = require('./PositionError');
 
-module.exports = {
+// Native watchPosition method is called async after permissions prompt.
+// So we use additional map and own ids to return watch id synchronously.
+var pluginToNativeWatchMap = {};
 
+module.exports = {
     getCurrentPosition: function(success, error, args) {
         var win = function() {
           var geo = cordova.require('cordova/modulemapper').getOriginalSymbol(window, 'navigator.geolocation');
@@ -67,7 +69,3 @@
         exec(win, null, "Geolocation", "getPermission", []);
     }
 };
-
-// Native watchPosition method is called async after permissions prompt.
-// So we use additional map and own ids to return watch id synchronously.
-var pluginToNativeWatchMap = {};