start of geolocation rewrite to conform with Geolocation W3C API spec.
Project: http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/commit/95d979d6 Tree: http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/tree/95d979d6 Diff: http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/diff/95d979d6 Branch: refs/heads/master Commit: 95d979d69e306517c5487f431b0140da0886806f Parents: 34f5fec Author: Fil Maj <maj....@gmail.com> Authored: Thu Mar 22 15:26:08 2012 -0700 Committer: Fil Maj <maj....@gmail.com> Committed: Mon May 7 13:51:05 2012 -0700 ---------------------------------------------------------------------- lib/common/plugin/Position.js | 8 +++- lib/common/plugin/geolocation.js | 77 ++++++++++++++++++++++++++++---- test/test.geolocation.js | 40 +++++++++++++---- 3 files changed, 104 insertions(+), 21 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/95d979d6/lib/common/plugin/Position.js ---------------------------------------------------------------------- diff --git a/lib/common/plugin/Position.js b/lib/common/plugin/Position.js index 33c5be9..deb7c49 100644 --- a/lib/common/plugin/Position.js +++ b/lib/common/plugin/Position.js @@ -1,8 +1,12 @@ var Coordinates = require('cordova/plugin/Coordinates'); var Position = function(coords, timestamp) { - this.coords = new Coordinates(coords.latitude, coords.longitude, coords.altitude, coords.accuracy, coords.heading, coords.velocity, coords.altitudeAccuracy); + if (coords) { + this.coords = new Coordinates(coords.latitude, coords.longitude, coords.altitude, coords.accuracy, coords.heading, coords.velocity, coords.altitudeAccuracy); + } else { + this.coords = new Coordinates(); + } this.timestamp = (timestamp !== undefined) ? timestamp : new Date().getTime(); }; -module.exports = Position; \ No newline at end of file +module.exports = Position; http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/95d979d6/lib/common/plugin/geolocation.js ---------------------------------------------------------------------- diff --git a/lib/common/plugin/geolocation.js b/lib/common/plugin/geolocation.js index 1b86f02..443fb61 100644 --- a/lib/common/plugin/geolocation.js +++ b/lib/common/plugin/geolocation.js @@ -8,20 +8,24 @@ var timers = {}; // list of timers in use // Returns default params, overrides if provided with values function parseParameters(options) { var opt = { - maximumAge: 10000, + maximumAge: 0, enableHighAccuracy: false, - timeout: 10000 + timeout: Infinity }; if (options) { - if (options.maximumAge !== undefined) { + if (options.maximumAge !== undefined && !isNaN(options.maximumAge) && options.maximumAge > 0) { opt.maximumAge = options.maximumAge; } if (options.enableHighAccuracy !== undefined) { opt.enableHighAccuracy = options.enableHighAccuracy; } - if (options.timeout !== undefined) { - opt.timeout = options.timeout; + if (options.timeout !== undefined && !isNaN(options.timeout)) { + if (options.timeout < 0) { + opt.timeout = 0; + } else { + opt.timeout = options.timeout; + } } } @@ -29,6 +33,7 @@ function parseParameters(options) { } var geolocation = { + lastPosition:null, // reference to last known (cached) position returned /** * Asynchronously aquires the current position. * @@ -37,10 +42,24 @@ var geolocation = { * @param {PositionOptions} options The options for getting the position data. (OPTIONAL) */ getCurrentPosition:function(successCallback, errorCallback, options) { + if (arguments.length === 0) { + throw new Error("getCurrentPosition must be called with at least one argument."); + } options = parseParameters(options); + // Timer var that will fire an error callback if no position is retrieved from native + // before the "timeout" param provided expires + var timeoutTimer = null; + var win = function(p) { - successCallback(new Position( + clearTimeout(timeoutTimer); + if (!timeoutTimer) { + // Timeout already happened, or native fired error callback for + // this geo request. + // Don't continue with success callback. + return; + } + var pos = new Position( { latitude:p.latitude, longitude:p.longitude, @@ -51,13 +70,51 @@ var geolocation = { altitudeAccuracy:p.altitudeAccuracy }, p.timestamp || new Date() - )); + ); + geolocation.lastPosition = pos; + successCallback(pos); }; var fail = function(e) { - errorCallback(new PositionError(e.code, e.message)); + clearTimeout(timeoutTimer); + timeoutTimer = null; + var err = new PositionError(e.code, e.message); + if (errorCallback) { + errorCallback(err); + } }; - exec(win, fail, "Geolocation", "getLocation", [options.enableHighAccuracy, options.timeout, options.maximumAge]); + // Check our cached position, if its timestamp difference with current time is less than the timeout, then just + // fire the success callback with the cached position. + if (geolocation.lastPosition && options.timeout && ((new Date()).getTime() - geolocation.lastPosition.timestamp.getTime() <= options.timeout)) { + successCallback(cachedPosition); + // If the cached position check failed and the timeout was set to 0, error out with a TIMEOUT error object. + } else if (options.timeout === 0) { + fail({ + code:PositionError.TIMEOUT, + message:"timeout value in PositionOptions set to 0 and no cached Position object available, or cached Position object's age exceed's provided PositionOptions' maximumAge parameter." + }); + // Otherwise we have to call into native to retrieve a position. + } else { + if (options.timeout !== Infinity) { + // If the timeout value was not set to Infinity (default), then + // set up a timeout function that will fire the error callback + // if no successful position was retrieved before timeout expired. + timeoutTimer = setTimeout(function() { + clearTimeout(timeoutTimer); + timeoutTimer = null; + fail({ + code:PositionError.TIMEOUT, + message:"Position retrieval timed out." + }); + }, options.timeout); + } else { + // This is here so the check in the win function doesn't mess stuff up + // may seem weird but this guarantees timeoutTimer is + // always truthy before we call into native + timeoutTimer = true; + } + exec(win, fail, "Geolocation", "getLocation", [options.enableHighAccuracy, options.timeout, options.maximumAge]); + } }, /** * Asynchronously watches the geolocation for changes to geolocation. When a change occurs, @@ -91,4 +148,4 @@ var geolocation = { } }; -module.exports = geolocation; \ No newline at end of file +module.exports = geolocation; http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/95d979d6/test/test.geolocation.js ---------------------------------------------------------------------- diff --git a/test/test.geolocation.js b/test/test.geolocation.js index a24059c..e5fe4f2 100644 --- a/test/test.geolocation.js +++ b/test/test.geolocation.js @@ -1,32 +1,54 @@ describe("geolocation", function () { var geo = require('cordova/plugin/geolocation'), + Position = require('cordova/plugin/Position'), + PositionError = require('cordova/plugin/PositionError'), s, e; exec = require('cordova/exec'); beforeEach(function () { s = jasmine.createSpy("success"); e = jasmine.createSpy("error"); + exec.reset(); }); describe("when getting the current position", function () { - it("uses the default values", function () { + beforeEach(function() { + geo.lastPosition = null; // reset the cached position + }); + + it("uses default PositionOptions if none are specified", function () { geo.getCurrentPosition(s, e); - expect(exec).toHaveBeenCalledWith(jasmine.any(Function), jasmine.any(Function), "Geolocation", "getLocation", [false, 10000, 10000]); + expect(exec).toHaveBeenCalledWith(jasmine.any(Function), jasmine.any(Function), "Geolocation", "getLocation", [false, Infinity, 0]); }); - it("uses the maximumAge option", function () { + it("uses the maximumAge option if specified", function () { geo.getCurrentPosition(s, e, {maximumAge: 10}); - expect(exec).toHaveBeenCalledWith(jasmine.any(Function), jasmine.any(Function), "Geolocation", "getLocation", [false, 10000, 10]); + expect(exec).toHaveBeenCalledWith(jasmine.any(Function), jasmine.any(Function), "Geolocation", "getLocation", [false, Infinity, 10]); }); - it("uses the enableHighAccuracy option", function () { + it("uses the enableHighAccuracy option if specified", function () { geo.getCurrentPosition(s, e, {enableHighAccuracy: true, maximumAge: 100}); - expect(exec).toHaveBeenCalledWith(jasmine.any(Function), jasmine.any(Function), "Geolocation", "getLocation", [true, 10000, 100]); + expect(exec).toHaveBeenCalledWith(jasmine.any(Function), jasmine.any(Function), "Geolocation", "getLocation", [true, Infinity, 100]); }); - it("uses the timeout option", function () { + it("uses the timeout option if specified and positive", function () { geo.getCurrentPosition(s, e, {timeout: 1000}); - expect(exec).toHaveBeenCalledWith(jasmine.any(Function), jasmine.any(Function), "Geolocation", "getLocation", [false, 1000, 10000]); + expect(exec).toHaveBeenCalledWith(jasmine.any(Function), jasmine.any(Function), "Geolocation", "getLocation", [false, 1000, 0]); + }); + it("uses a timeout value of 0 if specified and negative, which should call the error callback immediately (since we have no cached position)", function () { + geo.getCurrentPosition(s, e, {timeout: -1000}); + expect(e).toHaveBeenCalledWith({ + code:PositionError.TIMEOUT, + message:"timeout value in PositionOptions set to 0 and no cached Position object available, or cached Position object's age exceed's provided PositionOptions' maximumAge parameter." + }); + }); + it("can be used with one, two or three arguments", function() { + expect(function() { geo.getCurrentPosition(s); }).not.toThrow(); + expect(function() { geo.getCurrentPosition(s,e); }).not.toThrow(); + expect(function() { geo.getCurrentPosition(s,e,{}); }).not.toThrow(); + }); + it("should throw an exception if used with no arguments", function() { + expect(function() { geo.getCurrentPosition();}).toThrow("getCurrentPosition must be called with at least one argument."); }); }); @@ -46,7 +68,7 @@ describe("geolocation", function () { it("sets an interval for the default timeout", function () { geo.watchPosition(s, e); - expect(window.setInterval).toHaveBeenCalledWith(jasmine.any(Function), 10000); + expect(window.setInterval).toHaveBeenCalledWith(jasmine.any(Function), Infinity); }); it("sets an interval for the provided timeout", function () {