]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * Provides a cross browser class for retrieving location information.\r | |
3 | *\r | |
4 | * Based on the [Geolocation API Specification](http://dev.w3.org/geo/api/spec-source.html)\r | |
5 | *\r | |
6 | * When instantiated, by default this class immediately begins tracking location information,\r | |
7 | * firing a {@link #locationupdate} event when new location information is available. To disable this\r | |
8 | * location tracking (which may be battery intensive on mobile devices), set {@link #autoUpdate} to `false`.\r | |
9 | *\r | |
10 | * When this is done, only calls to {@link #updateLocation} will trigger a location retrieval.\r | |
11 | *\r | |
12 | * A {@link #locationerror} event is raised when an error occurs retrieving the location, either due to a user\r | |
13 | * denying the application access to it, or the browser not supporting it.\r | |
14 | *\r | |
15 | * The below code shows a GeoLocation making a single retrieval of location information.\r | |
16 | *\r | |
17 | * var geo = Ext.create('Ext.util.Geolocation', {\r | |
18 | * autoUpdate: false,\r | |
19 | * listeners: {\r | |
20 | * locationupdate: function(geo) {\r | |
21 | * alert('New latitude: ' + geo.getLatitude());\r | |
22 | * },\r | |
23 | * locationerror: function(geo, bTimeout, bPermissionDenied, bLocationUnavailable, message) {\r | |
24 | * if(bTimeout){\r | |
25 | * alert('Timeout occurred.');\r | |
26 | * } else {\r | |
27 | * alert('Error occurred.');\r | |
28 | * }\r | |
29 | * }\r | |
30 | * }\r | |
31 | * });\r | |
32 | * geo.updateLocation();\r | |
33 | */\r | |
34 | Ext.define('Ext.util.Geolocation', {\r | |
35 | extend: 'Ext.Evented',\r | |
36 | alternateClassName: ['Ext.util.GeoLocation'],\r | |
37 | \r | |
38 | config: {\r | |
39 | /**\r | |
40 | * @event locationerror\r | |
41 | * Raised when a location retrieval operation failed.\r | |
42 | *\r | |
43 | * In the case of calling updateLocation, this event will be raised only once.\r | |
44 | *\r | |
45 | * If {@link #autoUpdate} is set to `true`, this event could be raised repeatedly.\r | |
46 | * The first error is relative to the moment {@link #autoUpdate} was set to `true`\r | |
47 | * (or this {@link Ext.util.Geolocation} was initialized with the {@link #autoUpdate} config option set to `true`).\r | |
48 | * Subsequent errors are relative to the moment when the device determines that it's position has changed.\r | |
49 | * @param {Ext.util.Geolocation} this\r | |
50 | * @param {Boolean} timeout\r | |
51 | * Boolean indicating a timeout occurred\r | |
52 | * @param {Boolean} permissionDenied\r | |
53 | * Boolean indicating the user denied the location request\r | |
54 | * @param {Boolean} locationUnavailable\r | |
55 | * Boolean indicating that the location of the device could not be determined.\r | |
56 | * For instance, one or more of the location providers used in the location acquisition\r | |
57 | * process reported an internal error that caused the process to fail entirely.\r | |
58 | * @param {String} message An error message describing the details of the error encountered.\r | |
59 | *\r | |
60 | * This attribute is primarily intended for debugging and should not be used\r | |
61 | * directly in an application user interface.\r | |
62 | */\r | |
63 | \r | |
64 | /**\r | |
65 | * @event locationupdate\r | |
66 | * Raised when a location retrieval operation has been completed successfully.\r | |
67 | * @param {Ext.util.Geolocation} this\r | |
68 | * Retrieve the current location information from the GeoLocation object by using the read-only\r | |
69 | * properties: {@link #latitude}, {@link #longitude}, {@link #accuracy}, {@link #altitude}, {@link #altitudeAccuracy}, {@link #heading}, and {@link #speed}.\r | |
70 | */\r | |
71 | \r | |
72 | /**\r | |
73 | * @cfg {Boolean} autoUpdate\r | |
74 | * When set to `true`, continually monitor the location of the device (beginning immediately)\r | |
75 | * and fire {@link #locationupdate} and {@link #locationerror} events.\r | |
76 | */\r | |
77 | autoUpdate: true,\r | |
78 | \r | |
79 | /**\r | |
80 | * @cfg {Number} frequency\r | |
81 | * The frequency of each update if {@link #autoUpdate} is set to `true`.\r | |
82 | */\r | |
83 | frequency: 10000,\r | |
84 | \r | |
85 | /**\r | |
86 | * @cfg {Number} latitude\r | |
87 | * Read-only property representing the last retrieved\r | |
88 | * geographical coordinate specified in degrees.\r | |
89 | * @readonly\r | |
90 | */\r | |
91 | latitude: null,\r | |
92 | \r | |
93 | /**\r | |
94 | * @cfg {Number} longitude\r | |
95 | * Read-only property representing the last retrieved\r | |
96 | * geographical coordinate specified in degrees.\r | |
97 | * @readonly\r | |
98 | */\r | |
99 | longitude: null,\r | |
100 | \r | |
101 | /**\r | |
102 | * @cfg {Number} accuracy\r | |
103 | * Read-only property representing the last retrieved\r | |
104 | * accuracy level of the latitude and longitude coordinates,\r | |
105 | * specified in meters.\r | |
106 | *\r | |
107 | * This will always be a non-negative number.\r | |
108 | *\r | |
109 | * This corresponds to a 95% confidence level.\r | |
110 | * @readonly\r | |
111 | */\r | |
112 | accuracy: null,\r | |
113 | \r | |
114 | /**\r | |
115 | * @cfg {Number} altitude\r | |
116 | * Read-only property representing the last retrieved\r | |
117 | * height of the position, specified in meters above the ellipsoid\r | |
118 | * [WGS84](http://dev.w3.org/geo/api/spec-source.html#ref-wgs).\r | |
119 | * @readonly\r | |
120 | */\r | |
121 | altitude: null,\r | |
122 | \r | |
123 | /**\r | |
124 | * @cfg {Number} altitudeAccuracy\r | |
125 | * Read-only property representing the last retrieved\r | |
126 | * accuracy level of the altitude coordinate, specified in meters.\r | |
127 | *\r | |
128 | * If altitude is not null then this will be a non-negative number.\r | |
129 | * Otherwise this returns `null`.\r | |
130 | *\r | |
131 | * This corresponds to a 95% confidence level.\r | |
132 | * @readonly\r | |
133 | */\r | |
134 | altitudeAccuracy: null,\r | |
135 | \r | |
136 | /**\r | |
137 | * @cfg {Number} heading\r | |
138 | * Read-only property representing the last retrieved\r | |
139 | * direction of travel of the hosting device,\r | |
140 | * specified in non-negative degrees between 0 and 359,\r | |
141 | * counting clockwise relative to the true north.\r | |
142 | *\r | |
143 | * If speed is 0 (device is stationary), then this returns `NaN`.\r | |
144 | * @readonly\r | |
145 | */\r | |
146 | heading: null,\r | |
147 | \r | |
148 | /**\r | |
149 | * @cfg {Number} speed\r | |
150 | * Read-only property representing the last retrieved\r | |
151 | * current ground speed of the device, specified in meters per second.\r | |
152 | *\r | |
153 | * If this feature is unsupported by the device, this returns `null`.\r | |
154 | *\r | |
155 | * If the device is stationary, this returns 0,\r | |
156 | * otherwise it returns a non-negative number.\r | |
157 | * @readonly\r | |
158 | */\r | |
159 | speed: null,\r | |
160 | \r | |
161 | /**\r | |
162 | * @cfg {Date} timestamp\r | |
163 | * Read-only property representing when the last retrieved\r | |
164 | * positioning information was acquired by the device.\r | |
165 | * @readonly\r | |
166 | */\r | |
167 | timestamp: null,\r | |
168 | \r | |
169 | //PositionOptions interface\r | |
170 | /**\r | |
171 | * @cfg {Boolean} allowHighAccuracy\r | |
172 | * When set to `true`, provide a hint that the application would like to receive\r | |
173 | * the best possible results. This may result in slower response times or increased power consumption.\r | |
174 | * The user might also deny this capability, or the device might not be able to provide more accurate\r | |
175 | * results than if this option was set to `false`.\r | |
176 | */\r | |
177 | allowHighAccuracy: false,\r | |
178 | \r | |
179 | /**\r | |
180 | * @cfg {Number} timeout\r | |
181 | * The maximum number of milliseconds allowed to elapse between a location update operation\r | |
182 | * and the corresponding {@link #locationupdate} event being raised. If a location was not successfully\r | |
183 | * acquired before the given timeout elapses (and no other internal errors have occurred in this interval),\r | |
184 | * then a {@link #locationerror} event will be raised indicating a timeout as the cause.\r | |
185 | *\r | |
186 | * Note that the time that is spent obtaining the user permission is **not** included in the period\r | |
187 | * covered by the timeout. The `timeout` attribute only applies to the location acquisition operation.\r | |
188 | *\r | |
189 | * In the case of calling `updateLocation`, the {@link #locationerror} event will be raised only once.\r | |
190 | *\r | |
191 | * If {@link #autoUpdate} is set to `true`, the {@link #locationerror} event could be raised repeatedly.\r | |
192 | * The first timeout is relative to the moment {@link #autoUpdate} was set to `true`\r | |
193 | * (or this {@link Ext.util.Geolocation} was initialized with the {@link #autoUpdate} config option set to `true`).\r | |
194 | * Subsequent timeouts are relative to the moment when the device determines that it's position has changed.\r | |
195 | */\r | |
196 | timeout: Infinity,\r | |
197 | \r | |
198 | /**\r | |
199 | * @cfg {Number} maximumAge\r | |
200 | * This option indicates that the application is willing to accept cached location information whose age\r | |
201 | * is no greater than the specified time in milliseconds. If `maximumAge` is set to 0, an attempt to retrieve\r | |
202 | * new location information is made immediately.\r | |
203 | *\r | |
204 | * Setting the `maximumAge` to Infinity returns a cached position regardless of its age.\r | |
205 | *\r | |
206 | * If the device does not have cached location information available whose age is no\r | |
207 | * greater than the specified `maximumAge`, then it must acquire new location information.\r | |
208 | *\r | |
209 | * For example, if location information no older than 10 minutes is required, set this property to 600000.\r | |
210 | */\r | |
211 | maximumAge: 0,\r | |
212 | \r | |
213 | /**\r | |
214 | * @private\r | |
215 | */\r | |
216 | provider : undefined\r | |
217 | },\r | |
218 | \r | |
219 | updateMaximumAge: function() {\r | |
220 | if (this.watchOperation) {\r | |
221 | this.updateWatchOperation();\r | |
222 | }\r | |
223 | },\r | |
224 | \r | |
225 | updateTimeout: function() {\r | |
226 | if (this.watchOperation) {\r | |
227 | this.updateWatchOperation();\r | |
228 | }\r | |
229 | },\r | |
230 | \r | |
231 | updateAllowHighAccuracy: function() {\r | |
232 | if (this.watchOperation) {\r | |
233 | this.updateWatchOperation();\r | |
234 | }\r | |
235 | },\r | |
236 | \r | |
237 | applyProvider: function(config) {\r | |
238 | if (Ext.feature.has.Geolocation) {\r | |
239 | if (!config) {\r | |
240 | if (navigator && navigator.geolocation) {\r | |
241 | config = navigator.geolocation;\r | |
242 | }\r | |
243 | else if (window.google) {\r | |
244 | config = google.gears.factory.create('beta.geolocation');\r | |
245 | }\r | |
246 | }\r | |
247 | }\r | |
248 | else {\r | |
249 | this.fireEvent('locationerror', this, false, false, true, 'This device does not support Geolocation.');\r | |
250 | }\r | |
251 | return config;\r | |
252 | },\r | |
253 | \r | |
254 | updateAutoUpdate: function(newAutoUpdate, oldAutoUpdate) {\r | |
255 | var me = this,\r | |
256 | provider = me.getProvider();\r | |
257 | \r | |
258 | if (oldAutoUpdate && provider) {\r | |
259 | clearInterval(me.watchOperationId);\r | |
260 | me.watchOperationId = null;\r | |
261 | }\r | |
262 | \r | |
263 | if (newAutoUpdate) {\r | |
264 | if (!provider) {\r | |
265 | me.fireEvent('locationerror', me, false, false, true, null);\r | |
266 | return;\r | |
267 | }\r | |
268 | \r | |
269 | try {\r | |
270 | me.updateWatchOperation();\r | |
271 | }\r | |
272 | catch(e) {\r | |
273 | me.fireEvent('locationerror', me, false, false, true, e.message);\r | |
274 | }\r | |
275 | }\r | |
276 | },\r | |
277 | \r | |
278 | /**\r | |
279 | * @private\r | |
280 | */\r | |
281 | updateWatchOperation: function() {\r | |
282 | var me = this,\r | |
283 | provider = me.getProvider();\r | |
284 | \r | |
285 | // The native watchPosition method is currently broken in iOS5...\r | |
286 | \r | |
287 | if (me.watchOperationId) {\r | |
288 | clearInterval(me.watchOperationId);\r | |
289 | }\r | |
290 | \r | |
291 | function pollPosition() {\r | |
292 | provider.getCurrentPosition(\r | |
293 | Ext.bind(me.fireUpdate, me),\r | |
294 | Ext.bind(me.fireError, me),\r | |
295 | me.parseOptions()\r | |
296 | );\r | |
297 | }\r | |
298 | \r | |
299 | pollPosition();\r | |
300 | me.watchOperationId = Ext.interval(pollPosition, this.getFrequency());\r | |
301 | },\r | |
302 | \r | |
303 | /**\r | |
304 | * Executes a onetime location update operation,\r | |
305 | * raising either a {@link #locationupdate} or {@link #locationerror} event.\r | |
306 | *\r | |
307 | * Does not interfere with or restart ongoing location monitoring.\r | |
308 | * @param {Function} callback\r | |
309 | * A callback method to be called when the location retrieval has been completed.\r | |
310 | *\r | |
311 | * Will be called on both success and failure.\r | |
312 | *\r | |
313 | * The method will be passed one parameter, {@link Ext.util.Geolocation}\r | |
314 | * (**this** reference), set to `null` on failure.\r | |
315 | *\r | |
316 | * geo.updateLocation(function (geo) {\r | |
317 | * alert('Latitude: ' + (geo !== null ? geo.latitude : 'failed'));\r | |
318 | * });\r | |
319 | *\r | |
320 | * @param {Object} [scope]\r | |
321 | * The scope (**this** reference) in which the handler function is executed.\r | |
322 | *\r | |
323 | * **If omitted, defaults to the object which fired the event.**\r | |
324 | *\r | |
325 | * <!--positonOptions undocumented param, see W3C spec-->\r | |
326 | */\r | |
327 | updateLocation: function(callback, scope, positionOptions) {\r | |
328 | var me = this,\r | |
329 | provider = me.getProvider();\r | |
330 | \r | |
331 | var failFunction = function(message, error) {\r | |
332 | if (error) {\r | |
333 | me.fireError(error);\r | |
334 | }\r | |
335 | else {\r | |
336 | me.fireEvent('locationerror', me, false, false, true, message);\r | |
337 | }\r | |
338 | if (callback) {\r | |
339 | callback.call(scope || me, null, me); //last parameter for legacy purposes\r | |
340 | }\r | |
341 | };\r | |
342 | \r | |
343 | if (!provider) {\r | |
344 | failFunction(null);\r | |
345 | return;\r | |
346 | }\r | |
347 | \r | |
348 | try {\r | |
349 | provider.getCurrentPosition(\r | |
350 | //success callback\r | |
351 | function(position) {\r | |
352 | me.fireUpdate(position);\r | |
353 | if (callback) {\r | |
354 | callback.call(scope || me, me, me); //last parameter for legacy purposes\r | |
355 | }\r | |
356 | },\r | |
357 | //error callback\r | |
358 | function(error) {\r | |
359 | failFunction(null, error);\r | |
360 | },\r | |
361 | positionOptions || me.parseOptions()\r | |
362 | );\r | |
363 | }\r | |
364 | catch(e) {\r | |
365 | failFunction(e.message);\r | |
366 | }\r | |
367 | },\r | |
368 | \r | |
369 | /**\r | |
370 | * @private\r | |
371 | */\r | |
372 | fireUpdate: function(position) {\r | |
373 | var me = this,\r | |
374 | coords = position.coords;\r | |
375 | \r | |
376 | this.position = position;\r | |
377 | \r | |
378 | me.setConfig({\r | |
379 | timestamp: position.timestamp,\r | |
380 | latitude: coords.latitude,\r | |
381 | longitude: coords.longitude,\r | |
382 | accuracy: coords.accuracy,\r | |
383 | altitude: coords.altitude,\r | |
384 | altitudeAccuracy: coords.altitudeAccuracy,\r | |
385 | heading: coords.heading,\r | |
386 | speed: coords.speed\r | |
387 | });\r | |
388 | \r | |
389 | me.fireEvent('locationupdate', me);\r | |
390 | },\r | |
391 | \r | |
392 | /**\r | |
393 | * @private\r | |
394 | */\r | |
395 | fireError: function(error) {\r | |
396 | var errorCode = error.code;\r | |
397 | this.fireEvent('locationerror', this,\r | |
398 | errorCode == error.TIMEOUT,\r | |
399 | errorCode == error.PERMISSION_DENIED,\r | |
400 | errorCode == error.POSITION_UNAVAILABLE,\r | |
401 | error.message == undefined ? null : error.message\r | |
402 | );\r | |
403 | },\r | |
404 | \r | |
405 | /**\r | |
406 | * @private\r | |
407 | */\r | |
408 | parseOptions: function() {\r | |
409 | var timeout = this.getTimeout(),\r | |
410 | ret = {\r | |
411 | maximumAge: this.getMaximumAge(),\r | |
412 | enableHighAccuracy: this.getAllowHighAccuracy()\r | |
413 | };\r | |
414 | \r | |
415 | //Google doesn't like Infinity\r | |
416 | if (timeout !== Infinity) {\r | |
417 | ret.timeout = timeout;\r | |
418 | }\r | |
419 | return ret;\r | |
420 | },\r | |
421 | \r | |
422 | destroy: function() {\r | |
423 | this.setAutoUpdate(false);\r | |
424 | this.callParent();\r | |
425 | }\r | |
426 | });\r |