Changed to MADDE; moved sb1 files to zouba.sb1 and made new zouba.madde for madde...
[ptas] / zouba.sb1 / wrt / preview / script / lib / sapi / Location.js
1 /**\r
2  * Location.js\r
3  * \r
4  * Nokia Web Runtime Service API emulation \r
5  * WRT v1.1\r
6  * \r
7  * Copyright 2009 Nokia Corporation. All rights reserved.\r
8 */\r
9 \r
10  \r
11 (function(){\r
12         \r
13         var provider = 'Service.Location',\r
14                 Interface = 'ILocation';\r
15 \r
16         /**\r
17          * Landmark service\r
18          */\r
19         var LocationService = function(){\r
20                 this.GetLocation                        = __GetLocation;\r
21                 this.Trace                                      = __Trace;\r
22                 this.Calculate                          = __Calculate;\r
23                 this.CancelNotification         = __CancelNotification;         \r
24         }\r
25 \r
26         device.implementation.extend(provider, Interface, new LocationService() );\r
27 \r
28 \r
29         /******************************************************/        \r
30         /******************************************************/        \r
31         /******************************************************/        \r
32 \r
33         var     context = device.implementation.context,\r
34                 _t = context._t,\r
35                 method = '',\r
36                 result = false,\r
37                 DBase = null;\r
38 \r
39 \r
40         var transactionIds = new Array();\r
41         var tTransactionId = -1;\r
42         var isTraceInProgress = false;\r
43         var criteriaTrace;\r
44         var callbackTrace;\r
45 \r
46         /**\r
47          * Landmarks: GetLocation\r
48          * @param {Object} criteria\r
49          */\r
50         function __GetLocation(criteria, callback, flag){       \r
51                 method = "GetLocation";\r
52                 //      Async call\r
53                 flag = flag || false;\r
54 \r
55                 if (!criteria) {\r
56                         criteria = new Object();\r
57                 }       \r
58                 \r
59                 if(typeof criteria.LocationInformationClass == "undefined")\r
60                         criteria.LocationInformationClass = "BasicLocationInformation"; // Default value of LocationInformationClass is "BasicLocationInformation" if not provided\r
61 \r
62                 var result = validateArgument('GetLocation',criteria);\r
63                 if(result.ErrorCode != 0)\r
64                         return result;\r
65                 \r
66                 if (typeof callback == 'function') {\r
67                         \r
68                         var retVal = context.callAsync(this, arguments.callee, criteria, callback,true);\r
69                         transactionIds.push(retVal.TransactionID);  // all transaction ids are pushed on this variable, because CancelNotification function of SAPI doesn't take TransactioID as input\r
70                         return retVal;\r
71                 }\r
72                 \r
73                 if(flag)\r
74                 {\r
75                         transactionIds.shift();  // Remove oldest TransactionID(FIFO) (Async call)\r
76                 }\r
77                 \r
78                 DBase = context.getData(provider);\r
79                 var returnValue = DBase[criteria.LocationInformationClass];\r
80                 locationNotify(criteria.Updateoptions);\r
81                 return context.Result(returnValue);\r
82         }\r
83         \r
84         /**\r
85          * Location: Trace\r
86          * @param {Object} criteria\r
87          * @param {Function} callback function for async call\r
88          */\r
89         function __Trace(criteria, callback){\r
90                 method = "Trace";\r
91 \r
92                 if (!criteria) {\r
93                         criteria = new Object();\r
94                 }       \r
95                 \r
96                 if(typeof criteria.LocationInformationClass == "undefined")\r
97                         criteria.LocationInformationClass = "BasicLocationInformation"; // Default value of LocationInformationClass is "BasicLocationInformation" if not provided\r
98 \r
99                 if (typeof callback != 'function') { // callback should be valid function\r
100                         return error(device.implementation.ERR_SERVICE_NOT_SUPPORTED,msg.msgCommandNotFound); \r
101                 }\r
102                 \r
103                 var result = validateArgument('Trace',criteria);\r
104                 if(result.ErrorCode != 0)\r
105                         return result;\r
106                 \r
107                 criteriaTrace = criteria;\r
108                 callbackTrace = callback;\r
109                 isTraceInProgress = true;\r
110                 locationNotify(criteria.Updateoptions);\r
111 \r
112                 return traceCall(criteria,callback);\r
113         }\r
114 \r
115         /**\r
116          * Location: Calculate\r
117          * @param {Object} criteria\r
118          */\r
119         function __Calculate(criteria){\r
120                 method = "Calculate";\r
121                 if(!criteria || !criteria.MathRequest)\r
122                         return error(device.implementation.ERR_BAD_ARGUMENT_TYPE,msg.msgCalcMissingMathReq);\r
123                         \r
124                 if(typeof criteria.MathRequest != "string" || (criteria.MathRequest != "FindDistance" && criteria.MathRequest != "FindBearingTo" && criteria.MathRequest != "MoveCoordinates")) // Error check for wrong MathRequest criteria\r
125                         return error(device.implementation.ERR_BAD_ARGUMENT_TYPE,msg.msgCalcWrongTypeMathReq);\r
126                         \r
127                 if(typeof criteria.DistanceParamSource != "object" || (typeof criteria.DistanceParamSource.Latitude != "number" || typeof criteria.DistanceParamSource.Longitude != "number" || typeof criteria.DistanceParamSource.Altitude != "number"))\r
128                         return error(device.implementation.ERR_BAD_ARGUMENT_TYPE,msg.msgCalcMissingArgLocCord);\r
129 \r
130                 if(criteria.MathRequest == "FindDistance" || criteria.MathRequest == "FindBearingTo")\r
131                 {\r
132                         if(typeof criteria.DistanceParamSource != "object" || (typeof criteria.DistanceParamDestination.Latitude != "number" || typeof criteria.DistanceParamDestination.Longitude != "number" || typeof criteria.DistanceParamDestination.Altitude != "number"))\r
133                                 return error(device.implementation.ERR_BAD_ARGUMENT_TYPE,msg.msgCalcMissingArgLocCord);\r
134                         if (criteria.MathRequest == "FindDistance") {\r
135                                 var dist = LatLon.distHaversine(criteria.DistanceParamDestination.Latitude, criteria.DistanceParamDestination.Longitude, criteria.DistanceParamSource.Latitude, criteria.DistanceParamSource.Longitude)*1000;\r
136                                 if (typeof criteria.DistanceParamDestination.Altitude == "number" && typeof criteria.DistanceParamSource.Altitude == "number") {\r
137                                         var delta = criteria.DistanceParamDestination.Altitude - criteria.DistanceParamSource.Altitude\r
138                                         dist = Math.sqrt(dist * dist + delta * delta);\r
139                                 }\r
140                                 return context.Result(dist);\r
141                         }\r
142                         else if (criteria.MathRequest == "FindBearingTo"){\r
143                                 var bearing = LatLon.bearing( criteria.DistanceParamSource.Latitude, criteria.DistanceParamSource.Longitude,criteria.DistanceParamDestination.Latitude, criteria.DistanceParamDestination.Longitude);\r
144                                 return context.Result(bearing);                         \r
145                         }\r
146                 }\r
147                 else if(criteria.MathRequest == "MoveCoordinates"){\r
148 \r
149                         if(typeof criteria.MoveByThisDistance == "undefined")\r
150                                 return error(device.implementation.ERR_BAD_ARGUMENT_TYPE,msg.msgCalcMissingArgMoveDist);\r
151                         \r
152                         if(typeof criteria.MoveByThisBearing == "undefined")\r
153                                 return error(device.implementation.ERR_BAD_ARGUMENT_TYPE,msg.msgCalcMissingArgMoveBear);\r
154                         \r
155 \r
156                         if(typeof criteria.MoveByThisDistance != "number")\r
157                                 return error(device.implementation.ERR_BAD_ARGUMENT_TYPE,msg.msgCalcWrongTypeMoveDist);\r
158                         \r
159                         if(typeof criteria.MoveByThisBearing != "number")\r
160                                 return error(device.implementation.ERR_BAD_ARGUMENT_TYPE,msg.msgCalcWrongTypeMoveBear);\r
161                         \r
162                         var latLon = new LatLon(criteria.DistanceParamSource.Latitude, criteria.DistanceParamSource.Longitude);\r
163                         var dlatLon = latLon.destPoint(criteria.MoveByThisBearing, criteria.MoveByThisDistance/1000);\r
164                         var retVal = new Object();\r
165                         retVal.Longitude = dlatLon.lon;\r
166                         retVal.Latitude = dlatLon.lat;\r
167                         retVal.Altitude = criteria.DistanceParamSource.Altitude;\r
168                         return context.Result(retVal);\r
169                 }\r
170         }\r
171                         \r
172         /**\r
173          * Location: CancelNotification\r
174          * @param {Object} criteria\r
175          */\r
176         function __CancelNotification(criteria){\r
177                 method = "Cancel";\r
178                 if(!criteria)\r
179                                 return error(device.implementation.ERR_MISSING_ARGUMENT,msg.msgCancelMissingType);\r
180                 \r
181                 var arr = new Array();\r
182                 var i = 0;\r
183                 var key\r
184                 for(key in criteria);\r
185                         arr[i++] = key;\r
186 \r
187                 if(!criteria.CancelRequestType && arr.length)\r
188                                 return error(device.implementation.ERR_NOT_FOUND,msg.msgCancelMissingType);\r
189                 \r
190                 if(criteria.CancelRequestType != "GetLocCancel" && criteria.CancelRequestType != "TraceCancel")\r
191                                 return error(device.implementation.ERR_BAD_ARGUMENT_TYPE,msg.msgCancelWrongType);\r
192                 \r
193                 if (criteria.CancelRequestType == "GetLocCancel") {\r
194                         for (var i = 0; i < transactionIds.length; i++) {\r
195                                 clearTimeout(transactionIds[i])\r
196                         }\r
197                 }\r
198                 \r
199                 if (criteria.CancelRequestType == "TraceCancel")\r
200                 {\r
201                         isTraceInProgress = false;\r
202                         tTransactionId = -1;\r
203                 }\r
204                 return context.ErrorResult(device.implementation.ERR_SUCCESS);\r
205         }\r
206                 \r
207 \r
208 \r
209         \r
210         /*******************************\r
211          * helper functions\r
212          *******************************/\r
213 \r
214         /**\r
215          * Location: traceCall\r
216          * @param {} \r
217          * This function emulates repetitive trace calls,It calls specified callback function after every UpdateInterval untill \r
218          * CancelNotification is called\r
219          */\r
220         function traceCall(){\r
221                 var tid = setTimeout(function(){\r
222                 if(!isTraceInProgress)\r
223                         return;\r
224                         \r
225                 DBase = context.getData(provider);\r
226                 var returnValue = DBase[criteriaTrace.LocationInformationClass];\r
227                 var result,\r
228                         eventCode = {completed:2, error:4, progress:9},\r
229                 code = eventCode.completed;\r
230 \r
231                 callbackTrace(tTransactionId,code,context.Result(returnValue,0));\r
232                 traceCall();\r
233                 }, criteriaTrace.Updateoptions.UpdateInterval/1000);\r
234                 if(tTransactionId == -1)\r
235                         tTransactionId = tid;\r
236                 return context.AsyncResult(tTransactionId);\r
237         }\r
238 \r
239         /**\r
240          * Location: validateArgument\r
241          * @param {string,object} callingMethod and criteria\r
242          * Validates arguments\r
243          */\r
244         function validateArgument(fun,criteria)\r
245         {\r
246                 method = fun;\r
247                 if(typeof criteria.Updateoptions != "undefined")\r
248                 {\r
249                         if(typeof criteria.Updateoptions != "object") // Checking for error in UpdateOptions criteria\r
250                                 return error(device.implementation.ERR_BAD_ARGUMENT_TYPE,msg.msgGetLocationBadArg);\r
251                         \r
252                         if(typeof criteria.Updateoptions.UpdateInterval != "undefined" && typeof criteria.Updateoptions.UpdateInterval != "number")\r
253                                 return error(device.implementation.ERR_BAD_ARGUMENT_TYPE,msg.msgGetLocationWrongType);\r
254                         \r
255                         if(typeof criteria.Updateoptions.UpdateTimeOut != "undefined" && typeof criteria.Updateoptions.UpdateTimeOut != "number")       \r
256                                 return error(device.implementation.ERR_BAD_ARGUMENT_TYPE,msg.msgGetLocationWrongType);\r
257 \r
258                         if(typeof criteria.Updateoptions.UpdateMaxAge != "undefined" && typeof criteria.Updateoptions.UpdateMaxAge != "number") \r
259                                 return error(device.implementation.ERR_BAD_ARGUMENT_TYPE,msg.msgGetLocationWrongType);\r
260 \r
261                         if(typeof criteria.Updateoptions.PartialUpdates != "undefined" && typeof criteria.Updateoptions.PartialUpdates != "boolean")    \r
262                                 return error(device.implementation.ERR_BAD_ARGUMENT_TYPE,msg.msgGetLocationWrongType);\r
263 \r
264                         if((typeof criteria.Updateoptions.UpdateInterval != "undefined" && criteria.Updateoptions.UpdateInterval  < 0) || \r
265                                         (typeof criteria.Updateoptions.UpdateTimeOut != "undefined" && criteria.Updateoptions.UpdateTimeOut  < 0) ||\r
266                                         (typeof criteria.Updateoptions.UpdateMaxAge != "undefined" && criteria.Updateoptions.UpdateMaxAge  < 0))\r
267                                 return error(device.implementation.ERR_BAD_ARGUMENT_TYPE,msg.msgGetLocationNegInt);\r
268 \r
269                         if(typeof criteria.Updateoptions.UpdateTimeOut != "undefined" && typeof criteria.Updateoptions.UpdateInterval != "undefined" && criteria.Updateoptions.UpdateInterval > criteria.Updateoptions.UpdateTimeOut)\r
270                         {\r
271                                 return error(device.implementation.ERR_BAD_ARGUMENT_TYPE,msg.msgNone);\r
272                         }\r
273 \r
274                         /*if((typeof criteria.Updateoptions.UpdateTimeOut != "undefined" && criteria.Updateoptions.UpdateTimeOut <= 1000000))// || (typeof criteria.Updateoptions.UpdateInterval != "undefined" && criteria.Updateoptions.UpdateInterval <= 1000000))\r
275                         {\r
276                                 return error(device.implementation.ERR_BAD_ARGUMENT_TYPE,msg.msgNone);\r
277                         }*/                             \r
278                 }\r
279 \r
280                 if(typeof criteria.LocationInformationClass != "undefined" && criteria.LocationInformationClass != "BasicLocationInformation" && criteria.LocationInformationClass != "GenericLocationInfo") // checking for errors in LocationInformationClass criteria\r
281                         return error(device.implementation.ERR_BAD_ARGUMENT_TYPE,msg.msgGetLocationWrongCat);\r
282                 \r
283                 if (/^Trace$/i.test(fun)&&(!criteria.Updateoptions || typeof criteria.Updateoptions.UpdateInterval == "undefined")) {\r
284                         if(!criteria.Updateoptions)\r
285                         {\r
286                                 criteria.Updateoptions = new Object();\r
287                         }\r
288                         criteria.Updateoptions.UpdateInterval = 1000000;  // Emulation only!! for convenience UpdateInterval is set to 1 second is not specified or if it less than 1 second\r
289                         context.notify("Using default UpdateInterval(1000000 micro seconds)"); \r
290                 }\r
291                 \r
292                 return context.ErrorResult(device.implementation.ERR_SUCCESS, "");\r
293                 \r
294         }\r
295 \r
296         /**\r
297          * Location: error\r
298          * @param {number,string} ErrorCode and ErrorString\r
299          * Replaces Error String with method name\r
300          */\r
301         function error(code, msg /*, args...*/){\r
302 \r
303                 var args = ['location',method].concat([].slice.call(arguments,2));\r
304                 msg = msg ? _t().arg.apply(msg,args) : undefined;\r
305                 return context.ErrorResult(code, msg);\r
306         }\r
307         \r
308         function locationNotify(updateoptions) {\r
309                 if(!updateoptions)\r
310                         return;\r
311                 if(typeof updateoptions.UpdateTimeOut != "undefined")\r
312                         context.notify(_t("%s:: %s : Updateoptions.UpdateTimeOut not implemented in preview").arg(provider, method));\r
313 \r
314                 if(typeof updateoptions.UpdateMaxAge != "undefined")\r
315                         context.notify(_t("%s:: %s : Updateoptions.UpdateMaxAge not implemented in preview").arg(provider, method));\r
316 \r
317                 if(typeof updateoptions.PartialUpdates != "undefined")\r
318                         context.notify(_t("%s:: %s : Updateoptions.PartialUpdates not implemented in preview").arg(provider, method));\r
319         }\r
320 \r
321         /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */\r
322         /*  Latitude/longitude spherical geodesy formulae & scripts (c) Chris Veness 2002-2009            */\r
323         /*      http://www.movable-type.co.uk/scripts/latlong.html                                                                                        */  \r
324         /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */\r
325         \r
326         /*\r
327          * Use Haversine formula to Calculate distance (in km) between two points specified by \r
328          * latitude/longitude (in numeric degrees)\r
329          *\r
330          * example usage from form:\r
331          *   result.value = LatLon.distHaversine(lat1.value.parseDeg(), long1.value.parseDeg(), \r
332          *                                       lat2.value.parseDeg(), long2.value.parseDeg());\r
333          * where lat1, long1, lat2, long2, and result are form fields\r
334          */\r
335         \r
336         \r
337         LatLon.distHaversine = function(lat1, lon1, lat2, lon2) {\r
338           var R = 6371; // earth's mean radius in km\r
339           var dLat = toRad(lat2-lat1);\r
340           var dLon = toRad(lon2-lon1);\r
341           lat1 = toRad(lat1), lat2 = toRad(lat2);\r
342         \r
343           var a = Math.sin(dLat/2) * Math.sin(dLat/2) +\r
344                   Math.cos(lat1) * Math.cos(lat2) * \r
345                   Math.sin(dLon/2) * Math.sin(dLon/2);\r
346           var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));\r
347           var d = R * c;\r
348           return d;\r
349         }\r
350         \r
351         \r
352         /*\r
353          * ditto using Law of Cosines\r
354          */\r
355         LatLon.distCosineLaw = function(lat1, lon1, lat2, lon2) {\r
356           var R = 6371; // earth's mean radius in km\r
357           var d = Math.acos(Math.sin(toRad(lat1))*Math.sin(toRad(lat2)) +\r
358                             Math.cos(toRad(lat1))*Math.cos(toRad(lat2))*Math.cos(toRad(lon2-lon1))) * R;\r
359           return d;\r
360         }\r
361         \r
362         \r
363         /*\r
364          * calculate (initial) bearing between two points\r
365          *   see http://williams.best.vwh.net/avform.htm#Crs\r
366          */\r
367         LatLon.bearing = function(lat1, lon1, lat2, lon2) {\r
368           lat1 = toRad(lat1); lat2 = toRad(lat2);\r
369           var dLon = toRad(lon2-lon1);\r
370 \r
371           var y = Math.sin(dLon) * Math.cos(lat2);\r
372           var x = Math.cos(lat1)*Math.sin(lat2) -\r
373                   Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);\r
374           return toBrng(Math.atan2(y, x));\r
375         }\r
376         \r
377         \r
378         /*\r
379          * calculate destination point given start point, initial bearing (deg) and distance (km)\r
380          *   see http://williams.best.vwh.net/avform.htm#LL\r
381          */\r
382         LatLon.prototype.destPoint = function(brng, d) {\r
383           var R = 6371; // earth's mean radius in km\r
384           var lat1 = toRad(this.lat), lon1 = toRad(this.lon);\r
385           brng = toRad(brng);\r
386         \r
387           var lat2 = Math.asin( Math.sin(lat1)*Math.cos(d/R) + \r
388                                 Math.cos(lat1)*Math.sin(d/R)*Math.cos(brng) );\r
389           var lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat1), \r
390                                        Math.cos(d/R)-Math.sin(lat1)*Math.sin(lat2));\r
391           lon2 = (lon2+Math.PI)%(2*Math.PI) - Math.PI;  // normalise to -180...+180\r
392         \r
393           if (isNaN(lat2) || isNaN(lon2)) return null;\r
394           return new LatLon(toDeg(lat2), toDeg(lon2));\r
395         }\r
396         \r
397         \r
398         /*\r
399          * construct a LatLon object: arguments in numeric degrees\r
400          *\r
401          * note all LatLong methods expect & return numeric degrees (for lat/long & for bearings)\r
402          */\r
403         function LatLon(lat, lon) {\r
404           this.lat = lat;\r
405           this.lon = lon;\r
406         }\r
407         \r
408         \r
409         /*\r
410          * represent point {lat, lon} in standard representation\r
411          */\r
412         LatLon.prototype.toString = function() {\r
413           return this.lat.toLat() + ', ' + this.lon.toLon();\r
414         }\r
415         \r
416         /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */\r
417         \r
418         // extend String object with method for parsing degrees or lat/long values to numeric degrees\r
419         //\r
420         // this is very flexible on formats, allowing signed decimal degrees, or deg-min-sec suffixed by \r
421         // compass direction (NSEW). A variety of separators are accepted (eg 3º 37' 09"W) or fixed-width \r
422         // format without separators (eg 0033709W). Seconds and minutes may be omitted. (Minimal validation \r
423         // is done).\r
424         \r
425         function parseDeg (str) {\r
426           if (!isNaN(str)) return Number(str);                 // signed decimal degrees without NSEW\r
427         \r
428           var degLL = str.replace(/^-/,'').replace(/[NSEW]/i,'');  // strip off any sign or compass dir'n\r
429           var dms = degLL.split(/[^0-9.]+/);                     // split out separate d/m/s\r
430           for (var i in dms) if (dms[i]=='') dms.splice(i,1);    // remove empty elements (see note below)\r
431           switch (dms.length) {                                  // convert to decimal degrees...\r
432             case 3:                                              // interpret 3-part result as d/m/s\r
433               var deg = dms[0]/1 + dms[1]/60 + dms[2]/3600; break;\r
434             case 2:                                              // interpret 2-part result as d/m\r
435               var deg = dms[0]/1 + dms[1]/60; break;\r
436             case 1:                                              // decimal or non-separated dddmmss\r
437               if (/[NS]/i.test(str)) degLL = '0' + degLL;       // - normalise N/S to 3-digit degrees\r
438               var deg = dms[0].slice(0,3)/1 + dms[0].slice(3,5)/60 + dms[0].slice(5)/3600; break;\r
439             default: return NaN;\r
440           }\r
441           if (/^-/.test(str) || /[WS]/i.test(str)) deg = -deg; // take '-', west and south as -ve\r
442           return deg;\r
443         }\r
444         // note: whitespace at start/end will split() into empty elements (except in IE)\r
445         \r
446         \r
447         /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */\r
448         \r
449         // extend Number object with methods for converting degrees/radians\r
450         \r
451         function toRad (deg) {  // convert degrees to radians\r
452           return deg * Math.PI / 180;\r
453         }\r
454         \r
455         function toDeg (rad) {  // convert radians to degrees (signed)\r
456           return rad * 180 / Math.PI;\r
457         }\r
458         \r
459         function toBrng (rad) {  // convert radians to degrees (as bearing: 0...360)\r
460           return (toDeg(rad)+360) % 360;\r
461         }\r
462         \r
463         \r
464         /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */\r
465         \r
466         // extend Number object with methods for presenting bearings & lat/longs\r
467         \r
468         function toDMS (num) {  // convert numeric degrees to deg/min/sec\r
469           var d = Math.abs(num);  // (unsigned result ready for appending compass dir'n)\r
470           d += 1/7200;  // add ½ second for rounding\r
471           var deg = Math.floor(d);\r
472           var min = Math.floor((d-deg)*60);\r
473           var sec = Math.floor((d-deg-min/60)*3600);\r
474           // add leading zeros if required\r
475           if (deg<100) deg = '0' + deg; if (deg<10) deg = '0' + deg;\r
476           if (min<10) min = '0' + min;\r
477           if (sec<10) sec = '0' + sec;\r
478           return deg + '\u00B0' + min + '\u2032' + sec + '\u2033';\r
479         }\r
480         \r
481         function toLat (deg) {  // convert numeric degrees to deg/min/sec latitude\r
482           return toDMS(deg).slice(1) + (deg<0 ? 'S' : 'N');  // knock off initial '0' for lat!\r
483         }\r
484         \r
485         function toLon (deg) {  // convert numeric degrees to deg/min/sec longitude\r
486           return toDMS(deg) + (deg>0 ? 'E' : 'W');\r
487         }\r
488         \r
489         function toPrecision (num,fig) {  // override toPrecision method with one which displays \r
490           if (num == 0) return 0;                      // trailing zeros in place of exponential notation\r
491           var scale = Math.ceil(Math.log(num)*Math.LOG10E);\r
492           var mult = Math.pow(10, fig-scale);\r
493           return Math.round(num*mult)/mult;\r
494         }\r
495 \r
496 \r
497         /** \r
498          * error messages\r
499          * order of %s args: Service name, method name, parameter name \r
500          */\r
501         var msg = {\r
502                 msgCommandNotFound                      : '%s : Command Not found',\r
503                 msgGetLocationWrongCat          : '%s : %s : wrong category info should be BasicLocationInformation/GenericLocationInfo ',\r
504                 msgGetLocationBadArg            : '%s : %s : BadArgument - Updateoptions',\r
505                 msgGetLocationNegInt            : '%s : %s : Negative Time Interval',\r
506                 msgGetLocationWrongType         : '%s : %s : UpdateOptions Type mismatch',\r
507                 msgTraceWrongCat                        : '%s : %s : Invalid LocationInformationClass',\r
508                 msgCalcMissingMathReq           : '%s : %s : Missing argument - MathRequest',\r
509                 msgCalcWrongTypeMathReq         : '%s : %s : Wrong argument - MathRequest',\r
510                 msgCalcMissingArgLocCord        : '%s : %s : Missing argument - locationcoordinate',\r
511                 msgCalcMissingArgMoveDist       : '%s : %s : Missing argument - MoveByThisDistance',\r
512                 msgCalcMissingArgMoveBear       : '%s : %s : Missing argument - MoveByThisBearing',\r
513                 msgCalcWrongTypeMoveDist        : '%s : %s : TypeMismatch - MoveByThisDistance',\r
514                 msgCalcWrongTypeMoveBear        : '%s : %s : TypeMismatch - MoveByThisBearing',\r
515                 msgCancelBadArg                         : '%s : %s : BadArgument – cancel type',\r
516                 msgCancelMissingType            : '%s : %s : Missing cancel type',\r
517                 msgCancelWrongType                      : '%s : %s : Wrong cancel type' ,\r
518                 msgNone                                         : ''    \r
519         };\r
520                 \r
521 \r
522 }) ()\r
523 \r