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