/**
* @class Ext.util.GeoLocation
* @extends Ext.util.Observable
*
* Provides a cross browser class for retrieving location information.
*
* Based on the Geolocation API Specification.
* If the browser does not implement that specification (Internet Explorer 6-8), it can fallback on Google Gears
* as long as the browser has it installed, and the following javascript file from google is included on the page:
* <script type="text/javascript" src="http://code.google.com/apis/gears/gears_init.js"></script>
*
* Note: Location implementations are only required to return timestamp, longitude, latitude, and accuracy.
* Other properties (altitude, altitudeAccuracy, heading, speed) can be null or sporadically returned.
*
* When instantiated, by default this class immediately begins tracking location information,
* firing a {@link #locationupdate} event when new location information is available. To disable this
* location tracking (which may be battery intensive on mobile devices), set {@link #autoUpdate} to false.
* When this is done, only calls to {@link #updateLocation} will trigger a location retrieval.
*
* A {@link #locationerror} event is raised when an error occurs retrieving the location, either due to a user
* denying the application access to it, or the browser not supporting it.
*
* The below code shows a GeoLocation making a single retrieval of location information.
*
var geo = new Ext.util.GeoLocation({
autoUpdate: false,
listeners: {
locationupdate: function (geo) {
alert('New latitude: ' + geo.latitude);
},
locationerror: function ( geo,
bTimeout,
bPermissionDenied,
bLocationUnavailable,
message) {
if(bTimeout){
alert('Timeout occurred.');
}
else{
alert('Error occurred.');
}
}
}
});
geo.updateLocation();
*/
Ext.util.GeoLocation = Ext.extend(Ext.util.Observable, {
/**
* @cfg {Boolean} autoUpdate
* Defaults to true.
* When set to true, continually monitor the location of the device
* (beginning immediately) and fire {@link #locationupdate}/{@link #locationerror} events.
*
* When using google gears, if the user denies access or another error occurs, this will be reset to false.
*/
autoUpdate: true,
//Position interface
/**
* Read-only property representing the last retrieved
* geographical coordinate specified in degrees.
* @type Number
*/
latitude: null,
/**
* Read-only property representing the last retrieved
* geographical coordinate specified in degrees.
* @type Number
*/
longitude: null,
/**
* Read-only property representing the last retrieved
* accuracy level of the latitude and longitude coordinates,
* specified in meters.
* This will always be a non-negative number.
* This corresponds to a 95% confidence level.
* @type Number
*/
accuracy: null,
/**
* Read-only property representing the last retrieved
* height of the position, specified in meters above the ellipsoid
* [WGS84].
* @type Number/null
*/
altitude: null,
/**
* Read-only property representing the last retrieved
* accuracy level of the altitude coordinate, specified in meters.
* If altitude is not null then this will be a non-negative number.
* Otherwise this returns null.
* This corresponds to a 95% confidence level.
* @type Number/null
*/
altitudeAccuracy: null,
/**
* Read-only property representing the last retrieved
* direction of travel of the hosting device,
* specified in non-negative degrees between 0 and 359,
* counting clockwise relative to the true north.
* If speed is 0 (device is stationary), then this returns NaN
* @type Number/null
*/
heading: null,
/**
* Read-only property representing the last retrieved
* current ground speed of the device, specified in meters per second.
* If this feature is unsupported by the device, this returns null.
* If the device is stationary, this returns 0,
* otherwise it returns a non-negative number.
* @type Number/null
*/
speed: null,
/**
* Read-only property representing when the last retrieved
* positioning information was acquired by the device.
* @type Date
*/
timestamp: null,
//PositionOptions interface
/**
* @cfg {Boolean} allowHighAccuracy
* Defaults to false.
* When set to true, provide a hint that the application would like to receive
* the best possible results. This may result in slower response times or increased power consumption.
* The user might also deny this capability, or the device might not be able to provide more accurate
* results than if this option was set to false.
*/
allowHighAccuracy: false,
/**
* @cfg {Number} timeout
* Defaults to Infinity.
* The maximum number of milliseconds allowed to elapse between a location update operation
* and the corresponding {@link #locationupdate} event being raised. If a location was not successfully
* acquired before the given timeout elapses (and no other internal errors have occurred in this interval),
* then a {@link #locationerror} event will be raised indicating a timeout as the cause.
* Note that the time that is spent obtaining the user permission is not included in the period
* covered by the timeout. The timeout attribute only applies to the location acquisition operation.
* In the case of calling updateLocation, the {@link #locationerror} event will be raised only once.
* If {@link #autoUpdate} is set to true, the {@link #locationerror} event could be raised repeatedly.
* The first timeout is relative to the moment {@link #autoUpdate} was set to true
* (or this {@link Ext.util.GeoLocation} was initialized with the {@link #autoUpdate} config option set to true).
* Subsequent timeouts are relative to the moment when the device determines that it's position has changed.
*/
timeout: Infinity,
/**
* @cfg {Number} maximumAge
* Defaults to 0.
* This option indicates that the application is willing to accept cached location information whose age
* is no greater than the specified time in milliseconds. If maximumAge is set to 0, an attempt to retrieve
* new location information is made immediately.
* Setting the maximumAge to Infinity returns a cached position regardless of its age.
* If the device does not have cached location information available whose age is no
* greater than the specified maximumAge, then it must acquire new location information.
* For example, if location information no older than 10 minutes is required, set this property to 600000.
*/
maximumAge: 0,
/**
* Changes the {@link #maximumAge} option and restarts any active
* location monitoring with the updated setting.
* @param {Number} maximumAge The value to set the maximumAge option to.
*/
setMaximumAge: function(maximumAge) {
this.maximumAge = maximumAge;
this.setAutoUpdate(this.autoUpdate);
},
/**
* Changes the {@link #timeout} option and restarts any active
* location monitoring with the updated setting.
* @param {Number} timeout The value to set the timeout option to.
*/
setTimeout: function(timeout) {
this.timeout = timeout;
this.setAutoUpdate(this.autoUpdate);
},
/**
* Changes the {@link #allowHighAccuracy} option and restarts any active
* location monitoring with the updated setting.
* @param {Number} allowHighAccuracy The value to set the allowHighAccuracy option to.
*/
setAllowHighAccuracy: function(allowHighAccuracy) {
this.allowHighAccuracy = allowHighAccuracy;
this.setAutoUpdate(this.autoUpdate);
},
//
setEnableHighAccuracy : function() {
console.warn("GeoLocation: setEnableHighAccuracy has been deprecated. Please use setAllowHighAccuracy.");
return this.setAllowHighAccuracy.apply(this, arguments);
},
//
// private Object geolocation provider
provider : null,
// private Number tracking current watchPosition
watchOperation : null,
constructor : function(config) {
Ext.apply(this, config);
//
if (Ext.isDefined(this.enableHighAccuracy)) {
console.warn("GeoLocation: enableHighAccuracy has been removed. Please use allowHighAccuracy.");
this.allowHighAccuracy = this.enableHighAccuracy;
}
//
this.coords = this; //@deprecated
if (Ext.supports.GeoLocation) {
this.provider = this.provider ||
(navigator.geolocation ? navigator.geolocation :
(window.google || {}).gears ? google.gears.factory.create('beta.geolocation') : null);
}
this.addEvents(
/**
* @private
* @event update
* @param {Ext.util.GeoLocation/False} coords
* Will return false if geolocation fails (disabled, denied access, timed out).
* @param {Ext.util.GeoLocation} this
* @deprecated
*/
'update',
/**
* @event locationerror
* Raised when a location retrieval operation failed.
* In the case of calling updateLocation, this event will be raised only once.
* If {@link #autoUpdate} is set to true, this event could be raised repeatedly.
* The first error is relative to the moment {@link #autoUpdate} was set to true
* (or this {@link Ext.util.GeoLocation} was initialized with the {@link #autoUpdate} config option set to true).
* Subsequent errors are relative to the moment when the device determines that it's position has changed.
* @param {Ext.util.GeoLocation} this
* @param {Boolean} timeout
* Boolean indicating a timeout occurred
* @param {Boolean} permissionDenied
* Boolean indicating the user denied the location request
* @param {Boolean} locationUnavailable
* Boolean indicating that the location of the device could not be determined.
* For instance, one or more of the location providers used in the location acquisition
* process reported an internal error that caused the process to fail entirely.
* @param {String} message
* An error message describing the details of the error encountered.
* This attribute is primarily intended for debugging and should not be used
* directly in an application user interface.
*/
'locationerror',
/**
* @event locationupdate
* Raised when a location retrieval operation has been completed successfully.
* @param {Ext.util.GeoLocation} this
* Retrieve the current location information from the GeoLocation object by using the read-only
* properties latitude, longitude, accuracy, altitude, altitudeAccuracy, heading, and speed.
*/
'locationupdate'
);
Ext.util.GeoLocation.superclass.constructor.call(this);
if(this.autoUpdate){
var me = this;
setTimeout(function(){
me.setAutoUpdate(me.autoUpdate);
}, 0);
}
},
/**
* Enabled/disables the auto-retrieval of the location information.
* If called with autoUpdate=true, it will execute an immediate location update
* and continue monitoring for location updates.
* If autoUpdate=false, any current location change monitoring will be disabled.
* @param {Boolean} autoUpdate Whether to start/stop location monitoring.
* @return {Boolean} If enabling autoUpdate, returns false if the location tracking
* cannot begin due to an error supporting geolocation.
* A locationerror event is also fired.
*/
setAutoUpdate : function(autoUpdate) {
if (this.watchOperation !== null) {
this.provider.clearWatch(this.watchOperation);
this.watchOperation = null;
}
if (!autoUpdate) {
return true;
}
if (!Ext.supports.GeoLocation) {
this.fireEvent('locationerror', this, false, false, true, null);
return false;
}
try{
this.watchOperation = this.provider.watchPosition(
Ext.createDelegate(this.fireUpdate, this),
Ext.createDelegate(this.fireError, this),
this.parseOptions());
}
catch(e){
this.autoUpdate = false;
this.fireEvent('locationerror', this, false, false, true, e.message);
return false;
}
return true;
},
/**
* Executes a onetime location update operation,
* raising either a {@link #locationupdate} or {@link #locationerror} event.
* Does not interfere with or restart ongoing location monitoring.
* @param {Function} callback
* A callback method to be called when the location retrieval has been completed.
* Will be called on both success and failure.
* The method will be passed one parameter, {@link Ext.GeoLocation} (this reference),
* set to null on failure.
*
geo.updateLocation(function (geo) {
alert('Latitude: ' + (geo != null ? geo.latitude : 'failed'));
});
* @param {Object} scope (optional)
* (optional) The scope (this reference) in which the handler function is executed.
* If omitted, defaults to the object which fired the event.
*
*/
updateLocation : function(callback, scope, positionOptions) {
var me = this;
var failFunction = function(message, error){
if(error){
me.fireError(error);
}
else{
me.fireEvent('locationerror', me, false, false, true, message);
}
if(callback){
callback.call(scope || me, null, me); //last parameter for legacy purposes
}
me.fireEvent('update', false, me); //legacy, deprecated
};
if (!Ext.supports.GeoLocation) {
setTimeout(function() {
failFunction(null);
}, 0);
return;
}
try{
this.provider.getCurrentPosition(
//success callback
function(position){
me.fireUpdate(position);
if(callback){
callback.call(scope || me, me, me); //last parameter for legacy purposes
}
me.fireEvent('update', me, me); //legacy, deprecated
},
//error callback
function(error){
failFunction(null, error);
},
positionOptions ? positionOptions : this.parseOptions());
}
catch(e){
setTimeout(function(){
failFunction(e.message);
}, 0);
}
},
// private
fireUpdate: function(position){
this.timestamp = position.timestamp;
this.latitude = position.coords.latitude;
this.longitude = position.coords.longitude;
this.accuracy = position.coords.accuracy;
this.altitude = position.coords.altitude;
this.altitudeAccuracy = position.coords.altitudeAccuracy;
//google doesn't provide these two
this.heading = typeof position.coords.heading == 'undefined' ? null : position.coords.heading;
this.speed = typeof position.coords.speed == 'undefined' ? null : position.coords.speed;
this.fireEvent('locationupdate', this);
},
fireError: function(error){
this.fireEvent('locationerror', this,
error.code == error.TIMEOUT,
error.code == error.PERMISSION_DENIED,
error.code == error.POSITION_UNAVAILABLE,
error.message == undefined ? null : error.message);
},
parseOptions: function(){
var ret = {
maximumAge: this.maximumAge,
allowHighAccuracy: this.allowHighAccuracy
};
//Google doesn't like Infinity
if(this.timeout !== Infinity){
ret.timeout = this.timeout;
}
return ret;
},
/**
* @private
* Returns cached coordinates, and updates if there are no cached coords yet.
* @deprecated
*/
getLocation : function(callback, scope) {
var me = this;
if(this.latitude !== null){
callback.call(scope || me, me, me);
}
else {
me.updateLocation(callback, scope);
}
}
});