1 if (Garmin == undefined) var Garmin = {}; 2 /** Copyright � 2007 Garmin Ltd. or its subsidiaries. 3 * 4 * Licensed under the Apache License, Version 2.0 (the 'License') 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an 'AS IS' BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * @fileoverview Garmin.DeviceControl A high-level JavaScript API which supports listener and callback functionality. 17 * 18 * @author Diana Chow diana.chow[at]garmin.com, Carlo Latasa carlo.latasa@garmin.com 19 * @version 1.0 20 */ 21 /** A controller object that can retrieve and send data to a Garmin 22 * device.<br><br> 23 * @class Garmin.DeviceControl 24 * 25 * The controller must be unlocked before anything can be done with it. 26 * Then you'll have to find a device before you can start to read data from 27 * and write data to the device.<br><br> 28 * 29 * We use the <a href="http://en.wikipedia.org/wiki/Observer_pattern">observer pattern</a> 30 * to handle the asynchronous nature of device communication. You must register 31 * your class as a listener to this Object and then implement methods that will 32 * get called on certain events.<br><br> 33 * 34 * Events:<br><br> 35 * onStartFindDevices called when starting to search for devices. 36 * the object returned is {controller: this}<br><br> 37 * 38 * onCancelFindDevices is called when the controller is told to cancel finding 39 * devices {controller: this}<br><br> 40 * 41 * onFinishFindDevices called when the devices are found. 42 * the object returned is {controller: this}<br><br> 43 * 44 * onException is called when an exception occurs in a method 45 * object passed back is {msg: exception}<br><br> 46 * 47 * onInteractionWithNoDevice is called when the device is lazy loaded, but finds no devices, 48 * yet still attempts a read/write action {controller: this}<br><br> 49 * 50 * onStartReadFromDevice is called when the controller is about to start 51 * reading from the device {controller: this}<br><br> 52 * 53 * onFinishReadFromDevice is called when the controller is done reading 54 * the device. the read is either a success or failure, which is 55 * communicated via json. object passed back contains 56 * {success:this.garminPlugin.GpsTransferSucceeded, controller: this} <br><br> 57 * 58 * onWaitingReadFromDevice is called when the controller is waiting for input 59 * from the user about the device. object passed back contains: 60 * {message: this.garminPlugin.MessageBoxXml, controller: this}<br><br> 61 * 62 * onProgressReadFromDevice is called when the controller is still reading information 63 * from the device. in this case the message is a percent complete/ 64 * {progress: this.getDeviceStatus(), controller: this}<br><br> 65 * 66 * onCancelReadFromDevice is called when the controller is told to cancel reading 67 * from the device {controller: this}<br><br> 68 * 69 * onFinishWriteToDevice is called when the controller is done writing to 70 * the device. the write is either a success or failure, which is 71 * communicated via json. object passed back contains 72 * {success:this.garminPlugin.GpsTransferSucceeded, controller: this}<br><br> 73 * 74 * onWaitingWriteToDevice is called when the controller is waiting for input 75 * from the user about the device. object passed back contains: 76 * {message: this.garminPlugin.MessageBoxXml, controller: this}<br><br> 77 * 78 * onProgressWriteToDevice is called when the controller is still writing information 79 * to the device. in this case the message is a percent complete/ 80 * {progress: this.getDeviceStatus(), controller: this}<br><br> 81 * 82 * onCancelWriteToDevice is called when the controller is told to cancel writing 83 * to the device {controller: this}<br><br> 84 * 85 * @constructor 86 * 87 * requires Prototype 88 * @requires BrowserDetect 89 * @requires Garmin.DevicePlugin 90 * @requires Garmin.Broadcaster 91 * @requires Garmin.XmlConverter 92 */ 93 Garmin.DeviceControl = function(){}; //just here for jsdoc 94 Garmin.DeviceControl = Class.create(); 95 Garmin.DeviceControl.prototype = { 96 97 98 /////////////////////// Initialization Code /////////////////////// 99 100 /** Instantiates a Garmin.DeviceControl object, but does not unlock/activate plugin. 101 */ 102 initialize: function() { 103 104 this.pluginUnlocked = false; 105 106 try { 107 if (typeof(Garmin.DevicePlugin) == 'undefined') throw ''; 108 } catch(e) { 109 throw new Error(Garmin.DeviceControl.MESSAGES.deviceControlMissing); 110 }; 111 112 // check that the browser is supported 113 if(!BrowserSupport.isBrowserSupported()) { 114 var notSupported = new Error(Garmin.DeviceControl.MESSAGES.browserNotSupported); 115 notSupported.name = "BrowserNotSupportedException"; 116 //console.debug("Control.validatePlugin throw BrowserNotSupportedException") 117 throw notSupported; 118 } 119 120 // make sure the browser has the plugin installed 121 if (!PluginDetect.detectGarminCommunicatorPlugin()) { 122 var notInstalled = new Error(Garmin.DeviceControl.MESSAGES.pluginNotInstalled); 123 notInstalled.name = "PluginNotInstalledException"; 124 throw notInstalled; 125 } 126 127 // grab the plugin object on the page 128 var pluginElement; 129 if( window.ActiveXObject ) { // IE 130 pluginElement = $("GarminActiveXControl"); 131 } else { // FireFox 132 pluginElement = $("GarminNetscapePlugin"); 133 } 134 135 // make sure the plugin object exists on the page 136 if (pluginElement == null) { 137 var error = new Error(Garmin.DeviceControl.MESSAGES.missingPluginTag); 138 error.name = "HtmlTagNotFoundException"; 139 throw error; 140 } 141 142 // instantiate a garmin plugin 143 this.garminPlugin = new Garmin.DevicePlugin(pluginElement); 144 145 // validate the garmin plugin 146 this.validatePlugin(); 147 148 // instantiate a broacaster 149 this._broadcaster = new Garmin.Broadcaster(); 150 151 this.getDetailedDeviceData = true; 152 this.devices = new Array(); 153 this.deviceNumber = null; 154 this.numDevices = 0; 155 156 this.gpsData = null; 157 this.gpsDataType = null; //used by both read and write methods to track data context 158 this.gpsDataString = ""; 159 this.gpsDataStringCompressed = ""; // Compresed version of gpsDataString. gzip compressed and base 64 expanded. 160 161 //this.wasMessageHack = false; //needed because garminPlugin.finishDownloadData returns true after out-of-memory error message is returned 162 }, 163 164 /** Checks plugin validity: browser support, installation and required version. 165 * @private 166 * @throws BrowserNotSupportedException 167 * @throws PluginNotInstalledException 168 * @throws OutOfDatePluginException 169 */ 170 validatePlugin: function() { 171 if (!this.isPluginInstalled()) { 172 var notInstalled = new Error(Garmin.DeviceControl.MESSAGES.pluginNotInstalled); 173 notInstalled.name = "PluginNotInstalledException"; 174 throw notInstalled; 175 } 176 if(this.garminPlugin.isPluginOutOfDate()) { 177 var outOfDate = new Error(Garmin.DeviceControl.MESSAGES.outOfDatePlugin1+Garmin.DevicePlugin.REQUIRED_VERSION.toString()+Garmin.DeviceControl.MESSAGES.outOfDatePlugin2+this.getPluginVersionString()); 178 outOfDate.name = "OutOfDatePluginException"; 179 outOfDate.version = this.getPluginVersionString(); 180 throw outOfDate; 181 } 182 }, 183 184 /** Checks plugin for updates. Throws an exception if the user's plugin version is 185 * older than the one set by the API. 186 * 187 * Plugin updates are not required so use this function with caution. 188 * @see #setPluginLatestVersion 189 */ 190 checkForUpdates: function() { 191 if(this.garminPlugin.isUpdateAvailable()) { 192 var notLatest = new Error(Garmin.DeviceControl.MESSAGES.updatePlugin1+Garmin.DevicePlugin.LATEST_VERSION.toString()+Garmin.DeviceControl.MESSAGES.updatePlugin2+this.getPluginVersionString()); 193 notLatest.name = "UpdatePluginException"; 194 notLatest.version = this.getPluginVersionString(); 195 throw notLatest; 196 } 197 }, 198 199 /////////////////////// Device Handling Methods /////////////////////// 200 201 /** Finds any connected Garmin Devices. 202 * When it's done finding the devices, onFinishFindDevices is dispatched <br/> 203 * <br/> 204 * this.numDevices = the number of devices found<br/> 205 * this.deviceNumber is the device that we'll use to communicate with<br/> 206 * <br/> 207 * Use this.getDevices() to get an array of the found devices and 208 * this.setDeviceNumber({Number}) to change the device. <br/> 209 * <br/> 210 * Minimum Plugin version 2.0.0.4 211 * 212 * @see #getDevices 213 * @see #setDeviceNumber 214 */ 215 findDevices: function() { 216 if (!this.isUnlocked()) 217 throw new Error(Garmin.DeviceControl.MESSAGES.pluginNotUnlocked); 218 this.garminPlugin.startFindDevices(); 219 this._broadcaster.dispatch("onStartFindDevices", {controller: this}); 220 setTimeout(function() { this._finishFindDevices() }.bind(this), 1000); 221 }, 222 223 /** Cancels the current find devices interaction. <br/> 224 * <br/> 225 * Minimum Plugin version 2.0.0.4 226 */ 227 cancelFindDevices: function() { 228 this.garminPlugin.cancelFindDevices(); 229 this._broadcaster.dispatch("onCancelFindDevices", {controller: this}); 230 }, 231 232 /** Loads device data into devices array. 233 * 234 * Minimum Plugin version 2.0.0.4 235 * 236 * @private 237 */ 238 _finishFindDevices: function() { 239 if(this.garminPlugin.finishFindDevices()) { 240 //console.debug("_finishFindDevices devXml="+this.garminPlugin.getDevicesXml()) 241 this.devices = Garmin.PluginUtils.parseDeviceXml(this.garminPlugin, this.getDetailedDeviceData); 242 //console.debug("_finishFindDevices devXml="+this.garminPlugin.getDevicesXml()) 243 244 this.numDevices = this.devices.length; 245 this.deviceNumber = 0; 246 this._broadcaster.dispatch("onFinishFindDevices", {controller: this}); 247 } else { 248 setTimeout(function() { this._finishFindDevices() }.bind(this), 500); 249 } 250 }, 251 252 /** Sets the deviceNumber variable which determines which connected device to talk to. 253 * @param {Number} deviceNumber The device number 254 */ 255 setDeviceNumber: function(deviceNumber) { 256 this.deviceNumber = deviceNumber; 257 }, 258 259 /** 260 * Get the device number of the connected device to communicate with (multiple devices may 261 * be connected simultaneously, but the plugin only transfers data with one at a time). 262 * @return the device number (assigned by the plugin) determining which connected device 263 * to talk to. 264 */ 265 getDeviceNumber: function() { 266 return this.deviceNumber; 267 }, 268 269 /** Get a list of the devices found 270 * @type Array<Garmin.Device> 271 */ 272 getDevices: function() { 273 return this.devices; 274 }, 275 276 /** Returns the DeviceXML of the current device, as a string. 277 */ 278 getCurrentDeviceXml: function() { 279 return this.garminPlugin.getDeviceDescriptionXml(this.deviceNumber); 280 }, 281 282 /////////////////////// Read Methods /////////////////////// 283 284 /** Generic read method, supporting GPX and TCX Fitness types: Courses, Workouts, User Profiles, Activity Goals, 285 * TCX activity directory, and TCX course directory reads. <br/> 286 * <br/> 287 * Fitness detail reading (one specific activity) is not supported by this read method, refer to 288 * readDetailFromDevice for that. <br/> 289 * 290 * @param {String} fileType the filetype to read from device. Possible values for fileType are located in Garmin.DeviceControl.FILE_TYPES--detail 291 * types are not supported. 292 * @see #readDetailFromDevice, Garmin.DeviceControl#FILE_TYPES 293 * @throws InvalidTypeException, UnsupportedTransferTypeException 294 */ 295 readDataFromDevice: function(fileType) { 296 if (!this.isUnlocked()) 297 throw new Error(Garmin.DeviceControl.MESSAGES.pluginNotUnlocked); 298 if (this.numDevices == 0) 299 throw new Error(Garmin.DeviceControl.MESSAGES.noDevicesConnected); 300 // Make sure filetype passed in is a valid type 301 if ( ! this._isAMember(fileType, [Garmin.DeviceControl.FILE_TYPES.gpx, 302 Garmin.DeviceControl.FILE_TYPES.gpxDetail, 303 Garmin.DeviceControl.FILE_TYPES.gpxDir, 304 Garmin.DeviceControl.FILE_TYPES.tcx, 305 Garmin.DeviceControl.FILE_TYPES.crs, 306 Garmin.DeviceControl.FILE_TYPES.tcxDir, 307 Garmin.DeviceControl.FILE_TYPES.crsDir, 308 Garmin.DeviceControl.FILE_TYPES.wkt, 309 Garmin.DeviceControl.FILE_TYPES.tcxProfile, 310 Garmin.DeviceControl.FILE_TYPES.goals, 311 Garmin.DeviceControl.FILE_TYPES.fit, 312 Garmin.DeviceControl.FILE_TYPES.fitDir 313 ])) { 314 var error = new Error(Garmin.DeviceControl.MESSAGES.invalidFileType + fileType); 315 error.name = "InvalidTypeException"; 316 throw error; 317 } 318 // Make sure the device supports this type of data transfer for this type 319 if( !this.checkDeviceReadSupport(fileType) ) { 320 var error = new Error(Garmin.DeviceControl.MESSAGES.unsupportedReadDataType + fileType); 321 error.name = "UnsupportedDataTypeException"; 322 throw error; 323 } 324 this.gpsDataType = fileType; 325 this.gpsData = null; 326 this.gpsDataString = null; 327 this.idle = false; 328 329 try { 330 this._broadcaster.dispatch("onStartReadFromDevice", {controller: this}); 331 332 switch(this.gpsDataType) { 333 case Garmin.DeviceControl.FILE_TYPES.gpxDir: 334 case Garmin.DeviceControl.FILE_TYPES.gpx: 335 this.garminPlugin.startReadFromGps( this.deviceNumber ); 336 break; 337 case Garmin.DeviceControl.FILE_TYPES.tcx: 338 case Garmin.DeviceControl.FILE_TYPES.crs: 339 case Garmin.DeviceControl.FILE_TYPES.wkt: 340 case Garmin.DeviceControl.FILE_TYPES.goals: 341 case Garmin.DeviceControl.FILE_TYPES.tcxProfile: 342 this.garminPlugin.startReadFitnessData( this.deviceNumber, this.gpsDataType ); 343 break; 344 case Garmin.DeviceControl.FILE_TYPES.tcxDir: 345 this.garminPlugin.startReadFitnessDirectory(this.deviceNumber, Garmin.DeviceControl.FILE_TYPES.tcx); 346 break; 347 case Garmin.DeviceControl.FILE_TYPES.crsDir: 348 this.garminPlugin.startReadFitnessDirectory(this.deviceNumber, Garmin.DeviceControl.FILE_TYPES.crs); 349 break; 350 case Garmin.DeviceControl.FILE_TYPES.fitDir: 351 this.garminPlugin.startReadFitDirectory(this.deviceNumber); 352 break; 353 case Garmin.DeviceControl.FILE_TYPES.deviceXml: 354 this.gpsDataString = this.getCurrentDeviceXml(); 355 break; 356 } 357 this._progressRead(); 358 } catch(e) { 359 this._reportException(e); 360 } 361 }, 362 363 /** Generic detail read method, which reads a specific fitness activity from the device given an activity ID. 364 * Supported detail types are history activities and course activities. The resulting data read is available 365 * in via gpsData as an XML DOM and gpsDataString as an XML string once the read successfully finishes. 366 * Typically used after calling readDataFromDevice to read a fitness directory.<br/> 367 * <br/> 368 * Minimum plugin version 2.2.0.2 369 * 370 * @param {String} fileType Filetype to be read from the device. Supported values are 371 * Garmin.DeviceControl.FILE_TYPES.tcxDetail and Garmin.DeviceControl.FILE_TYPES.crsDetail 372 * @param {String} dataId The ID of the data to be read from the device. The format of these values depends 373 * on the type of data being read (i.e. course data or history data). The CourseName element in the course schema 374 * is used to identify courses, and the Id element is used to identify history activities. 375 * @see #readDataFromDevice, #readHistoryDetailFromFitnessDevice, #readCourseDetailFromFitnessDevice 376 * @throws InvalidTypeException, UnsupportedTransferTypeException 377 */ 378 readDetailFromDevice: function(fileType, dataId) { 379 if (!this.isUnlocked()) 380 throw new Error(Garmin.DeviceControl.MESSAGES.pluginNotUnlocked); 381 if (this.numDevices == 0) 382 throw new Error(Garmin.DeviceControl.MESSAGES.noDevicesConnected); 383 if ( ! this._isAMember(fileType, [Garmin.DeviceControl.FILE_TYPES.tcxDetail, Garmin.DeviceControl.FILE_TYPES.crsDetail])) { 384 var error = new Error(Garmin.DeviceControl.MESSAGES.invalidFileType + fileType); 385 error.name = "InvalidTypeException"; 386 throw error; 387 } 388 if( !this.checkDeviceReadSupport(fileType) ) { 389 throw new Error(Garmin.DeviceControl.MESSAGES.unsupportedReadDataType + fileType); 390 } 391 392 this.gpsDataType = fileType; 393 this.gpsData = null; 394 this.gpsDataString = null; 395 this.idle = false; 396 397 try { 398 this._broadcaster.dispatch("onStartReadFromDevice", {controller: this}); 399 400 switch(this.gpsDataType) { 401 case Garmin.DeviceControl.FILE_TYPES.tcxDetail: 402 this.garminPlugin.startReadFitnessDetail(this.deviceNumber, Garmin.DeviceControl.FILE_TYPES.tcx, dataId); 403 break; 404 case Garmin.DeviceControl.FILE_TYPES.crsDetail: 405 this.garminPlugin.startReadFitnessDetail(this.deviceNumber, Garmin.DeviceControl.FILE_TYPES.crs, dataId); 406 break; 407 } 408 this._progressRead(); 409 } catch(e) { 410 this._reportException(e); 411 } 412 }, 413 414 /** Asynchronously reads GPX data from the connected device. Only handles reading 415 * from the device in this.deviceNumber. <br/> 416 * <br/> 417 * When the data has been gathered, the onFinishedReadFromDevice is fired, and the 418 * data is stored in this.gpsDataString and this.gpsData 419 * 420 * @see #readDataFromDevice 421 */ 422 readFromDevice: function() { 423 this.readDataFromDevice(Garmin.DeviceControl.FILE_TYPES.gpx); 424 }, 425 426 /** Asynchronously reads a single fitness history record from the connected device as TCX format. 427 * Only handles reading from the device in this.deviceNumber.<br/> 428 * <br/> 429 * When the data has been gathered, the onFinishedReadFromDevice is fired, and the 430 * data is stored in this.gpsDataString 431 * 432 * Minimum plugin version 2.2.0.2 433 * 434 * @param {String} historyId The ID of the history record on the device. 435 * 436 * @see #readDetailFromDevice 437 */ 438 readHistoryDetailFromFitnessDevice: function(historyId) { 439 this.readDetailFromDevice(Garmin.DeviceControl.FILE_TYPES.tcx, historyId) 440 }, 441 442 /** Asynchronously reads a single fitness course from the connected device as TCX format. 443 * Only handles reading from the device in this.deviceNumber. <br/> 444 * <br/> 445 * When the data has been gathered, the onFinishedReadFromDevice is fired, and the 446 * data is stored in this.gpsDataString<br/> 447 * <br/> 448 * Minimum plugin version 2.2.0.2 449 * 450 * @param {String} courseId The name of the course on the device. 451 * 452 * @see #readDetailFromDevice 453 */ 454 readCourseDetailFromFitnessDevice: function(courseId){ 455 this.readDetailFromDevice(Garmin.DeviceControl.FILE_TYPES.crs, courseId) 456 }, 457 458 /** Asynchronously reads entire fitness history data (TCX) from the connected device. 459 * Only handles reading from the device in this.deviceNumber. <br/> 460 * <br/> 461 * When the data has been gathered, the onFinishedReadFromDevice is fired, and the 462 * data is stored in this.gpsDataString<br/> 463 * <br/> 464 * Minimum plugin version 2.1.0.3 465 * 466 * @see #readDataFromDevice 467 */ 468 readHistoryFromFitnessDevice: function() { 469 this.readDataFromDevice(Garmin.DeviceControl.FILE_TYPES.tcx); 470 }, 471 472 /** Asynchronously reads entire fitness course data (CRS) from the connected device. 473 * Only handles reading from the device in this.deviceNumber<br/> 474 * <br/> 475 * When the data has been gathered, the onFinishedReadFromDevice is fired, and the 476 * data is stored in this.gpsDataString<br/> 477 * <br/> 478 * Minimum plugin version 2.2.0.1 479 * 480 * @see #readDataFromDevice 481 */ 482 readCoursesFromFitnessDevice: function() { 483 this.readDataFromDevice(Garmin.DeviceControl.FILE_TYPES.crs); 484 }, 485 486 /** Asynchronously reads fitness workout data (WKT) from the connected device. 487 * Only handles reading from the device in this.deviceNumber<br/> 488 * <br/> 489 * When the data has been gathered, the onFinishedReadFromDevice is fired, and the 490 * data is stored in this.gpsDataString<br/> 491 * <br/> 492 * Minimum plugin version 2.2.0.1 493 * 494 * @see #readDataFromDevice 495 */ 496 readWorkoutsFromFitnessDevice: function() { 497 this.readDataFromDevice(Garmin.DeviceControl.FILE_TYPES.wkt); 498 }, 499 500 /** Asynchronously reads fitness profile data (TCX) from the connected device. 501 * Only handles reading from the device in this.deviceNumber<br/> 502 * <br/> 503 * When the data has been gathered, the onFinishedReadFromDevice is fired, and the 504 * data is stored in this.gpsDataString<br/> 505 * <br/> 506 * Minimum plugin version 2.2.0.1 507 * 508 * @see #readDataFromDevice 509 */ 510 readUserProfileFromFitnessDevice: function() { 511 this.readDataFromDevice(Garmin.DeviceControl.FILE_TYPES.tcxProfile); 512 }, 513 514 /** Asynchronously reads fitness goals data (TCX) from the connected device. 515 * Only handles reading from the device in this.deviceNumber<br/> 516 * <br/> 517 * When the data has been gathered, the onFinishedReadFromDevice is fired, and the 518 * data is stored in this.gpsDataString<br/> 519 * <br/> 520 * Minimum plugin version 2.2.0.1 521 * 522 * @see #readDataFromDevice 523 */ 524 readGoalsFromFitnessDevice: function() { 525 this.readDataFromDevice(Garmin.DeviceControl.FILE_TYPES.goals); 526 }, 527 528 529 530 /** Returns the GPS data that was last read as an XML DOM. <br/> 531 * Pre-requisite - Read function was called successfully. <br/> 532 * <br/> 533 * Minimum plugin version 2.1.0.3 534 * 535 * @return XML DOM of read GPS data 536 * @see #readDataFromDevice 537 * @see #readHistoryFromFitnessDevice 538 * @see #readHistoryDetailFromFitnessDevice 539 * @see #readCourseDetailFromFitnessDevice 540 */ 541 getGpsData: function() { 542 543 if (!this.isUnlocked()) 544 throw new Error(Garmin.DeviceControl.MESSAGES.pluginNotUnlocked); 545 if (this.numDevices == 0) 546 throw new Error(Garmin.DeviceControl.MESSAGES.noDevicesConnected); 547 if( this.getReadCompletionState != Garmin.DeviceControl.FINISH_STATES.finished ) { 548 throw new Error(Garmin.DeviceControl.MESSAGES.incompleteRead); 549 } 550 551 return this.gpsData; 552 }, 553 554 /** Returns the GPS data that was last read as an XML string. <br/> 555 * Pre-requisite - Read function was called successfully. <br/> 556 * <br/> 557 * Minimum plugin version 2.1.0.3 558 * 559 * @return XML string of read GPS data 560 * @see #readDataFromDevice 561 * @see #readHistoryFromFitnessDevice 562 * @see #readHistoryDetailFromFitnessDevice 563 * @see #readCourseDetailFromFitnessDevice 564 */ 565 getGpsDataString: function() { 566 if (!this.isUnlocked()) 567 throw new Error(Garmin.DeviceControl.MESSAGES.pluginNotUnlocked); 568 if (this.numDevices == 0) 569 throw new Error(Garmin.DeviceControl.MESSAGES.noDevicesConnected); 570 if( this.getReadCompletionState != Garmin.DeviceControl.FINISH_STATES.finished ) { 571 throw new Error(Garmin.DeviceControl.MESSAGES.incompleteRead); 572 } 573 574 return this.gpsDataString; 575 }, 576 577 /** Returns the last read fitness data in compressed format. A fitness read method must be called and the read must 578 * finish successfully before this function returns good data. <br/> 579 * <br/> 580 * Minimum plugin version 2.2.0.2 581 * 582 * @return Compressed fitness XML data from the last successful read. The data is gzp compressed and base64 expanded. 583 * @see #readDataFromDevice 584 * @see #readHistoryFromFitnessDevice 585 * @see #readHistoryDetailFromFitnessDevice 586 * @see #readCourseDetailFromFitnessDevice 587 */ 588 getCompressedFitnessData: function() { 589 590 if (!this.isUnlocked()) 591 throw new Error(Garmin.DeviceControl.MESSAGES.pluginNotUnlocked); 592 if (this.numDevices == 0) 593 throw new Error(Garmin.DeviceControl.MESSAGES.noDevicesConnected); 594 if( this.getReadCompletionState != Garmin.DeviceControl.FINISH_STATES.finished ) { 595 throw new Error(Garmin.DeviceControl.MESSAGES.incompleteRead); 596 } 597 598 try{ 599 this.garminPlugin.getTcdXmlz(); 600 } 601 catch( aException ) { 602 this._reportException( aException ); 603 } 604 }, 605 606 /** Returns the completion state of the current read. This function can be used with 607 * GPX and TCX (fitness) reads. 608 * 609 * @type Number 610 * @return {Number} The completion state of the current read. The completion state can be one of the following: <br/> 611 * <br/> 612 * 0 = idle <br/> 613 * 1 = working <br/> 614 * 2 = waiting <br/> 615 * 3 = finished <br/> 616 */ 617 getReadCompletionState: function() { 618 switch(this.gpsDataType) { 619 case Garmin.DeviceControl.FILE_TYPES.gpxDir: 620 case Garmin.DeviceControl.FILE_TYPES.gpxDetail: 621 case Garmin.DeviceControl.FILE_TYPES.gpx: 622 return this.garminPlugin.finishReadFromGps(); 623 case Garmin.DeviceControl.FILE_TYPES.tcx: 624 case Garmin.DeviceControl.FILE_TYPES.crs: 625 case Garmin.DeviceControl.FILE_TYPES.wkt: 626 case Garmin.DeviceControl.FILE_TYPES.tcxProfile: 627 case Garmin.DeviceControl.FILE_TYPES.goals: 628 return this.garminPlugin.finishReadFitnessData(); 629 case Garmin.DeviceControl.FILE_TYPES.tcxDir: 630 case Garmin.DeviceControl.FILE_TYPES.crsDir: 631 return this.garminPlugin.finishReadFitnessDirectory(); 632 case Garmin.DeviceControl.FILE_TYPES.tcxDetail: 633 case Garmin.DeviceControl.FILE_TYPES.crsDetail: 634 return this.garminPlugin.finishReadFitnessDetail(); 635 case Garmin.DeviceControl.FILE_TYPES.fitDir: 636 return this.garminPlugin.finishReadFitDirectory(); 637 } 638 }, 639 640 /** Internal read dispatching and polling delay. 641 * @private 642 */ 643 _progressRead: function() { 644 this._broadcaster.dispatch("onProgressReadFromDevice", {progress: this.getDeviceStatus(), controller: this}); 645 setTimeout(function() { this._finishReadFromDevice() }.bind(this), 200); //200 646 }, 647 648 /** Internal read state logic. <br/> 649 * <br/> 650 * Minimum plugin version 2.0.0.4 for GPX and TCX history read.<br/> 651 * Minimum plugin version 2.2.0.2 for directory and detail read.<br/> 652 * Minimum plugin version 2.2.0.2 for compressed file get. 653 * 654 * @private 655 */ 656 _finishReadFromDevice: function() { 657 var completionState = this.getReadCompletionState(); 658 659 //console.debug("control._finishReadFromDevice this.gpsDataType="+this.gpsDataType+" completionState="+completionState) 660 try { 661 662 if( completionState == Garmin.DeviceControl.FINISH_STATES.finished ) { 663 switch( this.gpsDataType ) { 664 case Garmin.DeviceControl.FILE_TYPES.gpxDir: 665 case Garmin.DeviceControl.FILE_TYPES.gpxDetail: 666 case Garmin.DeviceControl.FILE_TYPES.gpx: 667 if (this.garminPlugin.gpsTransferSucceeded()) { 668 this.gpsDataString = this.garminPlugin.getGpsXml(); 669 this.gpsData = Garmin.XmlConverter.toDocument(this.gpsDataString); 670 this._broadcaster.dispatch("onFinishReadFromDevice", {success: this.garminPlugin.gpsTransferSucceeded(), controller: this}); 671 } 672 break; 673 case Garmin.DeviceControl.FILE_TYPES.tcx: 674 case Garmin.DeviceControl.FILE_TYPES.crs: 675 case Garmin.DeviceControl.FILE_TYPES.tcxDir: 676 case Garmin.DeviceControl.FILE_TYPES.crsDir: 677 case Garmin.DeviceControl.FILE_TYPES.tcxDetail: 678 case Garmin.DeviceControl.FILE_TYPES.crsDetail: 679 case Garmin.DeviceControl.FILE_TYPES.wkt: 680 case Garmin.DeviceControl.FILE_TYPES.tcxProfile: 681 case Garmin.DeviceControl.FILE_TYPES.goals: 682 if (this.garminPlugin.fitnessTransferSucceeded()) { 683 this.gpsDataString = this.garminPlugin.getTcdXml(); 684 this.gpsDataStringCompressed = this.garminPlugin.getTcdXmlz(); 685 686 this.gpsData = Garmin.XmlConverter.toDocument(this.gpsDataString); 687 this._broadcaster.dispatch("onFinishReadFromDevice", {success: this.garminPlugin.fitnessTransferSucceeded(), controller: this}); 688 } 689 break; 690 case Garmin.DeviceControl.FILE_TYPES.fitDir: 691 // TODO is there a fitness transfer succeeded with fit? 692 // if(this.garminPlugin.fitnessTransferSucceeded()) { 693 this.gpsDataString = this.garminPlugin.getDirectoryXml(); 694 // this.gpsDataStringCompressed = this.garminPlugin.getTcdXmlz(); 695 this.gpsData = Garmin.XmlConverter.toDocument(this.gpsDataString); 696 this._broadcaster.dispatch("onFinishReadFromDevice", {success: this.garminPlugin.fitnessTransferSucceeded(), controller: this}); 697 // } 698 break; 699 } 700 } else if( completionState == Garmin.DeviceControl.FINISH_STATES.messageWaiting ) { 701 var msg = this._messageWaiting(); 702 this._broadcaster.dispatch("onWaitingReadFromDevice", {message: msg, controller: this}); 703 } else { 704 this._progressRead(); 705 } 706 } catch( aException ) { 707 this._reportException( aException ); 708 } 709 }, 710 711 /** User canceled the read. <br/> 712 * <br/> 713 * Minimum plugin version 2.0.0.4 714 */ 715 cancelReadFromDevice: function() { 716 if (this.gpsDataType == Garmin.DeviceControl.FILE_TYPES.gpx) { 717 this.garminPlugin.cancelReadFromGps(); 718 } else { 719 this.garminPlugin.cancelReadFitnessData(); 720 } 721 this._broadcaster.dispatch("onCancelReadFromDevice", {controller: this}); 722 }, 723 724 725 /** Return the specified file as a UU-Encoded string 726 * <br/> 727 * Minimum version 2.6.3.1 728 * 729 * If the file is known to be compressed, compressed should be 730 * set to false. Otherwise, set compressed to true to retrieve a 731 * gzipped and uuencoded file. 732 * 733 * @param relativeFilePath {String} path relative to the Garmin folder on the device 734 */ 735 getBinaryFile: function(deviceNumber, relativeFilePath) { 736 if (!this.isUnlocked()) 737 throw new Error(Garmin.DeviceControl.MESSAGES.pluginNotUnlocked); 738 if(this.numDevices == 0) 739 throw new Error(Garmin.DeviceControl.MESSAGES.noDevicesConnected); 740 // Attempt to detect Fit file 741 if(relativeFilePath.capitalize().endsWith(".fit")) { 742 // Capitalize makes all but first letters lowercase. I can't believe prototype doesn't have a lowercase method. :( 743 this.gpsDataType = Garmin.DeviceControl.FILE_TYPES.fit; 744 } else { 745 this.gpsDataType = Garmin.DeviceControl.FILE_TYPES.binary; 746 } 747 var success; 748 try { 749 this.gpsDataString = this.garminPlugin.getBinaryFile(deviceNumber, relativeFilePath, false); 750 this.gpsDataStringCompressed = this.garminPlugin.getBinaryFile(deviceNumber, relativeFilePath, true); 751 // this.gpsData = this.garminPlugin.getBinaryFile(deviceNumber, relativeFilePath, compressed); 752 success = true; 753 } catch(e) { 754 success = false; 755 this._reportException(e); 756 } 757 758 this._broadcaster.dispatch("onFinishReadFromDevice", {success: success, controller: this}); 759 return this.gpsData; 760 }, 761 762 /////////////////////// Web Drop Methods (Write) /////////////////////// 763 764 /** Writes an address to the currently selected device. 765 * 766 * @param {String} address The address to be written to the device. This doesn't check validity 767 */ 768 writeAddressToDevice: function(address) { 769 if (!this.isUnlocked()) 770 throw new Error(Garmin.DeviceControl.MESSAGES.pluginNotUnlocked); 771 if (!this.geocoder) { 772 this.geocoder = new Garmin.Geocode(); 773 this.geocoder.register(this); 774 } 775 this.geocoder.findLatLng(address); 776 }, 777 778 /** Handles call-back from geocoder and forwards call to onException on registered listeners. 779 * @private 780 * @param {Error} json error wrapped in JSON 'msg' object. 781 */ 782 onException: function(json) { 783 this._reportException(json.msg); 784 }, 785 786 /** Handles call-back from geocoder and forwards call to writeToDevice. 787 * Registered listeners will recieve an onFinishedFindLatLon call before writeToDevice is invoked. 788 * Listeners can change the 'fileName' if they choose avoiding overwritting old waypoints on 789 * some devices. 790 * @private 791 * @param {Object} json waypoint, fileName and controller in JSON wrapper. 792 */ 793 onFinishedFindLatLon: function(json) { 794 json.fileName = "address.gpx"; 795 json.controller = this; 796 this._broadcaster.dispatch("onFinishedFindLatLon", json); 797 var factory = new Garmin.GpsDataFactory(); 798 var gpxStr = factory.produceGpxString(null, [json.waypoint]); 799 this.writeToDevice(gpxStr, json.fileName); 800 }, 801 802 /////////////////////// More Write Methods /////////////////////// 803 /** 804 * Generic write method for GPX and TCX file formats. For binary write, use {@link downloadToDevice}. 805 * 806 * @param {String} dataType - the datatype to write to device. Possible values are located in {@link #Garmin.DeviceControl.FILE_TYPES} 807 * @param {String} dataString - the datastring to write to device. Should be in the format of the dataType value. 808 * @param {String} fileName - the filename to write the data to on the device. File extension is not necessary, 809 * but is suggested for device compatibility. This parameter is ignored when the dataType value is FitnessActivityGoals (see {@link #writeGoalsToFitnessDevice}). 810 * @see #writeToDevice, #writeFitnessToDevice 811 * @throws InvalidTypeException, UnsupportedTransferTypeException 812 */ 813 writeDataToDevice: function(dataType, dataString, fileName) { 814 if (!this.isUnlocked()) { 815 throw new Error(Garmin.DeviceControl.MESSAGES.pluginNotUnlocked); 816 } 817 818 if (this.numDevices == 0) { 819 throw new Error(Garmin.DeviceControl.MESSAGES.noDevicesConnected); 820 } 821 822 this.gpsDataType = dataType; 823 824 if (!this.checkDeviceWriteSupport(this.gpsDataType)) { 825 throw new Error(Garmin.DeviceControl.MESSAGES.unsupportedWriteDataType + this.gpsDataType); 826 } 827 828 try { 829 this._broadcaster.dispatch("onStartWriteToDevice", {controller: this}); 830 831 switch(this.gpsDataType) { 832 case Garmin.DeviceControl.FILE_TYPES.gpx: 833 this.garminPlugin.startWriteToGps(dataString, fileName, this.deviceNumber); 834 break; 835 case Garmin.DeviceControl.FILE_TYPES.crs: 836 case Garmin.DeviceControl.FILE_TYPES.wkt: 837 case Garmin.DeviceControl.FILE_TYPES.goals: 838 case Garmin.DeviceControl.FILE_TYPES.tcxProfile: 839 case Garmin.DeviceControl.FILE_TYPES.nlf: 840 this.garminPlugin.startWriteFitnessData(dataString, this.deviceNumber, fileName, this.gpsDataType); 841 break; 842 default: 843 throw new Error(Garmin.DeviceControl.MESSAGES.unsupportedWriteDataType + this.gpsDataType); 844 } 845 this._progressWrite(); 846 } catch(e) { 847 this._reportException(e); 848 } 849 }, 850 851 /** Writes the given GPX XML string to the device selected in this.deviceNumber. <br/> 852 * <br/> 853 * Minimum plugin version 2.0.0.4 854 * 855 * @param gpxString XML to be written to the device. This doesn't check validity. 856 * @param fileName The filename to write data to. Validity is not checked here. 857 */ 858 writeToDevice: function(gpxString, fileName) { 859 this.writeDataToDevice(Garmin.DeviceControl.FILE_TYPES.gpx, gpxString, fileName); 860 }, 861 862 /** DEPRECATED - See {@link #writeCoursesToFitnessDevice}<br/> 863 * <br/> 864 * Writes fitness course data (TCX) to the device selected in this.deviceNumber. <br/> 865 * <br/> 866 * Minimum plugin version 2.2.0.1 867 * 868 * @param tcxString {String} TCX Course XML string to be written to the device. This doesn't check validity. 869 * @param fileName {String} filename to write data to on the device. Validity is not checked here. 870 */ 871 writeFitnessToDevice: function(tcxString, fileName) { 872 this.writeDataToDevice(Garmin.DeviceControl.FILE_TYPES.crs, tcxString, fileName); 873 }, 874 875 /** Writes fitness course data (TCX) to the device selected in this.deviceNumber. <br/> 876 * <br/> 877 * Minimum plugin version 2.2.0.1 878 * 879 * @param tcxString {String} TCX Course XML string to be written to the device. This doesn't check validity. 880 * @param fileName {String} filename to write data to on the device. Validity is not checked here. 881 */ 882 writeCoursesToFitnessDevice: function(tcxString, fileName) { 883 this.writeDataToDevice(Garmin.DeviceControl.FILE_TYPES.crs, tcxString, fileName); 884 }, 885 886 /** Writes fitness goals data (TCX) string to the device selected in this.deviceNumber. All fitness goals 887 * are written to the filename 'ActivityGoals.TCX' in the device's goals directory, in order for the device 888 * to recognize the file.<br/> 889 * <br/> 890 * Minimum plugin version 2.2.0.1 891 * 892 * @param tcxString {String} ActivityGoals TCX string to be written to the device. This doesn't check validity. 893 */ 894 writeGoalsToFitnessDevice: function(tcxString) { 895 this.writeDataToDevice(Garmin.DeviceControl.FILE_TYPES.goals, tcxString, ''); 896 }, 897 898 /** Writes fitness workouts data (XML) string to the device selected in this.deviceNumber. <br/> 899 * <br/> 900 * Minimum plugin version 2.2.0.1 901 * 902 * @param tcxString XML (workouts) string to be written to the device. This doesn't check validity. 903 * @param fileName String of filename to write it to on the device. Validity is not checked here. 904 */ 905 writeWorkoutsToFitnessDevice: function(tcxString, fileName) { 906 this.writeDataToDevice(Garmin.DeviceControl.FILE_TYPES.wkt, tcxString, fileName); 907 }, 908 909 /** Writes fitness user profile data (TCX) string to the device selected in this.deviceNumber. <br/> 910 * <br/> 911 * Minimum plugin version 2.2.0.1 912 * 913 * @param tcxString XML (user profile) string to be written to the device. This doesn't check validity. 914 * @param fileName String of filename to write it to on the device. Validity is not checked here. 915 */ 916 writeUserProfileToFitnessDevice: function(tcxString, fileName) { 917 this.writeDataToDevice(Garmin.DeviceControl.FILE_TYPES.tcxProfile, tcxString, fileName); 918 }, 919 920 /** Downloads and writes binary data asynchronously to device. <br/> 921 * <br/> 922 * Minimum plugin version 2.0.0.4 923 * 924 * @param xmlDownloadDescription {String} xml string containing information about the files to be downloaded onto the device. 925 * @param fileName {String} this parameter is ignored! We will remove this param from the API in a future compatibility release. 926 */ 927 downloadToDevice: function(xmlDownloadDescription) { 928 if (!this.isUnlocked()) 929 throw new Error(Garmin.DeviceControl.MESSAGES.pluginNotUnlocked); 930 if(this.numDevices == 0) 931 throw new Error(Garmin.DeviceControl.MESSAGES.noDevicesConnected); 932 this.gpsDataType = Garmin.DeviceControl.FILE_TYPES.binary; 933 try { 934 this.garminPlugin.startDownloadData(xmlDownloadDescription, this.deviceNumber ); 935 this._progressWrite(); 936 } catch(e) { 937 this._reportException(e); 938 } 939 }, 940 941 /** Internal dispatch and polling delay. 942 * @private 943 */ 944 _progressWrite: function() { 945 //console.debug("control._progressWrite gpsDataType="+this.gpsDataType) 946 this._broadcaster.dispatch("onProgressWriteToDevice", {progress: this.getDeviceStatus(), controller: this}); 947 setTimeout(function() { this._finishWriteToDevice() }.bind(this), 200); 948 }, 949 950 /** Internal write lifecycle handling. 951 * @private 952 */ 953 _finishWriteToDevice: function() { 954 try { 955 var completionState; 956 var success; 957 958 switch( this.gpsDataType ) { 959 960 case Garmin.DeviceControl.FILE_TYPES.gpx : 961 completionState = this.garminPlugin.finishWriteToGps(); 962 success = this.garminPlugin.gpsTransferSucceeded(); 963 break; 964 case Garmin.DeviceControl.FILE_TYPES.crs : 965 case Garmin.DeviceControl.FILE_TYPES.goals : 966 case Garmin.DeviceControl.FILE_TYPES.wkt : 967 case Garmin.DeviceControl.FILE_TYPES.tcxProfile : 968 case Garmin.DeviceControl.FILE_TYPES.nlf : 969 completionState = this.garminPlugin.finishWriteFitnessData(); 970 success = this.garminPlugin.fitnessTransferSucceeded(); 971 break; 972 case Garmin.DeviceControl.FILE_TYPES.gpi : 973 case Garmin.DeviceControl.FILE_TYPES.fitCourse : 974 case Garmin.DeviceControl.FILE_TYPES.fitSettings : 975 case Garmin.DeviceControl.FILE_TYPES.fitSport : 976 case Garmin.DeviceControl.FILE_TYPES.binary : 977 completionState = this.garminPlugin.finishDownloadData(); 978 success = this.garminPlugin.downloadDataSucceeded(); 979 break; 980 case Garmin.DeviceControl.FILE_TYPES.firmware : 981 completionState = this.garminPlugin.finishUnitSoftwareUpdate(); 982 success = this.garminPlugin.downloadDataSucceeded(); 983 break; 984 default: 985 throw new Error(Garmin.DeviceControl.MESSAGES.unsupportedWriteDataType + this.gpsDataType); 986 } 987 988 if( completionState == Garmin.DeviceControl.FINISH_STATES.finished ) { 989 this._broadcaster.dispatch("onFinishWriteToDevice", {success: success, controller: this}); 990 } else if( completionState == Garmin.DeviceControl.FINISH_STATES.messageWaiting ) { 991 var msg = this._messageWaiting(); 992 this._broadcaster.dispatch("onWaitingWriteToDevice", {message: msg, controller: this}); 993 } else { 994 this._progressWrite(); 995 } 996 } catch( aException ) { 997 this._reportException( aException ); 998 } 999 }, 1000 1001 /** Cancels the current write transfer to the device. <br/> 1002 * <br/> 1003 * Minimum plugin version 2.0.0.4<br/> 1004 * Minimum plugin version 2.2.0.1 for writes of GPX to SD Card 1005 */ 1006 cancelWriteToDevice: function() { 1007 switch( this.gpsDataType) { 1008 case Garmin.DeviceControl.FILE_TYPES.gpx: 1009 this.garminPlugin.cancelWriteToGps(); 1010 break; 1011 case Garmin.DeviceControl.FILE_TYPES.gpi: 1012 case Garmin.DeviceControl.FILE_TYPES.binary: 1013 this.garminPlugin.cancelDownloadData(); 1014 break; 1015 case Garmin.DeviceControl.FILE_TYPES.firmware: 1016 this.garminPlugin.cancelUnitSoftwareUpdate(); 1017 break; 1018 case Garmin.DeviceControl.FILE_TYPES.crs: 1019 case Garmin.DeviceControl.FILE_TYPES.goals: 1020 case Garmin.DeviceControl.FILE_TYPES.wkt: 1021 case Garmin.DeviceControl.FILE_TYPES.tcxProfile: 1022 case Garmin.DeviceControl.FILE_TYPES.nlf: 1023 this.garminPlugin.cancelWriteFitnessData(); 1024 break; 1025 } 1026 this._broadcaster.dispatch("onCancelWriteToDevice", {controller: this}); 1027 }, 1028 1029 /** 1030 * Determine the amount of space available on a mass storage mode device (the 1031 * currently selected device according to this.deviceNumber). 1032 * <br/> 1033 * Minimum Plugin version 2.5.1 1034 * 1035 * @param {String} relativeFilePath - if a file is being replaced, set to relative path on device, otherwise set to empty string. 1036 * @return -1 for non-mass storage mode devices. 1037 * @see downloadToDevice 1038 */ 1039 bytesAvailable: function(relativeFilePath) { 1040 return this.garminPlugin.bytesAvailable(this.getDeviceNumber(), relativeFilePath); 1041 }, 1042 1043 /** Download and install a list of unit software updates. Start the asynchronous 1044 * StartUnitSoftwareUpdate operation. 1045 * 1046 * Check for completion with the FinishUnitSoftwareUpdate() method. After 1047 * completion check the DownloadDataSucceeded property to make sure that all of the downloads 1048 * were successfully placed on the device. 1049 * 1050 * See the Schema UnitSoftwareUpdatev3.xsd for the format of the UpdateResponsesXml description 1051 * 1052 * @see Garmin.DeviceControl.cancelWriteToDevice 1053 * @see Garmin.DevicePlugin.downloadDataSucceeded 1054 * @see Garmin.DevicePlugin._finishWriteToDevice 1055 * @version plugin v2.6.2.0 1056 */ 1057 downloadFirmwareToDevice: function(updateResponsesXml) { 1058 if (!this.isUnlocked()) 1059 throw new Error(Garmin.DeviceControl.MESSAGES.pluginNotUnlocked); 1060 if(this.numDevices == 0) 1061 throw new Error(Garmin.DeviceControl.MESSAGES.noDevicesConnected); 1062 this.gpsDataType = Garmin.DeviceControl.FILE_TYPES.firmware; 1063 try { 1064 this.garminPlugin.startUnitSoftwareUpdate(updateResponsesXml, this.deviceNumber); 1065 this._progressWrite(); 1066 } catch(e) { 1067 this._reportException(e); 1068 } 1069 }, 1070 1071 /////////////////////// Support Methods /////////////////////// 1072 1073 1074 /** Unlocks the GpsControl object to be used at the given web address. <br/> 1075 * <br/> 1076 * Minimum Plugin version 2.0.0.4 1077 * 1078 * @param {Array} pathKeyPairsArray baseURL and key pairs. 1079 * @type Boolean 1080 * @return True if the plug-in was unlocked successfully 1081 */ 1082 unlock: function(pathKeyPairsArray) { 1083 this.pluginUnlocked = this.garminPlugin.unlock(pathKeyPairsArray); 1084 return this.pluginUnlocked; 1085 }, 1086 1087 /** Register to be an event listener. An object that is registered will be dispatched 1088 * a method if they have a function with the same dispatch name. So if you register a 1089 * listener with an onFinishFindDevices, and the onFinishFindDevices message is called, you'll 1090 * get that message. See class comments for event types 1091 * 1092 * @param {Object} listener Object that will listen for events coming from this object 1093 * @see {Garmin.Broadcaster} 1094 */ 1095 register: function(listener) { 1096 this._broadcaster.register(listener); 1097 }, 1098 1099 /** True if plugin has been successfully created and unlocked. 1100 * @type Boolean 1101 */ 1102 isUnlocked: function() { 1103 return this.pluginUnlocked; 1104 }, 1105 1106 /** Responds to a message box on the device. 1107 * 1108 * Minimum version 2.0.0.4 1109 * 1110 * @param {Number} response should be an int which corresponds to a button value from this.garminPlugin.MessageBoxXml 1111 */ 1112 // TODO: this method only works with writes - should it work with reads? 1113 respondToMessageBox: function(response) { 1114 this.garminPlugin.respondToMessageBox(response ? 1 : 2); 1115 this._progressWrite(); 1116 }, 1117 1118 /** Called when device generates a message. 1119 * This occurs when completionState == Garmin.DeviceControl.FINISH_STATES.messageWaiting. 1120 * @private 1121 */ 1122 _messageWaiting: function() { 1123 var messageDoc = Garmin.XmlConverter.toDocument(this.garminPlugin.getMessageBoxXml()); 1124 //var type = messageDoc.getElementsByTagName("Icon")[0].childNodes[0].nodeValue; 1125 var text = messageDoc.getElementsByTagName("Text")[0].childNodes[0].nodeValue; 1126 1127 var message = new Garmin.MessageBox("Question",text); 1128 1129 var buttonNodes = messageDoc.getElementsByTagName("Button"); 1130 for(var i=0; i<buttonNodes.length; i++) { 1131 var caption = buttonNodes[i].getAttribute("Caption"); 1132 var value = buttonNodes[i].getAttribute("Value"); 1133 message.addButton(caption, value); 1134 } 1135 return message; 1136 }, 1137 1138 /** Get the status/progress of the current state or transfer 1139 * @type Garmin.TransferProgress 1140 */ 1141 getDeviceStatus: function() { 1142 var aProgressXml = this.garminPlugin.getProgressXml(); 1143 var theProgressDoc = Garmin.XmlConverter.toDocument(aProgressXml); 1144 1145 var title = ""; 1146 if(theProgressDoc.getElementsByTagName("Title").length > 0) { 1147 title = theProgressDoc.getElementsByTagName("Title")[0].childNodes[0].nodeValue; 1148 } 1149 1150 var progress = new Garmin.TransferProgress(title); 1151 1152 var textNodes = theProgressDoc.getElementsByTagName("Text"); 1153 for( var i=0; i < textNodes.length; i++ ) { 1154 if(textNodes[i].childNodes.length > 0) { 1155 var text = textNodes[i].childNodes[0].nodeValue; 1156 if(text != "") progress.addText(text); 1157 } 1158 } 1159 1160 var percentageNode = theProgressDoc.getElementsByTagName("ProgressBar")[0]; 1161 if(percentageNode != undefined) { 1162 if(percentageNode.getAttribute("Type") == "Percentage") { 1163 progress.setPercentage(percentageNode.getAttribute("Value")); 1164 } else if (percentageNode.getAttribute("Type") == "Indefinite") { 1165 progress.setPercentage(100); 1166 } 1167 } 1168 1169 return progress; 1170 }, 1171 1172 /** 1173 * @private 1174 */ 1175 _isAMember: function(element, array) { 1176 return array.any( function(str){ return str==element; } ); 1177 }, 1178 1179 /** Gets the version number for the plugin the user has currently installed. 1180 * @type Array 1181 * @return An array of the format [versionMajor, versionMinor, buildMajor, buildMinor]. 1182 * @see #getPluginVersionString 1183 */ 1184 getPluginVersion: function() { 1185 1186 return this.garminPlugin.getPluginVersion(); 1187 }, 1188 1189 /** Gets a string of the version number for the plugin the user has currently installed. 1190 * @type String 1191 * @return A string of the format "versionMajor.versionMinor.buildMajor.buildMinor", i.e. "2.0.0.4" 1192 * @see #getPluginVersion 1193 */ 1194 getPluginVersionString: function() { 1195 return this.garminPlugin.getPluginVersionString(); 1196 }, 1197 1198 /** Sets the required version number for the plugin for the application. 1199 * @param reqVersionArray {Array} The required version to set to. In the format [versionMajor, versionMinor, buildMajor, buildMinor] 1200 * i.e. [2,2,0,1] 1201 */ 1202 setPluginRequiredVersion: function(reqVersionArray) { 1203 if( reqVersionArray != null ) { 1204 this.garminPlugin.setPluginRequiredVersion(reqVersionArray); 1205 } 1206 }, 1207 1208 /** Sets the latest plugin version number. This represents the latest version available for download at Garmin. 1209 * We will attempt to keep the default value of this up to date with each API release, but this is not guaranteed, 1210 * so set this to be safe or if you don't want to upgrade to the latest API. 1211 * 1212 * @param reqVersionArray {Array} The latest version to set to. In the format [versionMajor, versionMinor, buildMajor, buildMinor] 1213 * i.e. [2,2,0,1] 1214 */ 1215 setPluginLatestVersion: function(reqVersionArray) { 1216 if( reqVersionArray != null ) { 1217 this.garminPlugin.setPluginLatestVersion(reqVersionArray); 1218 } 1219 }, 1220 1221 /** Determines if the plugin is initialized 1222 * @type Boolean 1223 */ 1224 isPluginInitialized: function() { 1225 return (this.garminPlugin != null); 1226 }, 1227 1228 /** Determines if the plugin is installed on the user's machine 1229 * @type Boolean 1230 */ 1231 isPluginInstalled: function() { 1232 return (this.garminPlugin.getVersionXml() != undefined); 1233 }, 1234 1235 /** Internal exception handling for asynchronous calls. 1236 * @private 1237 */ 1238 _reportException: function(exception) { 1239 this._broadcaster.dispatch("onException", {msg: exception, controller: this}); 1240 }, 1241 1242 /** Number of devices detected by plugin. 1243 * @type Number 1244 } */ 1245 getDevicesCount: function() { 1246 return this.numDevices; 1247 }, 1248 1249 /** Checks if the device lists the given datatype as a supported readable type. 1250 * Plugin version affects the results of this function. The latest plugin version is encouraged. 1251 * 1252 * Internal file type support (such as directory types) is detected based on base 1253 * type. i.e. tcxDir -> tcx, fitDir -> fit 1254 */ 1255 checkDeviceReadSupport: function( datatype ) { 1256 1257 // Do the plugin version check early for fit directory reading 1258 if( datatype == Garmin.DeviceControl.FILE_TYPES.fitDir) { 1259 if( this.garminPlugin.getSupportsFitDirectoryRead() == false) { 1260 // Yeah, breaking the 1 return rule... This is still cleaner than all the other options 1261 // and at least eliminates confusion between other types. 1262 return false; 1263 } 1264 } 1265 1266 var isDatatypeSupported; 1267 1268 // The selected device 1269 var device = this._getDeviceByNumber(this.deviceNumber); 1270 var baseDatatype; 1271 1272 // Internal types use base type for the support check. 1273 switch(datatype) { 1274 case Garmin.DeviceControl.FILE_TYPES.gpxDir: 1275 case Garmin.DeviceControl.FILE_TYPES.gpxDetail: 1276 baseDatatype = Garmin.DeviceControl.FILE_TYPES.gpx; 1277 break; 1278 case Garmin.DeviceControl.FILE_TYPES.tcxDir: 1279 case Garmin.DeviceControl.FILE_TYPES.tcxDetail: 1280 baseDatatype = Garmin.DeviceControl.FILE_TYPES.tcx; 1281 break; 1282 case Garmin.DeviceControl.FILE_TYPES.crsDir: 1283 case Garmin.DeviceControl.FILE_TYPES.crsDetail: 1284 baseDatatype = Garmin.DeviceControl.FILE_TYPES.crs; 1285 break; 1286 case Garmin.DeviceControl.FILE_TYPES.fitDir: 1287 case Garmin.DeviceControl.FILE_TYPES.fitFile: 1288 baseDatatype = Garmin.DeviceControl.FILE_TYPES.fit; 1289 break; 1290 default: 1291 baseDatatype = datatype; 1292 } 1293 1294 // Every device has a device xml and firmware 1295 if(baseDatatype == Garmin.DeviceControl.FILE_TYPES.deviceXml 1296 || baseDatatype == Garmin.DeviceControl.FILE_TYPES.firmware) { 1297 isDatatypeSupported = true; 1298 } else { 1299 isDatatypeSupported = device.supportDeviceDataTypeRead(baseDatatype); 1300 } 1301 1302 return isDatatypeSupported; 1303 }, 1304 1305 /** Checks if the device lists the given datatype as a supported writeable type. 1306 * Plugin version affects the results of this function. The latest plugin version is encouraged. 1307 * 1308 * Internal file types (such as directory types) are NOT detected as supported write types. 1309 */ 1310 checkDeviceWriteSupport: function(datatype) { 1311 var isDatatypeSupported = false; 1312 1313 // The selected device 1314 var device = this._getDeviceByNumber(this.deviceNumber); 1315 1316 // Don't include types that aren't in the Device XML 1317 if ( datatype == Garmin.DeviceControl.FILE_TYPES.binary 1318 || datatype == Garmin.DeviceControl.FILE_TYPES.gpi) { 1319 isDatatypeSupported = true; 1320 } else { 1321 isDatatypeSupported = device.supportDeviceDataTypeWrite(datatype); 1322 } 1323 1324 return isDatatypeSupported; 1325 }, 1326 1327 /** Retrieve a device from the list of found devices by device number. 1328 * @return Garmin.Device 1329 */ 1330 _getDeviceByNumber: function(deviceNum) { 1331 for( var index = 0; index < this.devices.length; index++) { 1332 if( this.devices[index].getNumber() == deviceNum){ 1333 return this.devices[index]; 1334 } 1335 } 1336 }, 1337 1338 /** String representation of instance. 1339 * @type String 1340 */ 1341 toString: function() { 1342 return "Garmin Javascript GPS Controller managing " + this.numDevices + " device(s)"; 1343 } 1344 }; 1345 1346 /** Dedicated browser support singleton. 1347 */ 1348 var BrowserSupport = { 1349 /** Determines if the users browser is currently supported by the plugin 1350 * @type Boolean 1351 */ 1352 isBrowserSupported: function() { 1353 //console.debug("Display.isBrowserSupported BrowserDetect.OS="+BrowserDetect.OS+", BrowserDetect.browser="+BrowserDetect.browser) 1354 // TODO Extract strings to constants 1355 // TODO Move this out to plugin layer? 1356 return ( (BrowserDetect.OS == "Windows" && 1357 (BrowserDetect.browser == "Firefox" 1358 || BrowserDetect.browser == "Mozilla" 1359 || BrowserDetect.browser == "Explorer" 1360 || BrowserDetect.browser == "Safari")) 1361 || (BrowserDetect.OS == "Mac" && 1362 (BrowserDetect.browser == "Firefox" 1363 || BrowserDetect.browser == "Safari")) ); 1364 } 1365 }; 1366 1367 /** Constants defining possible errors messages for various errors on the page 1368 */ 1369 Garmin.DeviceControl.MESSAGES = { 1370 deviceControlMissing: "Garmin.DeviceControl depends on the Garmin.DevicePlugin framework.", 1371 missingPluginTag: "Plug-In HTML tag not found.", 1372 browserNotSupported: "Your browser is not supported to use the Garmin Communicator Plug-In.", 1373 pluginNotInstalled: "Garmin Communicator Plugin NOT detected.", 1374 outOfDatePlugin1: "Your version of the Garmin Communicator Plug-In is out of date.<br/>Required: ", 1375 outOfDatePlugin2: "Current: ", 1376 updatePlugin1: "Your version of the Garmin Communicator Plug-In is not the latest version. Latest version: ", 1377 updatePlugin2: ", current: ", 1378 pluginNotUnlocked: "Garmin Plugin has not been unlocked", 1379 noDevicesConnected: "No device connected, can't communicate with device.", 1380 invalidFileType: "Cannot process the device file type: ", 1381 incompleteRead: "Incomplete read, cannot get compressed format.", 1382 unsupportedReadDataType: "Your device does not support reading of the type: ", 1383 unsupportedWriteDataType: "Your device does not support writing of the type: " 1384 }; 1385 1386 /** Constants defining possible states when you poll the finishActions 1387 */ 1388 Garmin.DeviceControl.FINISH_STATES = { 1389 idle: 0, 1390 working: 1, 1391 messageWaiting: 2, 1392 finished: 3 1393 }; 1394 1395 /** Constants defining possible file types associated with read and write methods. File types can 1396 * be accessed in a static way, like so:<br/> 1397 * <br/> 1398 * Garmin.DeviceControl.FILE_TYPES.gpx<br/> 1399 * <br/> 1400 * NOTE: 'gpi' is being deprecated--please use 'binary' instead for gpi and other binary data. 1401 */ 1402 Garmin.DeviceControl.FILE_TYPES = { 1403 gpx: "GPSData", 1404 tcx: "FitnessHistory", 1405 gpi: "gpi", //deprecated, use binary instead 1406 crs: "FitnessCourses", 1407 wkt: "FitnessWorkouts", 1408 goals: "FitnessActivityGoals", 1409 tcxProfile: "FitnessUserProfile", 1410 binary: "BinaryData", // Not in Device XML, so writing this type is "supported" for all devices. For FIT data, use fitFile. 1411 voices: "Voices", 1412 nlf: "FitnessNewLeaf", 1413 fit: "FITBinary", 1414 fitCourse: "FIT_TYPE_6", 1415 fitSettings: "FIT_TYPE_2", 1416 fitSport: "FIT_TYPE_3", 1417 1418 // The following types are internal types used by the API only and cannot be found in the Device XML. 1419 // NOTE: When adding or removing types to this internal list, modify checkDeviceReadSupport() accordingly. 1420 tcxDir: "FitnessHistoryDirectory", 1421 crsDir: "FitnessCoursesDirectory", 1422 gpxDir: "GPSDataDirectory", 1423 tcxDetail: "FitnessHistoryDetail", 1424 crsDetail: "FitnessCoursesDetail", 1425 gpxDetail: "GPSDataDetail", 1426 deviceXml: "DeviceXml", 1427 fitDir: "FitDirectory", 1428 fitFile: "FitFile", 1429 firmware: "Firmware" 1430 }; 1431 1432 /** Constants defining the strings used by the Device.xml to indicate 1433 * transfer direction of each file type 1434 */ 1435 Garmin.DeviceControl.TRANSFER_DIRECTIONS = { 1436 read: "OutputFromUnit", 1437 write: "InputToUnit", 1438 both: "InputOutput" 1439 }; 1440 1441 /** Encapsulates the data provided by the device for the current process' progress. 1442 * Use this to relay progress information to the user. 1443 * @class Garmin.TransferProgress 1444 * @constructor 1445 */ 1446 Garmin.TransferProgress = Class.create(); 1447 Garmin.TransferProgress.prototype = { 1448 initialize: function(title) { 1449 this.title = title; 1450 this.text = new Array(); 1451 this.percentage = null; 1452 }, 1453 1454 addText: function(textString) { 1455 this.text.push(textString); 1456 }, 1457 1458 /** Get all the text entries for the transfer 1459 * @type Array 1460 */ 1461 getText: function() { 1462 return this.text; 1463 }, 1464 1465 /** Get the title for the transfer 1466 * @type String 1467 */ 1468 getTitle: function() { 1469 return this.title; 1470 }, 1471 1472 setPercentage: function(percentage) { 1473 this.percentage = percentage; 1474 }, 1475 1476 /** Get the completed percentage value for the transfer 1477 * @type Number 1478 */ 1479 getPercentage: function() { 1480 return this.percentage; 1481 }, 1482 1483 /** String representation of instance. 1484 * @type String 1485 */ 1486 toString: function() { 1487 var progressString = ""; 1488 if(this.getTitle() != null) { 1489 progressString += this.getTitle(); 1490 } 1491 if(this.getPercentage() != null) { 1492 progressString += ": " + this.getPercentage() + "%"; 1493 } 1494 return progressString; 1495 } 1496 }; 1497 1498 1499 /** Encapsulates the data to display a message box to the user when the plug-in is waiting for feedback 1500 * @class Garmin.MessageBox 1501 * @constructor 1502 */ 1503 Garmin.MessageBox = Class.create(); 1504 Garmin.MessageBox.prototype = { 1505 initialize: function(type, text) { 1506 this.type = type; 1507 this.text = text; 1508 this.buttons = new Array(); 1509 }, 1510 1511 /** Get the type of the message box 1512 * @type String 1513 */ 1514 getType: function() { 1515 return this.type; 1516 }, 1517 1518 /** Get the text entry for the message box 1519 * @type String 1520 */ 1521 getText: function() { 1522 return this.text; 1523 }, 1524 1525 /** Get the text entry for the message box 1526 */ 1527 addButton: function(caption, value) { 1528 this.buttons.push({caption: caption, value: value}); 1529 }, 1530 1531 /** Get the buttons for the message box 1532 * @type Array 1533 */ 1534 getButtons: function() { 1535 return this.buttons; 1536 }, 1537 1538 getButtonValue: function(caption) { 1539 for(var i=0; i< this.buttons.length; i++) { 1540 if(this.buttons[i].caption == caption) { 1541 return this.buttons[i].value; 1542 } 1543 } 1544 return null; 1545 }, 1546 1547 /** 1548 * @type String 1549 */ 1550 toString: function() { 1551 return this.getText(); 1552 } 1553 }; 1554 1555 /* 1556 * Dynamic include of required libraries and check for Prototype 1557 * Code taken from scriptaculous (http://script.aculo.us/) - thanks guys! 1558 var GarminDeviceControl = { 1559 require: function(libraryName) { 1560 // inserting via DOM fails in Safari 2.0, so brute force approach 1561 document.write('<script type="text/javascript" src="'+libraryName+'"></script>'); 1562 }, 1563 1564 load: function() { 1565 if((typeof Prototype=='undefined') || 1566 (typeof Element == 'undefined') || 1567 (typeof Element.Methods=='undefined') || 1568 parseFloat(Prototype.Version.split(".")[0] + "." + 1569 Prototype.Version.split(".")[1]) < 1.5) { 1570 throw("GarminDeviceControl requires the Prototype JavaScript framework >= 1.5.0"); 1571 } 1572 1573 $A(document.getElementsByTagName("script")) 1574 .findAll( 1575 function(s) { 1576 return (s.src && s.src.match(/GarminDeviceControl\.js(\?.*)?$/)) 1577 } 1578 ) 1579 .each( 1580 function(s) { 1581 var path = s.src.replace(/GarminDeviceControl\.js(\?.*)?$/,'../../'); 1582 var includes = s.src.match(/\?.*load=([a-z,]*)/); 1583 var dependencies = 'garmin/device/GarminDevicePlugin' + 1584 ',garmin/device/GarminDevice' + 1585 ',garmin/util/Util-XmlConverter' + 1586 ',garmin/util/Util-Broadcaster' + 1587 ',garmin/util/Util-DateTimeFormat' + 1588 ',garmin/util/Util-BrowserDetect' + 1589 ',garmin/util/Util-PluginDetect' + 1590 ',garmin/device/GarminObjectGenerator'; 1591 (includes ? includes[1] : dependencies).split(',').each( 1592 function(include) { 1593 GarminDeviceControl.require(path+include+'.js') 1594 } 1595 ); 1596 } 1597 ); 1598 } 1599 } 1600 1601 GarminDeviceControl.load(); 1602 */ 1603 1604