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 * A high-level UI widget for talking with Garmin Devices. 17 * 18 * @fileoverview GarminDeviceDisplay.js 19 * @author Michael Bina michael.bina at garmin.com, Diana Chow diana.chow at garmin.com 20 * @version 1.4.4 21 */ 22 23 /** Provides the easiest avenue for getting a working instance of the plug-in onto your page. 24 * Generates the UI elements and places them on the page. 25 * 26 * @class Garmin.DeviceDisplay 27 * 28 * requires Prototype 29 * @requires Garmin.DeviceControl 30 * @namespace Garmin 31 */ 32 Garmin.DeviceDisplay= function(mainElement, options){}; //just here for jsdoc 33 Garmin.DeviceDisplay = Class.create(); 34 Garmin.DeviceDisplay.prototype = { 35 36 /** Constructor. 37 * @constructor 38 * @param String mainElement - id of the element in which to generate the contents 39 * (can also be a reference to the dom element itself). 40 * @param Object options - Object with options (see {@link Garmin.DeviceDisplayDefaultOptions} for descriptions of possible options). 41 */ 42 initialize: function(mainElement, options) { 43 if(typeof(mainElement) == "string") { 44 this.mainElement = $(mainElement); 45 } else { 46 this.mainElement = mainElement; 47 } 48 49 if(this.mainElement != null) { 50 this.options = null; 51 this.setOptions(options); 52 53 this.garminController = null; 54 this.garminRemoteTransfer = null; 55 this.activities = new Array(); 56 this.devices = new Array(); 57 this.factory = null; 58 this.tracks = null; 59 this.waypoints = null; 60 61 this.activityDirectory = null; // Array of activity ID strings in the directory 62 this.activityQueue = null; // Queue of activity IDs to sync events 63 this.numQueuedActivities = null; // Number of total queued activities for status reporting 64 this.uploadData = null; // Payload element for upload data 65 this.activityMatcher = null; // The activity filter for synchronizing activities 66 67 this.currentActivity = null; // The top of the activity queue and/or the activity being processed. 68 this.finishedFirstActivity = false; 69 this.xhr = null; // The XHR object (see GarminRemoteTransfer) 70 this.advancedUploadMode = true; // Internal option to show the activity selection table on upload 71 72 this.error = null; 73 74 this._generateElements(); 75 76 if (this.options.unlockOnPageLoad) { 77 this.getController(true); 78 } 79 if (!this.error && this.options.autoFindDevices) { 80 this.startFindDevices(); 81 } 82 } 83 }, 84 85 ////////////////////////// UI GENERATION METHODS /////////////////////////// 86 87 /* Primary UI build method. 88 * @private 89 */ 90 _generateElements: function() { 91 if (BrowserSupport.isBrowserSupported() || !this.options.hideIfBrowserNotSupported) { 92 this._generateStatusElement(); 93 if(this.options.showFindDevicesElement) { 94 this._generateFindDevicesElement(); 95 } 96 if(this.options.showReadDataElement) { 97 this._generateReadDataElement(); 98 } 99 if(this.options.showActivityDirectoryElement) { 100 this._generateActivityDirectoryElement(); 101 } 102 if(this.options.showWriteDataElement) { 103 this._generateWriteDataElement(); 104 } 105 if(this.options.showSendDataElement) { 106 this._generateSendDataElement(); 107 } 108 if(this.options.showAboutElement) { 109 this._generateAboutElement(); 110 } 111 this.resetUI(); 112 } 113 }, 114 115 /** Resets UI widgets based on state of application. 116 */ 117 resetUI: function() { 118 //console.debug("Display.resetUI") 119 this.hideProgressBar(); 120 121 var noDevicesAvailable = this.garminController ? (this.getController().numDevices==0) : true; 122 if(this.options.showFindDevicesElement) { 123 if (this.findDevicesButton) 124 this.findDevicesButton.disabled = false; 125 if (this.deviceSelectInput) 126 this.deviceSelectInput.disabled = noDevicesAvailable; 127 if (this.cancelFindDevicesButton) 128 this.cancelFindDevicesButton.disabled = true; 129 if (this.readDataTypesSelect) 130 this.readDataTypesSelect.disabled = false; 131 } 132 if(this.options.showReadDataElement) { 133 if (this.readDataButton) 134 this.readDataButton.disabled = noDevicesAvailable; 135 if (this.cancelReadDataButton) 136 this.cancelReadDataButton.disabled = true; 137 if(this.loadingContentElement) { 138 this.loadingContentElement.hide(); 139 } 140 } 141 if(this.options.showWriteDataElement) { 142 if (this.writeDataButton) 143 this.writeDataButton.disabled = noDevicesAvailable; 144 if (this.cancelWriteDataButton) 145 this.cancelWriteDataButton.disabled = true; 146 } 147 }, 148 149 150 /* Build status UI components. 151 * @private 152 */ 153 _generateStatusElement: function() { 154 this.statusElement = document.createElement("div"); 155 Element.extend(this.statusElement); 156 this.statusElement.id = this.options.statusElementId; 157 this.statusElement.addClassName(this.options.elementClassName); 158 this.mainElement.appendChild(this.statusElement); 159 160 // Status text 161 this.statusText = document.createElement("div"); 162 Element.extend(this.statusText); 163 this.statusText.id = this.options.statusTextId; 164 this.statusElement.appendChild(this.statusText); 165 166 // Progress bars 167 this._generateProgressBars(); 168 }, 169 170 /* Build status progress bar UI components. 171 * @private 172 */ 173 _generateProgressBars: function() { 174 // Device transfer progress bar 175 this.progressBar = document.createElement("div"); 176 Element.extend(this.progressBar); 177 this.progressBar.id = this.options.progressBarId; 178 this.progressBar.className = this.options.progressBarClass; 179 this.progressBarBack = document.createElement("div"); 180 Element.extend(this.progressBarBack); 181 this.progressBarBack.id = this.options.progressBarBackId; 182 this.progressBarBack.addClassName(this.options.progressBarBackClass); 183 this.progressBarBack.innerHTML = '<span/>'; 184 this.progressBar.appendChild(this.progressBarBack); 185 this.progressBarDisplay = document.createElement("div"); 186 Element.extend(this.progressBarDisplay); 187 this.progressBarDisplay.id = this.options.progressBarDisplayId; 188 this.progressBarDisplay.addClassName(this.options.progressBarDisplayClass); 189 this.progressBarDisplay.innerHTML = '<span/>'; 190 this.progressBar.appendChild(this.progressBarDisplay); 191 this.progressBar.hide(); 192 this.statusElement.appendChild(this.progressBar); 193 194 // Device transfer progress bar text 195 this.progressBarText = document.createElement("div"); 196 Element.extend(this.progressBarText); 197 this.progressBarText.id = this.options.progressBarTextId; 198 this.progressBarText.className = this.options.progressBarTextClass; 199 this.progressBar.appendChild(this.progressBarText); 200 201 // Upload progress bar 202 this.uploadProgressBar = document.createElement("div"); 203 Element.extend(this.uploadProgressBar); 204 this.uploadProgressBar.id = this.options.uploadProgressBarId; 205 this.uploadProgressBar.className = this.options.uploadProgressBarClass; 206 this.uploadProgressBarBack = document.createElement("div"); 207 Element.extend(this.uploadProgressBarBack); 208 this.uploadProgressBarBack.id = this.options.uploadProgressBarBackId; 209 this.uploadProgressBarBack.addClassName(this.options.uploadProgressBarBackClass); 210 this.uploadProgressBarBack.innerHTML = '<span/>'; 211 this.uploadProgressBar.appendChild(this.uploadProgressBarBack); 212 this.uploadProgressBarDisplay = document.createElement("div"); 213 Element.extend(this.uploadProgressBarDisplay); 214 this.uploadProgressBarDisplay.id = this.options.uploadProgressBarDisplayId; 215 this.uploadProgressBarDisplay.addClassName(this.options.uploadProgressBarDisplayClass); 216 this.uploadProgressBarDisplay.innerHTML = '<span/>'; 217 this.uploadProgressBar.appendChild(this.uploadProgressBarDisplay); 218 this.uploadProgressBar.hide(); 219 this.statusElement.appendChild(this.uploadProgressBar); 220 221 // Upload progress bar text 222 this.uploadProgressBarText = document.createElement("div"); 223 Element.extend(this.uploadProgressBarText); 224 this.uploadProgressBarText.id = this.options.uploadProgressBarTextId; 225 this.uploadProgressBarText.className = this.options.uploadProgressBarTextClass; 226 this.uploadProgressBar.appendChild(this.uploadProgressBarText); 227 228 //TODO This is totally the wrong place to put this. Move this out somewhere. 229 this.cancelUploadButton = new Element(this.options.useLinks ? 'div' : 'input', { 230 id: this.options.cancelUploadButtonId, 231 className: this.options.cancelUploadButtonClass 232 }); 233 if (this.options.useLinks) { 234 this.cancelUploadButton.update('<a href="#">'+this.options.cancelUploadButtonText+'</a>'); 235 } else { 236 this.cancelUploadButton.type = "button"; 237 this.cancelUploadButton.value = this.options.cancelUploadButtonText; 238 } 239 this.cancelUploadButton.onclick = function() { 240 this.resetUI(); 241 this.hideProgressBar(); 242 243 // Kill the string of event handlers 244 this.garminRemoteTransfer.abortRequest(); 245 246 try { 247 // Update the status of the active upload. 248 this.options.afterFinishSendData.call(this, 249 this.xhr, 250 this.currentActivity ? $(this.currentActivity.replace(/Checkbox/,"Status")) : null, 251 this); 252 } catch (error) { 253 this.handleException(error); 254 } 255 256 // Clear the queue. Affects cancel and progress. 257 this.activityQueue = null; 258 // this.clearActivityQueue(); // Clearing does not work for whatever reason... timing/async 259 260 // Go to the finished screen 261 this.getController()._broadcaster.dispatch("onFinishUploads", { display: this }); 262 }.bind(this) 263 this.uploadProgressBar.insert(this.cancelUploadButton); 264 }, 265 266 _createElement: function(id, text, type, parent) { 267 var elem = document.createElement(type); 268 Element.extend(elem); 269 if (type=="a") { 270 elem.href = location; 271 elem.innerHTML = text; 272 } else if (type=="button"){ 273 elem.type = type; 274 elem.value = text; 275 } 276 elem.id = id; 277 parent.appendChild(elem); 278 return elem; 279 }, 280 281 /** Build device browser list, a singleton. This list will be juxtaposed with the activity directory. 282 * This is a different list than the default device select drop down. It adds on the computer file browser 283 * as well. 284 * @private 285 */ 286 generateDeviceBrowserElement: function(devices) { 287 288 if( this.deviceBrowserElement != null) { 289 throw new Error("Unable to generate device browser because an instance already exists."); 290 } 291 292 this.deviceBrowserElement = new Element('div', { 293 id: this.options.deviceBrowserElementId, 294 className: this.options.deviceBrowserElementClass 295 }); 296 this.deviceBrowserLabel = new Element('div', { 297 id: this.options.deviceBrowserLabelId, 298 className: this.options.deviceBrowserLabelClass 299 }).update(this.options.deviceBrowserLabel); 300 this.deviceBrowserElement.insert(this.deviceBrowserLabel); 301 302 this.deviceBrowserList = document.createElement("ul"); 303 Element.extend(this.deviceBrowserList); 304 this.deviceBrowserList.id = this.options.deviceBrowserListId; 305 306 this.deviceBrowserElement.appendChild(this.deviceBrowserList); 307 this.deviceBrowserElement.hide(); 308 this.mainElement.appendChild(this.deviceBrowserElement); 309 310 // Fill up the list with real data 311 this._populateDeviceList(this.deviceBrowserList, this.options.afterSelectDevice ? this.options.afterSelectDevice : 312 function(selectedDeviceNumber, devices, deviceXml){ 313 if (this.options.readDataTypes != null) { 314 this.readFromDevice(this.options.readDataTypes); 315 // this.readSpecificTypeFromDevice(this.options.readDataType); 316 } 317 }); 318 319 if( this.options.uploadSelectedActivities && this.options.showBrowseComputer ) { 320 this._generateBrowseComputerElement(); 321 322 // Add My Computer to the list 323 var itemLink; 324 var listItem; 325 listItem = document.createElement("li"); 326 Element.extend(listItem); 327 listItem.className = "unselected"; 328 itemLink = document.createElement("a"); 329 Element.extend(itemLink); 330 itemLink.href = "#"; 331 itemLink.innerHTML = this.options.browseComputerLabel; 332 itemLink.onclick = function(deviceListElement) { 333 this._displayBrowseComputer(deviceListElement); 334 }.bind(this, this.deviceBrowserList) 335 listItem.appendChild(itemLink); 336 this.deviceBrowserList.appendChild(listItem); 337 } 338 }, 339 340 /* Displays the Browse Computer element and updates the device list accordingly. 341 * @private 342 * @param deviceListElement {Element} 343 */ 344 _displayBrowseComputer: function(deviceListElement) { 345 // Mark My Computer as selected 346 var browseComputerItem = deviceListElement.childNodes[this.devices.length]; 347 browseComputerItem.className = "selected"; 348 349 // Stop any existing reads and hide stuffs 350 if(this.getController() && this.isUnlocked()) { // browsing computer does not require valid plugin 351 this.getController().cancelReadFromDevice(); 352 } 353 354 // Mark all the rest of the devices as unselected 355 if( this.devices != null) { 356 for(var j=0; j< this.devices.length; j++) { 357 var listItem = deviceListElement.childNodes[j]; 358 listItem.className = "unselected"; 359 } 360 } 361 362 // The callback function has to take the parameter! Even if it ignores it. 363 this.activityDirectoryElement.hide(); 364 this.statusElement.hide(); 365 this.browseComputerElement.show(); 366 }, 367 368 /* Generates the browse computer element, an iframe that contains 369 * the manual upload page. 370 * @private 371 */ 372 _generateBrowseComputerElement: function() { 373 this.browseComputerElement = document.createElement("div"); 374 Element.extend(this.browseComputerElement); 375 this.browseComputerElement.id = this.options.browseComputerElementId; 376 this.browseComputerElement.className = this.options.browseComputerElementClass; 377 378 var browseComputerTitle = document.createElement("div"); 379 browseComputerTitle.id = 'manualUploadTitle'; 380 browseComputerTitle.innerHTML = this.options.browseComputerLabel; 381 this.browseComputerElement.appendChild(browseComputerTitle); 382 383 var browseComputerContent = document.createElement("iframe"); 384 Element.extend(browseComputerContent); 385 browseComputerContent.id = browseComputerContent.name = this.options.browseComputerElementId + 'Contents'; 386 browseComputerContent.src = this.options.browseComputerContentUrl; 387 388 this.browseComputerElement.appendChild(browseComputerContent); 389 this.browseComputerElement.hide(); 390 this.mainElement.appendChild(this.browseComputerElement); 391 392 // Have to set these after append for IE :E 393 // This doesn't work anyway!! I hate IE!! 394 browseComputerContent.setAttribute('frameborder', '0'); 395 browseComputerContent.setAttribute('allowtransparency', 'true'); 396 }, 397 398 /* Generates the loading screen as the passed in element is loading. 399 * @private 400 */ 401 _generateLoadingContent: function(loadingElement) { 402 if( this.loadingContentElement != null) { 403 throw new Error("Unable to generate loading screen because an instance already exists."); 404 } 405 // 'Loading' display 406 this.loadingContentElement = document.createElement("div"); 407 Element.extend(this.loadingContentElement); 408 this.loadingContentElement.className = "shortStatus"; 409 this.loadingContentElement.innerHTML = this.evaluateTemplate(this.options.loadingContentText, {deviceName:this.getShortDeviceName(this.getCurrentDevice())}); 410 loadingElement.appendChild(this.loadingContentElement); 411 412 this.showProgressBar(); 413 }, 414 415 /* Update the content inside of the loading content element and display it. 416 */ 417 _updateLoadingContent: function(content) { 418 // Update the device name displayed 419 if(this.loadingContentElement != null) { 420 this.loadingContentElement.update(content); 421 this.loadingContentElement.show(); 422 } 423 }, 424 425 /* Build find device UI components. 426 * @private 427 */ 428 _generateFindDevicesElement: function() { 429 this.findDevicesElement = document.createElement("div"); 430 Element.extend(this.findDevicesElement); 431 this.findDevicesElement.id = this.options.findDevicesElementId; 432 this.findDevicesElement.addClassName(this.options.elementClassName); 433 this.mainElement.appendChild(this.findDevicesElement); 434 435 // Find devices button 436 if( this.options.showFindDevicesButton) { 437 this.findDevicesButton = document.createElement( this.options.useLinks ? "div" : "input" ); 438 Element.extend(this.findDevicesButton); 439 if (this.options.useLinks) { 440 this.findDevicesButton.innerHTML = '<a href="#">'+this.options.findDevicesButtonText+'</a>'; 441 } else { 442 this.findDevicesButton.type = "button"; 443 this.findDevicesButton.value = this.options.findDevicesButtonText; 444 } 445 this.findDevicesButton.id = this.options.findDevicesButtonId; 446 this.findDevicesButton.addClassName(this.options.actionButtonClassName); 447 this.findDevicesElement.appendChild(this.findDevicesButton); 448 this.findDevicesButton.onclick = function() { 449 this.startFindDevices(); 450 }.bind(this) 451 } 452 453 if(!this.options.showFindDevicesElementOnLoad) { 454 if( this.findDevicesElement) { 455 Element.hide(this.findDevicesElement); 456 } 457 } 458 459 // Cancel Find devices button 460 if (this.options.showCancelFindDevicesButton) { 461 this.cancelFindDevicesButton = document.createElement( this.options.useLinks ? "div" : "input" ); 462 Element.extend(this.cancelFindDevicesButton); 463 if (this.options.useLinks) { 464 this.cancelFindDevicesButton.innerHTML = '<a href="#">'+this.options.cancelFindDevicesButtonText+'</a>'; 465 } else { 466 this.cancelFindDevicesButton.type = "button"; 467 this.cancelFindDevicesButton.value = this.options.cancelFindDevicesButtonText; 468 } 469 this.cancelFindDevicesButton.id = this.options.cancelFindDevicesButtonId; 470 this.cancelFindDevicesButton.addClassName(this.options.actionButtonClassName); 471 this.cancelFindDevicesButton.disabled = true; 472 this.cancelFindDevicesButton.onclick = function() { 473 this.cancelFindDevices(); 474 }.bind(this) 475 this.findDevicesElement.appendChild(this.cancelFindDevicesButton); 476 } 477 478 if (!this.options.showDeviceButtonsOnLoad) { 479 if (this.findDevicesButton) { 480 Element.hide(this.findDevicesButton); 481 } 482 if (this.cancelFindDevicesButton) 483 Element.hide(this.cancelFindDevicesButton); 484 } 485 486 // Device select drop-down list 487 this.deviceSelectElement = document.createElement("div"); 488 Element.extend(this.deviceSelectElement); 489 this.deviceSelectElement.id = this.options.deviceSelectElementId; 490 this.deviceSelectElement.innerHTML = '<div id="' + this.options.deviceSelectLabelId + '">' + this.options.deviceSelectLabel + '</div>'; 491 this.findDevicesElement.appendChild(this.deviceSelectElement); 492 493 this.deviceSelectInput = document.createElement( this.options.useDeviceSelectList ? "ul" : "select"); 494 Element.extend(this.deviceSelectInput); 495 this.deviceSelectInput.id = this.options.deviceSelectId; 496 this.deviceSelectInput.disabled = true; 497 498 if (!this.options.showDeviceSelectOnLoad || !this.options.showDeviceSelectOnSingle || this.options.autoSelectFirstDevice) { 499 Element.hide(this.deviceSelectElement); 500 } 501 502 /* Browse computer */ 503 this.browseComputerButton = new Element(this.options.useLinks ? "div" : "input", { 504 id: this.options.browseComputerButtonId 505 , className: this.options.browseComputerButtonClass 506 }); 507 this.browseComputerButton.onclick = function(){ 508 if( this.deviceBrowserList == null) { 509 this.generateDeviceBrowserElement(this.devices) 510 }; 511 this.findDevicesElement.hide(); 512 this.readDataElement.hide(); 513 this.deviceBrowserElement.show(); 514 this._displayBrowseComputer(this.deviceBrowserList); 515 }.bind(this) 516 if (this.options.useLinks) { 517 this.browseComputerButton.innerHTML = '<a href="#">'+this.options.browseComputerButtonText+'</a>'; 518 } else { 519 this.browseComputerButton.type = "button"; 520 this.browseComputerButton.value = this.options.browseComputerButtonText; 521 } 522 if(!this.options.uploadSelectedActivities || !this.options.showBrowseComputer ) { 523 this.browseComputerButton.hide(); 524 } 525 this.findDevicesElement.appendChild(this.browseComputerButton); 526 }, 527 528 _generateSendDataElement: function() { 529 this.sendDataElement = document.createElement("div"); 530 Element.extend(this.sendDataElement); 531 this.sendDataElement.id = this.options.sendDataElementId; 532 this.sendDataElement.addClassName(this.options.elementClassName); 533 this.mainElement.appendChild(this.sendDataElement); 534 535 this.sendDataButton = document.createElement( this.options.useLinks ? "div" : "input" ); 536 Element.extend(this.sendDataButton); 537 if (this.options.useLinks) { 538 this.sendDataButton.innerHTML = '<a href="#">'+this.options.sendDataButtonText+'</a>'; 539 } else { 540 this.sendDataButton.type = "button"; 541 this.sendDataButton.value = this.options.sendDataButtonText; 542 } 543 this.sendDataButton.id = this.options.sendDataButtonId; 544 this.sendDataButton.addClassName(this.options.actionButtonClassName); 545 this.sendDataButton.onclick = function() { 546 this.setStatus( 547 this.evaluateTemplate( 548 this.options.sendingDataToServer, 549 {deviceName:this.getShortDeviceName(this.getCurrentDevice())} 550 ) 551 ); 552 Element.hide(this.findDevicesElement); 553 Element.hide(this.sendDataElement); 554 this.showProgressBar(); 555 556 setTimeout(function(){this.postToServer()}.bind(this),1000); 557 return false; 558 }.bind(this) 559 this.sendDataElement.appendChild(this.sendDataButton); 560 561 if(this.options.showSendDataElementOnDeviceFound) { 562 Element.hide(this.sendDataElement); 563 } 564 }, 565 566 /** Post data to an external server. Request options should be provided by the 567 * parameters element in {@link Garmin.DeviceDisplayDefaultOptions.sendDataOptions} 568 * 569 * @see Garmin.DeviceDisplayDefaultOptions.sendDataOptions 570 * @see Garmin.DeviceDisplay.handleException 571 * @version 1.6 572 * @param callback {function} function executed after onSuccess of the AJAX request. If failure, exception is thrown {@link Garmin.DeviceDisplay.handleException} 573 */ 574 postToServer: function(callback) { 575 var error; 576 var exceptionName = 'RemoteTransferException'; 577 578 // getSendOptions overwrites those already set in sendDataOptions 579 if (this.options.sendDataOptions != null) { 580 if (this.options.getSendOptions != null) { 581 this.options.sendDataOptions = this.options.getSendOptions.call(this, this.options.sendDataOptions, this.garminController.getCurrentDeviceXml(), this.readDataString); 582 } 583 } 584 585 this.options.sendDataOptions.onSuccess = function(xhr) { 586 this.xhr = xhr; 587 if( this.options.afterFinishSendData != null) { 588 try { 589 this.options.afterFinishSendData.call(this, 590 this.xhr, 591 this.currentActivity ? $(this.currentActivity.replace(/Checkbox/, "Status")) : null, 592 this.activityDirectory, 593 this); 594 } catch (error) { 595 this.handleException(error); 596 } 597 } 598 callback.call(this); 599 }.bind(this); 600 601 this.options.sendDataOptions.onComplete = function(xhr) { 602 // Cross domain... 603 if( xhr == null) { 604 error = new Error(Garmin.RemoteTransfer.MESSAGES.generalException); 605 error.name = exceptionName; 606 this.handleException(error); 607 throw new Error(Garmin.RemoteTransfer.MESSAGES.noResponseException); 608 } 609 }.bind(this); 610 611 this.options.sendDataOptions.onFailure = function(xhr) { 612 error = new Error(xhr.statusText); 613 error.name = exceptionName; 614 this.handleException(error); 615 }.bind(this); 616 617 // Make the request 618 this.apiResponse = this.garminRemoteTransfer.openRequest(this.options.sendDataUrl, this.options.sendDataOptions); 619 }, 620 621 /* Build read data UI components. 622 * @private 623 */ 624 _generateReadDataElement: function() { 625 this.readDataElement = document.createElement("div"); 626 Element.extend(this.readDataElement); 627 this.readDataElement.id = this.options.readDataElementId; 628 this.readDataElement.addClassName(this.options.elementClassName); 629 this.mainElement.appendChild(this.readDataElement); 630 631 this.readDataButton = document.createElement( this.options.useLinks ? "div" : "input" ); 632 Element.extend(this.readDataButton); 633 if (this.options.useLinks) { 634 this.readDataButton.innerHTML = '<a href="#">'+this.options.readDataButtonText+'</a>'; 635 } else { 636 this.readDataButton.type = "button"; 637 this.readDataButton.value = this.options.readDataButtonText; 638 } 639 this.readDataButton.id = this.options.readDataButtonId; 640 this.readDataButton.addClassName(this.options.actionButtonClassName); 641 this.readDataButton.disabled = true; 642 this.readDataButton.onclick = function() { 643 var isSupportedDevice = true; 644 if( this.options.restrictByDevice.length > 0){ 645 isSupportedDevice = this._restrictByDevice(); 646 } 647 648 if( isSupportedDevice) { 649 if( this.options.autoHideUnusedElements ) { 650 if(this.findDevicesElement) Element.hide(this.findDevicesElement); 651 if(this.readDataElement) Element.hide(this.readDataElement); 652 if(this.deviceSelectElement) Element.hide(this.deviceSelectElement); 653 if(this.activityDirectoryElement) Element.hide(this.activityDirectoryElement); 654 } 655 this.readDataButton.disabled = true; 656 this.cancelReadDataButton.disabled = false; 657 this.showProgressBar(); 658 if (this.options.showReadDataTypesSelect) { 659 this.readSpecificTypeFromDevice(this.readDataTypesSelect.value); 660 } else if (this.options.readDataTypes != null) { 661 this.readFromDevice(this.options.readDataTypes); 662 } else { 663 this.readFromDevice(new Array(this.options.readDataType)); 664 } 665 } 666 }.bind(this) 667 this.readDataElement.appendChild(this.readDataButton); 668 if(!this.options.showReadDataButton) { 669 Element.hide(this.readDataButton); 670 } 671 672 this.cancelReadDataButton = document.createElement( this.options.useLinks ? "div" : "input" ); 673 Element.extend(this.cancelReadDataButton); 674 if (this.options.useLinks) { 675 this.cancelReadDataButton.innerHTML = '<a href="#">'+this.options.cancelReadDataButtonText+'</a>'; 676 } else { 677 this.cancelReadDataButton.type = "button"; 678 this.cancelReadDataButton.value = this.options.cancelReadDataButtonText; 679 } 680 this.cancelReadDataButton.id = this.options.cancelReadDataButtonId; 681 this.cancelReadDataButton.addClassName(this.options.actionButtonClassName); 682 this.cancelReadDataButton.disabled = true; 683 this.cancelReadDataButton.onclick = function() { 684 this.resetUI(); 685 this.hideProgressBar(); 686 this.getController().cancelReadFromDevice(); 687 }.bind(this) 688 this.readDataElement.appendChild(this.cancelReadDataButton); 689 690 if(!this.options.showCancelReadDataButton) { 691 Element.hide(this.cancelReadDataButton); 692 } 693 694 /* Upload without showing selection table */ 695 this.uploadNewButton = document.createElement( this.options.useLinks ? "div" : "input" ); 696 Element.extend(this.uploadNewButton); 697 if (this.options.useLinks) { 698 this.uploadNewButton.innerHTML = '<a href="#">'+this.options.uploadNewButtonText+'</a>'; 699 } else { 700 this.uploadNewButton.type = "button"; 701 this.uploadNewButton.value = this.options.uploadNewButtonText; 702 } 703 this.uploadNewButton.id = this.options.uploadNewButtonId; 704 this.uploadNewButton.addClassName(this.options.actionButtonClassName); 705 this.uploadNewButton.onclick = function() { 706 707 var isSupportedDevice = true; 708 if( this.options.restrictByDevice.length > 0){ 709 isSupportedDevice = this._restrictByDevice(); 710 } 711 712 if( isSupportedDevice) { 713 if( this.options.autoHideUnusedElements ) { this.advancedUploadMode = false; 714 this.readDataElement.hide(); 715 this.browseComputerButton.hide(); 716 this.uploadProgressBar.hide(); 717 if(this.changeDeviceElement != null) { this.changeDeviceElement.hide(); } 718 if(this.connectedDevices != null) { this.connectedDevices.hide(); } 719 if(this.deviceSelectInput != null) {this.deviceSelectInput.hide();} 720 721 if( this.loadingContentElement == null) { 722 this._generateLoadingContent(this.statusElement); 723 this.loadingContentElement.className = "longStatus"; 724 this.progressBar.className = "longProgressBar"; 725 this.progressBarText.className = "longProgressText"; 726 } 727 } 728 this.readDataButton.disabled = true; 729 this.cancelReadDataButton.disabled = false; 730 731 if (this.options.showReadDataTypesSelect) { 732 this.readSpecificTypeFromDevice(this.readDataTypesSelect.value); 733 } else if (this.options.readDataTypes != null) { 734 this.readFromDevice(this.options.readDataTypes); 735 } else { 736 this.readFromDevice(new Array(this.options.readDataType)); 737 } 738 } 739 }.bind(this) 740 this.readDataElement.appendChild(this.uploadNewButton); 741 742 if(!this.options.showUploadNewButton) { 743 Element.hide(this.uploadNewButton); 744 } 745 746 if(this.options.showReadDataTypesSelect) { 747 this.readDataTypesSelect = document.createElement("select"); 748 Element.extend(this.readDataTypesSelect); 749 this.readDataTypesSelect.id = this.options.readDataTypeSelectId; 750 this.readDataTypesSelect.disabled = true; 751 this.readDataElement.appendChild(this.readDataTypesSelect); 752 753 // TODO: need a more elegant way of adding options 754 this.readDataTypesSelect.options[0] = new Option(this.options.gpsData, Garmin.DeviceControl.FILE_TYPES.gpx); 755 this.readDataTypesSelect.options[1] = new Option(this.options.trainingData, Garmin.DeviceControl.FILE_TYPES.tcx); 756 } 757 758 if(this.options.showReadRoutesSelect) { 759 this.readRoutesElement = document.createElement("div"); 760 Element.extend(this.readRoutesElement); 761 this.readRoutesElement.id = this.options.readRoutesElementId; 762 this.readRoutesElement.addClassName(this.options.readResultsElementClass); 763 this.readRoutesElement.innerHTML = "<span id=\"" + this.options.readRoutesSelectLabelId + "\">" + this.options.readRoutesSelectLabel + "</span>"; 764 765 this.readRoutesSelect = document.createElement("select"); 766 Element.extend(this.readRoutesSelect); 767 this.readRoutesSelect.id = this.options.readRoutesSelectId; 768 this.readRoutesSelect.addClassName(this.options.readResultsSelectClass); 769 this.readRoutesSelect.disabled = true; 770 this.readRoutesSelect.onchange = function() { 771 this.displayTrack( this._seriesFromSelect(this.readRoutesSelect) ); 772 }.bind(this); 773 this.readRoutesElement.appendChild(this.readRoutesSelect); 774 this.readDataElement.appendChild(this.readRoutesElement); 775 776 if(!this.options.showReadResultsSelectOnLoad) { 777 Element.hide(this.readRoutesElement); 778 } 779 } 780 781 if(this.options.showReadTracksSelect) { 782 this.readTracksElement = document.createElement("div"); 783 Element.extend(this.readTracksElement); 784 this.readTracksElement.id = this.options.readTracksElementId; 785 this.readTracksElement.addClassName(this.options.readResultsElementClass); 786 this.readTracksElement.innerHTML = "<span id=\"" + this.options.readTracksSelectLabelId + "\">" + this.options.readTracksSelectLabel + "</span>"; 787 788 this.readTracksSelect = document.createElement("select"); 789 Element.extend(this.readTracksSelect); 790 this.readTracksSelect.id = this.options.readTracksSelectId; 791 this.readTracksSelect.addClassName(this.options.readResultsSelectClass); 792 this.readTracksSelect.disabled = true; 793 this.readTracksSelect.onchange = function() { 794 this.displayTrack( this._seriesFromSelect(this.readTracksSelect) ); 795 }.bind(this); 796 this.readTracksElement.appendChild(this.readTracksSelect); 797 this.readDataElement.appendChild(this.readTracksElement); 798 799 if(!this.options.showReadResultsSelectOnLoad) { 800 Element.hide(this.readTracksElement); 801 } 802 } 803 804 if(this.options.showReadWaypointsSelect) { 805 this.readWaypointsElement = document.createElement("div"); 806 Element.extend(this.readWaypointsElement); 807 this.readWaypointsElement.id = this.options.readWaypointsElementId; 808 this.readWaypointsElement.addClassName(this.options.readResultsElementClass); 809 this.readWaypointsElement.innerHTML = "<span id=\"" + this.options.readWaypointsSelectLabelId + "\">" + this.options.readWaypointsSelectLabel + "</span>"; 810 811 this.readWaypointsSelect = document.createElement("select"); 812 Element.extend(this.readWaypointsSelect); 813 this.readWaypointsSelect.id = this.options.readWaypointsSelectId; 814 this.readWaypointsSelect.addClassName(this.options.readResultsSelectClass); 815 this.readWaypointsSelect.disabled = true; 816 this.readWaypointsSelect.onchange = function() { 817 this.displayWaypoint( this._seriesFromSelect(this.readWaypointsSelect) ); 818 }.bind(this); 819 this.readWaypointsElement.appendChild(this.readWaypointsSelect); 820 this.readDataElement.appendChild(this.readWaypointsElement); 821 822 if(!this.options.showReadResultsSelectOnLoad) { 823 Element.hide(this.readWaypointsElement); 824 } 825 } 826 827 // Read Tracks Google Map 828 if(this.options.showReadGoogleMap) { 829 this.readGoogleMap = document.createElement("div"); 830 Element.extend(this.readGoogleMap); 831 this.readGoogleMap.id = this.options.readGoogleMapId; 832 this.readGoogleMap.addClassName(this.options.readResultsElementClass); 833 this.readDataElement.appendChild(this.readGoogleMap); 834 this.readMapController = new Garmin.MapController(this.options.readGoogleMapId); 835 } 836 837 if(this.options.showReadDataElementOnDeviceFound) { 838 Element.hide(this.readDataElement); 839 } 840 }, 841 842 /* Generates the activity directory element. Only one instance of the directory 843 * can exist on a page. 844 * 845 * @private 846 */ 847 _generateActivityDirectoryElement: function() { 848 if( this.activityDirectoryElement != null) { 849 throw new Error("Unable to generate activity directory because an instance already exists."); 850 } 851 // Create the container div to hold the directory elements 852 this.activityDirectoryElement = document.createElement("div"); 853 Element.extend(this.activityDirectoryElement); 854 this.activityDirectoryElement.id = this.options.activityDirectoryElementId; 855 this.activityDirectoryElement.addClassName(this.options.activityDirectoryClass); 856 this.activityDirectoryElement.hide(); 857 858 this.mainElement.appendChild(this.activityDirectoryElement); 859 }, 860 861 _generateActivityTableElement: function() { 862 if( this.activityTable != null ) { 863 throw new Error("Unable to generate activity table with id " + this.options.activityTableId + " because an instance already exists."); 864 } 865 this.activities = null; 866 867 this._generateActivityTableHeader(); 868 869 // Create container div that holds the table data only 870 this.activityDirectoryData = document.createElement("div"); 871 Element.extend(this.activityDirectoryData); 872 this.activityDirectoryData.id = this.options.activityDirectoryDataId; 873 this.activityDirectoryElement.appendChild(this.activityDirectoryData); 874 875 // Create the table 876 this.activityTable = document.createElement("table"); 877 Element.extend(this.activityTable); 878 this.activityTable.id = this.options.activityTableId; 879 this.activityTable.setAttribute('cellspacing','0'); 880 this.activityTable.setAttribute('cellpadding','0'); 881 this.activityDirectoryData.appendChild(this.activityTable); 882 883 this.readSelectedButton = document.createElement( this.options.useLinks ? "div" : "input" ); 884 Element.extend(this.readSelectedButton); 885 if (this.options.useLinks) { 886 this.readSelectedButton.innerHTML = '<a href="#">'+this.options.readSelectedButtonText+'</a>'; 887 } else { 888 this.readSelectedButton.type = "button"; 889 this.readSelectedButton.value = this.options.readSelectedButtonText; 890 } 891 this.readSelectedButton.id = this.options.readSelectedButtonId; 892 this.readSelectedButton.addClassName(this.options.actionButtonClassName); 893 this.readSelectedButton.disabled = false; 894 this.readSelectedButton.onclick = function() { 895 // Read activities filtered by the API (including user selected activities) 896 this.readFilteredActivities(); 897 }.bind(this); 898 this.activityDirectoryElement.appendChild(this.readSelectedButton); 899 }, 900 901 /* Generate the singleton activity table header for the activity directory. 902 * The visible elements in the table are added dynamically after the directory is read. 903 * See _addToActivityTableHeader(). 904 * 905 * @private 906 */ 907 _generateActivityTableHeader: function() { 908 if( this.activityTableHeader != null) { 909 throw new Error("Unable to generate activity table header: Instance of the activity table header already exists."); 910 } 911 912 // Create the table header 913 this.activityTableHeader = document.createElement("table"); 914 Element.extend(this.activityTableHeader); 915 this.activityTableHeader.id = this.options.activityTableHeaderId; 916 this.activityTableHeader.setAttribute('cellspacing','0'); 917 this.activityTableHeader.setAttribute('cellpadding','0'); 918 919 this.activityDirectoryElement.appendChild(this.activityTableHeader); 920 }, 921 922 /* Build write data UI components. 923 * @private 924 */ 925 _generateWriteDataElement: function() { 926 this.writeDataElement = document.createElement("div"); 927 Element.extend(this.writeDataElement); 928 this.writeDataElement.id = this.options.writeDataElementId; 929 this.writeDataElement.addClassName(this.options.elementClassName); 930 this.mainElement.appendChild(this.writeDataElement); 931 932 if (!this.options.getWriteData && !this.options.getGpiWriteDescription && !this.options.getBinaryWriteDescription) 933 throw new Error("Can't write data because getWriteData() function nor getGpiWriteDescription() is defined"); 934 this.writeDataButton = document.createElement( this.options.useLinks ? "div" : "input" ); 935 Element.extend(this.writeDataButton); 936 if (this.options.useLinks) { 937 this.writeDataButton.innerHTML = '<a href="#">'+this.options.writeDataButtonText+'</a>'; 938 } else { 939 this.writeDataButton.type = "button"; 940 this.writeDataButton.value = this.options.writeDataButtonText; 941 } 942 this.writeDataButton.id = this.options.writeDataButtonId; 943 this.writeDataButton.addClassName(this.options.actionButtonClassName); 944 this.writeDataButton.disabled = true; 945 this.writeDataButton.onclick = function() { 946 var isSupportedDevice = true; 947 if( this.options.restrictByDevice.length > 0){ 948 isSupportedDevice = this._restrictByDevice(); 949 } 950 if( isSupportedDevice) { 951 this.writeDataButton.disabled = true; 952 this.cancelWriteDataButton.disabled = false; 953 if( this.options.autoHideUnusedElements ) { 954 if(this.findDevicesElement) { 955 this.findDevicesElement.hide(); 956 } 957 if(this.writeDataElement) { 958 this.writeDataElement.hide(); 959 } 960 } 961 this.showProgressBar(); 962 this.writeToDevice(); 963 } 964 }.bind(this); 965 this.writeDataElement.appendChild(this.writeDataButton); 966 967 this.cancelWriteDataButton = document.createElement( this.options.useLinks ? "div" : "input" ); 968 Element.extend(this.cancelWriteDataButton); 969 if (this.options.useLinks) { 970 this.cancelWriteDataButton.innerHTML = '<a href="#">'+this.options.cancelWriteDataButtonText+'</a>'; 971 } else { 972 this.cancelWriteDataButton.type = "button"; 973 this.cancelWriteDataButton.value = this.options.cancelWriteDataButtonText; 974 } 975 this.cancelWriteDataButton.id = this.options.cancelWriteDataButtonId; 976 this.cancelWriteDataButton.addClassName(this.options.actionButtonClassName); 977 this.cancelWriteDataButton.disabled = false; 978 this.cancelWriteDataButton.onclick = function() { 979 this.resetUI(); 980 this.hideProgressBar(); 981 this.getController().cancelWriteToDevice(); 982 }.bind(this); 983 this.writeDataElement.appendChild(this.cancelWriteDataButton); 984 985 if(!this.options.showCancelWriteDataButton) { 986 Element.hide(this.cancelWriteDataButton); 987 } 988 989 if(this.options.showWriteDataElementOnDeviceFound) { 990 Element.hide(this.writeDataElement); 991 } 992 }, 993 994 /* Build "Powered by" UI components. 995 * @private 996 */ 997 _generateAboutElement: function() { 998 this.aboutElement = document.createElement("div"); 999 Element.extend(this.aboutElement); 1000 this.aboutElement.id = "aboutElement"; 1001 this.aboutElement.addClassName(this.options.elementClassName); 1002 this.mainElement.appendChild(this.aboutElement); 1003 1004 this.copyrightText = document.createElement("span"); 1005 this.copyrightText.innerHTML = this.options.poweredByGarmin; 1006 this.aboutElement.appendChild(this.copyrightText); 1007 }, 1008 1009 /* Checks the connected device against those listed in this.options.restrictByDevice 1010 * and throws an error if the connected device is not supported by 1011 * the application. 1012 * 1013 * @return true if the connected device is supported, false otherwise. 1014 */ 1015 _restrictByDevice: function() { 1016 1017 // Get connected device 1018 var device = this.getController().getDevices()[this.deviceSelectInput.value]; 1019 var devicePartNumber = device.getPartNumber(); 1020 1021 var isSupportedDevice = false; 1022 // Compare to restricted list 1023 for(var i=0; i < this.options.restrictByDevice.length; i++) { 1024 var supportedPartNumber = this.options.restrictByDevice[i]; 1025 if(devicePartNumber == supportedPartNumber) { 1026 isSupportedDevice = true; 1027 } 1028 } 1029 1030 // Hide everything but status 1031 if( !isSupportedDevice ) { 1032 error = new Error(this.options.unsupportedDevice); 1033 error.name = "UnsupportedDeviceException"; 1034 this.handleException(error); 1035 } 1036 1037 return isSupportedDevice; 1038 }, 1039 1040 ////////////////////////// FIND DEVICES METHODS ////////////////////////// 1041 1042 /** Entry point for searching for connected devices. 1043 * Will attempt to unlock the plugin if necessary. 1044 * @see Garmin.DeviceDisplay.cancelFindDevices 1045 * @see Garmin.DeviceDisplay.onStartFindDevices 1046 */ 1047 startFindDevices: function() { 1048 this.getController(true); //try to unlock plugin 1049 if(this.options.autoHideUnusedElements){ 1050 if( this.findDevicesButton) { 1051 this.findDevicesButton.hide(); 1052 } 1053 if( this.browseComputerButton ) { 1054 this.browseComputerButton.hide(); 1055 } 1056 } 1057 if(this.findDevicesButton) 1058 this.findDevicesButton.disabled = true; 1059 if (this.cancelFindDevicesButton) 1060 this.cancelFindDevicesButton.disabled = !this.isUnlocked(); 1061 if (this.isUnlocked()) { 1062 this.getController().findDevices(); 1063 } 1064 }, 1065 1066 /** Entry point for cancelling search for connected devices. 1067 * @see Garmin.DeviceDisplay.onCancelFindDevices 1068 */ 1069 cancelFindDevices: function() { 1070 this.resetUI(); 1071 this.getController().cancelFindDevices(); 1072 }, 1073 1074 /** Call-back triggered before plugin searches for connected devices. 1075 * @event 1076 * @param {JSON} json 1077 * @see Garmin.DeviceControl 1078 * @see Garmin.DeviceDisplay.startFindDevices 1079 */ 1080 onStartFindDevices: function(json) { 1081 this.setStatus(this.options.lookingForDevices); 1082 }, 1083 1084 /** Call-back triggered after plugin has completed its search for devices. 1085 * @event 1086 * @param {JSON} json 1087 * @see Garmin.DeviceControl 1088 * @see Garmin.DeviceDisplay.startFindDevices 1089 */ 1090 onFinishFindDevices: function(json) { 1091 this.resetUI(); 1092 if(json.controller.numDevices > 0) { 1093 this.devices = json.controller.getDevices(); 1094 1095 var template = (this.devices.length == 1) ? this.options.foundDevice : this.options.foundDevices; 1096 var values = {deviceName: this.getShortDeviceName(this.devices[0]), deviceCount: json.controller.numDevices}; 1097 this.setStatus( this.evaluateTemplate(template, values) ); 1098 1099 if(this.options.showFindDevicesElement ) { 1100 Element.show(this.findDevicesElement); 1101 if (this.options.showDeviceButtonsOnFound) { 1102 if (this.findDevicesButton && this.options.showFindDevicesButton) 1103 Element.show(this.findDevicesButton); 1104 if (this.cancelFindDevicesButton) 1105 Element.show(this.cancelFindDevicesButton); 1106 } else { 1107 if (this.findDevicesButton) 1108 Element.hide(this.findDevicesButton); 1109 if (this.cancelFindDevicesButton) 1110 Element.hide(this.cancelFindDevicesButton); 1111 } 1112 // Hide device select on single device 1113 if ((this.devices.length < 2 && !this.options.showDeviceSelectOnSingle) || this.options.autoSelectFirstDevice) { 1114 Element.hide(this.deviceSelectElement); 1115 } else { 1116 Element.show(this.deviceSelectElement); 1117 } 1118 // Populate the devices list based on UI option 1119 this.options.useDeviceSelectList ? this._generateDeviceListView() : this._populateDeviceSelectDropDown(); 1120 } 1121 1122 if(this.options.showReadDataElementOnDeviceFound) { 1123 Element.show(this.readDataElement); 1124 } 1125 1126 if(this.options.showSendDataElementOnDeviceFound) { 1127 Element.show(this.sendDataElement); 1128 } 1129 1130 if(this.options.showWriteDataElementOnDeviceFound) { 1131 Element.show(this.writeDataElement); 1132 } 1133 1134 if(this.options.autoHideUnusedElements) { 1135 if(this.activityDirectoryElement) { 1136 Element.hide(this.activityDirectoryElement); 1137 } 1138 if(this.readDataElement) { 1139 Element.show(this.readDataElement); 1140 } 1141 if(this.options.showBrowseComputer) { 1142 if( this.browseComputerButton) { 1143 this.browseComputerButton.show(); 1144 } 1145 } 1146 } 1147 1148 if (this.options.autoReadData) { 1149 this.showProgressBar(); 1150 if (this.options.showReadDataTypesSelect) { 1151 this.readSpecificTypeFromDevice(this.readDataTypesSelect.value); 1152 } else if (this.options.readDataType != null) { 1153 this.readSpecificTypeFromDevice(this.options.readDataType); 1154 } else { 1155 this.readFromDevice(); 1156 } 1157 } 1158 1159 if (this.options.autoWriteData) { 1160 this.showProgressBar(); 1161 this.writeToDevice(); 1162 } 1163 } else { // No devices found! 1164 if ((this.options.autoReadData || this.options.autoWriteData) && !this.options.showStatusElement) { 1165 alert(this.options.noDeviceDetectedStatusText); 1166 } 1167 1168 this.setStatus(this.options.noDeviceDetectedStatusText); 1169 if(this.findDevicesButton) { 1170 this.findDevicesButton.show(); // allow user to retry 1171 } 1172 1173 //allow user to browse computer in activity directory 1174 if(this.options.uploadSelectedActivities) { 1175 this.browseComputerButton.show(); 1176 } 1177 1178 if(this.options.showFindDevicesElement) { 1179 if (this.options.showCancelFindDevicesButton) { 1180 Element.show(this.cancelFindDevicesButton); 1181 } 1182 if (this.options.showDeviceSelectNoDevice && !this.options.autoSelectFirstDevice) { 1183 Element.show(this.deviceSelectElement); 1184 } 1185 } 1186 } 1187 if (this.options.afterFinishFindDevices) { 1188 this.options.afterFinishFindDevices.call(this, this.devices); 1189 } 1190 }, 1191 1192 /** Call-back for find device cancelled. 1193 * @event 1194 * @param {JSON} json 1195 * @see Garmin.DeviceControl 1196 * @see Garmin.DeviceDisplay.cancelFindDevices 1197 */ 1198 onCancelFindDevices: function(json) { 1199 this.setStatus(this.options.findCancelled); 1200 this.resetUI(); 1201 }, 1202 1203 /** Load device list into select UI component. 1204 * @private 1205 */ 1206 _populateDeviceSelectDropDown: function() { 1207 this.deviceSelectElement.appendChild(this.deviceSelectInput); 1208 this._clearHtmlSelect(this.deviceSelectInput); 1209 if(this.options.showFindDevicesElement) { 1210 for( var i=0; i < this.devices.length; i++ ) { 1211 this.deviceSelectInput.options[i] = new Option(this.getShortDeviceName(this.devices[i]),this.devices[i].getNumber()); 1212 1213 if(this.devices[i].getNumber() == this.getController().deviceNumber) { 1214 this.deviceSelectInput.selectedIndex = i; 1215 // Adding afterSelectDevice functionality to the old select UI 1216 if (this.options.afterSelectDevice != null) { 1217 this.options.afterSelectDevice.call(this, this.getController().deviceNumber, this.devices, this.garminController.getCurrentDeviceXml()); 1218 } 1219 } 1220 } 1221 this.deviceSelectInput.onchange = function() { 1222 var device = this.getController().getDevices()[this.deviceSelectInput.value]; 1223 this.setStatus(this.evaluateTemplate(this.options.usingDevice, {deviceName:this.getShortDeviceName(device)})); 1224 this.getController().setDeviceNumber(this.deviceSelectInput.value); 1225 // Adding afterSelectDevice functionality to the old select UI 1226 if (this.options.afterSelectDevice != null) { 1227 this.options.afterSelectDevice.call(this, this.getController().deviceNumber, this.devices, this.garminController.getCurrentDeviceXml()); 1228 } 1229 }.bind(this); 1230 this.deviceSelectInput.disabled = false; 1231 } 1232 }, 1233 1234 /* Load device browser content. 1235 * @private 1236 */ 1237 _generateAndDisplayDeviceBrowser: function() { 1238 1239 // Create device browser 1240 if( this.options.useDeviceBrowser) { 1241 if( this.deviceBrowserElement == null ) { 1242 this.generateDeviceBrowserElement(this.devices); 1243 } 1244 1245 if( !this.advancedUploadMode) { 1246 // Simple upload 1247 this.devicePreviewElement.show(); 1248 this.progressBar.hide(); 1249 } else { 1250 // Advanced upload 1251 this.deviceBrowserElement.show(); 1252 this.activityDirectoryElement.show(); 1253 } 1254 this.statusText.hide(); 1255 this.showProgressBar(); 1256 1257 // Show loading screen while reading device 1258 if( this.loadingContentElement == null) { 1259 this._generateLoadingContent(this.statusElement); 1260 } else { 1261 // Update the device name displayed 1262 this._updateLoadingContent(this.evaluateTemplate(this.options.loadingContentText, {deviceName:this.getShortDeviceName(this.getCurrentDevice())})); 1263 } 1264 } 1265 }, 1266 1267 /* Load device list into select UI component. 1268 * @private 1269 */ 1270 _generateDeviceListView: function() { 1271 var deviceSelectContainer; 1272 1273 this._clearHtmlSelect(this.deviceSelectInput); 1274 1275 deviceSelectContainer = document.createElement("div"); 1276 Element.extend(deviceSelectContainer); 1277 deviceSelectContainer.className = this.options.deviceSelectClass; 1278 1279 // Display change only if there are multiple devices 1280 if( this.devices.length > 1) { 1281 // Change device link 1282 this.changeDeviceElement = document.createElement("div"); 1283 Element.extend(this.changeDeviceElement); 1284 this.changeDeviceElement.id = this.options.changeDeviceElementId; 1285 this.changeDeviceElement.className = this.options.changeDeviceClass; 1286 this.changeDeviceElement.innerHTML = '<a href="#">'+this.options.changeDeviceButtonText+'</a>'; 1287 this.changeDeviceElement.onclick = function() { 1288 this.devicePreviewElement.toggle(); 1289 this.connectedDevices.toggle(); 1290 this.deviceSelectInput.toggle(); 1291 }.bind(this); 1292 this.deviceSelectElement.appendChild(this.changeDeviceElement); 1293 } 1294 1295 // Display pre-selected device (first device) 1296 this.devicePreviewElement = document.createElement("div"); 1297 Element.extend(this.devicePreviewElement); 1298 this.devicePreviewElement.id = this.options.previewDeviceElementId; 1299 this.devicePreviewElement.innerHTML = '<p>'+this.getShortDeviceName(this.getCurrentDevice())+'</p>'; 1300 deviceSelectContainer.appendChild(this.devicePreviewElement); 1301 this.deviceSelectElement.appendChild(deviceSelectContainer); 1302 1303 if(this.options.showFindDevicesElement) { 1304 1305 // Connected devices label 1306 this.connectedDevices = new Element("div", {className: this.options.connectedDevicesClass}); 1307 this.connectedDevices.update('<img src="'+ this.options.connectedDevicesImg +'" />' + this.options.connectedDevicesLabel); 1308 this.connectedDevices.hide(); 1309 deviceSelectContainer.appendChild(this.connectedDevices); 1310 1311 this._populateDeviceList(this.deviceSelectInput, this._updateDevicePreview); 1312 1313 deviceSelectContainer.appendChild(this.deviceSelectInput); 1314 this.deviceSelectElement.appendChild(deviceSelectContainer); 1315 this.deviceSelectInput.hide(); 1316 this.deviceSelectInput.disabled = false; 1317 } 1318 }, 1319 1320 /* Returns the current selected device according to the control object. 1321 * @private 1322 * @return {Device} the Device object belonging to the selected device 1323 * @see Garmin.Device 1324 */ 1325 getCurrentDevice: function() { 1326 return this.devices[this.getController().deviceNumber]; 1327 }, 1328 1329 /* Populates the device list. 'List' is emphasized because we are counting 1330 * on the fact that it is not a select drop down. See _populateDeviceSelectDropDown 1331 * for that. 1332 * 1333 * Sets the onclick event for each device in the list. When a device is selected, 1334 * the device number in control is set to that device. The class names of the 1335 * entire list are also updated in order to indicate visually which device is selected. 1336 * 1337 * After the above is finished, the callback method is executed. 1338 * 1339 * @private 1340 * @param {Element} deviceListElement the list element for the device listing 1341 * @param {function} callback(deviceIndex) - the callback function to use when a device is selected. 1342 * deviceIndex is the device number selected by the user. 1343 */ 1344 _populateDeviceList: function(deviceListElement, callback) { 1345 var itemLink; 1346 var listItem; 1347 1348 // Insert detected devices into the display list 1349 for( var i=0; i < this.devices.length; i++ ) { 1350 listItem = document.createElement("li"); 1351 Element.extend(listItem); 1352 listItem.className = (i == this.getController().deviceNumber) ? "selected" : "unselected"; 1353 itemLink = document.createElement("a"); 1354 Element.extend(itemLink); 1355 itemLink.href = "#"; 1356 itemLink.innerHTML = this.getShortDeviceName(this.devices[i]); 1357 itemLink.onclick = function(deviceListElement, deviceIndex, devices, callback){ 1358 1359 // Stop any existing reads and hide stuffs 1360 this.getController().cancelReadFromDevice(); 1361 1362 // Hide and unselect My Computer if selected 1363 if( this.browseComputerElement != null) { 1364 this.browseComputerElement.hide(); 1365 deviceListElement.childNodes[this.devices.length].className = "unselected"; 1366 } 1367 this.statusElement.show(); 1368 1369 // Set the new device to talk to 1370 this.getController().setDeviceNumber(deviceIndex); 1371 // Update the class names in the entire list 1372 for(var j=0; j< this.devices.length; j++) { 1373 var listItem = deviceListElement.childNodes[j]; 1374 listItem.className = (j == this.getController().deviceNumber) ? "selected" : "unselected"; 1375 } 1376 // The callback function has to take these two parameters! Even if it ignores em. 1377 callback.call(this, deviceIndex, this.devices, this.getController().getCurrentDeviceXml()); 1378 }.bind(this,deviceListElement,i,this.devices,callback); //bind with parameter 1379 listItem.appendChild(itemLink); 1380 deviceListElement.appendChild(listItem); 1381 } 1382 1383 // Select first device 1384 if(this.options.autoSelectFirstDevice) { 1385 this.options.afterSelectDevice.call(this, this.getController().deviceNumber, this.devices, this.garminController.getCurrentDeviceXml()); 1386 } 1387 }, 1388 1389 /* Process the filename, trim it if its too long 1390 * @param {Garmin.Device} the device whose name is to be processed 1391 * @return {String} the truncated device name, according to the max size set in the display options 1392 * @see Garmin.DeviceDisplayDefaultOptions.deviceLabelMaxSize 1393 * @see Garmin.Device 1394 */ 1395 getShortDeviceName : function(device) { 1396 var deviceName = device.getDisplayName(); 1397 if (deviceName.length > this.options.deviceLabelMaxSize) { 1398 deviceName = deviceName.substring(0, this.options.deviceLabelMaxSize) + "..."; 1399 } 1400 return deviceName; 1401 }, 1402 1403 /* Update the device preview display to show the device selected by the user. 1404 * Hides the input list and shows the preview element. 1405 * @param int deviceNumber - the device number (index) selected by the user 1406 * @param String deviceXml - describes the device (unused right now) 1407 */ 1408 _updateDevicePreview : function(deviceNumber, deviceXml){ 1409 this.devicePreviewElement.innerHTML = '<p>'+this.getShortDeviceName(this.devices[deviceNumber])+'</p>'; 1410 this.devicePreviewElement.show(); 1411 if(this.deviceSelectInput) this.deviceSelectInput.hide(); 1412 if(this.connectedDevices) this.connectedDevices.hide(); 1413 }, 1414 1415 ////////////////////////////// READ METHODS ////////////////////////////// 1416 1417 /** Initiation call for reading from a device. If a fitness device is detected reads TCX 1418 * otherwise reads GPX. 1419 * Upon completion if the afterFinishReadFromDevice method is defined 1420 * it will be called. At this time you may also obtain location data using the 1421 * getTracks and getWaypoints methods. 1422 * @param {Array} readDataTypes list of read data types 1423 * @see Garmin.DeviceControl.FILE_TYPES 1424 */ 1425 readFromDevice: function(readDataTypes) { 1426 var deviceNumber = this.getController().deviceNumber; 1427 var device = this.getController().getDevices()[deviceNumber]; 1428 1429 // TODO remove this later 1430 // Backwards compatability for deprecated method 1431 if( this.options.readDataType != null ) { 1432 readDataTypes = new Array(); 1433 readDataTypes[0] = this.options.readDataType; 1434 } 1435 1436 // Read the first supported type in the list 1437 var supported = null; 1438 for(var i=0; i < readDataTypes.length; i++) { 1439 var datatype = readDataTypes[i]; 1440 if(supported == null && this.getController().checkDeviceReadSupport(datatype)) { 1441 supported = datatype; 1442 1443 if(this.options.uploadSelectedActivities) { 1444 // Handle directory types 1445 switch(datatype) { 1446 case Garmin.DeviceControl.FILE_TYPES.gpxDir: 1447 case Garmin.DeviceControl.FILE_TYPES.tcxDir: 1448 case Garmin.DeviceControl.FILE_TYPES.fitDir: 1449 if( this.activityTable == null) { 1450 this._generateActivityTableElement(); 1451 } else { 1452 this._clearActivityTable(); 1453 } 1454 this._generateAndDisplayDeviceBrowser(); 1455 } 1456 } 1457 this.getController().readDataFromDevice(datatype); 1458 } 1459 } 1460 1461 // No supported types found, throw error 1462 if( supported == null) { 1463 var error = new Error(this.options.unsupportedDevice); 1464 error.name = "UnsupportedDataTypeException"; 1465 this.handleException(error); 1466 } 1467 }, 1468 1469 /** Read the filtered activities. Filtered activities are those picked 1470 * by the API as well as any user-selected activities, if applicable. 1471 * 1472 * Activities may be uploaded after being read. 1473 * 1474 * Filtered activities are detected before 1475 * the activities themselves are read, and data filters filter the data 1476 * after the activities are read. 1477 * 1478 * @see Garmin.DeviceDisplay.onFinishUploads 1479 */ 1480 readFilteredActivities: function() { 1481 // Make sure there are selected activities to read 1482 if( this._directoryHasSelected() == false) { 1483 if( this.advancedUploadMode) { 1484 // Alert user to select 1485 alert(this.options.errorActivitySelect); 1486 } else { 1487 // No new activities 1488 this.numQueuedActivities = 0; 1489 this.setStatus(this.options.noFilteredActivities); 1490 if( this.options.uploadSelectedActivities ) { 1491 this.getController()._broadcaster.dispatch("onFinishUploads", { display: this }); 1492 } 1493 } 1494 } else { 1495 this.activities = null; 1496 this.readTracksSelect.length = 0; 1497 this.readSelectedButton.disabled = true; 1498 if(this.options.useLinks) { 1499 this.readSelectedButton.hide(); 1500 } 1501 if(this.checkAllBox != null) { 1502 this.checkAllBox.disabled = false; 1503 } 1504 1505 if(this.options.useDeviceBrowser && this.advancedUploadMode){ 1506 this.statusElement.hide(); 1507 } else { 1508 this.showProgressBar(); 1509 } 1510 1511 this._populateActivityQueue(); 1512 1513 if(this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.tcxDir) { 1514 this.fileTypeRead = Garmin.DeviceControl.FILE_TYPES.tcxDetail; 1515 //setTimeout(function(){this._readNextSelected();}.bind(this), 0); 1516 this._readNextSelected(); 1517 } else if(this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.fitDir) { 1518 this.fileTypeRead = Garmin.DeviceControl.FILE_TYPES.fit; 1519 //setTimeout(function(){this._readNextSelected();}.bind(this), 0); 1520 this._readNextSelected(); 1521 } else if (this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.gpxDir) { 1522 this.fileTypeRead = Garmin.DeviceControl.FILE_TYPES.gpxDetail; 1523 1524 while (this.activityQueue.size() != 0) { 1525 // Display "Uploading..." 1526 this._displayProcessingForCurrentActivity(this.activityQueue.last()); 1527 this.activityQueue.pop(); 1528 } 1529 1530 this.garminController.readDataFromDevice(this.fileTypeRead); 1531 } 1532 } 1533 }, 1534 1535 /** Generic read method, supporting GPX, TCX, Courses, Workouts, User Profiles, 1536 * TCX activity directory, and TCX course directory reads. <br/> 1537 * <br/> 1538 * Upon completion if the afterFinishReadFromDevice method is defined 1539 * it will be called. At this time you may also obtain location data using the 1540 * getTracks and getWaypoints methods.<br/> 1541 * <br/> 1542 * Fitness detail reading (one specific activity) is not supported by this read method, refer to 1543 * readDetailFromDevice for that. <br/> 1544 * 1545 * @param String readDataType - type of data to read. 1546 * @see Garmin.DeviceControl.FILE_TYPES 1547 * @see Garmin.DeviceDisplayDefaultOptions.afterFinishReadFromDevice 1548 */ 1549 readSpecificTypeFromDevice: function(readDataType) { 1550 // Check to make sure device supports reading this type. Must do this at display layer otherwise exception will not 1551 // bubble up to the user. 1552 if( this.getController().checkDeviceReadSupport(readDataType) == false) { 1553 var error = new Error(this.evaluateTemplate(this.options.unsupportedReadDataType, {dataType: readDataType})); 1554 error.name = "UnsupportedDataTypeException"; 1555 this.handleException(error); 1556 } else { 1557 var deviceNumber = this.getController().deviceNumber; 1558 var device = this.getController().getDevices()[deviceNumber]; 1559 1560 switch(readDataType) { 1561 case Garmin.DeviceControl.FILE_TYPES.tcxDir: 1562 case Garmin.DeviceControl.FILE_TYPES.fitDir: 1563 if( this.activityTable == null) { 1564 this._generateActivityTableElement(); 1565 } else { 1566 this._clearActivityTable(); 1567 } 1568 this._generateAndDisplayDeviceBrowser(); 1569 // no break! keep on goin' 1570 case Garmin.DeviceControl.FILE_TYPES.gpx: 1571 case Garmin.DeviceControl.FILE_TYPES.gpxDir: 1572 case Garmin.DeviceControl.FILE_TYPES.tcx: 1573 case Garmin.DeviceControl.FILE_TYPES.crs: 1574 case Garmin.DeviceControl.FILE_TYPES.wkt: 1575 case Garmin.DeviceControl.FILE_TYPES.tcxProfile: 1576 case Garmin.DeviceControl.FILE_TYPES.crsDir: 1577 this.getController().readDataFromDevice(readDataType); 1578 break; 1579 case Garmin.DeviceControl.FILE_TYPES.deviceXml: 1580 this.getController().readDataFromDevice(readDataType); 1581 break; 1582 default: 1583 var error = new Error(Garmin.DeviceControl.MESSAGES.invalidFileType + readDataType); 1584 error.name = "InvalidTypeException"; 1585 this.handleException(error); 1586 } 1587 } 1588 }, 1589 1590 /** Call-back for device read progress. 1591 * @param {JSON} json the progress report in JSON format 1592 * @see Garmin.DeviceDisplay.onFinishReadFromDevice 1593 * @event 1594 */ 1595 onProgressReadFromDevice: function(json) { 1596 if(this.options.showProgressBar) { 1597 this.updateProgressBar(this.progressBarDisplay, json.progress.getPercentage()); 1598 this.updateProgressBarText(this.progressBarText, this.options.showDetailedStatus ? json.progress.text[0] + json.progress.text[1] : json.progress.text[1]); 1599 } else { 1600 this.setStatus(json.progress); 1601 } 1602 }, 1603 1604 /** Call-back for device read cancelled. 1605 * @see Garmin.DeviceControl 1606 * @see Garmin.DeviceDisplay.onStartReadFromDevice 1607 * @param {JSON} json the progress report in JSON format 1608 */ 1609 onCancelReadFromDevice: function(json) { 1610 this.setStatus(this.options.cancelReadStatusText); 1611 this.resetUI(); 1612 }, 1613 1614 /** Call-back for device read. 1615 * @see Garmin.DeviceControl 1616 * @param {JSON} json the progress report in JSON format 1617 */ 1618 onFinishReadFromDevice: function(json) { 1619 1620 this.fileTypeRead = json.controller.gpsDataType; 1621 this.readDataDoc = json.controller.gpsData; 1622 this.readDataString = json.controller.gpsDataString; 1623 1624 this.setStatus(this.options.dataReadProcessing); 1625 this.resetUI(); 1626 1627 this.clearMapDisplay(); 1628 1629 // select the correct factory for the parsing job, except for binary, which just passes through 1630 switch(this.fileTypeRead) { 1631 case Garmin.DeviceControl.FILE_TYPES.tcx: 1632 case Garmin.DeviceControl.FILE_TYPES.tcxDir: 1633 case Garmin.DeviceControl.FILE_TYPES.tcxDetail: 1634 this.factory = Garmin.TcxActivityFactory; 1635 break; 1636 case Garmin.DeviceControl.FILE_TYPES.gpx: 1637 case Garmin.DeviceControl.FILE_TYPES.gpxDir: 1638 case Garmin.DeviceControl.FILE_TYPES.gpxDetail: 1639 this.factory = Garmin.GpxActivityFactory; 1640 break; 1641 case Garmin.DeviceControl.FILE_TYPES.fitDir: 1642 this.factory = Garmin.DirectoryFactory; 1643 break; 1644 case Garmin.DeviceControl.FILE_TYPES.fit: 1645 case Garmin.DeviceControl.FILE_TYPES.binary: 1646 // Post to server immediately (and finishes reading activities on the queue) 1647 if(this.options.uploadSelectedActivities){ 1648 // Compressed data 1649 if(this.options.uploadCompressedData) { 1650 this.readDataString = json.controller.gpsDataStringCompressed; 1651 } 1652 this._postDataUpdateDisplay(this.readDataString); 1653 } 1654 this._finishReadProcessing(json); 1655 break; 1656 1657 default: 1658 var error = new Error( + this.fileTypeRead); 1659 error.name = "InvalidTypeException"; 1660 this.handleException(error); 1661 } 1662 1663 // parse the data into activities if possible 1664 if (this.factory != null) { 1665 // Convert the data obtained from the device into activities. 1666 // If we're starting a new read session (as opposed to individual 1667 // activity reads from the activity directory), start a new activities array 1668 if( this.activities == null) { 1669 this.activities = new Array(); 1670 } 1671 1672 // Populate this.activities 1673 switch(this.fileTypeRead) { 1674 case Garmin.DeviceControl.FILE_TYPES.gpxDir: 1675 case Garmin.DeviceControl.FILE_TYPES.tcxDir: 1676 case Garmin.DeviceControl.FILE_TYPES.fitDir: 1677 1678 // TODO should merge tcx and fit directory types at some point so we can share code 1679 if(this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.tcxDir) { 1680 this.activities = this.factory.parseDocument(this.readDataDoc); 1681 this._createActivityDirectory(Garmin.DeviceControl.FILE_TYPES.tcxDir, this.activities); 1682 } else if(this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.fitDir) { 1683 var files = this.factory.parseDocument(this.readDataDoc); 1684 var activityFiles = Garmin.DirectoryFactory.getActivityFiles(files); 1685 // Only use activity files for the activity directory 1686 this._createActivityDirectory(Garmin.DeviceControl.FILE_TYPES.fitDir, activityFiles); 1687 } else if(this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.gpxDir) { 1688 this.activities = this.factory.parseDocumentByType(this.readDataDoc, Garmin.GpxActivityFactory.GPX_TYPE.tracks); 1689 if(this.options.uploadSelectedActivities) { 1690 this._createActivityDirectory(Garmin.DeviceControl.FILE_TYPES.gpxDir, this.activities); 1691 } 1692 } 1693 1694 if( this.options.detectNewActivities && this.options.uploadSelectedActivities) { 1695 // No activities on device 1696 if( this.activityDirectory.size() == 0) { 1697 if(this.advancedUploadMode) { 1698 this._updateLoadingContent(this.options.noActivitiesOnDevice); 1699 } else { 1700 this.getController()._broadcaster.dispatch("onFinishUploads", { display: this }); 1701 } 1702 } 1703 else { 1704 // There are activities to compare 1705 this.activityMatcher = new Garmin.ActivityMatcher(this.garminController.getCurrentDeviceXml(), 1706 this.activityDirectory.getIds(), this.options.syncDataUrl, this.options.syncDataOptions, 1707 function(){this._finishReadProcessing(json)}.bind(this)); 1708 this.activityMatcher.run(); 1709 } 1710 } else { 1711 // Finished reading activities in queue, if any, so list them. 1712 this._finishReadProcessing(json); 1713 } 1714 break; 1715 case Garmin.DeviceControl.FILE_TYPES.gpxDetail: 1716 if(this.options.uploadSelectedActivities){ 1717 this._postDataUpdateDisplay(this.readDataString); 1718 } 1719 break; 1720 case Garmin.DeviceControl.FILE_TYPES.tcxDetail: 1721 1722 // Store this read activity 1723 // TODO: May not need this line, merge logic with binary type 1724 this.activities = this.activities.concat( this.factory.parseDocument(this.readDataDoc) ); 1725 1726 // Post to server (and finishes reading activities on the queue) 1727 if(this.options.uploadSelectedActivities){ 1728 // Compressed data 1729 if(this.options.uploadCompressedData) { 1730 this.readDataString = json.controller.gpsDataStringCompressed; 1731 } 1732 this._postDataUpdateDisplay(this.readDataString); 1733 } 1734 // Finished reading activities in queue, if any, so list them. 1735 this._finishReadProcessing(json); 1736 break; 1737 default: 1738 this.activities = this.factory.parseDocument(this.readDataDoc); 1739 // filter the activities 1740 this._applyDataFilters(); 1741 // Finished reading activities in queue, if any, so list them. 1742 this._finishReadProcessing(json); 1743 break; 1744 } 1745 1746 } 1747 }, 1748 1749 _postDataUpdateDisplay: function(data) { 1750 // Post to server (and finishes reading activities on the queue) 1751 if( this.loadingContentElement != null) { 1752 this.loadingContentElement.innerHTML = this.options.uploadingStatusText; 1753 this.loadingContentElement.show(); 1754 } 1755 this._postActivityToServer(data); 1756 }, 1757 1758 _applyDataFilters: function() { 1759 var dataFilters = this.options.dataFilters; 1760 if (dataFilters != null) { 1761 for (var i = 0; i < dataFilters.length; i++) { 1762 if (dataFilters[i].run != null) { 1763 dataFilters[i].run(this.activities, garminFilterQueue); 1764 } 1765 } 1766 } 1767 }, 1768 1769 /** Process the read data. Calls afterFinishReadFromDevice when finished. 1770 * @see Garmin.DeviceDisplay.afterFinishReadFromDevice 1771 * @param {JSON} json the progress report in JSON format 1772 */ 1773 _finishReadProcessing: function(json) { 1774 if (garminFilterQueue != null && garminFilterQueue.length > 0) { 1775 //console.debug("waiting for filters to finish..."); 1776 setTimeout(function(){this._finishReadProcessing(json);}.bind(this), 500); 1777 } else { 1778 1779 // list activities and set status to indicate how many were found 1780 if( this.activityQueue == null || this.activityQueue.length == 0 ) { 1781 1782 // List the activities 1783 if(this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.fitDir){ 1784 1785 var summary = this._listDirectory(this.activityDirectory); 1786 // Display # of activities found 1787 this.setStatus( this.evaluateTemplate(this.options.dataFound, summary) ); 1788 } 1789 if( this.activities != null && this.activities.length > 0) { 1790 if( this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.tcxDir || 1791 this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.gpxDir) { 1792 var summary = this._listDirectory(this.activityDirectory); 1793 } else { 1794 var summary = this._listActivities(this.activities); 1795 } 1796 1797 // Display # of activities found 1798 this.setStatus( this.evaluateTemplate(this.options.dataFound, summary) ); 1799 } 1800 1801 // Disable appropriate buttons after read is finished 1802 if(this.options.uploadSelectedActivities) { 1803 switch(this.fileTypeRead) { 1804 case Garmin.DeviceControl.FILE_TYPES.gpx: 1805 case Garmin.DeviceControl.FILE_TYPES.gpxDir: 1806 case Garmin.DeviceControl.FILE_TYPES.tcx: 1807 case Garmin.DeviceControl.FILE_TYPES.crs: 1808 case Garmin.DeviceControl.FILE_TYPES.tcxDir: 1809 case Garmin.DeviceControl.FILE_TYPES.crsDir: 1810 case Garmin.DeviceControl.FILE_TYPES.fitDir: 1811 this.deviceSelectInput.disabled = true; 1812 if( this.advancedUploadMode) { 1813 // Advanced upload 1814 if(this.loadingContentElement != null) { 1815 this.loadingContentElement.hide(); 1816 } 1817 } else { 1818 // Simple upload 1819 this.progressBar.hide(); 1820 this.uploadProgressBar.show(); 1821 this.readFilteredActivities(); 1822 } 1823 break; 1824 case Garmin.DeviceControl.FILE_TYPES.gpxDetail: 1825 case Garmin.DeviceControl.FILE_TYPES.tcxDetail: 1826 case Garmin.DeviceControl.FILE_TYPES.crsDetail: 1827 case Garmin.DeviceControl.FILE_TYPES.fit: 1828 case Garmin.DeviceControl.FILE_TYPES.binary: 1829 this.readSelectedButton.disabled = false; 1830 if( this.options.useLinks) { 1831 this.readSelectedButton.show(); 1832 } 1833 this.readSelectedButton.disabled = false; 1834 if(this.checkAllBox != null) { 1835 this.checkAllBox.disabled = false; 1836 } 1837 break; 1838 } 1839 } 1840 } 1841 1842 // pass data to the user if they want it 1843 if (this.options.afterFinishReadFromDevice) { 1844 var dataString = this.factory != null ? this.factory.produceString(this.activities) : json.controller.gpsDataString; 1845 var dataDoc = this.factory != null ? Garmin.XmlConverter.toDocument(dataString): json.controller.gpsData; 1846 this.options.afterFinishReadFromDevice(dataString, dataDoc, json.controller.gpsDataType, this.activities, this); 1847 } 1848 } 1849 }, 1850 1851 /** As uploads continue processing, this method will be called. This is called once 1852 * per upload item. This does not track byte-progress of a single upload. 1853 * @event 1854 * @param {JSON} json the progress report in JSON format 1855 * @see Garmin.DeviceDisplay.onFinishUploads 1856 */ 1857 onProgressUpload: function(json) { 1858 if(this.options.showProgressBar) { 1859 this.updateProgressBar(this.uploadProgressBarDisplay, json.progress.percentage); 1860 this.updateProgressBarText(this.uploadProgressBarText, json.progress.text); 1861 } else { 1862 this.setStatus(json.progress); 1863 } 1864 }, 1865 1866 /** Returns the current status of the upload progress based on the activity queue. 1867 * If there is no upload in progress, all values will be 0. 1868 * @returns {JSON} json object with report values and current DeviceDisplay instance 1869 * @returns json.progress 1870 * @returns {String} json.progress.current the current upload index from the activity queue 1871 * @returns {String} json.progress.total whole number of uploads finished 1872 * @returns {String} json.progress.percentage percentage value of uploads finished 1873 * @returns {String} json.progress.text upload progress text to display to user 1874 * @returns {Garmin.DeviceDisplay} json.display the current DeviceDisplay instance for UI purposes 1875 */ 1876 getUploadProgressJson: function() { 1877 1878 var currentVal; 1879 var totalVal; 1880 var percentageVal; 1881 1882 if( this.numQueuedActivities == null || this.activityQueue == null || this.activityQueue.length == 0) { 1883 currentVal = 0; 1884 totalVal = 0; 1885 percentageVal = 0; 1886 } else { 1887 currentVal = this.numQueuedActivities - this.activityQueue.length; 1888 totalVal = this.numQueuedActivities; 1889 percentageVal = currentVal / totalVal * 100; 1890 } 1891 1892 return { 1893 progress: { 1894 current: currentVal, 1895 total: totalVal, 1896 percentage: percentageVal, 1897 text: this.evaluateTemplate(this.options.uploadProgressStatusText, {currentUpload: currentVal, totalUploads: totalVal}) 1898 }, 1899 display: this 1900 }; 1901 }, 1902 1903 /* Reads the user-selected activities from the device by using the activity queue. 1904 */ 1905 _readNextSelected: function() { 1906 // Look at the next selected activity on the queue. (The queue only holds selected activities) 1907 this._displayProcessingForCurrentActivity(this.activityQueue.last()); 1908 this.setStatus(this.options.uploadingActivities); 1909 1910 var currentActivityId = $(this.currentActivity).value; 1911 1912 if( this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.tcxDetail ) { 1913 this.garminController.readDetailFromDevice(this.fileTypeRead, currentActivityId); 1914 } else if( this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.fit ) { 1915 var deviceNumber = this.getCurrentDevice().getNumber(); 1916 this.garminController.getBinaryFile(deviceNumber, this.activityDirectory.getEntry(currentActivityId).path); 1917 } 1918 }, 1919 1920 /** 1921 * Displays the processing icon for a given activity 1922 * 1923 * @param activity - the activity that will have the processing icon 1924 */ 1925 _displayProcessingForCurrentActivity: function(activity) { 1926 this.currentActivity = activity; 1927 1928 // Display 'processing' image next to corresponding activity in table 1929 var statusCellIdElement = $(this.currentActivity.replace(/Checkbox/, "Status")); 1930 statusCellIdElement.innerHTML = this.options.statusCellProcessingImg; 1931 }, 1932 1933 /** Stop uploading activities in the queue, and go on to finished screen. 1934 * Useful for certain error cases. 1935 * @see Garmin.DeviceDisplay.onFinishUploads 1936 */ 1937 stopQueuedUploads: function() { 1938 this.clearActivityQueue(); 1939 // Broadcast all uploads finished 1940 this.getController()._broadcaster.dispatch("onFinishUploads", { display: this }); 1941 }, 1942 1943 /* Posts the last read activity data from the activityQueue. See {@link this.options.sendDataUrl}, 1944 * {@link this.options.sendDataOptions} for designating the server and options for the AJAX request. 1945 * 1946 * A custom handler is also possible by defining {@link this.options.postActivityHandler}. Defining 1947 * this method will override the default Send Data implementation provided by this API. 1948 * 1949 * @param String dataString - the data string to post to server 1950 * @see Garmin.DeviceDisplayDefaultOptions.postActivityHandler 1951 */ 1952 _postActivityToServer: function(dataString) { 1953 if( this.options.sendDataUrl == null && this.options.postActivityHandler == null ) { 1954 throw new Error("Need to define either sendDataUrl or the postActivityHandler in display" + 1955 " options, depending on desired behavior."); 1956 } 1957 else { 1958 // nested function 1959 var finishPostProcessing = function() { 1960 // Exceptions are handled in postToServer. Even if errors occur, doesn't necessarily mean 1961 // that the rest of the uploads should be stopped. The uploadQueue needs to be cleared 1962 // if that is the desired behavior. 1963 this.activityQueue.pop(); 1964 1965 // Broadcast upload progress 1966 this.getController()._broadcaster.dispatch("onProgressUpload", this.getUploadProgressJson()); 1967 1968 // TODO: This doesn't quite belong here, but it's the only way to ensure synchronization. 1969 if(this.activityQueue.length > 0) { 1970 // Read what's left in the queue 1971 this._readNextSelected(); 1972 } else { 1973 // Broadcast all uploads finished 1974 this.getController()._broadcaster.dispatch("onFinishUploads", { display: this }); 1975 } 1976 }.bind(this); 1977 1978 if( this.options.sendDataUrl != null ) { 1979 // post the activity and then read the next one 1980 this.postToServer(finishPostProcessing); 1981 } 1982 else if( this.options.postActivityHandler != null) { 1983 this.options.postActivityHandler(dataString, this); 1984 finishPostProcessing(); 1985 } 1986 } 1987 }, 1988 1989 /** Callback when all uploads are finished. The display is passed in as the single param. 1990 * @event 1991 * @param {Garmin.DeviceDisplay} the current DeviceDisplay instance 1992 * @see Garmin.DeviceDisplayDefaultOptions.afterFinishUploads 1993 */ 1994 onFinishUploads: function(display) { 1995 1996 // Activities were uploaded 1997 if( this.numQueuedActivities > 0) { 1998 this.loadingContentElement.hide(); 1999 } 2000 // Nothing to upload, so show it 2001 else if( !this.advancedUploadMode ) { 2002 this.loadingContentElement.className = 'shortStatus'; 2003 this.activityTable.hide(); 2004 this._updateLoadingContent('No new activities to upload.'); 2005 } 2006 // Show the directory for results 2007 this.activityDirectoryElement.show(); 2008 this.uploadProgressBar.hide(); 2009 this.findDevicesElement.hide(); 2010 this.readSelectedButton.hide(); 2011 this.deviceBrowserElement.hide(); 2012 2013 if( this.options.afterFinishUploads ) { 2014 this.options.afterFinishUploads.call(this, this); 2015 } 2016 }, 2017 2018 _clearActivityTable: function() { 2019 //clear previous data, if any, including the header 2020 while(this.activityTableHeader.rows.length > 0) { 2021 this.activityTableHeader.deleteRow(0); 2022 } 2023 while(this.activityTable.rows.length > 0) { 2024 this.activityTable.deleteRow(0); 2025 } 2026 }, 2027 2028 /** Creates the activity directory of all activities (activity IDs) on the device 2029 * of the user-selected type. Most recent entries are first. 2030 * @param listType String type of directory described by the list 2031 * @param list Array list of directory entries, of any type. Currently expects activities (tcx) or files (fit) 2032 * @private 2033 */ 2034 _createActivityDirectory: function(listType, list) { 2035 2036 if( this.advancedUploadMode ) { 2037 this.activityDirectoryElement.show(); 2038 } 2039 this.activityQueue = new Array(); // Initialized here so that we can detect activity selection read status 2040 2041 this.activityDirectory = new Garmin.ActivityDirectory(); 2042 2043 for( var jj = 0; jj < list.length; jj++) { 2044 var id; 2045 var name; 2046 var duration; 2047 var entry; 2048 2049 if( listType == Garmin.DeviceControl.FILE_TYPES.tcxDir) { 2050 // list of Garmin.Activity 2051 var activity = list[jj]; 2052 id = activity.getAttribute(Garmin.Activity.ATTRIBUTE_KEYS.activityName); 2053 name = activity.getSummaryValue(Garmin.Activity.SUMMARY_KEYS.startTime).getValue().getTimeString(); 2054 duration = activity.getStartTime().getDurationTo(activity.getEndTime()); // Correct time zone 2055 2056 entry = this.activityDirectory.addEntry(id, name, duration, null); 2057 2058 } else if( listType == Garmin.DeviceControl.FILE_TYPES.fitDir) { 2059 // list of Garmin.File 2060 var file = list[jj]; 2061 id = file.getIdValue(Garmin.FileId.KEYS.id); 2062 name = file.getAttribute(Garmin.File.ATTRIBUTE_KEYS.creationTime).getTimeString(); 2063 this.activityDirectory.addEntry(id, name, null, null); 2064 this.activityDirectory.getEntry(id).path = file.getAttribute(Garmin.File.ATTRIBUTE_KEYS.path); 2065 } else if (listType == Garmin.DeviceControl.FILE_TYPES.gpxDir) { 2066 // list of Garmin.Activity 2067 var activity = list[jj]; 2068 var summaryValue = activity.getSummaryValue(Garmin.Activity.SUMMARY_KEYS.startTime); 2069 var attribute = activity.getAttribute(Garmin.Activity.ATTRIBUTE_KEYS.activityName); 2070 2071 // Make sure these are not null or else we will skip 2072 if (summaryValue != null && attribute != null) 2073 { 2074 id = summaryValue.getValue().getXsdString(); 2075 name = attribute; 2076 2077 this.activityDirectory.addEntry(id, name, null, null); 2078 } 2079 } 2080 } 2081 }, 2082 2083 /* Creates the activity queue of selected activities to read in detail from device. 2084 * Called after the user has finished selecting activities and also after the API 2085 * does its synchronization thing). The queue is an Array that is constructed and 2086 * then reversed to simulate a queue. 2087 */ 2088 _populateActivityQueue: function() { 2089 var checkBoxName = "activityItemCheckbox"; 2090 // TODO Create a class for the activity queue 2091 for( var jj = 0; jj < this.activityDirectory.size(); jj++) { 2092 var checkBoxElementId = checkBoxName + jj; 2093 2094 if($(checkBoxElementId).checked == true){ 2095 this.activityQueue.push(checkBoxElementId); 2096 } 2097 2098 var activityId = this.activityDirectory.getIds()[jj]; 2099 this.activityDirectory.getEntry(activityId).displayElementId = checkBoxElementId; 2100 } 2101 // Reverse the array to turn it into a queue 2102 this.activityQueue.reverse(); 2103 2104 // Save the original size for status reporting 2105 this.numQueuedActivities = this.activityQueue.length; 2106 }, 2107 2108 /** Empties the activity queue if it has any entries. 2109 */ 2110 clearActivityQueue: function() { 2111 for( var i=0; i < this.activityQueue.length; i++) { 2112 this.activityQueue.pop(); 2113 } 2114 }, 2115 2116 /* The activityTable object is the HTML table element on the demo page. This function 2117 * adds the necessary row to the table with the activity data. 2118 * @param int index - the internal index assigned to the activity value in order 2119 * to update the table status 2120 * @param {Garmin.ActivityDirectory.Entry} entry - entry to add to the table 2121 * @see afterTableInsert 2122 */ 2123 _addToActivityTable: function(index, entry) { 2124 2125 var tableIndex = 0; 2126 2127 var activityId = entry.id; 2128 2129 var row = this.activityTable.insertRow(this.activityTable.rows.length); // append a new row to the table 2130 // Color odd rows 2131 if( (index+2) % 2 != 0) { 2132 row.setAttribute('bgcolor', '#f3f3f3'); 2133 } 2134 2135 var selectCell = row.insertCell(tableIndex++); 2136 selectCell.width = '40'; // Set widths to match header 2137 selectCell.align = 'right'; 2138 2139 var checkbox = document.createElement("input"); 2140 Element.extend(checkbox); 2141 checkbox.id = "activityItemCheckbox" + index; 2142 checkbox.type = "checkbox"; 2143 checkbox.value = activityId; 2144 2145 // When checkbox is clicked, pass last 2 args to callback method, which is bounded to the display object 2146 // TODO pass the entire directory object and handle appropriately 2147 checkbox.observe('click', this.onActivitySelect.bind(this, checkbox.id, this.activityDirectory.getIds())); 2148 selectCell.appendChild(checkbox); 2149 2150 var nameCell = row.insertCell(tableIndex++); 2151 nameCell.width = '220'; 2152 2153 if( entry.duration != null) { 2154 var durationCell = row.insertCell(tableIndex++); 2155 durationCell.width = '210'; 2156 } 2157 2158 var statusCell = row.insertCell(tableIndex++); 2159 statusCell.id = "activityItemStatus" + index; 2160 2161 // Name and duration cells 2162 if( this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.tcxDir || 2163 this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.fitDir || 2164 this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.gpxDir) { 2165 nameCell.innerHTML = entry.name; 2166 2167 if( durationCell != null) { 2168 durationCell.innerHTML = entry.duration; 2169 } 2170 } 2171 else if( this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.crsDir ) { 2172 nameCell.innerHTML = activityId; 2173 } 2174 2175 if( this.options.afterTableInsert ) { 2176 this.options.afterTableInsert.call(this, index, entry, statusCell, checkbox, row, this.activityMatcher); 2177 } 2178 }, 2179 2180 /** 2181 * Adds the single row to the activity table header. The columns in the table 2182 * are determined by the data available in the directory, using an all or nothing 2183 * check. 2184 * @param directory {Garmin.ActivityDirectory} the activity directory to build the table header off of 2185 */ 2186 _addToActivityTableHeader: function(directory) { 2187 2188 var tableIndex = 0; 2189 2190 var row = this.activityTableHeader.insertRow(0); // append a new row to the table 2191 2192 var selectCell = row.insertCell(tableIndex++); 2193 selectCell.id = 'selectAllHeader'; 2194 selectCell.width = '40'; 2195 selectCell.align = 'left'; 2196 2197 var nameCell = row.insertCell(tableIndex++); 2198 nameCell.id = 'nameHeader'; 2199 nameCell.width = '220'; 2200 nameCell.align = 'left'; 2201 nameCell.innerHTML = this.options.getActivityDirectoryHeaderIdLabel.call(this); 2202 2203 if( directory.getFirstEntry().duration != null) { 2204 var durationCell = row.insertCell(tableIndex++); 2205 durationCell.id = 'durationHeader'; 2206 durationCell.width = '210'; 2207 durationCell.align = 'left'; 2208 durationCell.innerHTML = this.options.activityDirectoryHeaderDuration; 2209 } 2210 2211 var statusCell = row.insertCell(tableIndex++); 2212 statusCell.innerHTML = this.options.activityDirectoryHeaderStatus; 2213 2214 // Only display 'check all' box if there's no upload limit 2215 if( this.options.uploadMaximum < 1) { 2216 this.checkAllBox = document.createElement("input"); 2217 Element.extend(this.checkAllBox); 2218 this.checkAllBox.id = "checkAllBox"; 2219 this.checkAllBox.type = "checkbox"; 2220 2221 if( this.options.uploadMaximum == 0) { 2222 this.checkAllBox.hide(); 2223 } 2224 selectCell.appendChild(this.checkAllBox); 2225 2226 this.checkAllBox.onclick = function() { this._checkAllDirectory(); }.bind(this); 2227 } 2228 }, 2229 2230 /** Callback for enforcing upload selection limit. Called each time the user modifies selection. 2231 * @param String elementId - the ID of the input element selected to trigger this callback 2232 * @param Array activityDirectory - array of all activity IDs listed in the activity directory 2233 * @see uploadSelectionLimit 2234 */ 2235 onActivitySelect: function(elementId, activityDirectory) { 2236 var selectedCount = 0; 2237 for( var jj = 0; jj < activityDirectory.length; jj++) { 2238 if( $("activityItemCheckbox" + jj).checked == true){ 2239 selectedCount++; 2240 } 2241 } 2242 if( this.options.uploadMaximum > 0 ) { 2243 if( selectedCount > this.options.uploadMaximum ) { 2244 // Cancel the selection 2245 $(elementId).checked = false; 2246 alert( this.evaluateTemplate(this.options.uploadMaximumReached, {activities:this.options.uploadMaximum}) ); 2247 } 2248 } 2249 }, 2250 2251 /* Selects all checkboxes in the activity directory, which selects all activities to be read from the device. 2252 * uploadMaximum must be -1 or 0 (no limit) for this method to be called. 2253 */ 2254 _checkAllDirectory: function() { 2255 for( var boxIndex=0; boxIndex < this.activityDirectory.size(); boxIndex++ ) { 2256 $("activityItemCheckbox" + boxIndex).checked = this.checkAllBox.checked; 2257 } 2258 }, 2259 2260 /* Checks if any activities in directory listing are selected. Returns true if so, false otherwise. 2261 */ 2262 _directoryHasSelected: function() { 2263 for( var boxIndex=0; boxIndex < this.activityDirectory.size(); boxIndex++ ) { 2264 if ( $("activityItemCheckbox" + boxIndex).checked == true) { 2265 return true; 2266 } 2267 } 2268 2269 return false; 2270 }, 2271 2272 /* Lists the directory and returns summary data (# of entries). 2273 * @param entries {Garmin.ActivityDirectory} 2274 */ 2275 _listDirectory: function(activityDirectory) { 2276 // clear existing entries 2277 this._clearHtmlSelect(this.readTracksSelect); 2278 2279 this._addToActivityTableHeader(activityDirectory); 2280 2281 // loop through each entry 2282 var entries = activityDirectory.getEntries(); 2283 for (var i = 0; i < activityDirectory.size(); i++) { 2284 var entry = entries[i]; 2285 2286 // Directory entry 2287 if(this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.tcxDir 2288 || this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.crsDir 2289 || this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.fitDir 2290 || this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.gpxDir ){ 2291 this._addToActivityTable(i, entry); 2292 } 2293 } 2294 2295 return {tracks: activityDirectory.size()}; 2296 }, 2297 2298 _listActivities: function(activities) { 2299 var numOfRoutes = 0; 2300 var numOfTracks = 0; 2301 var numOfWaypoints = 0; 2302 2303 // clear existing entries 2304 this._clearHtmlSelect(this.readRoutesSelect); 2305 this._clearHtmlSelect(this.readTracksSelect); 2306 this._clearHtmlSelect(this.readWaypointsSelect); 2307 2308 // loop through each activity 2309 for (var i = 0; i < activities.length; i++) { 2310 var activity = activities[i]; 2311 var series = activity.getSeries(); 2312 // loop through each series in the activity 2313 for (var j = 0; j < series.length; j++) { 2314 var curSeries = series[j]; 2315 if (curSeries.getSeriesType() == Garmin.Series.TYPES.history) { 2316 // activity contains a series of type history, list the track 2317 this._listTrack(activity, curSeries, i, j); 2318 numOfTracks++; 2319 } else if (curSeries.getSeriesType() == Garmin.Series.TYPES.route) { 2320 // activity contains a series of type route, list the route 2321 this._listRoute(activity, curSeries, i, j); 2322 numOfRoutes++; 2323 } else if (curSeries.getSeriesType() == Garmin.Series.TYPES.waypoint) { 2324 // activity contains a series of type waypoint, list the waypoint 2325 this._listWaypoint(activity, curSeries, i, j); 2326 numOfWaypoints++; 2327 } 2328 } 2329 } 2330 if (this.options.showReadRoutesSelect) { 2331 if(numOfRoutes > 0) { 2332 Element.show(this.readRoutesElement); 2333 this.readRoutesSelect.disabled = false; 2334 this.displayTrack( this._seriesFromSelect(this.readRoutesSelect) ); 2335 } else { 2336 Element.hide(this.readRoutesElement); 2337 this.readRoutesSelect.disabled = true; 2338 } 2339 } 2340 if (this.options.showReadTracksSelect) { 2341 if(numOfTracks > 0) { 2342 Element.show(this.readTracksElement); 2343 this.readTracksSelect.disabled = false; 2344 this.displayTrack( this._seriesFromSelect(this.readTracksSelect) ); 2345 } else { 2346 Element.hide(this.readTracksElement); 2347 this.readTracksSelect.disabled = true; 2348 } 2349 } 2350 if (this.options.showReadWaypointsSelect) { 2351 if(numOfWaypoints > 0) { 2352 Element.show(this.readWaypointsElement); 2353 this.readWaypointsSelect.disabled = false; 2354 this.displayWaypoint( this._seriesFromSelect(this.readWaypointsSelect) ); 2355 } else { 2356 Element.hide(this.readWaypointsElement); 2357 this.readWaypointsSelect.disabled = true; 2358 } 2359 } 2360 return {routes: numOfRoutes, tracks: numOfTracks, waypoints: numOfWaypoints}; 2361 }, 2362 2363 /* Load route names into select UI component. 2364 * 2365 */ 2366 _listRoute: function(activity, series, activityIndex, seriesIndex) { 2367 // make sure the select component exists 2368 if (this.readRoutesSelect) { 2369 var routeName = activity.getAttribute(Garmin.Activity.ATTRIBUTE_KEYS.activityName); 2370 this.readRoutesSelect.options[this.readRoutesSelect.length] = new Option(routeName, activityIndex + "," + seriesIndex); 2371 } 2372 }, 2373 2374 /* Load track name into select UI component. 2375 * 2376 */ 2377 _listTrack: function(activity, series, activityIndex, seriesIndex) { 2378 // make sure the select component exists 2379 if (this.readTracksSelect) { 2380 var startDate = activity.getSummaryValue(Garmin.Activity.SUMMARY_KEYS.startTime).getValue(); 2381 var endDate = activity.getSummaryValue(Garmin.Activity.SUMMARY_KEYS.endTime).getValue(); 2382 var values = {date:startDate.getDateString(), duration:startDate.getDurationTo(endDate)} 2383 var trackName = this.evaluateTemplate(this.options.trackListing, values) 2384 this.readTracksSelect.options[this.readTracksSelect.length] = new Option(trackName, activityIndex + "," + seriesIndex); 2385 } 2386 }, 2387 2388 /* Load waypoint name into select UI component. 2389 * 2390 */ 2391 _listWaypoint: function(activity, series, activityIndex, seriesIndex) { 2392 // make sure the select component exists 2393 if (this.readWaypointsSelect) { 2394 var wptName = activity.getAttribute(Garmin.Activity.ATTRIBUTE_KEYS.activityName); 2395 this.readWaypointsSelect.options[this.readWaypointsSelect.length] = new Option(wptName, activityIndex + "," + seriesIndex); 2396 } 2397 }, 2398 2399 2400 /* Retreive the two index string value from the selected index. 2401 * Activities are stored in Select objects as strings with 2 2402 * numbers: "(index of array), (index of series)", for example: "1,1". 2403 * @param Select select - the Select DOM instance 2404 * @type Garmin.Series 2405 * @return a Series instance 2406 */ 2407 _seriesFromSelect: function(select) { 2408 var indexesStr = select.options[select.selectedIndex].value; 2409 var indexes = indexesStr.split(",", 2); 2410 var activity = this.activities[parseInt(indexes[0])]; 2411 var series = activity.getSeries()[parseInt(indexes[1])]; 2412 return series; 2413 }, 2414 2415 2416 /** Draws a simple line on the map using the Garmin.MapController. 2417 * 2418 * @param Garmin.Series series - that contains a track. 2419 */ 2420 displayTrack: function(series) { 2421 if(this.options.showReadGoogleMap) { 2422 this.readMapController.map.clearOverlays(); 2423 this.readMapController.drawTrack(series); 2424 } 2425 }, 2426 2427 /** Draws a point (usualy as a thumb tack) on the map using the Garmin.MapController. 2428 * @param Garmin.Series series - that contains the lat/lon position of the point. 2429 */ 2430 displayWaypoint: function(series) { 2431 if(this.options.showReadGoogleMap) { 2432 this.readMapController.map.clearOverlays(); 2433 this.readMapController.drawWaypoint(series); 2434 } 2435 }, 2436 2437 /** Clears overlays from map. 2438 * 2439 */ 2440 clearMapDisplay: function() { 2441 if(this.options.showReadGoogleMap) { 2442 this.readMapController.map.clearOverlays(); 2443 } 2444 }, 2445 2446 2447 ////////////////////////////// WRITE METHODS ////////////////////////////// 2448 2449 /** Writes any supported data to the device. 2450 * 2451 * Requires that the option writeDataType field be set correctly to any of the following values 2452 * located in Garmin.DeviceControl.FILE_TYPES: 2453 * 2454 * gpx, crs, binary, goals 2455 * 2456 * @throws InvalidTypeException 2457 * @see Garmin.DeviceDisplayDefaultOptions.writeDataType 2458 * @see Garmin.DeviceControl.FILE_TYPES 2459 */ 2460 writeToDevice: function() { 2461 var dataType = null; 2462 var supported = null; 2463 2464 // TODO remove this later 2465 // Backwards compatability for deprecated method 2466 if( this.options.writeDataType != null ) { 2467 this.options.writeDataTypes = new Array(); 2468 this.options.writeDataTypes[0] = this.options.writeDataType; 2469 } 2470 2471 for (var i = 0; i < this.options.writeDataTypes.length; i++) { 2472 dataType = this.options.writeDataTypes[i]; 2473 var deviceWriteSupport = this.getController().checkDeviceWriteSupport(dataType); 2474 //var deviceWriteSupport = this.getController().checkDeviceWriteSupport(this.options.writeDataType); 2475 2476 if (supported == null && deviceWriteSupport == true) { 2477 supported = dataType; 2478 2479 switch (dataType) { 2480 case Garmin.DeviceControl.FILE_TYPES.goals: 2481 case Garmin.DeviceControl.FILE_TYPES.gpx: 2482 case Garmin.DeviceControl.FILE_TYPES.crs: 2483 case Garmin.DeviceControl.FILE_TYPES.wkt: 2484 case Garmin.DeviceControl.FILE_TYPES.tcxProfile: 2485 case Garmin.DeviceControl.FILE_TYPES.nlf: 2486 var writeData = this.options.getWriteData(); 2487 var writeDataFileName = this.options.getWriteDataFileName(); 2488 this.getController().writeDataToDevice(dataType, writeData, writeDataFileName); 2489 break; 2490 // TODO Deprecated. Delete this fella. 2491 case Garmin.DeviceControl.FILE_TYPES.gpi: 2492 var xmlDescription = Garmin.GpiUtil.buildMultipleDeviceDownloadsXML(this.options.getGpiWriteDescription()); 2493 this.getController().downloadToDevice(xmlDescription); 2494 break; 2495 case Garmin.DeviceControl.FILE_TYPES.fitSettings: 2496 case Garmin.DeviceControl.FILE_TYPES.fitSport: 2497 case Garmin.DeviceControl.FILE_TYPES.fitCourse: 2498 case Garmin.DeviceControl.FILE_TYPES.binary: 2499 var xmlDescription = Garmin.GpiUtil.buildMultipleDeviceDownloadsXML(this.options.getBinaryWriteDescription()); 2500 this.getController().downloadToDevice(xmlDescription); 2501 break; 2502 default: 2503 var error = new Error(Garmin.DeviceControl.MESSAGES.invalidFileType + dataType); 2504 error.name = "InvalidTypeException"; 2505 this.handleException(error); 2506 } 2507 } 2508 } 2509 2510 // No supported types found, throw error 2511 if (deviceWriteSupport == false) { 2512 var error = new Error(this.evaluateTemplate(this.options.unsupportedWriteDataType, {dataType: dataType})); 2513 error.name = "UnsupportedDataTypeException"; 2514 this.handleException(error); 2515 } 2516 }, 2517 2518 /** Call-back triggered before writing to a device. 2519 * @see Garmin.DeviceControl 2520 * @event 2521 */ 2522 onStartWriteToDevice: function(json) { 2523 this.setStatus(this.options.writingToDevice); 2524 }, 2525 2526 /** Call-back triggered when write has been cancelled. 2527 * @see Garmin.DeviceControl 2528 * @event 2529 */ 2530 onCancelWriteToDevice: function(json) { 2531 this.setStatus(this.options.writingCancelled); 2532 }, 2533 2534 /** Call-back when the device already has a file with this name on it. Do we want to override? 1 is yes, 2 is no 2535 * @see Garmin.DeviceControl 2536 * @event 2537 */ 2538 onWaitingWriteToDevice: function(json) { 2539 if(confirm(json.message.getText())) { 2540 this.setStatus(this.options.overwritingFile); 2541 json.controller.respondToMessageBox(true); 2542 } else { 2543 this.setStatus(this.options.notOverwritingFile); 2544 json.controller.respondToMessageBox(false); 2545 } 2546 }, 2547 2548 /** 2549 * @event 2550 */ 2551 onProgressWriteToDevice: function(json) { 2552 if(this.options.showProgressBar) { 2553 this.updateProgressBar(this.progressBarDisplay, json.progress.getPercentage()); 2554 } 2555 this.setStatus( json.progress.percentage==100 ? this.options.dataDownloadProcessing : json.progress ); 2556 }, 2557 2558 /** 2559 * @event 2560 */ 2561 onFinishWriteToDevice: function(json) { 2562 this.setStatus(this.options.writtenToDevice); 2563 this.resetUI(); 2564 if (this.options.afterFinishWriteToDevice) { 2565 this.options.afterFinishWriteToDevice.call(this, json.success); 2566 } 2567 }, 2568 2569 ////////////////////////////// UTILITY METHODS ////////////////////////////// 2570 2571 /** Sets up the device control which handles most of the work that isn't user 2572 * interface related. The controller is lazy loaded the first time it is called. 2573 * Early calls must specify the unlock parameter, but read and write methods should 2574 * not because they should follow a call to findDevice. 2575 * 2576 * Also initializes the RemoteTransfer object used to transfer data to remote servers. 2577 * 2578 * @param Boolean optional request to unlock the plugin if not already done. 2579 */ 2580 getController: function(unlock) { 2581 if (!this.garminController) { 2582 try { 2583 this.garminController = new Garmin.DeviceControl(); 2584 this.garminController.register(this); 2585 this.garminController.setPluginRequiredVersion(this.options.pluginRequiredVersion); 2586 this.garminController.setPluginLatestVersion(this.options.pluginLatestVersion); 2587 this.garminController.validatePlugin(); 2588 } catch (e) { 2589 this.handleException(e); 2590 return null; 2591 } 2592 } 2593 if (!this.error && unlock && !this.isUnlocked()) { 2594 if(this.garminController.unlock(this.options.pathKeyPairsArray)) { 2595 this.setStatus(this.options.pluginUnlocked); 2596 } else { 2597 this.setStatus(this.options.pluginNotUnlocked); 2598 } 2599 this.garminRemoteTransfer = new Garmin.RemoteTransfer(); 2600 } 2601 return this.garminController; 2602 }, 2603 2604 /** Plugin unlock status 2605 * @returns Boolean 2606 */ 2607 isUnlocked: function() { 2608 return (this.garminController && this.garminController.isUnlocked()); 2609 }, 2610 2611 /** Sets options for this display object. Any options that are set will override 2612 * the default values. 2613 * 2614 * @see Garmin.DeviceDisplayDefaultOptions for possible options and default values. 2615 * @throws InvalidOptionException 2616 * @param Object options - Object with options. 2617 */ 2618 setOptions: function(options) { 2619 for(key in options || {}) { 2620 if ( ! (key in Garmin.DeviceDisplayDefaultOptions) ) { 2621 var err = new Error(key+" is not a valid option name, see Garmin.DeviceDisplayDefaultOptions"); 2622 err.name = "InvalidOptionException"; 2623 throw err; 2624 } 2625 } 2626 this.options = Object.extend(Garmin.DeviceDisplayDefaultOptions, options || {}); 2627 }, 2628 2629 /*Sets the size of the select to zero which essentially clears it from 2630 * any values. 2631 * 2632 * @param {HTMLElement} select DOM element 2633 */ 2634 _clearHtmlSelect: function(select) { 2635 if(select) { 2636 select.length = 0; 2637 } 2638 }, 2639 2640 /** Set status text if showStatusElement is visible. 2641 * @param String text to display. 2642 */ 2643 setStatus: function(statusText) { 2644 if(statusText == null) { 2645 statusText = ''; 2646 } 2647 if(this.options.showStatusElement) { 2648 2649 var statusDisplayString; 2650 2651 if( this.options.showDetailedStatus) { 2652 statusDisplayString = this._buildDescriptiveStatusString(statusText); 2653 } else { 2654 if( statusText.getText ) { 2655 2656 var text = statusText.getText(); 2657 if( text instanceof Array) { 2658 if( text.length == 0) { 2659 statusDisplayString = ''; 2660 } else { 2661 statusDisplayString = statusText.getText()[0]; 2662 } 2663 } 2664 } else { 2665 statusDisplayString = statusText; 2666 } 2667 } 2668 this.statusText.innerHTML = statusDisplayString; 2669 } 2670 }, 2671 2672 /** Builds a descriptive string from the status response (typically JSON, but also can be a plain ol' string). 2673 */ 2674 _buildDescriptiveStatusString: function(statusText) { 2675 if(statusText == null) { 2676 statusText = ''; 2677 } 2678 var resultString = statusText; 2679 2680 if( statusText.getTitle ) { 2681 resultString = statusText.getTitle() + "<br />"; 2682 } 2683 if( statusText.getText ) { 2684 resultString += statusText.getText(); 2685 } 2686 2687 return resultString; 2688 }, 2689 2690 /** Helper method. Evaluates Prototype's Template object, and returns the evaluated string. 2691 * 2692 * Templates should contain fields as in the following format: 'My cow has #{numSpots}' spots!' 2693 * Fields are in the following format: {numSpots: 22} 2694 * 2695 * @param String template - the template to evaluate 2696 * @param Hash fields - the fields referenced in template 2697 * @return the evaluated template string 2698 */ 2699 // TODO Move out to js-util 2700 evaluateTemplate: function(template, fields) { 2701 return new Template(template).evaluate(fields); 2702 }, 2703 2704 /** Makes progress bar visible. 2705 * @deprecated 2706 */ 2707 showProgressBar: function() { 2708 if(this.options.showStatusElement && this.options.showProgressBar) { 2709 Element.show(this.progressBar); 2710 } 2711 }, 2712 2713 /** Hides progress bar. 2714 */ 2715 hideProgressBar: function() { 2716 if(this.options.showStatusElement && this.options.showProgressBar) { 2717 Element.hide(this.progressBar); 2718 } 2719 }, 2720 2721 /** Update percentage representation of progress bar. 2722 * The progress bar starts out as full and then resets to empty. This is to 2723 * take care of extremely short transfers. 2724 * @param {Element} progressBarDisplay - the progress bar display DOM element to update. 2725 * @param int percentage completion: 0-100 2726 */ 2727 updateProgressBar: function(progressBarDisplay, value) { 2728 if(this.options.showStatusElement && this.options.showProgressBar && value) { 2729 var percent = (0 < value && value <= 100) ? value : 100; 2730 progressBarDisplay.style.width = percent + "%"; 2731 } 2732 }, 2733 2734 /** Update the progress text of the progress bar. 2735 * @param {Element} progressBarText - the progress bar text DOM element to update. 2736 * @param String the progress text to display near the progress bar 2737 */ 2738 updateProgressBarText: function(progressBarText, text) { 2739 if(this.options.showStatusElement && this.options.showProgressBar && text) { 2740 progressBarText.innerHTML = text; 2741 } 2742 }, 2743 2744 /** Call-back for asynchronous method exceptions. 2745 * @see Garmin.DeviceControl 2746 * @event 2747 */ 2748 onException: function(json) { 2749 this.handleException(json.msg); 2750 }, 2751 2752 /** Central exception dispatch method will delegate to options.customExceptionHandler 2753 * if defined or call defaultExceptionHandler otherwise. 2754 * @param {Error} error to process. 2755 */ 2756 handleException: function(error) { 2757 this.error = true; 2758 //console.debug("Display.handleException error="+error) 2759 if (this.options.customExceptionHandler) { 2760 this.options.customExceptionHandler.call(this, error); 2761 } else { 2762 this.defaultExceptionHandler(error); 2763 } 2764 }, 2765 /** Default exception handler method handles plug-in support/downloads/upgrades. 2766 * If options.showStatusElement is true then put error messages in the status div otherwise 2767 * put it in a alert popup. 2768 * @param {Error} error - error to process. 2769 */ 2770 defaultExceptionHandler: function(error) { 2771 var errorStatus; 2772 var hideFromBrowser = false; 2773 if(error.name == "BrowserNotSupportedException") { 2774 errorStatus = error.message; 2775 if (this.options.hideIfBrowserNotSupported) { 2776 hideFromBrowser = true; 2777 } 2778 } else if (error.name == "PluginNotInstalledException" || error.name == "OutOfDatePluginException") { 2779 errorStatus = error.message; 2780 errorStatus += " <a href=\""+Garmin.DeviceDisplay.LINKS.pluginDownload+"\" target=\"_blank\">" + this.options.downloadAndInstall + "</a>"; 2781 } else if (Garmin.PluginUtils.isDeviceErrorXml(error)) { 2782 errorStatus = Garmin.PluginUtils.getDeviceErrorMessage(error); 2783 } else if (error.name == "UnsupportedDeviceException" || error.name == "UnsupportedDataTypeException" || error.name == "RemoteTransferException") { 2784 errorStatus = error.message; 2785 } else { 2786 errorStatus = error.name + ": " + error.message; 2787 } 2788 2789 this.setStatus(errorStatus); 2790 this.resetUI(); 2791 //if no status UI div is defined, make sure the user sees the error 2792 if (!this.options.showStatusElement && !hideFromBrowser) { 2793 if (error.name == "PluginNotInstalledException" || error.name == "OutOfDatePluginException") { 2794 if (window.confirm(error.message+"\n" + this.options.installNow)) { 2795 window.open(Garmin.DeviceDisplay.LINKS.pluginDownload, "_blank"); 2796 } 2797 } else { 2798 alert(errorStatus); 2799 } 2800 } 2801 } 2802 2803 }; 2804 2805 /** Constant defining links referenced in the DeviceDisplay 2806 * 2807 * @struct {public} Garmin.DeviceDisplay.LINKS 2808 */ 2809 Garmin.DeviceDisplay.LINKS = { 2810 pluginDownload: "http://www.garmin.com/products/communicator/", 2811 /** Function that returns a direct download link based on the user's OS. 2812 */ 2813 // TODO Use this when we have a way of getting the latest version all the time. 2814 pluginDownloadDetectOs: function() { 2815 if( BrowserDetect.OS == "Windows") return "http://www.garmin.com/products/communicator/"; 2816 else if( BrowserDetect.OS == "Mac") return "http://www.garmin.com/products/communicator/"; 2817 else return "http://www.garmin.com/products/communicator/" 2818 } 2819 }; 2820 2821 /** A queue of filters to be applied to activities after data is 2822 * obtained from the device. Also used by display to determine 2823 * if the filtering process is finished; 2824 */ 2825 var garminFilterQueue = new Array(); 2826 2827 /** The default display options for the generated plug-in elements including 2828 * booleans for which sub-items to show. Override specific option values by 2829 * calling setOptions(optionsHash) on your instance of Garmin.DeviceDisplay 2830 * to customize your display options. 2831 * 2832 * @class Garmin.DeviceDisplayDefaultOptions 2833 * @name Garmin.DeviceDisplayDefaultOptions 2834 */ 2835 Garmin.DeviceDisplayDefaultOptions = 2836 /** @lends Garmin.DeviceDisplayDefaultOptions */ 2837 { 2838 // ================== Plugin unlock ====================== 2839 2840 /**Unlock plugin when user lands on containing page which may result in security or 2841 * plugin-not-installed messages. Set to false to supress plugin acivity 2842 * until user initiates an action. 2843 * 2844 * @default true 2845 * @type Boolean 2846 */ 2847 unlockOnPageLoad: true, 2848 2849 /**The array of strings that contain the unlock codes for the plugin. 2850 * @example [URL1,KEY1,URL2,KEY2] 2851 * Add as many url/key pairs as you'd like. 2852 * @type String[] 2853 */ 2854 pathKeyPairsArray: ["file:///C:/dev/", "bd04dc1f5e97a6ff1ea76c564d133b7e"], 2855 2856 2857 // ================== Global Options ====================== 2858 /**The class name used by various parts of the display to make 2859 * CSS styling easier. 2860 * 2861 * @see Garmin.DeviceDisplayDefaultOptions.statusElementId 2862 * @see Garmin.DeviceDisplayDefaultOptions.readDataElementId 2863 * @see Garmin.DeviceDisplayDefaultOptions.findDevicesElementId 2864 * @default "pluginElement" 2865 * @type String 2866 */ 2867 elementClassName: "pluginElement", 2868 2869 /**Display link instead of buttons. Currently this only applies to the 'Find Devices' button. 2870 * @default false 2871 * @type Boolean 2872 */ 2873 useLinks: false, 2874 2875 /**Action to take if browser is not supported: 2876 * if true on't display the application, 2877 * else if status bar is visible, display message, otherwise popup an alert dialog 2878 * @default false 2879 * @type Boolean 2880 */ 2881 hideIfBrowserNotSupported: false, 2882 2883 /**The function called when an error occurs. This is here to allow 2884 * custom error handling logic. <br/> 2885 * <br/> 2886 * The function should accept an arguement of type Error (Javascript 2887 * Error Object) and a second arguement for the DeviceDisplay instance.<br/> 2888 * <br/> 2889 * <br/>Error.name - the type of the error in a String format. 2890 * <br/>Error.message - the detailed message of the error.<br/> 2891 *<br/> 2892 * Some Errors:<br/> 2893 * PluginNotInstalledException - the plugin is not installed<br/> 2894 * OutOfDatePluginException - the plugin is out of date<br/> 2895 * BrowserNotSupportedException - the browser is not support by the site<br/> 2896 * @example 2897 * function(error, display){ display.defaultExceptionHandler(error); } 2898 * @type function 2899 * @function 2900 */ 2901 customExceptionHandler: null, 2902 2903 /**Class name to add for all buttons/links that perform an action 2904 * @default "actionButton" 2905 * @type String 2906 */ 2907 actionButtonClassName: "actionButton", 2908 2909 /**DEPRECATED - Use autoHideUnusedElements instead! 2910 * Auto-hides read elements when they are not in use. This is currently used for the Upload Activities use case. 2911 * @default false 2912 * @type String 2913 */ 2914 autoHideUnusedReadElements: false, 2915 2916 /**Auto-hides elements when they are not in use. This is used to simulate a UI with different screens, until we design a better way to do this. 2917 * @default false 2918 * @type String 2919 */ 2920 autoHideUnusedElements: false, 2921 2922 /**The choice to display the about ('Powered by...') element 2923 * @default true 2924 * @type Boolean 2925 */ 2926 showAboutElement: true, 2927 2928 /** Optional - Restricts transfer of data to specifically listed devices ONLY. This is an array list of 2929 * device part numbers, which can be found in the GarminDevice.XML. 2930 * @example ["006-B1018-00"] // Forerunner 310 only 2931 * @type Array 2932 */ 2933 restrictByDevice: [], 2934 2935 /** Sets the required version of the Communicator Plugin that is compatible with the given application. 2936 * This value is set using an array of the major and minor build numbers. * 2937 * @example version 2.2.0.1 is given by [2,2,0,1]. 2938 * @default [2,2,0,1] (for version 2.2.0.1) 2939 * @type Array 2940 */ 2941 pluginRequiredVersion: [2,2,0,1], 2942 2943 /** Sets the latest plugin version number. This represents the latest version available for download at Garmin. 2944 * We will attempt to keep the default value of this up to date with each API release, but this is not guaranteed, 2945 * so set this to be safe or if you don't want to upgrade to the latest API. 2946 * 2947 * @param reqVersionArray Array The latest version to set to. In the format [versionMajor, versionMinor, buildMajor, buildMinor] 2948 * i.e. [2,2,0,1] 2949 * @default [2,5,1,0] (for version 2.5.1.0) 2950 * @type Array 2951 */ 2952 pluginLatestVersion: [2,5,2,0], 2953 2954 // ================== Status Element Options ====================== 2955 /**The choice to display the feedback regarding the communications 2956 * with the device. 2957 * 2958 * @default true 2959 * @type Boolean 2960 */ 2961 showStatusElement: true, 2962 2963 /** The choice to display more detailed status when transferring data to and from device. 2964 * @default false 2965 * @type Boolean 2966 */ 2967 showDetailedStatus: false, 2968 2969 /**The id of the HTML element where the statusBox is to be rendered. 2970 * @type String 2971 * @default "statusBox" 2972 */ 2973 statusElementId: "statusBox", 2974 2975 /**The id of the HTML element where the status text messages are to be displayed. 2976 * @default "statusText" 2977 * @type String 2978 */ 2979 statusTextId: "statusText", 2980 2981 /**The progress bar is a graphical percentage bar used to display 2982 * the amount of reading/writing is complete. 2983 * @default true 2984 * @type Boolean 2985 */ 2986 showProgressBar: true, 2987 2988 /** The id of the HTML element where the progress status text is to be displayed. 2989 * This element will be a child of the statusText element. 2990 * @default "progressText" 2991 * @type String 2992 */ 2993 progressTextId: "progressText", 2994 2995 /** The class name for the progress status text. 2996 * This element will be a child of the statusText element. 2997 * @type String 2998 */ 2999 progressTextClass: "progressTextClass", 3000 3001 /**The class name for the progress bar container element. 3002 * @default 'progressBarClass' 3003 * @type String 3004 */ 3005 progressBarClass: "progressBarClass", 3006 3007 /** The background of the progress bar. This stays static during transfer. 3008 * @default 'progressBarBackClass' 3009 * @type String 3010 */ 3011 progressBarBackClass: "progressBarBackClass", 3012 3013 /** The class name for the dynamic progress bar that is overlaid 3014 * on top of the progressBar element. This controls 3015 * the part that 'moves'. 3016 * @default 'progressBarDisplayClass' 3017 * @type String 3018 */ 3019 progressBarDisplayClass: "progressBarDisplayClass", 3020 3021 /**The container for the progress bar. 3022 * @default 'progressBar' 3023 * @type String 3024 */ 3025 progressBarId: "progressBar", 3026 3027 /** The background of the progress bar. This stays static during transfer. 3028 * @default 'progressBarBack' 3029 * @type String 3030 */ 3031 progressBarBackId: "progressBarBack", 3032 3033 /**The id of the displayed progress element. This is dynamic during transfer. 3034 * @default 'progressBarDisplay' 3035 * @type String 3036 */ 3037 progressBarDisplayId: "progressBarDisplay", 3038 3039 /** The class name for the progress bar text. 3040 * @default progressBarTextClass 3041 * @type String 3042 */ 3043 progressBarTextClass: "progressBarTextClass", 3044 3045 /** The id of the progress bar text. This is generally the percentage 3046 * value text displayed, but could also be in the context of what is being transferred 3047 * (i.e. 1 of 5 activities). 3048 * @default progressBarText 3049 * @type String 3050 */ 3051 progressBarTextId: "progressBarText", 3052 3053 // Upload Progress bar 3054 3055 /** The id of the HTML element where the upload progress status text is to be displayed. 3056 * This element will be a child of the statusText element. 3057 * @type String 3058 * @default "uploadProgressText" 3059 */ 3060 uploadProgressTextId: "uploadProgressText", 3061 3062 /** The class name for the uploadProgress status text. 3063 * This element will be a child of the statusText element. 3064 * @type String 3065 * @default "uploadProgressTextClass" 3066 */ 3067 uploadProgressTextClass: "uploadProgressTextClass", 3068 3069 /**The class name for the uploadProgress bar container element. 3070 * @default 'uploadProgressBarClass' 3071 * @type String 3072 */ 3073 uploadProgressBarClass: "uploadProgressBarClass", 3074 3075 /** The background of the uploadProgress bar. This stays static during transfer. 3076 * @default 'uploadProgressBarBackClass' 3077 * @type String 3078 */ 3079 uploadProgressBarBackClass: "uploadProgressBarBackClass", 3080 3081 /** The class name for the dynamic uploadProgress bar that is overlaid 3082 * on top of the uploadProgressBar element. This controls 3083 * the part that 'moves'. 3084 * @default 'uploadProgressBarDisplayClass' 3085 * @type String 3086 */ 3087 uploadProgressBarDisplayClass: "uploadProgressBarDisplayClass", 3088 3089 /**The container for the uploadProgress bar. 3090 * @default 'uploadProgressBar' 3091 * @type String 3092 */ 3093 uploadProgressBarId: "uploadProgressBar", 3094 3095 /** The background of the uploadProgress bar. This stays static during transfer. 3096 * @default 'uploadProgressBarBack' 3097 * @type String 3098 */ 3099 uploadProgressBarBackId: "uploadProgressBarBack", 3100 3101 /**The id of the displayed uploadProgress element. This is dynamic during transfer. 3102 * @default 'uploadProgressBarDisplay' 3103 * @type String 3104 */ 3105 uploadProgressBarDisplayId: "uploadProgressBarDisplay", 3106 3107 /** The class name for the uploadProgress bar text. 3108 * @default uploadProgressBarTextClass 3109 * @type String 3110 */ 3111 uploadProgressBarTextClass: "uploadProgressBarTextClass", 3112 3113 /** The id of the uploadProgress bar text. This is generally the percentage 3114 * value text displayed, but could also be in the context of what is being transferred 3115 * (i.e. 1 of 5 activities). 3116 * @default uploadProgressBarText 3117 * @type String 3118 */ 3119 uploadProgressBarTextId: "uploadProgressBarText", 3120 3121 /** The text to display above the upload progress bar. This is a static string. 3122 * @default "Uploading activities..." 3123 * @type String 3124 */ 3125 uploadingStatusText: "Uploading activities...", 3126 3127 /** Templated string used to display the upload progress status. 3128 * The template parameters currentUpload and totalUploads must be included 3129 * to work properly, as the default value does. 3130 * 3131 * @default #{currentUpload} of #{totalUploads} completed. 3132 * @type String 3133 */ 3134 uploadProgressStatusText: '#{currentUpload} of #{totalUploads} completed.', 3135 3136 //=================== Find Devices Element Options =============== 3137 3138 /**Choice to display the find devices area that will search for connected devices. 3139 * @type Boolean 3140 * @default true 3141 */ 3142 showFindDevicesElement: true, 3143 3144 /**Choice to display the find devices area that will search for connected devices when the page loads. 3145 * @type Boolean 3146 * @default true 3147 */ 3148 showFindDevicesElementOnLoad: true, 3149 3150 /**Looks for devices as soon as the page is loaded and the plugin unlocked. 3151 * This might be particularly annoying in many situations since the plugin 3152 * requires the user to authorize access to device information via a 3153 * dialog box. 3154 * @type Boolean 3155 * @default false 3156 */ 3157 autoFindDevices: false, 3158 3159 /**Controls the view of the buttons related to find devices (find & cancel) if 3160 * based on if the plugin finds one or more devices. 3161 * When set to <b>false</b> and 3162 * used with {@link showDeviceButtonsOnLoad} =false 3163 * and {@link autoFindDevices} =true these buttons will only 3164 * show up if a device is not found (minimizing confusion for the user). 3165 * <p> 3166 * More granular control is provided on each of the device buttons 3167 * {@link showFindDevicesButton} and {@link showCancelFindDevicesButton} . 3168 * </p> 3169 * @see Garmin.DeviceDisplayDefaultOptions.showFindDevicesElement 3170 * @type Boolean 3171 * @default true 3172 */ 3173 showDeviceButtonsOnFound: true, 3174 3175 /**If true the buttons will show when the page is rendered. 3176 * If false, the buttons will not be displayed until the plugin detects that a device is not found. 3177 * If you choose not to see the buttons at all (regardless if device is found or not) then 3178 * {@link showFindDevicesElement} should be set to false. 3179 * 3180 * @see Garmin.DeviceDisplayDefaultOptions.showDeviceButtonsOnFound 3181 * @see Garmin.DeviceDisplayDefaultOptions.showFindDevicesElement 3182 * @type Boolean 3183 * @default true 3184 */ 3185 showDeviceButtonsOnLoad: true, 3186 3187 /**Allows granular control to hide the find devices button independent 3188 * of the {@link showCancelFindDevicesButton} cancel button contol. 3189 * @type Boolean 3190 * @default true 3191 */ 3192 showFindDevicesButton: true, 3193 3194 /**The id referencing the HTML container around the find devices buttons. 3195 * This is useful for CSS customizations. 3196 * <p> 3197 * @default deviceBox 3198 * </p> 3199 * @type String 3200 * @default "deviceBox" 3201 */ 3202 findDevicesElementId: "deviceBox", 3203 3204 /**The id referencing the find devices button. This is useful for 3205 * CSS customizations. 3206 * 3207 * @type String 3208 * @default findDevicesButton 3209 */ 3210 findDevicesButtonId: "findDevicesButton", 3211 3212 /**The text for the find device button. 3213 * @type String 3214 * @default "Find Devices" 3215 */ 3216 findDevicesButtonText: "Find Devices", 3217 3218 /**Controls the view of the cancel find devices button. When 3219 * set to <b>false</b> the button will never show. When 3220 * set to <b>true</b> the button's behavior will depend on other 3221 * settings such as {@link showFindDevicesButton} , 3222 * {@link showDeviceButtonsOnFound} , {@link showDeviceButtonsOnLoad} , 3223 * and {@link showFindDevicesElement} . 3224 * @default false 3225 * @type Boolean 3226 */ 3227 showCancelFindDevicesButton: false, 3228 3229 /**The id referencing the cancel find devices button. This is useful for 3230 * CSS customizations. 3231 * @default cancelFindDevicesButton 3232 * @type String 3233 */ 3234 cancelFindDevicesButtonId: "cancelFindDevicesButton", 3235 3236 /**The text for the cancel find device button. 3237 * @type String 3238 * @default "Cancel Find Devices" 3239 */ 3240 cancelFindDevicesButtonText: "Cancel Find Devices", 3241 3242 /**Controls the view of the device select box. 3243 * When set to <b>true</b> the select device box will show even when only 3244 * one device is found. 3245 * When set to <b>false</b> the select device box will hide when only 3246 * one device is found. 3247 * When {@link showFindDevicesElement} is set to false, the device select 3248 * box will never show. 3249 * @default false 3250 * @see Garmin.DeviceDisplayDefaultOptions.showDeviceSelectNoDevice 3251 * @see Garmin.DeviceDisplayDefaultOptions.showDeviceSelectOnLoad 3252 * @see Garmin.DeviceDisplayDefaultOptions.showFindDevicesElement 3253 * @type Boolean 3254 */ 3255 showDeviceSelectOnSingle: false, 3256 3257 /**Controls the view of the device select box. 3258 * When set to <b>true</b> the select device box will show even when 3259 * no device is found. 3260 * When set to <b>false</b> the select device box will hide when 3261 * no device is found. 3262 * When {@link showFindDevicesElement} is set to false, the device select 3263 * box will never show. 3264 * 3265 * @default true 3266 * 3267 * @see Garmin.DeviceDisplayDefaultOptions.showDeviceSelectOnSingle 3268 * @see Garmin.DeviceDisplayDefaultOptions.showDeviceSelectOnLoad 3269 * @see Garmin.DeviceDisplayDefaultOptions.showFindDevicesElement 3270 * @type Boolean 3271 */ 3272 showDeviceSelectNoDevice: false, 3273 3274 /**Controls the view of the device select box. 3275 * When set to <b>true</b> the select device box will show when 3276 * the display loads. 3277 * When set to <b>false</b> the select device box will never be visible. 3278 * When {@link showFindDevicesElement} is set to false, the device select 3279 * box will never show. 3280 * 3281 * @default true 3282 * 3283 * @see Garmin.DeviceDisplayDefaultOptions.showDeviceSelectOnSingle 3284 * @see Garmin.DeviceDisplayDefaultOptions.showDeviceSelectNoDevice 3285 * @see Garmin.DeviceDisplayDefaultOptions.showFindDevicesElement 3286 * @type Boolean 3287 */ 3288 showDeviceSelectOnLoad: true, 3289 3290 /**When more than one device is detected automaticly pick the first device. 3291 * This allows single button interfaces to avoid having to ask the user to 3292 * choose the device and keeps the deviceSelect hidden. 3293 * 3294 * @default false 3295 * 3296 * @see Garmin.DeviceDisplayDefaultOptions.showDeviceSelectOnSingle 3297 * @see Garmin.DeviceDisplayDefaultOptions.showDeviceSelectNoDevice 3298 * @see Garmin.DeviceDisplayDefaultOptions.showFindDevicesElement 3299 * @type Boolean 3300 */ 3301 autoSelectFirstDevice: false, 3302 3303 //=================== Upload UI Options =============== 3304 3305 /**The id referencing the device select box. This is useful for 3306 * CSS customizations. 3307 * 3308 * @default deviceSelectBox 3309 * @type String 3310 */ 3311 deviceSelectElementId: "deviceSelectBox", 3312 3313 /**The label for the device select box. Shows up next to the 3314 * device select box. 3315 * @type String 3316 * @default "Devices: " 3317 */ 3318 deviceSelectLabel: "Devices: ", 3319 3320 /**The id referencing the device select box label. This is useful for 3321 * CSS customizations. 3322 * 3323 * @default deviceSelectLabel 3324 * @type String 3325 */ 3326 deviceSelectLabelId: "deviceSelectLabel", 3327 3328 /** The class name referencing the device select element. This is useful for CSS customizations. 3329 * 3330 * @default deviceSelectClass 3331 * @type String 3332 */ 3333 deviceSelectClass: "deviceSelectClass", 3334 3335 /**The id referencing the device select element. This is useful for 3336 * CSS customizations. 3337 * 3338 * @default deviceSelect 3339 * @type String 3340 */ 3341 deviceSelectId: "deviceSelect", 3342 3343 /**The id referencing the element that displays what device was selected. This is useful for CSS customizations. 3344 * 3345 * @default deviceSelected 3346 * @type String 3347 */ 3348 deviceSelectedElementId: "deviceSelected", 3349 3350 /**The label for the device selected element. This label preceeds the device selected element. This is useful for CSS customizations. 3351 * 3352 * @default "Previewing " 3353 * @type String 3354 */ 3355 deviceSelectedLabel: "Previewing ", 3356 3357 /**The id referencing the label for the element that displays what device was selected. This is useful for CSS customizations. 3358 * 3359 * @default deviceSelectedLabel 3360 * @type String 3361 */ 3362 deviceSelectedLabelId: "deviceSelectedLabel", 3363 3364 /**The status text that is displayed when no devices are found. The Find Devices button is 3365 * displayed to allow the user to try again. To change the button text, set findDevicesButtonText. 3366 * 3367 * @type String 3368 * @default "No devices found." 3369 */ 3370 noDeviceDetectedStatusText: "No devices found.", 3371 /**The status text that prepends itself to the device name when a single device is found. 3372 * 3373 * @type String 3374 * @default "Found " 3375 */ 3376 singleDeviceDetectedStatusText: "Found ", 3377 3378 /**The function called when device search completes successfully or unsuccessfully. 3379 * The function should have two arguments: 3380 * devices {Array<Garmin.Device>} - an array of device descriptors or an empty array in none were found. 3381 * display {Garmin.DeviceDisplay} - the current instance of the DeveiceDisplay 3382 * @example function(devices){...} 3383 * @type function 3384 * @function 3385 */ 3386 afterFinishFindDevices: null, 3387 3388 /** The function called after all item uploads complete successfully or unsuccessfully. 3389 * The function will have one argument: 3390 * display {Garmin.DeviceDisplay - the current instance of the DeviceDisplay 3391 * @example 3392 * function(display) {...} 3393 * @type function 3394 * @function 3395 */ 3396 afterFinishUploads: null, 3397 3398 // ================== Read Element Options ====================== 3399 /**Start reading data from the device when one or more device(s) 3400 * is found. 3401 * 3402 * @default false 3403 * 3404 * @see Garmin.DeviceDisplayDefaultOptions.autoFindDevices 3405 * @see Garmin.DeviceDisplayDefaultOptions.autoWriteData 3406 * @type Boolean 3407 */ 3408 autoReadData: false, 3409 3410 /**Display the user interface associated with reading from 3411 * a connected device. 3412 * 3413 * @default true 3414 * @type Boolean 3415 */ 3416 showReadDataElement: true, 3417 3418 /**Controls the view of the read data button. When 3419 * set to <b>false</b> the button will never show. When 3420 * set to <b>true</b> the button's behavior will depend on other 3421 * settings such as {@link showReadDataElement} . 3422 * 3423 * @default true 3424 * @type Boolean 3425 */ 3426 showReadDataButton: true, 3427 3428 /**Controls the view of the read data element. When 3429 * set to <b>true</b> the element will only show after a 3430 * device has been found. When set to <b>false</b> the 3431 * element will show on page load. 3432 * Behavior will depend on other settings such as 3433 * and {@link showReadDataElement} . 3434 * 3435 * @default false 3436 * @type Boolean 3437 */ 3438 showReadDataElementOnDeviceFound: false, 3439 3440 /**The id referencing the box containing read elements. This is 3441 * useful for CSS customizations. 3442 * 3443 * @default readBox 3444 * @type String 3445 */ 3446 readDataElementId: "readBox", 3447 3448 /**The id referencing the read data button. This is useful for 3449 * CSS customizations. 3450 * 3451 * @default readDataButton 3452 * @type String 3453 */ 3454 readDataButtonId: "readDataButton", 3455 3456 /**The text on the read button. 3457 * 3458 * @type String 3459 */ 3460 readDataButtonText: "Get Data", 3461 3462 /**Controls the view of the cancel read data button. When 3463 * set to <b>false</b> the button will never show. When 3464 * set to <b>true</b> the button's behavior will depend on other 3465 * settings such as {@link showReadDataButton} , 3466 * and {@link showReadDataElement} . 3467 * 3468 * @default true 3469 * @type Boolean 3470 */ 3471 showCancelReadDataButton: true, 3472 3473 /**The id referencing the cancel read data button. This is 3474 * useful for CSS customizations. 3475 * 3476 * @default cancelReadDataButton 3477 * @type String 3478 */ 3479 cancelReadDataButtonId: "cancelReadDataButton", 3480 3481 /**The text on the cancel read button. 3482 * 3483 * @type String 3484 */ 3485 cancelReadDataButtonText: "Cancel Get Data", 3486 3487 /**The status text that is displayed when user cancels the 3488 * read progress. 3489 * 3490 * @type String 3491 */ 3492 cancelReadStatusText: "Read cancelled", 3493 3494 /**Controls the view of the device select box. 3495 * When set to <b>true</b> the select device box will show when 3496 * the display loads. 3497 * When set to <b>false</b> the select device box will hide when 3498 * the display loads. 3499 * When {@link showReadDataElement} is set to false, the results select 3500 * box will never show. 3501 * 3502 * @default false 3503 * 3504 * @see Garmin.DeviceDisplayDefaultOptions.showReadDataElement 3505 * @type Boolean 3506 */ 3507 showReadResultsSelectOnLoad: false, 3508 3509 /**The class to set for select lists that are displaying results 3510 * from a read operation. This is useful for CSS customizations. 3511 * 3512 * @default readResultsSelect 3513 * @type String 3514 */ 3515 readResultsSelectClass: "readResultsSelect", 3516 3517 /**The class to set for results elements. This is useful for CSS customizations. 3518 * 3519 * @default readResultsElement 3520 * @type String 3521 */ 3522 readResultsElementClass: "readResultsElement", 3523 3524 /**Display the route select dropdown. When 3525 * <@link showReadDataElement> is set to false, the select 3526 * track dropdown will not show. 3527 * 3528 * @default true 3529 * @see Garmin.DeviceDisplayDefaultOptions.showReadDataElement 3530 * @type Boolean 3531 */ 3532 showReadRoutesSelect: true, 3533 3534 /**The id referencing the read routes element. This is 3535 * useful for CSS customizations. 3536 * 3537 * @default readRoutesElement 3538 * @type String 3539 */ 3540 readRoutesElementId : "readRoutesElement", 3541 3542 /**The id referencing the route select dropdown. This is 3543 * useful for CSS customizations. 3544 * 3545 * @default readRoutesSelect 3546 * @type String 3547 */ 3548 readRoutesSelectId: "readRoutesSelect", 3549 3550 /**The label for the read routes select box. Shows up next to the 3551 * read routes select box. 3552 * 3553 * @type String 3554 */ 3555 readRoutesSelectLabel: "Routes: ", 3556 3557 /**The id referencing the read routes select box label. This is useful for 3558 * CSS customizations. 3559 * 3560 * @default readRoutesSelectLabel 3561 * @type String 3562 */ 3563 readRoutesSelectLabelId: "readRoutesSelectLabel", 3564 3565 /** The id referencing the button for uploading selected activities button. This is useful for CSS customizations. 3566 * 3567 * @default readSelectedButton 3568 * @type String 3569 */ 3570 readSelectedButtonId: "readSelectedButton", 3571 3572 /** The text label for the upload selected data button. This is useful for CSS customizations. 3573 * 3574 * @default Upload Selected 3575 * @type String 3576 */ 3577 readSelectedButtonText: "Upload Selected", 3578 3579 /**Display the track select dropdown. When 3580 * <@link showReadDataElement> is set to false, the select 3581 * track dropdown will not show. 3582 * 3583 * @default true 3584 * @see Garmin.DeviceDisplayDefaultOptions.showReadDataElement 3585 * @type Boolean 3586 */ 3587 showReadTracksSelect: true, 3588 3589 /**The id referencing the read tracks element. This is 3590 * useful for CSS customizations. 3591 * 3592 * @default readTracksElement 3593 * @type String 3594 */ 3595 readTracksElementId: "readTracksElement", 3596 3597 /**The id referencing the track select dropdown. This is 3598 * useful for CSS customizations. 3599 * 3600 * @default readTracksSelect 3601 * @type String 3602 */ 3603 readTracksSelectId: "readTracksSelect", 3604 3605 /**The label for the read tracks select box. Shows up next to the 3606 * read tracks select box. 3607 * 3608 * @type String 3609 */ 3610 readTracksSelectLabel: "Tracks: ", 3611 3612 /**The id referencing the read tracks select box label. This is useful for 3613 * CSS customizations. 3614 * 3615 * @default deviceSelectLabel 3616 * @type String 3617 */ 3618 readTracksSelectLabelId: "readTracksSelectLabel", 3619 3620 /**The id referencing the read tracks element. This is 3621 * useful for CSS customizations. 3622 * 3623 * @default readTracksElement 3624 * @type String 3625 */ 3626 readWaypointsElementId: "readWaypointsElement", 3627 3628 /**Display the waypoint select dropdown. When 3629 * <@link showReadDataElement> is set to false, the select 3630 * waypoint dropdown will not show. 3631 * 3632 * @default true 3633 * @see Garmin.DeviceDisplayDefaultOptions.showReadDataElement 3634 * @type Boolean 3635 */ 3636 showReadWaypointsSelect: true, 3637 3638 /**The id referencing the waypoint select dropdown. This is 3639 * useful for CSS customizations. 3640 * 3641 * @default readWaypointsSelect 3642 * @type String 3643 */ 3644 readWaypointsSelectId: "readWaypointsSelect", 3645 3646 /**The label for the read waypoints select box. Shows up next to the 3647 * read tracks select box. 3648 * 3649 * @type String 3650 */ 3651 readWaypointsSelectLabel: "Waypoints: ", 3652 3653 /**The id referencing the read waypoints select box label. This is useful for 3654 * CSS customizations. 3655 * 3656 * @default readWaypointsSelectLabel 3657 * @type String 3658 */ 3659 readWaypointsSelectLabelId: "readWaypointsSelectLabel", 3660 3661 /**Display Google map to show tracks and laps that have been read. When <@link showReadDataElement> is 3662 * set to false, the Google map will not show. 3663 * 3664 * @default false 3665 * @see Garmin.DeviceDisplayDefaultOptions.showReadDataElement 3666 * @type Boolean 3667 */ 3668 showReadGoogleMap: false, 3669 3670 /**The id referencing the google map display. This is 3671 * useful for CSS customizations. 3672 * 3673 * @default readMap 3674 * @type String 3675 */ 3676 readGoogleMapId: "readMap", 3677 3678 /**DEPRECATED - Use {@link Garmin.DeviceDisplayDefaultOptions.readDataTypes} Tells the plug-in what data type to read from the device. Options for this 3679 * are currently constants listed in {@link Garmin.DeviceControl.FILE_TYPES} , 3680 * and the values are: crs, gpx, gpi, or null to skip this option altogether and get the default data type from 3681 * the device. 3682 * <p> 3683 * This property works in conjunction with the following functions, based on the datatype: 3684 * <p> 3685 * For CRS and GPX: Define the getWriteData() and getWriteDataFileName() functions in your options section. 3686 * <p> 3687 * For GPI: Define the getWriteData() and getWriteDataFileName() functions in your options section. 3688 * The getGpiWriteDescription() function replaces getWriteData(). 3689 * <p> 3690 * @default Garmin.DeviceControl.FILE_TYPES.gpx 3691 * @see Garmin.DeviceDisplayDefaultOptions.showReadDataElement 3692 * @see Garmin.DeviceControl.FILE_TYPES 3693 * @see Garmin.DeviceDisplayDefaultOptions.readDataTypes 3694 * @type String 3695 * @deprecated 3696 */ 3697 readDataType: null, 3698 3699 /** OVERRIDES readDataType! 3700 * 3701 * Tells the plug-in what data types to read from the device, in order of preference. Options for this 3702 * are currently constants listed in {@link Garmin.DeviceControl.FILE_TYPES}. 3703 * 3704 * @see Garmin.DeviceDisplayDefaultOptions.showReadDataElement 3705 * @see Garmin.DeviceControl.FILE_TYPES 3706 * @default TCX, GPX 3707 * @example ["FitnessHistory", "GPSData"] 3708 * @type Array 3709 */ 3710 readDataTypes: ["GPSData"], 3711 3712 /**Display the dropdown select box for selecting what type 3713 * of data to read from the device. When 3714 * <@link showReadDataElement> is set to false, 3715 * this device type select will not show. 3716 * 3717 * @default false 3718 * @see Garmin.DeviceDisplayDefaultOptions.showReadDataElement 3719 * @type Boolean 3720 */ 3721 showReadDataTypesSelect: false, 3722 3723 /**The id referencing the data type select. This is 3724 * useful for CSS customizations. 3725 * 3726 * @default readDataTypesSelect 3727 * @type String 3728 */ 3729 readDataTypesSelectId: "readDataTypesSelect", 3730 3731 /**The function called when data is successfully read from 3732 * the device. The function should have three arguements:<br/> 3733 * <br/> 3734 * dataString - the xml received from the device in String format<br/> 3735 * dataDoc - the xml received from the device in Document format<br/> 3736 * extension - the file type extension of the data, used to determine 3737 * the type of data received.<br/> 3738 * activities - list of <@link Garmin.Activity> parsed from the xml.<br/> 3739 * display - the display object<br/> 3740 * @see Garmin.DeviceDisplayDefaultOptions.Garmin.Activity 3741 * @example function(dataString, dataDoc, extension, activities, display){...} 3742 * @type function 3743 * @function 3744 */ 3745 afterFinishReadFromDevice: null, 3746 3747 /**Load tracks even if they don't have a timestamp (technically these are 3748 * routes). Set to false if you need to do synchronization with existing 3749 * track log database (like MotionBased does). 3750 * 3751 * @default true 3752 * @see Garmin.DeviceDisplayDefaultOptions._listTracks 3753 * @type Boolean 3754 */ 3755 loadTracksWithoutATimestamp: true, 3756 3757 // ================== Write Element Options ====================== 3758 3759 /**Start writing data to the device when one or more device(s) 3760 * is found. 3761 * 3762 * @default false 3763 * 3764 * @see Garmin.DeviceDisplayDefaultOptions.autoFindDevices 3765 * @see Garmin.DeviceDisplayDefaultOptions.autoReadData 3766 * @type Boolean 3767 */ 3768 autoWriteData: false, 3769 3770 /**Display the user interface associated with writing to 3771 * a connected device. 3772 * 3773 * @default false 3774 * @type Boolean 3775 */ 3776 showWriteDataElement: false, 3777 3778 /**Controls the view of the write data element. When 3779 * set to <b>true</b> the element will only show after a 3780 * device has been found. When set to <b>false</b> the 3781 * element will show on page load. 3782 * Behavior will depend on other settings such as 3783 * and {@link showWriteDataElement} . 3784 * 3785 * @default false 3786 * @type Boolean 3787 */ 3788 showWriteDataElementOnDeviceFound: false, 3789 3790 /**The id referencing the box containing write elements. This is 3791 * useful for CSS customizations. 3792 * 3793 * @default writeBox 3794 * @type String 3795 */ 3796 writeDataElementId: "writeBox", 3797 3798 /**The id referencing the write data button. This is 3799 * useful for CSS customizations. 3800 * 3801 * @default writeDataButton 3802 * @type String 3803 */ 3804 writeDataButtonId: "writeDataButton", 3805 3806 /**The text on the write button. 3807 * 3808 * @type String 3809 */ 3810 writeDataButtonText: "Write", 3811 3812 /**Controls the view of the cancel write data button. When 3813 * set to <b>false</b> the button will never show. When 3814 * set to <b>true</b> the button's behavior will depend on other 3815 * settings such as {@link showWriteDataButton} , 3816 * and {@link showWriteDataElement} . 3817 * 3818 * @default true 3819 * @type Boolean 3820 */ 3821 showCancelWriteDataButton: true, 3822 3823 /**The id referencing the cancel write data button. This is 3824 * useful for CSS customizations. 3825 * 3826 * @default cancelWriteDataButton 3827 * @type String 3828 */ 3829 cancelWriteDataButtonId: "cancelWriteDataButton", 3830 3831 /**The text on the cancel write button. 3832 * 3833 * @type String 3834 */ 3835 cancelWriteDataButtonText: "Cancel Write", 3836 3837 /**The function called when data is successfully written to 3838 * the device. This method takes two parameters: 3839 * success Boolean - true if data was written 3840 * display {Garmin.DeviceDisplay} - the current instance of the DeviceDisplay 3841 * @type function 3842 * @example function(success, display) {...} 3843 * @function 3844 */ 3845 afterFinishWriteToDevice: null, 3846 3847 /**Array of filters to sequencialy apply to activities before being sent or displayed. 3848 * 3849 * @see Garmin.FILTERS 3850 * @type Array dataFilters 3851 * @example [Garmin.FILTERS.historyOnly] 3852 */ 3853 dataFilters: [], 3854 3855 /**The function called by the display in order to acquire the data 3856 * that will be written to the device during the writing operation. 3857 * 3858 * This function should return a String. 3859 * 3860 * @see Garmin.DeviceDisplayDefaultOptions.getWriteDataFileName 3861 * @type function 3862 * @example function() { return $("myTextAreaId").value; } 3863 * @function 3864 */ 3865 getWriteData: null, 3866 3867 /**The function called by the display in order to acquire the filename 3868 * of the data that will be written to the device during the writing 3869 * operation. 3870 * 3871 * This function should return a String. 3872 * @default function(){ return "myData.gpx"; } 3873 * @see Garmin.DeviceDisplayDefaultOptions.getWriteData 3874 * @type function 3875 */ 3876 getWriteDataFileName: function(){ return "myData.gpx"; } , 3877 3878 /**DEPRECATED (see {@link getBinaryWriteDescription}) - The function called by the display in order to acquire the data 3879 * that will be written to the device during the writing operation. 3880 * 3881 * This function should return an array of strings where adjacent items 3882 * indicate the source (URL) of the gpi to be written and the destination 3883 * (device path and filename) to write to the device. 3884 * 3885 * e.g.: [SOURCE,DESTINATION,SOURCE2,DESTINATION2] add as many source/destination 3886 * pairs as you'd like. 3887 * 3888 * @example function() { return ["http://connect.garmin.com/SampleGpi.gpi", "Garmin\\POI\\Test.gpi"] } 3889 * @type function 3890 * @function 3891 */ 3892 getGpiWriteDescription: null, 3893 3894 /**The function called by the display in order to acquire the data 3895 * that will be written to the device during the writing operation. 3896 * 3897 * This function should return an array of strings where adjacent items 3898 * indicate the source (URL) of the binary data to be written and the destination 3899 * (device path and filename) to write to the device. 3900 * 3901 * e.g.: [SOURCE,DESTINATION,SOURCE2,DESTINATION2] add as many source/destination 3902 * pairs as you'd like. 3903 * 3904 * @example function() { return ["http://connect.garmin.com/SampleGpi.gpi", "Garmin\\POI\\Test.gpi"] } 3905 * @type function 3906 * @function 3907 */ 3908 getBinaryWriteDescription: null, 3909 3910 /**DEPRECATED - Use {@link Garmin.DeviceDisplayDefaultOptions.writeDataTypes} 3911 * Tells the plug-in what data type to write to the device. 3912 * Options are "gpx" which will use {@link getWriteData} to get the data 3913 * or "gpi" which will use {@link getGpiWriteDescription} to get the data to 3914 * save to the device. 3915 * 3916 * @default Garmin.DeviceControl.FILE_TYPES.gpx 3917 * @see Garmin.DeviceDisplayDefaultOptions.showWriteDataElement 3918 * @see Garmin.DeviceDisplayDefaultOptions.getWriteData 3919 * @see Garmin.DeviceDisplayDefaultOptions.getGpiWriteDescription 3920 * @see Garmin.DeviceDisplayDefaultOptions.writeDataTypes 3921 * @type String 3922 * @deprecated 3923 */ 3924 writeDataType: null, 3925 3926 /** OVERRIDES writeDataType! 3927 * 3928 * Tells the plug-in what data type to write to the device. 3929 * Options are "gpx" which will use {@link getWriteData} to get the data 3930 * or "gpi" which will use {@link getGpiWriteDescription} to get the data to 3931 * save to the device. 3932 * 3933 * @default Garmin.DeviceControl.FILE_TYPES.gpx 3934 * @see Garmin.DeviceDisplayDefaultOptions.showWriteDataElement 3935 * @see Garmin.DeviceDisplayDefaultOptions.getWriteData 3936 * @see Garmin.DeviceDisplayDefaultOptions.getGpiWriteDescription 3937 * @type Array 3938 */ 3939 writeDataTypes: ["GPSData"], 3940 3941 //=================== Activity Directory Element Options =============== 3942 3943 /** Displays the activity directory table, which essentially 3944 * allows users to select individual activities to read from 3945 * the device. showReadDataElement must be true for this 3946 * to display. 3947 * 3948 * @default true 3949 * 3950 * @see Garmin.DeviceDisplayDefaultOptions.showReadDataElement 3951 * @type Boolean 3952 */ 3953 showActivityDirectoryElement: true, 3954 3955 /** 3956 * The classname referencing the element that lists individual activities. This element includes the 3957 * data table as well as the related buttons (such as for uploading data). This is useful for CSS customizations. 3958 * @default activityDirectoryClass 3959 * @type String 3960 */ 3961 activityDirectoryClass: "activityDirectoryClass", 3962 3963 /** The id referencing the table that holds the activity directory data. This is useful for CSS customizations. 3964 * 3965 * @default activityDirectoryData 3966 * @type String 3967 */ 3968 activityDirectoryDataId: "activityDirectoryData", 3969 3970 /** The id referencing the element that lists individual activities. This element includes the 3971 * data table as well as the related buttons (such as for uploading data). This is useful for CSS customizations. 3972 * 3973 * @default activityDirectory 3974 * @type String 3975 */ 3976 activityDirectoryElementId: "activityDirectory", 3977 3978 /** The id referencing the header of the activity table that lists individual activities. 3979 * This is useful for CSS customizations. 3980 * @default activityTableHeader 3981 * @type String 3982 */ 3983 activityTableHeaderId: "activityTableHeader", 3984 3985 /** The id referencing the activity table that lists individual activities. This table contains 3986 * the activity data. This is useful for CSS customizations. 3987 * 3988 * @default activityTable 3989 * @type String 3990 */ 3991 activityTableId: "activityTable", 3992 3993 /** The function called after an activity entry is added to the activity listing table. 3994 * Useful for updating the status cell in a unique way. 3995 * @param Boolean index - the index in the table where the activity was added 3996 * @param {Garmin.Activity} activity 3997 * @param {Element} statusCell - the status td element associated with the activity 3998 * @param {Element} checkbox - the checkbox input element associated with the activity 3999 * @param Object row - the table row element associated with the activity 4000 * @param {Garmin.ActivityMatcher} activityMatcher - the activity matcher object, if available. 4001 * @param {Garmin.DeviceDisplay} display - the current instance of the display object 4002 * @type function 4003 * @function 4004 * @example function(index, activity, statusCell, checkbox, row, this.activityMatcher, this) {...} 4005 */ 4006 afterTableInsert: null, 4007 4008 //=================== Upload Options =============== 4009 4010 /** Class name for the cancel upload button. 4011 * Useful for CSS customizations. 4012 * @default cancelUploadButtonClass 4013 * @type String 4014 */ 4015 cancelUploadButtonClass: 'cancelUploadButtonClass', 4016 4017 /** Element ID for the cancel upload button. 4018 * @default 'cancelUploadButton' 4019 * @type String 4020 */ 4021 cancelUploadButtonId: 'cancelUploadButton', 4022 4023 /** Text to display on Cancel upload button. 4024 * @default '(Cancel)' 4025 * @type String 4026 */ 4027 cancelUploadButtonText: '(Cancel)', 4028 4029 /**Display the user interface associated with uploading new activities from a connected device. 4030 * 4031 * @default true 4032 * @type Boolean 4033 */ 4034 showUploadNewButton: false, 4035 4036 /** The id referencing the button for uploading data to a server. This is useful for CSS customizations. 4037 * 4038 * @default uploadNewButton 4039 * @type String 4040 */ 4041 uploadNewButtonId: "uploadNewButton", 4042 4043 /** The text label for the upload data button. This is useful for CSS customizations. 4044 * 4045 * @default activityDirectory 4046 * @type String 4047 */ 4048 uploadNewButtonText: "Upload new activities", 4049 4050 /** Select activities to upload, rather than all activities read off the device. 4051 * 4052 * @default false 4053 * @type Boolean 4054 */ 4055 uploadSelectedActivities: false, 4056 4057 /** Upload compressed data. Compressed data is gzip base 64 encoded. Compression is supported 4058 * for fitness history activities only. 4059 * 4060 * @default false 4061 * @type Boolean 4062 */ 4063 uploadCompressedData: false, 4064 4065 /** Maximum number of activities allowed for upload selection. Users are notified during the selection 4066 * process if they try to exceed this value. uploadSelectedActivities must be set to true 4067 * for this to work. Note that if this value is > 0, the 'select all activities' feature will not be available. 4068 * 4069 * Set this value to 0 for no limit with NO 'select all activities' checkbox. 4070 * 4071 * @default -1 (no limit with 'select all' checkbox) 4072 * @type int 4073 * @see Garmin.DeviceDisplayDefaultOptions.uploadSelectedActivities 4074 */ 4075 uploadMaximum: -1, 4076 4077 // ================== Post to Server ====================== 4078 4079 /** The function called when a single activity is finished reading, and the data is ready to post. 4080 * Use this if you need a custom way of uploading data to your server (advanced users).<br/> 4081 * <br/> 4082 * Otherwise, if you just need an AJAX call, use Send Data. (See {@link this.options.sendDataUrl} 4083 * and {@link this.options.sendDataOptions}.) 4084 * 4085 * Parameters to this function:<br/> 4086 * <br/>datastring String - The XML datastring of the activity read from the device.<br/> 4087 * <br/>display {GarminDeviceDisplay} - the display object 4088 * @example function(datastring, display){...} 4089 * @function 4090 * @type function 4091 */ 4092 postActivityHandler: null, 4093 4094 /** Show the element to send data to a remote server. 4095 * 4096 * @type Boolean 4097 */ 4098 showSendDataElement: false, 4099 4100 /**Controls the view of the send data element. When 4101 * set to <b>true</b> the element will only show after a 4102 * device has been found. When set to <b>false</b> the 4103 * element will show on page load. 4104 * Behavior will depend on other settings such as 4105 * and {@link showSendDataElement} . 4106 * 4107 * @default false 4108 * @type Boolean 4109 */ 4110 showSendDataElementOnDeviceFound: false, 4111 4112 /** The callback function to set the request options when hitting the remote server. 4113 * This function is passed these parameters: 4114 * 4115 * options - The options object. Use this object to set the property values. 4116 * Some may already be set by sendDataOptions. getSendOptions will overwrite any existing ones. 4117 * deviceXml - the active device's XML 4118 * data - read data from the device, if any 4119 * 4120 * Don't forget to return the options object! 4121 * 4122 * @type function 4123 * @example function(options, deviceXml, data) {} 4124 * @function 4125 */ 4126 getSendOptions: null, 4127 4128 /** The URL to send the data to. 4129 * 4130 * @type String 4131 */ 4132 sendDataUrl: null, 4133 4134 /** The AJAX request options to use for sending data to a server. To be used in conjunction with {@link sendDataUrl} . 4135 * 4136 * See the <a href="http://www.prototypejs.org/api/ajax/options">AJAX options page</a> for configurable options and default values. 4137 * 4138 * @type Object 4139 */ 4140 sendDataOptions: null, 4141 4142 /**The id referencing the box containing send elements. This is 4143 * useful for CSS customizations. 4144 * 4145 * @default sendBox 4146 * @type String 4147 */ 4148 sendDataElementId: "sendBox", 4149 4150 /**The id referencing the send data button. This is useful for 4151 * CSS customizations. 4152 * 4153 * @default sendDataButton 4154 * @type String 4155 */ 4156 sendDataButtonId: "sendDataButton", 4157 4158 /**The text on the read button. 4159 * 4160 * @type String 4161 */ 4162 sendDataButtonText: "Send Data", 4163 4164 /** The callback function that will be passed the AJAX response 4165 * after making the request. The display object is passed in, so you can 4166 * make follow-up read or write calls if so desired. 4167 * 4168 * @see Garmin.DeviceDisplayDefaultOptions.sendDataUrl 4169 * @see Garmin.DeviceDisplayDefaultOptions.getSendOptions 4170 * @param response object (see <a href="http://www.prototypejs.org/api/ajax/response">Ajax.Response</a> for response attributes) 4171 * @default null 4172 * @type function 4173 * @function 4174 * @example function(response){} 4175 */ 4176 afterFinishSendData: null, 4177 4178 //=================== Device Browser Element Options =============== 4179 4180 /** The callback function that will be called when the user selects a 4181 * device from the device browser. 4182 * 4183 * @default null 4184 * @param deviceNum the device number of the selected device 4185 * @param devices an array of the detected devices 4186 * @param deviceXml the device xml of the selected device 4187 * @type function 4188 * @function 4189 * @example function(deviceXml){...} 4190 */ 4191 afterSelectDevice: null, 4192 4193 /** Show the device browser list. Currently the browser list is only available 4194 * when activity directory reading is on (readDataType = Garmin.DeviceControl.FILE_TYPES.tcxDir). 4195 * 4196 * @default true 4197 * 4198 * @see Garmin.DeviceDisplayDefaultOptions.uploadSelectedActivities 4199 * @type Boolean 4200 */ 4201 useDeviceBrowser: true, 4202 4203 /**Display list instead of select drop down. 4204 * @default false 4205 * @type Boolean 4206 */ 4207 useDeviceSelectList: false, 4208 4209 /** The classname for the device browser element. This is useful for custom CSS. 4210 * @default deviceBrowserBoxClass 4211 * @type String 4212 */ 4213 deviceBrowserElementClass: "deviceBrowserBoxClass", 4214 4215 /** The id referencing the device browser element. This is useful for custom CSS. 4216 * @default deviceBrowserList 4217 * @type String 4218 */ 4219 deviceBrowserElementId: "deviceBrowserBox", 4220 4221 /** The classname for the device browser text label. This is useful for custom CSS. 4222 * @default deviceBrowserLabelClass 4223 * @type String 4224 */ 4225 deviceBrowserLabelClass: "deviceBrowserLabelClass", 4226 4227 /** The id referencing the device browser text label. This is useful for custom CSS. 4228 * @default deviceBrowserLabelId 4229 * @type String 4230 */ 4231 deviceBrowserLabelId: "deviceBrowserLabel", 4232 4233 /** The text label to display above the device browser list. This is useful for custom CSS. 4234 * @default 'Browse devices:' 4235 * @type String 4236 */ 4237 deviceBrowserLabel: "Browse devices:", 4238 4239 /** The id referencing the device browser list. This is useful for 4240 * CSS customizations. 4241 * 4242 * @default deviceBrowserList 4243 * @type String 4244 */ 4245 deviceBrowserListId: "deviceBrowserList", 4246 4247 /** The classname referencing the button that displays the UI for browsing the user's 4248 * file system. 4249 * @default browseComputerButtonClass 4250 * @type String 4251 */ 4252 browseComputerButtonClass: "browseComputerButtonClass", 4253 4254 /** The id referencing the button that displays the UI for browsing the user's 4255 * file system. 4256 * @default browseComputerButton 4257 * @type String 4258 */ 4259 browseComputerButtonId: "browseComputerButton", 4260 4261 /** The text for the button that displays the UI for browsing the user's file system. 4262 * @default "Browse Computer" 4263 * @type String 4264 */ 4265 browseComputerButtonText: "Browse Computer", 4266 4267 /** The classname for the browse computer container. 4268 * @type String 4269 */ 4270 browseComputerElementClass: "browseComputerElementClass", 4271 4272 /** The id for the browse computer container. 4273 * @type String 4274 */ 4275 browseComputerElementId: "browseComputerElement", 4276 4277 /** The url of the iframe to load into the browse computer container. 4278 * The iframe object is generated by the API. See browseComputerElementClass 4279 * to change the dimensions of the iframe. 4280 * @type String 4281 */ 4282 browseComputerContentUrl: "about:blank", 4283 4284 /** The text label to display in the browser list for browsing the computer. 4285 * @default My Computer 4286 * @type String 4287 */ 4288 browseComputerLabel: "My Computer", 4289 4290 /** The text to display on the loading content screen. This is useful for internationalization. 4291 * @default 'Loading content...' 4292 * @type String 4293 */ 4294 loadingContentText: "Loading content from #{deviceName}, please wait...", 4295 4296 /** The text to display to change the device. This is useful for internationalization. 4297 * @default '(Change)' 4298 * @type String 4299 */ 4300 changeDeviceButtonText: "(Change)", 4301 4302 /** The classname referencing the change device element, which is a link that allows 4303 * the user to change device in list mode. 4304 * @default changeDevice 4305 * @type String 4306 */ 4307 changeDeviceClass: "change", 4308 4309 /** The id referencing the change device element, which is a link that allows 4310 * the user to change device in list mode. 4311 * @default changeDevice 4312 * @type String 4313 */ 4314 changeDeviceElementId: "changeDevice", 4315 4316 /** The class name referencing the connected devices label displayed when using 4317 * the device select list. 4318 * @default connectedDevicesClass 4319 * @type String 4320 */ 4321 connectedDevicesClass: "connectedDevicesClass", 4322 4323 /** The image to display in the connected devices label. 4324 * @type String 4325 */ 4326 connectedDevicesImg: null, 4327 4328 /** The label for connected devices, displayed when using the device select list. 4329 * @default 'Connected devices:' 4330 * @type String 4331 */ 4332 connectedDevicesLabel: "Connected devices:", 4333 4334 /** The classname referencing the preview device element, which displays an image 4335 * depending on the device selected. 4336 * 4337 * @default previewDevice 4338 * @see Garmin.DeviceDisplayDefaultOptions.previewDeviceDefaultImg 4339 * @type String 4340 */ 4341 previewDeviceClass: "preview", 4342 4343 /** The id referencing the change device element, which is a link that allows 4344 * the user to change device in list mode. 4345 * @default changeDevice 4346 * @type String 4347 */ 4348 previewDeviceElementId: "previewDevice", 4349 4350 /** The default image URL to display for any selected device. 4351 * @see Garmin.DeviceDisplayDefaultOptions.previewDeviceId 4352 * @see Garmin.DeviceDisplayDefaultOptions.useDeviceSelectList 4353 * @type String 4354 */ 4355 previewDeviceDefaultImg: "../../../theme/upload/images/icon-edge.png", 4356 4357 /**The maximum number of characters to display for a device name. 4358 *@type int 4359 */ 4360 deviceLabelMaxSize: 20, 4361 4362 /** Allows the user to browse their file system for upload. 4363 * {@link uploadSelectedActivities} must be set to true for this 4364 * option to take effect. 4365 * @default false 4366 * @type Boolean 4367 */ 4368 showBrowseComputer: false, 4369 4370 // ================== Synchronization ====================== 4371 4372 /**Detect new activities in the activity listing by comparing the device list to a server list. 4373 * @default true 4374 * @type Boolean 4375 */ 4376 detectNewActivities: false, 4377 4378 /** 4379 * The URL to make the sync request to. To be used in conjuntion with {@link syncDataOptions}. 4380 * {@link detectNewActivities} must be set to true. 4381 * @type String 4382 */ 4383 syncDataUrl: null, 4384 4385 /** The AJAX request options to use for posting the parameters to a server. To be used in conjunction with {@link syncDataUrl} . 4386 * 4387 * See the <a href="http://www.prototypejs.org/api/ajax/options">AJAX options page</a> for configurable options and default values. 4388 * 4389 * @type Object 4390 */ 4391 syncDataOptions: null, 4392 4393 // ================== Internationalization ====================== 4394 /** Status message exposed for internationalization. @type String */ 4395 pluginUnlocked: "Plug-in initialized. Find some devices to get started.", 4396 /** Status message exposed for internationalization. @type String */ 4397 pluginNotUnlocked: "The plug-in was not unlocked successfully", 4398 /** Read data selection option exposed for internationalization. @type String */ 4399 gpsData: "GPS Data", 4400 /** Read data selection option exposed for internationalization. @type String */ 4401 trainingData: "Training Data", 4402 /** Status message exposed for internationalization. Prepended to the device name after user selects which device to use. 4403 * i.e. "Using Diana's Forerunner 305" 4404 * @default "Using " 4405 * @type String 4406 */ 4407 usingDevice: "Using #{deviceName}", 4408 /** Track list box item template exposed for internationalization. @type String */ 4409 trackListing: "#{date} (Duration: #{duration} )", 4410 /** Status message template exposed for internationalization. @type String */ 4411 dataFound: "#{routes} routes, #{tracks} tracks and #{waypoints} waypoints found", 4412 /** Status message exposed for internationalization. @type String */ 4413 writingToDevice: "Writing data to the device", 4414 /** Status message exposed for internationalization. @type String */ 4415 writtenToDevice: "Data written to the device", 4416 /** Status message exposed for internationalization. @type String */ 4417 writingCancelled: "Writing cancelled", 4418 /** Status message exposed for internationalization. @type String */ 4419 overwritingFile: "Overwriting file", 4420 /** Status message exposed for internationalization. @type String */ 4421 notOverwritingFile: "Will not be overwriting file", 4422 /** Status message exposed for internationalization. @type String */ 4423 lookingForDevices: "Looking for connected devices...", 4424 /** Status template exposed for internationalization. When single device is found. @type String */ 4425 foundDevice: "Found #{deviceName} ", 4426 /** Status template exposed for internationalization. When multiple devices are found. @type String */ 4427 foundDevices: "Found #{deviceCount} devices", 4428 /** Status message exposed for internationalization. When user cancels Find Devices.@type String */ 4429 findCancelled: "Find cancelled", 4430 /** Status message exposed for internationalization. When reading data from the device. @type String */ 4431 dataReadProcessing: "Data read from device. Processing...", 4432 /** Status message exposed for internationalization. When large files are being written to device. @type String */ 4433 dataDownloadProcessing: "Processing data to write... ", 4434 /** Status message exposed for internationalization. When uploads have completed. @type String */ 4435 uploadsFinished: "Transfer Complete!", 4436 /** Error message exposed for internationalization. @type String */ 4437 noParseSupportForType: "The plugin does not have parsing support for file type ", 4438 /** Request message exposed for internationalization. @type String */ 4439 installNow: "Install now?", 4440 /** Request message exposed for internationalization. @type String */ 4441 downloadAndInstall: "Download and install now", 4442 /** Powered-by message. Required for plugin license agreement. @type String */ 4443 poweredByGarmin: "Powered by <a href='http://www.garmin.com/products/communicator/' target='_new'>Garmin Communicator</a>", 4444 /** Status message for devices that are not in the allowed devices list, or do not support any of the application's supported filetypes. @type String */ 4445 unsupportedDevice: "Your device is not supported by this application.", 4446 /** Error message to display when user attempts to upload 0 activities. @type String */ 4447 errorActivitySelect: "No selected or new activities to upload.", 4448 /** DEPRECATED Column header for the Date fields in the activity directory. Useful for CSS customizations and internationalization. @type String @deprecated @see Garmin.DeviceDisplayDefaultOptions.getActivityDirectoryHeaderIdLabel */ 4449 activityDirectoryHeaderDate: "<b>Date</b>", 4450 /** Column header for the Date/Name fields in the activity directory. Useful for CSS customizations and internationalization. Default to Date if nothing is specified @type String */ 4451 getActivityDirectoryHeaderIdLabel: function () { return "<b>Date</b>"; }, 4452 /** Column header for the Duration fields in the activity directory. Useful for CSS customizations and internationalization. @type String */ 4453 activityDirectoryHeaderDuration: "<b>Duration</b>", 4454 /** Column header for the Status fields in the activity directory. Useful for CSS customizations and internationalization. @type String */ 4455 activityDirectoryHeaderStatus: "<b>Status</b>", 4456 /** Error message to display when attempting to write to a device that does not support the provided datatype. Useful for CSS customizations and internationalization. @type String */ 4457 unsupportedReadDataType: "Your device does not support reading of the type #{dataType}.", 4458 /** Error message to display when attempting to write to a device that does not support the provided datatype. Useful for CSS customizations and internationalization. @type String */ 4459 unsupportedWriteDataType: "Your device does not support writing of the type #{dataType}.", 4460 /** Status message to display when activities are being uploaded. @type String */ 4461 uploadingActivities: "Uploading activities...", 4462 /** Error message exposed for internationalization. When maximum selection for upload is reached. @type String */ 4463 uploadMaximumReached: "Maximum upload selection of #{activities} activities reached.", 4464 /** The innerHTML to use for status while an activity is processing (for upload). Define using the img tag if you wish to use an image. 4465 * 4466 * @default '<img src="style/ajax-loader.gif" />' 4467 * 4468 * which is an animated loader image. You can customize this to be text instead of an image by not using the image tags. 4469 * @type String */ 4470 statusCellProcessingImg: '<img src="style/processing-arrows.gif" width="15" height="15" />', 4471 /** Status text to display when the plugin is sending data to a remote server. @type String */ 4472 sendingDataToServer: "Sending data from #{deviceName} to server...", 4473 /** Error message to display when there is an error getting the HTTP response back from the HTTP request. @type String */ 4474 errorHttpResponse: "Unable to get valid response from HTTP request object. Ensure that your options are set correctly and try again.", 4475 /** Status text to display when none of the activities from the device meet the filter requirements. @type String */ 4476 noFilteredActivities: "No new activities found on device.", 4477 noActivitiesOnDevice: "No activities found on selected device." 4478 } ; 4479 4480 4481 /* 4482 * DisplayBootstrap - not sure what form this should take: class or global var 4483 * It should probably be in the Garmin namesapce. 4484 * 4485 * Dynamic include of required libraries and check for Prototype 4486 * Code taken from scriptaculous (http://script.aculo.us/) - thanks guys! 4487 */ 4488 var GarminDeviceDisplay = { 4489 require: function(libraryName) { 4490 // inserting via DOM fails in Safari 2.0, so brute force approach 4491 document.write('<script type="text/javascript" src="'+libraryName+'"></script>'); 4492 }, 4493 4494 load: function() { 4495 if((typeof Prototype=='undefined') || 4496 (typeof Element == 'undefined') || 4497 (typeof Element.Methods=='undefined') || 4498 parseFloat(Prototype.Version.split(".")[0] + "." + 4499 Prototype.Version.split(".")[1]) < 1.5) { 4500 throw("GarminDeviceDisplay requires the Prototype JavaScript framework >= 1.5.0"); 4501 } 4502 4503 $A(document.getElementsByTagName("script")) 4504 .findAll( 4505 function(s) { 4506 return (s.src && s.src.match(/GarminDeviceDisplay\.js(\?.*)?$/)) 4507 } 4508 ) 4509 .each( 4510 function(s) { 4511 var path = s.src.replace(/GarminDeviceDisplay\.js(\?.*)?$/,'../../'); 4512 var includes = s.src.match(/\?.*load=([a-z,]*)/); 4513 var dependencies = 'garmin/device/GarminDeviceControl' + 4514 ',garmin/device/GarminDevicePlugin' + 4515 ',garmin/device/GarminGpsDataStructures' + 4516 ',garmin/device/GoogleMapController' + 4517 ',garmin/device/GarminDevice' + 4518 ',garmin/device/GarminPluginUtils' + 4519 ',garmin/api/GarminRemoteTransfer' + 4520 ',garmin/util/Util-XmlConverter' + 4521 ',garmin/util/Util-Broadcaster' + 4522 ',garmin/util/Util-DateTimeFormat' + 4523 ',garmin/util/Util-BrowserDetect' + 4524 ',garmin/util/Util-PluginDetect' + 4525 ',garmin/device/GarminObjectGenerator' + 4526 ',garmin/activity/GarminMeasurement' + 4527 ',garmin/activity/GarminSample' + 4528 ',garmin/activity/GarminSeries' + 4529 ',garmin/activity/GarminActivity' + 4530 ',garmin/activity/GarminActivityDirectory' + 4531 ',garmin/activity/GarminActivityFilter' + 4532 ',garmin/activity/GarminActivityMatcher' + 4533 ',garmin/activity/TcxActivityFactory' + 4534 ',garmin/activity/GpxActivityFactory'+ 4535 ',garmin/directory/GarminDirectoryFactory'+ 4536 ',garmin/directory/GarminFile'; 4537 (includes ? includes[1] : dependencies).split(',').each( 4538 function(include) { 4539 GarminDeviceDisplay.require(path+include+'.js') 4540 } 4541 ); 4542 } 4543 ); 4544 } 4545 } 4546 4547 GarminDeviceDisplay.load(); 4548