4548 lines
170 KiB
JavaScript
4548 lines
170 KiB
JavaScript
if (Garmin == undefined) var Garmin = {};
|
||
/** Copyright <20> 2007 Garmin Ltd. or its subsidiaries.
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the 'License')
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*
|
||
* A high-level UI widget for talking with Garmin Devices.
|
||
*
|
||
* @fileoverview GarminDeviceDisplay.js
|
||
* @author Michael Bina michael.bina at garmin.com, Diana Chow diana.chow at garmin.com
|
||
* @version 1.4.4
|
||
*/
|
||
|
||
/** Provides the easiest avenue for getting a working instance of the plug-in onto your page.
|
||
* Generates the UI elements and places them on the page.
|
||
*
|
||
* @class Garmin.DeviceDisplay
|
||
*
|
||
* requires Prototype
|
||
* @requires Garmin.DeviceControl
|
||
* @namespace Garmin
|
||
*/
|
||
Garmin.DeviceDisplay= function(mainElement, options){}; //just here for jsdoc
|
||
Garmin.DeviceDisplay = Class.create();
|
||
Garmin.DeviceDisplay.prototype = {
|
||
|
||
/** Constructor.
|
||
* @constructor
|
||
* @param String mainElement - id of the element in which to generate the contents
|
||
* (can also be a reference to the dom element itself).
|
||
* @param Object options - Object with options (see {@link Garmin.DeviceDisplayDefaultOptions} for descriptions of possible options).
|
||
*/
|
||
initialize: function(mainElement, options) {
|
||
if(typeof(mainElement) == "string") {
|
||
this.mainElement = $(mainElement);
|
||
} else {
|
||
this.mainElement = mainElement;
|
||
}
|
||
|
||
if(this.mainElement != null) {
|
||
this.options = null;
|
||
this.setOptions(options);
|
||
|
||
this.garminController = null;
|
||
this.garminRemoteTransfer = null;
|
||
this.activities = new Array();
|
||
this.devices = new Array();
|
||
this.factory = null;
|
||
this.tracks = null;
|
||
this.waypoints = null;
|
||
|
||
this.activityDirectory = null; // Array of activity ID strings in the directory
|
||
this.activityQueue = null; // Queue of activity IDs to sync events
|
||
this.numQueuedActivities = null; // Number of total queued activities for status reporting
|
||
this.uploadData = null; // Payload element for upload data
|
||
this.activityMatcher = null; // The activity filter for synchronizing activities
|
||
|
||
this.currentActivity = null; // The top of the activity queue and/or the activity being processed.
|
||
this.finishedFirstActivity = false;
|
||
this.xhr = null; // The XHR object (see GarminRemoteTransfer)
|
||
this.advancedUploadMode = true; // Internal option to show the activity selection table on upload
|
||
|
||
this.error = null;
|
||
|
||
this._generateElements();
|
||
|
||
if (this.options.unlockOnPageLoad) {
|
||
this.getController(true);
|
||
}
|
||
if (!this.error && this.options.autoFindDevices) {
|
||
this.startFindDevices();
|
||
}
|
||
}
|
||
},
|
||
|
||
////////////////////////// UI GENERATION METHODS ///////////////////////////
|
||
|
||
/* Primary UI build method.
|
||
* @private
|
||
*/
|
||
_generateElements: function() {
|
||
if (BrowserSupport.isBrowserSupported() || !this.options.hideIfBrowserNotSupported) {
|
||
this._generateStatusElement();
|
||
if(this.options.showFindDevicesElement) {
|
||
this._generateFindDevicesElement();
|
||
}
|
||
if(this.options.showReadDataElement) {
|
||
this._generateReadDataElement();
|
||
}
|
||
if(this.options.showActivityDirectoryElement) {
|
||
this._generateActivityDirectoryElement();
|
||
}
|
||
if(this.options.showWriteDataElement) {
|
||
this._generateWriteDataElement();
|
||
}
|
||
if(this.options.showSendDataElement) {
|
||
this._generateSendDataElement();
|
||
}
|
||
if(this.options.showAboutElement) {
|
||
this._generateAboutElement();
|
||
}
|
||
this.resetUI();
|
||
}
|
||
},
|
||
|
||
/** Resets UI widgets based on state of application.
|
||
*/
|
||
resetUI: function() {
|
||
//console.debug("Display.resetUI")
|
||
this.hideProgressBar();
|
||
|
||
var noDevicesAvailable = this.garminController ? (this.getController().numDevices==0) : true;
|
||
if(this.options.showFindDevicesElement) {
|
||
if (this.findDevicesButton)
|
||
this.findDevicesButton.disabled = false;
|
||
if (this.deviceSelectInput)
|
||
this.deviceSelectInput.disabled = noDevicesAvailable;
|
||
if (this.cancelFindDevicesButton)
|
||
this.cancelFindDevicesButton.disabled = true;
|
||
if (this.readDataTypesSelect)
|
||
this.readDataTypesSelect.disabled = false;
|
||
}
|
||
if(this.options.showReadDataElement) {
|
||
if (this.readDataButton)
|
||
this.readDataButton.disabled = noDevicesAvailable;
|
||
if (this.cancelReadDataButton)
|
||
this.cancelReadDataButton.disabled = true;
|
||
if(this.loadingContentElement) {
|
||
this.loadingContentElement.hide();
|
||
}
|
||
}
|
||
if(this.options.showWriteDataElement) {
|
||
if (this.writeDataButton)
|
||
this.writeDataButton.disabled = noDevicesAvailable;
|
||
if (this.cancelWriteDataButton)
|
||
this.cancelWriteDataButton.disabled = true;
|
||
}
|
||
},
|
||
|
||
|
||
/* Build status UI components.
|
||
* @private
|
||
*/
|
||
_generateStatusElement: function() {
|
||
this.statusElement = document.createElement("div");
|
||
Element.extend(this.statusElement);
|
||
this.statusElement.id = this.options.statusElementId;
|
||
this.statusElement.addClassName(this.options.elementClassName);
|
||
this.mainElement.appendChild(this.statusElement);
|
||
|
||
// Status text
|
||
this.statusText = document.createElement("div");
|
||
Element.extend(this.statusText);
|
||
this.statusText.id = this.options.statusTextId;
|
||
this.statusElement.appendChild(this.statusText);
|
||
|
||
// Progress bars
|
||
this._generateProgressBars();
|
||
},
|
||
|
||
/* Build status progress bar UI components.
|
||
* @private
|
||
*/
|
||
_generateProgressBars: function() {
|
||
// Device transfer progress bar
|
||
this.progressBar = document.createElement("div");
|
||
Element.extend(this.progressBar);
|
||
this.progressBar.id = this.options.progressBarId;
|
||
this.progressBar.className = this.options.progressBarClass;
|
||
this.progressBarBack = document.createElement("div");
|
||
Element.extend(this.progressBarBack);
|
||
this.progressBarBack.id = this.options.progressBarBackId;
|
||
this.progressBarBack.addClassName(this.options.progressBarBackClass);
|
||
this.progressBarBack.innerHTML = '<span/>';
|
||
this.progressBar.appendChild(this.progressBarBack);
|
||
this.progressBarDisplay = document.createElement("div");
|
||
Element.extend(this.progressBarDisplay);
|
||
this.progressBarDisplay.id = this.options.progressBarDisplayId;
|
||
this.progressBarDisplay.addClassName(this.options.progressBarDisplayClass);
|
||
this.progressBarDisplay.innerHTML = '<span/>';
|
||
this.progressBar.appendChild(this.progressBarDisplay);
|
||
this.progressBar.hide();
|
||
this.statusElement.appendChild(this.progressBar);
|
||
|
||
// Device transfer progress bar text
|
||
this.progressBarText = document.createElement("div");
|
||
Element.extend(this.progressBarText);
|
||
this.progressBarText.id = this.options.progressBarTextId;
|
||
this.progressBarText.className = this.options.progressBarTextClass;
|
||
this.progressBar.appendChild(this.progressBarText);
|
||
|
||
// Upload progress bar
|
||
this.uploadProgressBar = document.createElement("div");
|
||
Element.extend(this.uploadProgressBar);
|
||
this.uploadProgressBar.id = this.options.uploadProgressBarId;
|
||
this.uploadProgressBar.className = this.options.uploadProgressBarClass;
|
||
this.uploadProgressBarBack = document.createElement("div");
|
||
Element.extend(this.uploadProgressBarBack);
|
||
this.uploadProgressBarBack.id = this.options.uploadProgressBarBackId;
|
||
this.uploadProgressBarBack.addClassName(this.options.uploadProgressBarBackClass);
|
||
this.uploadProgressBarBack.innerHTML = '<span/>';
|
||
this.uploadProgressBar.appendChild(this.uploadProgressBarBack);
|
||
this.uploadProgressBarDisplay = document.createElement("div");
|
||
Element.extend(this.uploadProgressBarDisplay);
|
||
this.uploadProgressBarDisplay.id = this.options.uploadProgressBarDisplayId;
|
||
this.uploadProgressBarDisplay.addClassName(this.options.uploadProgressBarDisplayClass);
|
||
this.uploadProgressBarDisplay.innerHTML = '<span/>';
|
||
this.uploadProgressBar.appendChild(this.uploadProgressBarDisplay);
|
||
this.uploadProgressBar.hide();
|
||
this.statusElement.appendChild(this.uploadProgressBar);
|
||
|
||
// Upload progress bar text
|
||
this.uploadProgressBarText = document.createElement("div");
|
||
Element.extend(this.uploadProgressBarText);
|
||
this.uploadProgressBarText.id = this.options.uploadProgressBarTextId;
|
||
this.uploadProgressBarText.className = this.options.uploadProgressBarTextClass;
|
||
this.uploadProgressBar.appendChild(this.uploadProgressBarText);
|
||
|
||
//TODO This is totally the wrong place to put this. Move this out somewhere.
|
||
this.cancelUploadButton = new Element(this.options.useLinks ? 'div' : 'input', {
|
||
id: this.options.cancelUploadButtonId,
|
||
className: this.options.cancelUploadButtonClass
|
||
});
|
||
if (this.options.useLinks) {
|
||
this.cancelUploadButton.update('<a href="#">'+this.options.cancelUploadButtonText+'</a>');
|
||
} else {
|
||
this.cancelUploadButton.type = "button";
|
||
this.cancelUploadButton.value = this.options.cancelUploadButtonText;
|
||
}
|
||
this.cancelUploadButton.onclick = function() {
|
||
this.resetUI();
|
||
this.hideProgressBar();
|
||
|
||
// Kill the string of event handlers
|
||
this.garminRemoteTransfer.abortRequest();
|
||
|
||
try {
|
||
// Update the status of the active upload.
|
||
this.options.afterFinishSendData.call(this,
|
||
this.xhr,
|
||
this.currentActivity ? $(this.currentActivity.replace(/Checkbox/,"Status")) : null,
|
||
this);
|
||
} catch (error) {
|
||
this.handleException(error);
|
||
}
|
||
|
||
// Clear the queue. Affects cancel and progress.
|
||
this.activityQueue = null;
|
||
// this.clearActivityQueue(); // Clearing does not work for whatever reason... timing/async
|
||
|
||
// Go to the finished screen
|
||
this.getController()._broadcaster.dispatch("onFinishUploads", { display: this });
|
||
}.bind(this)
|
||
this.uploadProgressBar.insert(this.cancelUploadButton);
|
||
},
|
||
|
||
_createElement: function(id, text, type, parent) {
|
||
var elem = document.createElement(type);
|
||
Element.extend(elem);
|
||
if (type=="a") {
|
||
elem.href = location;
|
||
elem.innerHTML = text;
|
||
} else if (type=="button"){
|
||
elem.type = type;
|
||
elem.value = text;
|
||
}
|
||
elem.id = id;
|
||
parent.appendChild(elem);
|
||
return elem;
|
||
},
|
||
|
||
/** Build device browser list, a singleton. This list will be juxtaposed with the activity directory.
|
||
* This is a different list than the default device select drop down. It adds on the computer file browser
|
||
* as well.
|
||
* @private
|
||
*/
|
||
generateDeviceBrowserElement: function(devices) {
|
||
|
||
if( this.deviceBrowserElement != null) {
|
||
throw new Error("Unable to generate device browser because an instance already exists.");
|
||
}
|
||
|
||
this.deviceBrowserElement = new Element('div', {
|
||
id: this.options.deviceBrowserElementId,
|
||
className: this.options.deviceBrowserElementClass
|
||
});
|
||
this.deviceBrowserLabel = new Element('div', {
|
||
id: this.options.deviceBrowserLabelId,
|
||
className: this.options.deviceBrowserLabelClass
|
||
}).update(this.options.deviceBrowserLabel);
|
||
this.deviceBrowserElement.insert(this.deviceBrowserLabel);
|
||
|
||
this.deviceBrowserList = document.createElement("ul");
|
||
Element.extend(this.deviceBrowserList);
|
||
this.deviceBrowserList.id = this.options.deviceBrowserListId;
|
||
|
||
this.deviceBrowserElement.appendChild(this.deviceBrowserList);
|
||
this.deviceBrowserElement.hide();
|
||
this.mainElement.appendChild(this.deviceBrowserElement);
|
||
|
||
// Fill up the list with real data
|
||
this._populateDeviceList(this.deviceBrowserList, this.options.afterSelectDevice ? this.options.afterSelectDevice :
|
||
function(selectedDeviceNumber, devices, deviceXml){
|
||
if (this.options.readDataTypes != null) {
|
||
this.readFromDevice(this.options.readDataTypes);
|
||
// this.readSpecificTypeFromDevice(this.options.readDataType);
|
||
}
|
||
});
|
||
|
||
if( this.options.uploadSelectedActivities && this.options.showBrowseComputer ) {
|
||
this._generateBrowseComputerElement();
|
||
|
||
// Add My Computer to the list
|
||
var itemLink;
|
||
var listItem;
|
||
listItem = document.createElement("li");
|
||
Element.extend(listItem);
|
||
listItem.className = "unselected";
|
||
itemLink = document.createElement("a");
|
||
Element.extend(itemLink);
|
||
itemLink.href = "#";
|
||
itemLink.innerHTML = this.options.browseComputerLabel;
|
||
itemLink.onclick = function(deviceListElement) {
|
||
this._displayBrowseComputer(deviceListElement);
|
||
}.bind(this, this.deviceBrowserList)
|
||
listItem.appendChild(itemLink);
|
||
this.deviceBrowserList.appendChild(listItem);
|
||
}
|
||
},
|
||
|
||
/* Displays the Browse Computer element and updates the device list accordingly.
|
||
* @private
|
||
* @param deviceListElement {Element}
|
||
*/
|
||
_displayBrowseComputer: function(deviceListElement) {
|
||
// Mark My Computer as selected
|
||
var browseComputerItem = deviceListElement.childNodes[this.devices.length];
|
||
browseComputerItem.className = "selected";
|
||
|
||
// Stop any existing reads and hide stuffs
|
||
if(this.getController() && this.isUnlocked()) { // browsing computer does not require valid plugin
|
||
this.getController().cancelReadFromDevice();
|
||
}
|
||
|
||
// Mark all the rest of the devices as unselected
|
||
if( this.devices != null) {
|
||
for(var j=0; j< this.devices.length; j++) {
|
||
var listItem = deviceListElement.childNodes[j];
|
||
listItem.className = "unselected";
|
||
}
|
||
}
|
||
|
||
// The callback function has to take the parameter! Even if it ignores it.
|
||
this.activityDirectoryElement.hide();
|
||
this.statusElement.hide();
|
||
this.browseComputerElement.show();
|
||
},
|
||
|
||
/* Generates the browse computer element, an iframe that contains
|
||
* the manual upload page.
|
||
* @private
|
||
*/
|
||
_generateBrowseComputerElement: function() {
|
||
this.browseComputerElement = document.createElement("div");
|
||
Element.extend(this.browseComputerElement);
|
||
this.browseComputerElement.id = this.options.browseComputerElementId;
|
||
this.browseComputerElement.className = this.options.browseComputerElementClass;
|
||
|
||
var browseComputerTitle = document.createElement("div");
|
||
browseComputerTitle.id = 'manualUploadTitle';
|
||
browseComputerTitle.innerHTML = this.options.browseComputerLabel;
|
||
this.browseComputerElement.appendChild(browseComputerTitle);
|
||
|
||
var browseComputerContent = document.createElement("iframe");
|
||
Element.extend(browseComputerContent);
|
||
browseComputerContent.id = browseComputerContent.name = this.options.browseComputerElementId + 'Contents';
|
||
browseComputerContent.src = this.options.browseComputerContentUrl;
|
||
|
||
this.browseComputerElement.appendChild(browseComputerContent);
|
||
this.browseComputerElement.hide();
|
||
this.mainElement.appendChild(this.browseComputerElement);
|
||
|
||
// Have to set these after append for IE :E
|
||
// This doesn't work anyway!! I hate IE!!
|
||
browseComputerContent.setAttribute('frameborder', '0');
|
||
browseComputerContent.setAttribute('allowtransparency', 'true');
|
||
},
|
||
|
||
/* Generates the loading screen as the passed in element is loading.
|
||
* @private
|
||
*/
|
||
_generateLoadingContent: function(loadingElement) {
|
||
if( this.loadingContentElement != null) {
|
||
throw new Error("Unable to generate loading screen because an instance already exists.");
|
||
}
|
||
// 'Loading' display
|
||
this.loadingContentElement = document.createElement("div");
|
||
Element.extend(this.loadingContentElement);
|
||
this.loadingContentElement.className = "shortStatus";
|
||
this.loadingContentElement.innerHTML = this.evaluateTemplate(this.options.loadingContentText, {deviceName:this.getShortDeviceName(this.getCurrentDevice())});
|
||
loadingElement.appendChild(this.loadingContentElement);
|
||
|
||
this.showProgressBar();
|
||
},
|
||
|
||
/* Update the content inside of the loading content element and display it.
|
||
*/
|
||
_updateLoadingContent: function(content) {
|
||
// Update the device name displayed
|
||
if(this.loadingContentElement != null) {
|
||
this.loadingContentElement.update(content);
|
||
this.loadingContentElement.show();
|
||
}
|
||
},
|
||
|
||
/* Build find device UI components.
|
||
* @private
|
||
*/
|
||
_generateFindDevicesElement: function() {
|
||
this.findDevicesElement = document.createElement("div");
|
||
Element.extend(this.findDevicesElement);
|
||
this.findDevicesElement.id = this.options.findDevicesElementId;
|
||
this.findDevicesElement.addClassName(this.options.elementClassName);
|
||
this.mainElement.appendChild(this.findDevicesElement);
|
||
|
||
// Find devices button
|
||
if( this.options.showFindDevicesButton) {
|
||
this.findDevicesButton = document.createElement( this.options.useLinks ? "div" : "input" );
|
||
Element.extend(this.findDevicesButton);
|
||
if (this.options.useLinks) {
|
||
this.findDevicesButton.innerHTML = '<a href="#">'+this.options.findDevicesButtonText+'</a>';
|
||
} else {
|
||
this.findDevicesButton.type = "button";
|
||
this.findDevicesButton.value = this.options.findDevicesButtonText;
|
||
}
|
||
this.findDevicesButton.id = this.options.findDevicesButtonId;
|
||
this.findDevicesButton.addClassName(this.options.actionButtonClassName);
|
||
this.findDevicesElement.appendChild(this.findDevicesButton);
|
||
this.findDevicesButton.onclick = function() {
|
||
this.startFindDevices();
|
||
}.bind(this)
|
||
}
|
||
|
||
if(!this.options.showFindDevicesElementOnLoad) {
|
||
if( this.findDevicesElement) {
|
||
Element.hide(this.findDevicesElement);
|
||
}
|
||
}
|
||
|
||
// Cancel Find devices button
|
||
if (this.options.showCancelFindDevicesButton) {
|
||
this.cancelFindDevicesButton = document.createElement( this.options.useLinks ? "div" : "input" );
|
||
Element.extend(this.cancelFindDevicesButton);
|
||
if (this.options.useLinks) {
|
||
this.cancelFindDevicesButton.innerHTML = '<a href="#">'+this.options.cancelFindDevicesButtonText+'</a>';
|
||
} else {
|
||
this.cancelFindDevicesButton.type = "button";
|
||
this.cancelFindDevicesButton.value = this.options.cancelFindDevicesButtonText;
|
||
}
|
||
this.cancelFindDevicesButton.id = this.options.cancelFindDevicesButtonId;
|
||
this.cancelFindDevicesButton.addClassName(this.options.actionButtonClassName);
|
||
this.cancelFindDevicesButton.disabled = true;
|
||
this.cancelFindDevicesButton.onclick = function() {
|
||
this.cancelFindDevices();
|
||
}.bind(this)
|
||
this.findDevicesElement.appendChild(this.cancelFindDevicesButton);
|
||
}
|
||
|
||
if (!this.options.showDeviceButtonsOnLoad) {
|
||
if (this.findDevicesButton) {
|
||
Element.hide(this.findDevicesButton);
|
||
}
|
||
if (this.cancelFindDevicesButton)
|
||
Element.hide(this.cancelFindDevicesButton);
|
||
}
|
||
|
||
// Device select drop-down list
|
||
this.deviceSelectElement = document.createElement("div");
|
||
Element.extend(this.deviceSelectElement);
|
||
this.deviceSelectElement.id = this.options.deviceSelectElementId;
|
||
this.deviceSelectElement.innerHTML = '<div id="' + this.options.deviceSelectLabelId + '">' + this.options.deviceSelectLabel + '</div>';
|
||
this.findDevicesElement.appendChild(this.deviceSelectElement);
|
||
|
||
this.deviceSelectInput = document.createElement( this.options.useDeviceSelectList ? "ul" : "select");
|
||
Element.extend(this.deviceSelectInput);
|
||
this.deviceSelectInput.id = this.options.deviceSelectId;
|
||
this.deviceSelectInput.disabled = true;
|
||
|
||
if (!this.options.showDeviceSelectOnLoad || !this.options.showDeviceSelectOnSingle || this.options.autoSelectFirstDevice) {
|
||
Element.hide(this.deviceSelectElement);
|
||
}
|
||
|
||
/* Browse computer */
|
||
this.browseComputerButton = new Element(this.options.useLinks ? "div" : "input", {
|
||
id: this.options.browseComputerButtonId
|
||
, className: this.options.browseComputerButtonClass
|
||
});
|
||
this.browseComputerButton.onclick = function(){
|
||
if( this.deviceBrowserList == null) {
|
||
this.generateDeviceBrowserElement(this.devices)
|
||
};
|
||
this.findDevicesElement.hide();
|
||
this.readDataElement.hide();
|
||
this.deviceBrowserElement.show();
|
||
this._displayBrowseComputer(this.deviceBrowserList);
|
||
}.bind(this)
|
||
if (this.options.useLinks) {
|
||
this.browseComputerButton.innerHTML = '<a href="#">'+this.options.browseComputerButtonText+'</a>';
|
||
} else {
|
||
this.browseComputerButton.type = "button";
|
||
this.browseComputerButton.value = this.options.browseComputerButtonText;
|
||
}
|
||
if(!this.options.uploadSelectedActivities || !this.options.showBrowseComputer ) {
|
||
this.browseComputerButton.hide();
|
||
}
|
||
this.findDevicesElement.appendChild(this.browseComputerButton);
|
||
},
|
||
|
||
_generateSendDataElement: function() {
|
||
this.sendDataElement = document.createElement("div");
|
||
Element.extend(this.sendDataElement);
|
||
this.sendDataElement.id = this.options.sendDataElementId;
|
||
this.sendDataElement.addClassName(this.options.elementClassName);
|
||
this.mainElement.appendChild(this.sendDataElement);
|
||
|
||
this.sendDataButton = document.createElement( this.options.useLinks ? "div" : "input" );
|
||
Element.extend(this.sendDataButton);
|
||
if (this.options.useLinks) {
|
||
this.sendDataButton.innerHTML = '<a href="#">'+this.options.sendDataButtonText+'</a>';
|
||
} else {
|
||
this.sendDataButton.type = "button";
|
||
this.sendDataButton.value = this.options.sendDataButtonText;
|
||
}
|
||
this.sendDataButton.id = this.options.sendDataButtonId;
|
||
this.sendDataButton.addClassName(this.options.actionButtonClassName);
|
||
this.sendDataButton.onclick = function() {
|
||
this.setStatus(
|
||
this.evaluateTemplate(
|
||
this.options.sendingDataToServer,
|
||
{deviceName:this.getShortDeviceName(this.getCurrentDevice())}
|
||
)
|
||
);
|
||
Element.hide(this.findDevicesElement);
|
||
Element.hide(this.sendDataElement);
|
||
this.showProgressBar();
|
||
|
||
setTimeout(function(){this.postToServer()}.bind(this),1000);
|
||
return false;
|
||
}.bind(this)
|
||
this.sendDataElement.appendChild(this.sendDataButton);
|
||
|
||
if(this.options.showSendDataElementOnDeviceFound) {
|
||
Element.hide(this.sendDataElement);
|
||
}
|
||
},
|
||
|
||
/** Post data to an external server. Request options should be provided by the
|
||
* parameters element in {@link Garmin.DeviceDisplayDefaultOptions.sendDataOptions}
|
||
*
|
||
* @see Garmin.DeviceDisplayDefaultOptions.sendDataOptions
|
||
* @see Garmin.DeviceDisplay.handleException
|
||
* @version 1.6
|
||
* @param callback {function} function executed after onSuccess of the AJAX request. If failure, exception is thrown {@link Garmin.DeviceDisplay.handleException}
|
||
*/
|
||
postToServer: function(callback) {
|
||
var error;
|
||
var exceptionName = 'RemoteTransferException';
|
||
|
||
// getSendOptions overwrites those already set in sendDataOptions
|
||
if (this.options.sendDataOptions != null) {
|
||
if (this.options.getSendOptions != null) {
|
||
this.options.sendDataOptions = this.options.getSendOptions.call(this, this.options.sendDataOptions, this.garminController.getCurrentDeviceXml(), this.readDataString);
|
||
}
|
||
}
|
||
|
||
this.options.sendDataOptions.onSuccess = function(xhr) {
|
||
this.xhr = xhr;
|
||
if( this.options.afterFinishSendData != null) {
|
||
try {
|
||
this.options.afterFinishSendData.call(this,
|
||
this.xhr,
|
||
this.currentActivity ? $(this.currentActivity.replace(/Checkbox/, "Status")) : null,
|
||
this.activityDirectory,
|
||
this);
|
||
} catch (error) {
|
||
this.handleException(error);
|
||
}
|
||
}
|
||
callback.call(this);
|
||
}.bind(this);
|
||
|
||
this.options.sendDataOptions.onComplete = function(xhr) {
|
||
// Cross domain...
|
||
if( xhr == null) {
|
||
error = new Error(Garmin.RemoteTransfer.MESSAGES.generalException);
|
||
error.name = exceptionName;
|
||
this.handleException(error);
|
||
throw new Error(Garmin.RemoteTransfer.MESSAGES.noResponseException);
|
||
}
|
||
}.bind(this);
|
||
|
||
this.options.sendDataOptions.onFailure = function(xhr) {
|
||
error = new Error(xhr.statusText);
|
||
error.name = exceptionName;
|
||
this.handleException(error);
|
||
}.bind(this);
|
||
|
||
// Make the request
|
||
this.apiResponse = this.garminRemoteTransfer.openRequest(this.options.sendDataUrl, this.options.sendDataOptions);
|
||
},
|
||
|
||
/* Build read data UI components.
|
||
* @private
|
||
*/
|
||
_generateReadDataElement: function() {
|
||
this.readDataElement = document.createElement("div");
|
||
Element.extend(this.readDataElement);
|
||
this.readDataElement.id = this.options.readDataElementId;
|
||
this.readDataElement.addClassName(this.options.elementClassName);
|
||
this.mainElement.appendChild(this.readDataElement);
|
||
|
||
this.readDataButton = document.createElement( this.options.useLinks ? "div" : "input" );
|
||
Element.extend(this.readDataButton);
|
||
if (this.options.useLinks) {
|
||
this.readDataButton.innerHTML = '<a href="#">'+this.options.readDataButtonText+'</a>';
|
||
} else {
|
||
this.readDataButton.type = "button";
|
||
this.readDataButton.value = this.options.readDataButtonText;
|
||
}
|
||
this.readDataButton.id = this.options.readDataButtonId;
|
||
this.readDataButton.addClassName(this.options.actionButtonClassName);
|
||
this.readDataButton.disabled = true;
|
||
this.readDataButton.onclick = function() {
|
||
var isSupportedDevice = true;
|
||
if( this.options.restrictByDevice.length > 0){
|
||
isSupportedDevice = this._restrictByDevice();
|
||
}
|
||
|
||
if( isSupportedDevice) {
|
||
if( this.options.autoHideUnusedElements ) {
|
||
if(this.findDevicesElement) Element.hide(this.findDevicesElement);
|
||
if(this.readDataElement) Element.hide(this.readDataElement);
|
||
if(this.deviceSelectElement) Element.hide(this.deviceSelectElement);
|
||
if(this.activityDirectoryElement) Element.hide(this.activityDirectoryElement);
|
||
}
|
||
this.readDataButton.disabled = true;
|
||
this.cancelReadDataButton.disabled = false;
|
||
this.showProgressBar();
|
||
if (this.options.showReadDataTypesSelect) {
|
||
this.readSpecificTypeFromDevice(this.readDataTypesSelect.value);
|
||
} else if (this.options.readDataTypes != null) {
|
||
this.readFromDevice(this.options.readDataTypes);
|
||
} else {
|
||
this.readFromDevice(new Array(this.options.readDataType));
|
||
}
|
||
}
|
||
}.bind(this)
|
||
this.readDataElement.appendChild(this.readDataButton);
|
||
if(!this.options.showReadDataButton) {
|
||
Element.hide(this.readDataButton);
|
||
}
|
||
|
||
this.cancelReadDataButton = document.createElement( this.options.useLinks ? "div" : "input" );
|
||
Element.extend(this.cancelReadDataButton);
|
||
if (this.options.useLinks) {
|
||
this.cancelReadDataButton.innerHTML = '<a href="#">'+this.options.cancelReadDataButtonText+'</a>';
|
||
} else {
|
||
this.cancelReadDataButton.type = "button";
|
||
this.cancelReadDataButton.value = this.options.cancelReadDataButtonText;
|
||
}
|
||
this.cancelReadDataButton.id = this.options.cancelReadDataButtonId;
|
||
this.cancelReadDataButton.addClassName(this.options.actionButtonClassName);
|
||
this.cancelReadDataButton.disabled = true;
|
||
this.cancelReadDataButton.onclick = function() {
|
||
this.resetUI();
|
||
this.hideProgressBar();
|
||
this.getController().cancelReadFromDevice();
|
||
}.bind(this)
|
||
this.readDataElement.appendChild(this.cancelReadDataButton);
|
||
|
||
if(!this.options.showCancelReadDataButton) {
|
||
Element.hide(this.cancelReadDataButton);
|
||
}
|
||
|
||
/* Upload without showing selection table */
|
||
this.uploadNewButton = document.createElement( this.options.useLinks ? "div" : "input" );
|
||
Element.extend(this.uploadNewButton);
|
||
if (this.options.useLinks) {
|
||
this.uploadNewButton.innerHTML = '<a href="#">'+this.options.uploadNewButtonText+'</a>';
|
||
} else {
|
||
this.uploadNewButton.type = "button";
|
||
this.uploadNewButton.value = this.options.uploadNewButtonText;
|
||
}
|
||
this.uploadNewButton.id = this.options.uploadNewButtonId;
|
||
this.uploadNewButton.addClassName(this.options.actionButtonClassName);
|
||
this.uploadNewButton.onclick = function() {
|
||
|
||
var isSupportedDevice = true;
|
||
if( this.options.restrictByDevice.length > 0){
|
||
isSupportedDevice = this._restrictByDevice();
|
||
}
|
||
|
||
if( isSupportedDevice) {
|
||
if( this.options.autoHideUnusedElements ) {
|
||
this.advancedUploadMode = false;
|
||
this.readDataElement.hide();
|
||
this.browseComputerButton.hide();
|
||
this.uploadProgressBar.hide();
|
||
if(this.changeDeviceElement != null) { this.changeDeviceElement.hide(); }
|
||
if(this.connectedDevices != null) { this.connectedDevices.hide(); }
|
||
if(this.deviceSelectInput != null) {this.deviceSelectInput.hide();}
|
||
|
||
if( this.loadingContentElement == null) {
|
||
this._generateLoadingContent(this.statusElement);
|
||
this.loadingContentElement.className = "longStatus";
|
||
this.progressBar.className = "longProgressBar";
|
||
this.progressBarText.className = "longProgressText";
|
||
}
|
||
}
|
||
this.readDataButton.disabled = true;
|
||
this.cancelReadDataButton.disabled = false;
|
||
|
||
if (this.options.showReadDataTypesSelect) {
|
||
this.readSpecificTypeFromDevice(this.readDataTypesSelect.value);
|
||
} else if (this.options.readDataTypes != null) {
|
||
this.readFromDevice(this.options.readDataTypes);
|
||
} else {
|
||
this.readFromDevice(new Array(this.options.readDataType));
|
||
}
|
||
}
|
||
}.bind(this)
|
||
this.readDataElement.appendChild(this.uploadNewButton);
|
||
|
||
if(!this.options.showUploadNewButton) {
|
||
Element.hide(this.uploadNewButton);
|
||
}
|
||
|
||
if(this.options.showReadDataTypesSelect) {
|
||
this.readDataTypesSelect = document.createElement("select");
|
||
Element.extend(this.readDataTypesSelect);
|
||
this.readDataTypesSelect.id = this.options.readDataTypeSelectId;
|
||
this.readDataTypesSelect.disabled = true;
|
||
this.readDataElement.appendChild(this.readDataTypesSelect);
|
||
|
||
// TODO: need a more elegant way of adding options
|
||
this.readDataTypesSelect.options[0] = new Option(this.options.gpsData, Garmin.DeviceControl.FILE_TYPES.gpx);
|
||
this.readDataTypesSelect.options[1] = new Option(this.options.trainingData, Garmin.DeviceControl.FILE_TYPES.tcx);
|
||
}
|
||
|
||
if(this.options.showReadRoutesSelect) {
|
||
this.readRoutesElement = document.createElement("div");
|
||
Element.extend(this.readRoutesElement);
|
||
this.readRoutesElement.id = this.options.readRoutesElementId;
|
||
this.readRoutesElement.addClassName(this.options.readResultsElementClass);
|
||
this.readRoutesElement.innerHTML = "<span id=\"" + this.options.readRoutesSelectLabelId + "\">" + this.options.readRoutesSelectLabel + "</span>";
|
||
|
||
this.readRoutesSelect = document.createElement("select");
|
||
Element.extend(this.readRoutesSelect);
|
||
this.readRoutesSelect.id = this.options.readRoutesSelectId;
|
||
this.readRoutesSelect.addClassName(this.options.readResultsSelectClass);
|
||
this.readRoutesSelect.disabled = true;
|
||
this.readRoutesSelect.onchange = function() {
|
||
this.displayTrack( this._seriesFromSelect(this.readRoutesSelect) );
|
||
}.bind(this);
|
||
this.readRoutesElement.appendChild(this.readRoutesSelect);
|
||
this.readDataElement.appendChild(this.readRoutesElement);
|
||
|
||
if(!this.options.showReadResultsSelectOnLoad) {
|
||
Element.hide(this.readRoutesElement);
|
||
}
|
||
}
|
||
|
||
if(this.options.showReadTracksSelect) {
|
||
this.readTracksElement = document.createElement("div");
|
||
Element.extend(this.readTracksElement);
|
||
this.readTracksElement.id = this.options.readTracksElementId;
|
||
this.readTracksElement.addClassName(this.options.readResultsElementClass);
|
||
this.readTracksElement.innerHTML = "<span id=\"" + this.options.readTracksSelectLabelId + "\">" + this.options.readTracksSelectLabel + "</span>";
|
||
|
||
this.readTracksSelect = document.createElement("select");
|
||
Element.extend(this.readTracksSelect);
|
||
this.readTracksSelect.id = this.options.readTracksSelectId;
|
||
this.readTracksSelect.addClassName(this.options.readResultsSelectClass);
|
||
this.readTracksSelect.disabled = true;
|
||
this.readTracksSelect.onchange = function() {
|
||
this.displayTrack( this._seriesFromSelect(this.readTracksSelect) );
|
||
}.bind(this);
|
||
this.readTracksElement.appendChild(this.readTracksSelect);
|
||
this.readDataElement.appendChild(this.readTracksElement);
|
||
|
||
if(!this.options.showReadResultsSelectOnLoad) {
|
||
Element.hide(this.readTracksElement);
|
||
}
|
||
}
|
||
|
||
if(this.options.showReadWaypointsSelect) {
|
||
this.readWaypointsElement = document.createElement("div");
|
||
Element.extend(this.readWaypointsElement);
|
||
this.readWaypointsElement.id = this.options.readWaypointsElementId;
|
||
this.readWaypointsElement.addClassName(this.options.readResultsElementClass);
|
||
this.readWaypointsElement.innerHTML = "<span id=\"" + this.options.readWaypointsSelectLabelId + "\">" + this.options.readWaypointsSelectLabel + "</span>";
|
||
|
||
this.readWaypointsSelect = document.createElement("select");
|
||
Element.extend(this.readWaypointsSelect);
|
||
this.readWaypointsSelect.id = this.options.readWaypointsSelectId;
|
||
this.readWaypointsSelect.addClassName(this.options.readResultsSelectClass);
|
||
this.readWaypointsSelect.disabled = true;
|
||
this.readWaypointsSelect.onchange = function() {
|
||
this.displayWaypoint( this._seriesFromSelect(this.readWaypointsSelect) );
|
||
}.bind(this);
|
||
this.readWaypointsElement.appendChild(this.readWaypointsSelect);
|
||
this.readDataElement.appendChild(this.readWaypointsElement);
|
||
|
||
if(!this.options.showReadResultsSelectOnLoad) {
|
||
Element.hide(this.readWaypointsElement);
|
||
}
|
||
}
|
||
|
||
// Read Tracks Google Map
|
||
if(this.options.showReadGoogleMap) {
|
||
this.readGoogleMap = document.createElement("div");
|
||
Element.extend(this.readGoogleMap);
|
||
this.readGoogleMap.id = this.options.readGoogleMapId;
|
||
this.readGoogleMap.addClassName(this.options.readResultsElementClass);
|
||
this.readDataElement.appendChild(this.readGoogleMap);
|
||
this.readMapController = new Garmin.MapController(this.options.readGoogleMapId);
|
||
}
|
||
|
||
if(this.options.showReadDataElementOnDeviceFound) {
|
||
Element.hide(this.readDataElement);
|
||
}
|
||
},
|
||
|
||
/* Generates the activity directory element. Only one instance of the directory
|
||
* can exist on a page.
|
||
*
|
||
* @private
|
||
*/
|
||
_generateActivityDirectoryElement: function() {
|
||
if( this.activityDirectoryElement != null) {
|
||
throw new Error("Unable to generate activity directory because an instance already exists.");
|
||
}
|
||
// Create the container div to hold the directory elements
|
||
this.activityDirectoryElement = document.createElement("div");
|
||
Element.extend(this.activityDirectoryElement);
|
||
this.activityDirectoryElement.id = this.options.activityDirectoryElementId;
|
||
this.activityDirectoryElement.addClassName(this.options.activityDirectoryClass);
|
||
this.activityDirectoryElement.hide();
|
||
|
||
this.mainElement.appendChild(this.activityDirectoryElement);
|
||
},
|
||
|
||
_generateActivityTableElement: function() {
|
||
if( this.activityTable != null ) {
|
||
throw new Error("Unable to generate activity table with id " + this.options.activityTableId + " because an instance already exists.");
|
||
}
|
||
this.activities = null;
|
||
|
||
this._generateActivityTableHeader();
|
||
|
||
// Create container div that holds the table data only
|
||
this.activityDirectoryData = document.createElement("div");
|
||
Element.extend(this.activityDirectoryData);
|
||
this.activityDirectoryData.id = this.options.activityDirectoryDataId;
|
||
this.activityDirectoryElement.appendChild(this.activityDirectoryData);
|
||
|
||
// Create the table
|
||
this.activityTable = document.createElement("table");
|
||
Element.extend(this.activityTable);
|
||
this.activityTable.id = this.options.activityTableId;
|
||
this.activityTable.setAttribute('cellspacing','0');
|
||
this.activityTable.setAttribute('cellpadding','0');
|
||
this.activityDirectoryData.appendChild(this.activityTable);
|
||
|
||
this.readSelectedButton = document.createElement( this.options.useLinks ? "div" : "input" );
|
||
Element.extend(this.readSelectedButton);
|
||
if (this.options.useLinks) {
|
||
this.readSelectedButton.innerHTML = '<a href="#">'+this.options.readSelectedButtonText+'</a>';
|
||
} else {
|
||
this.readSelectedButton.type = "button";
|
||
this.readSelectedButton.value = this.options.readSelectedButtonText;
|
||
}
|
||
this.readSelectedButton.id = this.options.readSelectedButtonId;
|
||
this.readSelectedButton.addClassName(this.options.actionButtonClassName);
|
||
this.readSelectedButton.disabled = false;
|
||
this.readSelectedButton.onclick = function() {
|
||
// Read activities filtered by the API (including user selected activities)
|
||
this.readFilteredActivities();
|
||
}.bind(this);
|
||
this.activityDirectoryElement.appendChild(this.readSelectedButton);
|
||
},
|
||
|
||
/* Generate the singleton activity table header for the activity directory.
|
||
* The visible elements in the table are added dynamically after the directory is read.
|
||
* See _addToActivityTableHeader().
|
||
*
|
||
* @private
|
||
*/
|
||
_generateActivityTableHeader: function() {
|
||
if( this.activityTableHeader != null) {
|
||
throw new Error("Unable to generate activity table header: Instance of the activity table header already exists.");
|
||
}
|
||
|
||
// Create the table header
|
||
this.activityTableHeader = document.createElement("table");
|
||
Element.extend(this.activityTableHeader);
|
||
this.activityTableHeader.id = this.options.activityTableHeaderId;
|
||
this.activityTableHeader.setAttribute('cellspacing','0');
|
||
this.activityTableHeader.setAttribute('cellpadding','0');
|
||
|
||
this.activityDirectoryElement.appendChild(this.activityTableHeader);
|
||
},
|
||
|
||
/* Build write data UI components.
|
||
* @private
|
||
*/
|
||
_generateWriteDataElement: function() {
|
||
this.writeDataElement = document.createElement("div");
|
||
Element.extend(this.writeDataElement);
|
||
this.writeDataElement.id = this.options.writeDataElementId;
|
||
this.writeDataElement.addClassName(this.options.elementClassName);
|
||
this.mainElement.appendChild(this.writeDataElement);
|
||
|
||
if (!this.options.getWriteData && !this.options.getGpiWriteDescription && !this.options.getBinaryWriteDescription)
|
||
throw new Error("Can't write data because getWriteData() function nor getGpiWriteDescription() is defined");
|
||
this.writeDataButton = document.createElement( this.options.useLinks ? "div" : "input" );
|
||
Element.extend(this.writeDataButton);
|
||
if (this.options.useLinks) {
|
||
this.writeDataButton.innerHTML = '<a href="#">'+this.options.writeDataButtonText+'</a>';
|
||
} else {
|
||
this.writeDataButton.type = "button";
|
||
this.writeDataButton.value = this.options.writeDataButtonText;
|
||
}
|
||
this.writeDataButton.id = this.options.writeDataButtonId;
|
||
this.writeDataButton.addClassName(this.options.actionButtonClassName);
|
||
this.writeDataButton.disabled = true;
|
||
this.writeDataButton.onclick = function() {
|
||
var isSupportedDevice = true;
|
||
if( this.options.restrictByDevice.length > 0){
|
||
isSupportedDevice = this._restrictByDevice();
|
||
}
|
||
if( isSupportedDevice) {
|
||
this.writeDataButton.disabled = true;
|
||
this.cancelWriteDataButton.disabled = false;
|
||
if( this.options.autoHideUnusedElements ) {
|
||
if(this.findDevicesElement) {
|
||
this.findDevicesElement.hide();
|
||
}
|
||
if(this.writeDataElement) {
|
||
this.writeDataElement.hide();
|
||
}
|
||
}
|
||
this.showProgressBar();
|
||
this.writeToDevice();
|
||
}
|
||
}.bind(this);
|
||
this.writeDataElement.appendChild(this.writeDataButton);
|
||
|
||
this.cancelWriteDataButton = document.createElement( this.options.useLinks ? "div" : "input" );
|
||
Element.extend(this.cancelWriteDataButton);
|
||
if (this.options.useLinks) {
|
||
this.cancelWriteDataButton.innerHTML = '<a href="#">'+this.options.cancelWriteDataButtonText+'</a>';
|
||
} else {
|
||
this.cancelWriteDataButton.type = "button";
|
||
this.cancelWriteDataButton.value = this.options.cancelWriteDataButtonText;
|
||
}
|
||
this.cancelWriteDataButton.id = this.options.cancelWriteDataButtonId;
|
||
this.cancelWriteDataButton.addClassName(this.options.actionButtonClassName);
|
||
this.cancelWriteDataButton.disabled = false;
|
||
this.cancelWriteDataButton.onclick = function() {
|
||
this.resetUI();
|
||
this.hideProgressBar();
|
||
this.getController().cancelWriteToDevice();
|
||
}.bind(this);
|
||
this.writeDataElement.appendChild(this.cancelWriteDataButton);
|
||
|
||
if(!this.options.showCancelWriteDataButton) {
|
||
Element.hide(this.cancelWriteDataButton);
|
||
}
|
||
|
||
if(this.options.showWriteDataElementOnDeviceFound) {
|
||
Element.hide(this.writeDataElement);
|
||
}
|
||
},
|
||
|
||
/* Build "Powered by" UI components.
|
||
* @private
|
||
*/
|
||
_generateAboutElement: function() {
|
||
this.aboutElement = document.createElement("div");
|
||
Element.extend(this.aboutElement);
|
||
this.aboutElement.id = "aboutElement";
|
||
this.aboutElement.addClassName(this.options.elementClassName);
|
||
this.mainElement.appendChild(this.aboutElement);
|
||
|
||
this.copyrightText = document.createElement("span");
|
||
this.copyrightText.innerHTML = this.options.poweredByGarmin;
|
||
this.aboutElement.appendChild(this.copyrightText);
|
||
},
|
||
|
||
/* Checks the connected device against those listed in this.options.restrictByDevice
|
||
* and throws an error if the connected device is not supported by
|
||
* the application.
|
||
*
|
||
* @return true if the connected device is supported, false otherwise.
|
||
*/
|
||
_restrictByDevice: function() {
|
||
|
||
// Get connected device
|
||
var device = this.getController().getDevices()[this.deviceSelectInput.value];
|
||
var devicePartNumber = device.getPartNumber();
|
||
|
||
var isSupportedDevice = false;
|
||
// Compare to restricted list
|
||
for(var i=0; i < this.options.restrictByDevice.length; i++) {
|
||
var supportedPartNumber = this.options.restrictByDevice[i];
|
||
if(devicePartNumber == supportedPartNumber) {
|
||
isSupportedDevice = true;
|
||
}
|
||
}
|
||
|
||
// Hide everything but status
|
||
if( !isSupportedDevice ) {
|
||
error = new Error(this.options.unsupportedDevice);
|
||
error.name = "UnsupportedDeviceException";
|
||
this.handleException(error);
|
||
}
|
||
|
||
return isSupportedDevice;
|
||
},
|
||
|
||
////////////////////////// FIND DEVICES METHODS //////////////////////////
|
||
|
||
/** Entry point for searching for connected devices.
|
||
* Will attempt to unlock the plugin if necessary.
|
||
* @see Garmin.DeviceDisplay.cancelFindDevices
|
||
* @see Garmin.DeviceDisplay.onStartFindDevices
|
||
*/
|
||
startFindDevices: function() {
|
||
this.getController(true); //try to unlock plugin
|
||
if(this.options.autoHideUnusedElements){
|
||
if( this.findDevicesButton) {
|
||
this.findDevicesButton.hide();
|
||
}
|
||
if( this.browseComputerButton ) {
|
||
this.browseComputerButton.hide();
|
||
}
|
||
}
|
||
if(this.findDevicesButton)
|
||
this.findDevicesButton.disabled = true;
|
||
if (this.cancelFindDevicesButton)
|
||
this.cancelFindDevicesButton.disabled = !this.isUnlocked();
|
||
if (this.isUnlocked()) {
|
||
this.getController().findDevices();
|
||
}
|
||
},
|
||
|
||
/** Entry point for cancelling search for connected devices.
|
||
* @see Garmin.DeviceDisplay.onCancelFindDevices
|
||
*/
|
||
cancelFindDevices: function() {
|
||
this.resetUI();
|
||
this.getController().cancelFindDevices();
|
||
},
|
||
|
||
/** Call-back triggered before plugin searches for connected devices.
|
||
* @event
|
||
* @param {JSON} json
|
||
* @see Garmin.DeviceControl
|
||
* @see Garmin.DeviceDisplay.startFindDevices
|
||
*/
|
||
onStartFindDevices: function(json) {
|
||
this.setStatus(this.options.lookingForDevices);
|
||
},
|
||
|
||
/** Call-back triggered after plugin has completed its search for devices.
|
||
* @event
|
||
* @param {JSON} json
|
||
* @see Garmin.DeviceControl
|
||
* @see Garmin.DeviceDisplay.startFindDevices
|
||
*/
|
||
onFinishFindDevices: function(json) {
|
||
this.resetUI();
|
||
if(json.controller.numDevices > 0) {
|
||
this.devices = json.controller.getDevices();
|
||
|
||
var template = (this.devices.length == 1) ? this.options.foundDevice : this.options.foundDevices;
|
||
var values = {deviceName: this.getShortDeviceName(this.devices[0]), deviceCount: json.controller.numDevices};
|
||
this.setStatus( this.evaluateTemplate(template, values) );
|
||
|
||
if(this.options.showFindDevicesElement ) {
|
||
Element.show(this.findDevicesElement);
|
||
if (this.options.showDeviceButtonsOnFound) {
|
||
if (this.findDevicesButton && this.options.showFindDevicesButton)
|
||
Element.show(this.findDevicesButton);
|
||
if (this.cancelFindDevicesButton)
|
||
Element.show(this.cancelFindDevicesButton);
|
||
} else {
|
||
if (this.findDevicesButton)
|
||
Element.hide(this.findDevicesButton);
|
||
if (this.cancelFindDevicesButton)
|
||
Element.hide(this.cancelFindDevicesButton);
|
||
}
|
||
// Hide device select on single device
|
||
if ((this.devices.length < 2 && !this.options.showDeviceSelectOnSingle) || this.options.autoSelectFirstDevice) {
|
||
Element.hide(this.deviceSelectElement);
|
||
} else {
|
||
Element.show(this.deviceSelectElement);
|
||
}
|
||
// Populate the devices list based on UI option
|
||
this.options.useDeviceSelectList ? this._generateDeviceListView() : this._populateDeviceSelectDropDown();
|
||
}
|
||
|
||
if(this.options.showReadDataElementOnDeviceFound) {
|
||
Element.show(this.readDataElement);
|
||
}
|
||
|
||
if(this.options.showSendDataElementOnDeviceFound) {
|
||
Element.show(this.sendDataElement);
|
||
}
|
||
|
||
if(this.options.showWriteDataElementOnDeviceFound) {
|
||
Element.show(this.writeDataElement);
|
||
}
|
||
|
||
if(this.options.autoHideUnusedElements) {
|
||
if(this.activityDirectoryElement) {
|
||
Element.hide(this.activityDirectoryElement);
|
||
}
|
||
if(this.readDataElement) {
|
||
Element.show(this.readDataElement);
|
||
}
|
||
if(this.options.showBrowseComputer) {
|
||
if( this.browseComputerButton) {
|
||
this.browseComputerButton.show();
|
||
}
|
||
}
|
||
}
|
||
|
||
if (this.options.autoReadData) {
|
||
this.showProgressBar();
|
||
if (this.options.showReadDataTypesSelect) {
|
||
this.readSpecificTypeFromDevice(this.readDataTypesSelect.value);
|
||
} else if (this.options.readDataType != null) {
|
||
this.readSpecificTypeFromDevice(this.options.readDataType);
|
||
} else {
|
||
this.readFromDevice();
|
||
}
|
||
}
|
||
|
||
if (this.options.autoWriteData) {
|
||
this.showProgressBar();
|
||
this.writeToDevice();
|
||
}
|
||
} else { // No devices found!
|
||
if ((this.options.autoReadData || this.options.autoWriteData) && !this.options.showStatusElement) {
|
||
alert(this.options.noDeviceDetectedStatusText);
|
||
}
|
||
|
||
this.setStatus(this.options.noDeviceDetectedStatusText);
|
||
if(this.findDevicesButton) {
|
||
this.findDevicesButton.show(); // allow user to retry
|
||
}
|
||
|
||
//allow user to browse computer in activity directory
|
||
if(this.options.uploadSelectedActivities) {
|
||
this.browseComputerButton.show();
|
||
}
|
||
|
||
if(this.options.showFindDevicesElement) {
|
||
if (this.options.showCancelFindDevicesButton) {
|
||
Element.show(this.cancelFindDevicesButton);
|
||
}
|
||
if (this.options.showDeviceSelectNoDevice && !this.options.autoSelectFirstDevice) {
|
||
Element.show(this.deviceSelectElement);
|
||
}
|
||
}
|
||
}
|
||
if (this.options.afterFinishFindDevices) {
|
||
this.options.afterFinishFindDevices.call(this, this.devices);
|
||
}
|
||
},
|
||
|
||
/** Call-back for find device cancelled.
|
||
* @event
|
||
* @param {JSON} json
|
||
* @see Garmin.DeviceControl
|
||
* @see Garmin.DeviceDisplay.cancelFindDevices
|
||
*/
|
||
onCancelFindDevices: function(json) {
|
||
this.setStatus(this.options.findCancelled);
|
||
this.resetUI();
|
||
},
|
||
|
||
/** Load device list into select UI component.
|
||
* @private
|
||
*/
|
||
_populateDeviceSelectDropDown: function() {
|
||
this.deviceSelectElement.appendChild(this.deviceSelectInput);
|
||
this._clearHtmlSelect(this.deviceSelectInput);
|
||
if(this.options.showFindDevicesElement) {
|
||
for( var i=0; i < this.devices.length; i++ ) {
|
||
this.deviceSelectInput.options[i] = new Option(this.getShortDeviceName(this.devices[i]),this.devices[i].getNumber());
|
||
|
||
if(this.devices[i].getNumber() == this.getController().deviceNumber) {
|
||
this.deviceSelectInput.selectedIndex = i;
|
||
// Adding afterSelectDevice functionality to the old select UI
|
||
if (this.options.afterSelectDevice != null) {
|
||
this.options.afterSelectDevice.call(this, this.getController().deviceNumber, this.devices, this.garminController.getCurrentDeviceXml());
|
||
}
|
||
}
|
||
}
|
||
this.deviceSelectInput.onchange = function() {
|
||
var device = this.getController().getDevices()[this.deviceSelectInput.value];
|
||
this.setStatus(this.evaluateTemplate(this.options.usingDevice, {deviceName:this.getShortDeviceName(device)}));
|
||
this.getController().setDeviceNumber(this.deviceSelectInput.value);
|
||
// Adding afterSelectDevice functionality to the old select UI
|
||
if (this.options.afterSelectDevice != null) {
|
||
this.options.afterSelectDevice.call(this, this.getController().deviceNumber, this.devices, this.garminController.getCurrentDeviceXml());
|
||
}
|
||
}.bind(this);
|
||
this.deviceSelectInput.disabled = false;
|
||
}
|
||
},
|
||
|
||
/* Load device browser content.
|
||
* @private
|
||
*/
|
||
_generateAndDisplayDeviceBrowser: function() {
|
||
|
||
// Create device browser
|
||
if( this.options.useDeviceBrowser) {
|
||
if( this.deviceBrowserElement == null ) {
|
||
this.generateDeviceBrowserElement(this.devices);
|
||
}
|
||
|
||
if( !this.advancedUploadMode) {
|
||
// Simple upload
|
||
this.devicePreviewElement.show();
|
||
this.progressBar.hide();
|
||
} else {
|
||
// Advanced upload
|
||
this.deviceBrowserElement.show();
|
||
this.activityDirectoryElement.show();
|
||
}
|
||
this.statusText.hide();
|
||
this.showProgressBar();
|
||
|
||
// Show loading screen while reading device
|
||
if( this.loadingContentElement == null) {
|
||
this._generateLoadingContent(this.statusElement);
|
||
} else {
|
||
// Update the device name displayed
|
||
this._updateLoadingContent(this.evaluateTemplate(this.options.loadingContentText, {deviceName:this.getShortDeviceName(this.getCurrentDevice())}));
|
||
}
|
||
}
|
||
},
|
||
|
||
/* Load device list into select UI component.
|
||
* @private
|
||
*/
|
||
_generateDeviceListView: function() {
|
||
var deviceSelectContainer;
|
||
|
||
this._clearHtmlSelect(this.deviceSelectInput);
|
||
|
||
deviceSelectContainer = document.createElement("div");
|
||
Element.extend(deviceSelectContainer);
|
||
deviceSelectContainer.className = this.options.deviceSelectClass;
|
||
|
||
// Display change only if there are multiple devices
|
||
if( this.devices.length > 1) {
|
||
// Change device link
|
||
this.changeDeviceElement = document.createElement("div");
|
||
Element.extend(this.changeDeviceElement);
|
||
this.changeDeviceElement.id = this.options.changeDeviceElementId;
|
||
this.changeDeviceElement.className = this.options.changeDeviceClass;
|
||
this.changeDeviceElement.innerHTML = '<a href="#">'+this.options.changeDeviceButtonText+'</a>';
|
||
this.changeDeviceElement.onclick = function() {
|
||
this.devicePreviewElement.toggle();
|
||
this.connectedDevices.toggle();
|
||
this.deviceSelectInput.toggle();
|
||
}.bind(this);
|
||
this.deviceSelectElement.appendChild(this.changeDeviceElement);
|
||
}
|
||
|
||
// Display pre-selected device (first device)
|
||
this.devicePreviewElement = document.createElement("div");
|
||
Element.extend(this.devicePreviewElement);
|
||
this.devicePreviewElement.id = this.options.previewDeviceElementId;
|
||
this.devicePreviewElement.innerHTML = '<p>'+this.getShortDeviceName(this.getCurrentDevice())+'</p>';
|
||
deviceSelectContainer.appendChild(this.devicePreviewElement);
|
||
this.deviceSelectElement.appendChild(deviceSelectContainer);
|
||
|
||
if(this.options.showFindDevicesElement) {
|
||
|
||
// Connected devices label
|
||
this.connectedDevices = new Element("div", {className: this.options.connectedDevicesClass});
|
||
this.connectedDevices.update('<img src="'+ this.options.connectedDevicesImg +'" />' + this.options.connectedDevicesLabel);
|
||
this.connectedDevices.hide();
|
||
deviceSelectContainer.appendChild(this.connectedDevices);
|
||
|
||
this._populateDeviceList(this.deviceSelectInput, this._updateDevicePreview);
|
||
|
||
deviceSelectContainer.appendChild(this.deviceSelectInput);
|
||
this.deviceSelectElement.appendChild(deviceSelectContainer);
|
||
this.deviceSelectInput.hide();
|
||
this.deviceSelectInput.disabled = false;
|
||
}
|
||
},
|
||
|
||
/* Returns the current selected device according to the control object.
|
||
* @private
|
||
* @return {Device} the Device object belonging to the selected device
|
||
* @see Garmin.Device
|
||
*/
|
||
getCurrentDevice: function() {
|
||
return this.devices[this.getController().deviceNumber];
|
||
},
|
||
|
||
/* Populates the device list. 'List' is emphasized because we are counting
|
||
* on the fact that it is not a select drop down. See _populateDeviceSelectDropDown
|
||
* for that.
|
||
*
|
||
* Sets the onclick event for each device in the list. When a device is selected,
|
||
* the device number in control is set to that device. The class names of the
|
||
* entire list are also updated in order to indicate visually which device is selected.
|
||
*
|
||
* After the above is finished, the callback method is executed.
|
||
*
|
||
* @private
|
||
* @param {Element} deviceListElement the list element for the device listing
|
||
* @param {function} callback(deviceIndex) - the callback function to use when a device is selected.
|
||
* deviceIndex is the device number selected by the user.
|
||
*/
|
||
_populateDeviceList: function(deviceListElement, callback) {
|
||
var itemLink;
|
||
var listItem;
|
||
|
||
// Insert detected devices into the display list
|
||
for( var i=0; i < this.devices.length; i++ ) {
|
||
listItem = document.createElement("li");
|
||
Element.extend(listItem);
|
||
listItem.className = (i == this.getController().deviceNumber) ? "selected" : "unselected";
|
||
itemLink = document.createElement("a");
|
||
Element.extend(itemLink);
|
||
itemLink.href = "#";
|
||
itemLink.innerHTML = this.getShortDeviceName(this.devices[i]);
|
||
itemLink.onclick = function(deviceListElement, deviceIndex, devices, callback){
|
||
|
||
// Stop any existing reads and hide stuffs
|
||
this.getController().cancelReadFromDevice();
|
||
|
||
// Hide and unselect My Computer if selected
|
||
if( this.browseComputerElement != null) {
|
||
this.browseComputerElement.hide();
|
||
deviceListElement.childNodes[this.devices.length].className = "unselected";
|
||
}
|
||
this.statusElement.show();
|
||
|
||
// Set the new device to talk to
|
||
this.getController().setDeviceNumber(deviceIndex);
|
||
// Update the class names in the entire list
|
||
for(var j=0; j< this.devices.length; j++) {
|
||
var listItem = deviceListElement.childNodes[j];
|
||
listItem.className = (j == this.getController().deviceNumber) ? "selected" : "unselected";
|
||
}
|
||
// The callback function has to take these two parameters! Even if it ignores em.
|
||
callback.call(this, deviceIndex, this.devices, this.getController().getCurrentDeviceXml());
|
||
}.bind(this,deviceListElement,i,this.devices,callback); //bind with parameter
|
||
listItem.appendChild(itemLink);
|
||
deviceListElement.appendChild(listItem);
|
||
}
|
||
|
||
// Select first device
|
||
if(this.options.autoSelectFirstDevice) {
|
||
this.options.afterSelectDevice.call(this, this.getController().deviceNumber, this.devices, this.garminController.getCurrentDeviceXml());
|
||
}
|
||
},
|
||
|
||
/* Process the filename, trim it if its too long
|
||
* @param {Garmin.Device} the device whose name is to be processed
|
||
* @return {String} the truncated device name, according to the max size set in the display options
|
||
* @see Garmin.DeviceDisplayDefaultOptions.deviceLabelMaxSize
|
||
* @see Garmin.Device
|
||
*/
|
||
getShortDeviceName : function(device) {
|
||
var deviceName = device.getDisplayName();
|
||
if (deviceName.length > this.options.deviceLabelMaxSize) {
|
||
deviceName = deviceName.substring(0, this.options.deviceLabelMaxSize) + "...";
|
||
}
|
||
return deviceName;
|
||
},
|
||
|
||
/* Update the device preview display to show the device selected by the user.
|
||
* Hides the input list and shows the preview element.
|
||
* @param int deviceNumber - the device number (index) selected by the user
|
||
* @param String deviceXml - describes the device (unused right now)
|
||
*/
|
||
_updateDevicePreview : function(deviceNumber, deviceXml){
|
||
this.devicePreviewElement.innerHTML = '<p>'+this.getShortDeviceName(this.devices[deviceNumber])+'</p>';
|
||
this.devicePreviewElement.show();
|
||
if(this.deviceSelectInput) this.deviceSelectInput.hide();
|
||
if(this.connectedDevices) this.connectedDevices.hide();
|
||
},
|
||
|
||
////////////////////////////// READ METHODS //////////////////////////////
|
||
|
||
/** Initiation call for reading from a device. If a fitness device is detected reads TCX
|
||
* otherwise reads GPX.
|
||
* Upon completion if the afterFinishReadFromDevice method is defined
|
||
* it will be called. At this time you may also obtain location data using the
|
||
* getTracks and getWaypoints methods.
|
||
* @param {Array} readDataTypes list of read data types
|
||
* @see Garmin.DeviceControl.FILE_TYPES
|
||
*/
|
||
readFromDevice: function(readDataTypes) {
|
||
var deviceNumber = this.getController().deviceNumber;
|
||
var device = this.getController().getDevices()[deviceNumber];
|
||
|
||
// TODO remove this later
|
||
// Backwards compatability for deprecated method
|
||
if( this.options.readDataType != null ) {
|
||
readDataTypes = new Array();
|
||
readDataTypes[0] = this.options.readDataType;
|
||
}
|
||
|
||
// Read the first supported type in the list
|
||
var supported = null;
|
||
for(var i=0; i < readDataTypes.length; i++) {
|
||
var datatype = readDataTypes[i];
|
||
if(supported == null && this.getController().checkDeviceReadSupport(datatype)) {
|
||
supported = datatype;
|
||
|
||
if(this.options.uploadSelectedActivities) {
|
||
// Handle directory types
|
||
switch(datatype) {
|
||
case Garmin.DeviceControl.FILE_TYPES.gpxDir:
|
||
case Garmin.DeviceControl.FILE_TYPES.tcxDir:
|
||
case Garmin.DeviceControl.FILE_TYPES.fitDir:
|
||
if( this.activityTable == null) {
|
||
this._generateActivityTableElement();
|
||
} else {
|
||
this._clearActivityTable();
|
||
}
|
||
this._generateAndDisplayDeviceBrowser();
|
||
}
|
||
}
|
||
this.getController().readDataFromDevice(datatype);
|
||
}
|
||
}
|
||
|
||
// No supported types found, throw error
|
||
if( supported == null) {
|
||
var error = new Error(this.options.unsupportedDevice);
|
||
error.name = "UnsupportedDataTypeException";
|
||
this.handleException(error);
|
||
}
|
||
},
|
||
|
||
/** Read the filtered activities. Filtered activities are those picked
|
||
* by the API as well as any user-selected activities, if applicable.
|
||
*
|
||
* Activities may be uploaded after being read.
|
||
*
|
||
* Filtered activities are detected before
|
||
* the activities themselves are read, and data filters filter the data
|
||
* after the activities are read.
|
||
*
|
||
* @see Garmin.DeviceDisplay.onFinishUploads
|
||
*/
|
||
readFilteredActivities: function() {
|
||
// Make sure there are selected activities to read
|
||
if( this._directoryHasSelected() == false) {
|
||
if( this.advancedUploadMode) {
|
||
// Alert user to select
|
||
alert(this.options.errorActivitySelect);
|
||
} else {
|
||
// No new activities
|
||
this.numQueuedActivities = 0;
|
||
this.setStatus(this.options.noFilteredActivities);
|
||
if( this.options.uploadSelectedActivities ) {
|
||
this.getController()._broadcaster.dispatch("onFinishUploads", { display: this });
|
||
}
|
||
}
|
||
} else {
|
||
this.activities = null;
|
||
this.readTracksSelect.length = 0;
|
||
this.readSelectedButton.disabled = true;
|
||
if(this.options.useLinks) {
|
||
this.readSelectedButton.hide();
|
||
}
|
||
if(this.checkAllBox != null) {
|
||
this.checkAllBox.disabled = false;
|
||
}
|
||
|
||
if(this.options.useDeviceBrowser && this.advancedUploadMode){
|
||
this.statusElement.hide();
|
||
} else {
|
||
this.showProgressBar();
|
||
}
|
||
|
||
this._populateActivityQueue();
|
||
|
||
if(this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.tcxDir) {
|
||
this.fileTypeRead = Garmin.DeviceControl.FILE_TYPES.tcxDetail;
|
||
//setTimeout(function(){this._readNextSelected();}.bind(this), 0);
|
||
this._readNextSelected();
|
||
} else if(this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.fitDir) {
|
||
this.fileTypeRead = Garmin.DeviceControl.FILE_TYPES.fit;
|
||
//setTimeout(function(){this._readNextSelected();}.bind(this), 0);
|
||
this._readNextSelected();
|
||
} else if (this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.gpxDir) {
|
||
this.fileTypeRead = Garmin.DeviceControl.FILE_TYPES.gpxDetail;
|
||
|
||
while (this.activityQueue.size() != 0) {
|
||
// Display "Uploading..."
|
||
this._displayProcessingForCurrentActivity(this.activityQueue.last());
|
||
this.activityQueue.pop();
|
||
}
|
||
|
||
this.garminController.readDataFromDevice(this.fileTypeRead);
|
||
}
|
||
}
|
||
},
|
||
|
||
/** Generic read method, supporting GPX, TCX, Courses, Workouts, User Profiles,
|
||
* TCX activity directory, and TCX course directory reads. <br/>
|
||
* <br/>
|
||
* Upon completion if the afterFinishReadFromDevice method is defined
|
||
* it will be called. At this time you may also obtain location data using the
|
||
* getTracks and getWaypoints methods.<br/>
|
||
* <br/>
|
||
* Fitness detail reading (one specific activity) is not supported by this read method, refer to
|
||
* readDetailFromDevice for that. <br/>
|
||
*
|
||
* @param String readDataType - type of data to read.
|
||
* @see Garmin.DeviceControl.FILE_TYPES
|
||
* @see Garmin.DeviceDisplayDefaultOptions.afterFinishReadFromDevice
|
||
*/
|
||
readSpecificTypeFromDevice: function(readDataType) {
|
||
// Check to make sure device supports reading this type. Must do this at display layer otherwise exception will not
|
||
// bubble up to the user.
|
||
if( this.getController().checkDeviceReadSupport(readDataType) == false) {
|
||
var error = new Error(this.evaluateTemplate(this.options.unsupportedReadDataType, {dataType: readDataType}));
|
||
error.name = "UnsupportedDataTypeException";
|
||
this.handleException(error);
|
||
} else {
|
||
var deviceNumber = this.getController().deviceNumber;
|
||
var device = this.getController().getDevices()[deviceNumber];
|
||
|
||
switch(readDataType) {
|
||
case Garmin.DeviceControl.FILE_TYPES.tcxDir:
|
||
case Garmin.DeviceControl.FILE_TYPES.fitDir:
|
||
if( this.activityTable == null) {
|
||
this._generateActivityTableElement();
|
||
} else {
|
||
this._clearActivityTable();
|
||
}
|
||
this._generateAndDisplayDeviceBrowser();
|
||
// no break! keep on goin'
|
||
case Garmin.DeviceControl.FILE_TYPES.gpx:
|
||
case Garmin.DeviceControl.FILE_TYPES.gpxDir:
|
||
case Garmin.DeviceControl.FILE_TYPES.tcx:
|
||
case Garmin.DeviceControl.FILE_TYPES.crs:
|
||
case Garmin.DeviceControl.FILE_TYPES.wkt:
|
||
case Garmin.DeviceControl.FILE_TYPES.tcxProfile:
|
||
case Garmin.DeviceControl.FILE_TYPES.crsDir:
|
||
this.getController().readDataFromDevice(readDataType);
|
||
break;
|
||
case Garmin.DeviceControl.FILE_TYPES.deviceXml:
|
||
this.getController().readDataFromDevice(readDataType);
|
||
break;
|
||
default:
|
||
var error = new Error(Garmin.DeviceControl.MESSAGES.invalidFileType + readDataType);
|
||
error.name = "InvalidTypeException";
|
||
this.handleException(error);
|
||
}
|
||
}
|
||
},
|
||
|
||
/** Call-back for device read progress.
|
||
* @param {JSON} json the progress report in JSON format
|
||
* @see Garmin.DeviceDisplay.onFinishReadFromDevice
|
||
* @event
|
||
*/
|
||
onProgressReadFromDevice: function(json) {
|
||
if(this.options.showProgressBar) {
|
||
this.updateProgressBar(this.progressBarDisplay, json.progress.getPercentage());
|
||
this.updateProgressBarText(this.progressBarText, this.options.showDetailedStatus ? json.progress.text[0] + json.progress.text[1] : json.progress.text[1]);
|
||
} else {
|
||
this.setStatus(json.progress);
|
||
}
|
||
},
|
||
|
||
/** Call-back for device read cancelled.
|
||
* @see Garmin.DeviceControl
|
||
* @see Garmin.DeviceDisplay.onStartReadFromDevice
|
||
* @param {JSON} json the progress report in JSON format
|
||
*/
|
||
onCancelReadFromDevice: function(json) {
|
||
this.setStatus(this.options.cancelReadStatusText);
|
||
this.resetUI();
|
||
},
|
||
|
||
/** Call-back for device read.
|
||
* @see Garmin.DeviceControl
|
||
* @param {JSON} json the progress report in JSON format
|
||
*/
|
||
onFinishReadFromDevice: function(json) {
|
||
|
||
this.fileTypeRead = json.controller.gpsDataType;
|
||
this.readDataDoc = json.controller.gpsData;
|
||
this.readDataString = json.controller.gpsDataString;
|
||
|
||
this.setStatus(this.options.dataReadProcessing);
|
||
this.resetUI();
|
||
|
||
this.clearMapDisplay();
|
||
|
||
// select the correct factory for the parsing job, except for binary, which just passes through
|
||
switch(this.fileTypeRead) {
|
||
case Garmin.DeviceControl.FILE_TYPES.tcx:
|
||
case Garmin.DeviceControl.FILE_TYPES.tcxDir:
|
||
case Garmin.DeviceControl.FILE_TYPES.tcxDetail:
|
||
this.factory = Garmin.TcxActivityFactory;
|
||
break;
|
||
case Garmin.DeviceControl.FILE_TYPES.gpx:
|
||
case Garmin.DeviceControl.FILE_TYPES.gpxDir:
|
||
case Garmin.DeviceControl.FILE_TYPES.gpxDetail:
|
||
this.factory = Garmin.GpxActivityFactory;
|
||
break;
|
||
case Garmin.DeviceControl.FILE_TYPES.fitDir:
|
||
this.factory = Garmin.DirectoryFactory;
|
||
break;
|
||
case Garmin.DeviceControl.FILE_TYPES.fit:
|
||
case Garmin.DeviceControl.FILE_TYPES.binary:
|
||
// Post to server immediately (and finishes reading activities on the queue)
|
||
if(this.options.uploadSelectedActivities){
|
||
// Compressed data
|
||
if(this.options.uploadCompressedData) {
|
||
this.readDataString = json.controller.gpsDataStringCompressed;
|
||
}
|
||
this._postDataUpdateDisplay(this.readDataString);
|
||
}
|
||
this._finishReadProcessing(json);
|
||
break;
|
||
|
||
default:
|
||
var error = new Error( + this.fileTypeRead);
|
||
error.name = "InvalidTypeException";
|
||
this.handleException(error);
|
||
}
|
||
|
||
// parse the data into activities if possible
|
||
if (this.factory != null) {
|
||
// Convert the data obtained from the device into activities.
|
||
// If we're starting a new read session (as opposed to individual
|
||
// activity reads from the activity directory), start a new activities array
|
||
if( this.activities == null) {
|
||
this.activities = new Array();
|
||
}
|
||
|
||
// Populate this.activities
|
||
switch(this.fileTypeRead) {
|
||
case Garmin.DeviceControl.FILE_TYPES.gpxDir:
|
||
case Garmin.DeviceControl.FILE_TYPES.tcxDir:
|
||
case Garmin.DeviceControl.FILE_TYPES.fitDir:
|
||
|
||
// TODO should merge tcx and fit directory types at some point so we can share code
|
||
if(this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.tcxDir) {
|
||
this.activities = this.factory.parseDocument(this.readDataDoc);
|
||
this._createActivityDirectory(Garmin.DeviceControl.FILE_TYPES.tcxDir, this.activities);
|
||
} else if(this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.fitDir) {
|
||
var files = this.factory.parseDocument(this.readDataDoc);
|
||
var activityFiles = Garmin.DirectoryFactory.getActivityFiles(files);
|
||
// Only use activity files for the activity directory
|
||
this._createActivityDirectory(Garmin.DeviceControl.FILE_TYPES.fitDir, activityFiles);
|
||
} else if(this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.gpxDir) {
|
||
this.activities = this.factory.parseDocumentByType(this.readDataDoc, Garmin.GpxActivityFactory.GPX_TYPE.tracks);
|
||
if(this.options.uploadSelectedActivities) {
|
||
this._createActivityDirectory(Garmin.DeviceControl.FILE_TYPES.gpxDir, this.activities);
|
||
}
|
||
}
|
||
|
||
if( this.options.detectNewActivities && this.options.uploadSelectedActivities) {
|
||
// No activities on device
|
||
if( this.activityDirectory.size() == 0) {
|
||
if(this.advancedUploadMode) {
|
||
this._updateLoadingContent(this.options.noActivitiesOnDevice);
|
||
} else {
|
||
this.getController()._broadcaster.dispatch("onFinishUploads", { display: this });
|
||
}
|
||
}
|
||
else {
|
||
// There are activities to compare
|
||
this.activityMatcher = new Garmin.ActivityMatcher(this.garminController.getCurrentDeviceXml(),
|
||
this.activityDirectory.getIds(), this.options.syncDataUrl, this.options.syncDataOptions,
|
||
function(){this._finishReadProcessing(json)}.bind(this));
|
||
this.activityMatcher.run();
|
||
}
|
||
} else {
|
||
// Finished reading activities in queue, if any, so list them.
|
||
this._finishReadProcessing(json);
|
||
}
|
||
break;
|
||
case Garmin.DeviceControl.FILE_TYPES.gpxDetail:
|
||
if(this.options.uploadSelectedActivities){
|
||
this._postDataUpdateDisplay(this.readDataString);
|
||
}
|
||
break;
|
||
case Garmin.DeviceControl.FILE_TYPES.tcxDetail:
|
||
|
||
// Store this read activity
|
||
// TODO: May not need this line, merge logic with binary type
|
||
this.activities = this.activities.concat( this.factory.parseDocument(this.readDataDoc) );
|
||
|
||
// Post to server (and finishes reading activities on the queue)
|
||
if(this.options.uploadSelectedActivities){
|
||
// Compressed data
|
||
if(this.options.uploadCompressedData) {
|
||
this.readDataString = json.controller.gpsDataStringCompressed;
|
||
}
|
||
this._postDataUpdateDisplay(this.readDataString);
|
||
}
|
||
// Finished reading activities in queue, if any, so list them.
|
||
this._finishReadProcessing(json);
|
||
break;
|
||
default:
|
||
this.activities = this.factory.parseDocument(this.readDataDoc);
|
||
// filter the activities
|
||
this._applyDataFilters();
|
||
// Finished reading activities in queue, if any, so list them.
|
||
this._finishReadProcessing(json);
|
||
break;
|
||
}
|
||
|
||
}
|
||
},
|
||
|
||
_postDataUpdateDisplay: function(data) {
|
||
// Post to server (and finishes reading activities on the queue)
|
||
if( this.loadingContentElement != null) {
|
||
this.loadingContentElement.innerHTML = this.options.uploadingStatusText;
|
||
this.loadingContentElement.show();
|
||
}
|
||
this._postActivityToServer(data);
|
||
},
|
||
|
||
_applyDataFilters: function() {
|
||
var dataFilters = this.options.dataFilters;
|
||
if (dataFilters != null) {
|
||
for (var i = 0; i < dataFilters.length; i++) {
|
||
if (dataFilters[i].run != null) {
|
||
dataFilters[i].run(this.activities, garminFilterQueue);
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
/** Process the read data. Calls afterFinishReadFromDevice when finished.
|
||
* @see Garmin.DeviceDisplay.afterFinishReadFromDevice
|
||
* @param {JSON} json the progress report in JSON format
|
||
*/
|
||
_finishReadProcessing: function(json) {
|
||
if (garminFilterQueue != null && garminFilterQueue.length > 0) {
|
||
//console.debug("waiting for filters to finish...");
|
||
setTimeout(function(){this._finishReadProcessing(json);}.bind(this), 500);
|
||
} else {
|
||
|
||
// list activities and set status to indicate how many were found
|
||
if( this.activityQueue == null || this.activityQueue.length == 0 ) {
|
||
|
||
// List the activities
|
||
if(this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.fitDir){
|
||
|
||
var summary = this._listDirectory(this.activityDirectory);
|
||
// Display # of activities found
|
||
this.setStatus( this.evaluateTemplate(this.options.dataFound, summary) );
|
||
}
|
||
if( this.activities != null && this.activities.length > 0) {
|
||
if( this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.tcxDir ||
|
||
this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.gpxDir) {
|
||
var summary = this._listDirectory(this.activityDirectory);
|
||
} else {
|
||
var summary = this._listActivities(this.activities);
|
||
}
|
||
|
||
// Display # of activities found
|
||
this.setStatus( this.evaluateTemplate(this.options.dataFound, summary) );
|
||
}
|
||
|
||
// Disable appropriate buttons after read is finished
|
||
if(this.options.uploadSelectedActivities) {
|
||
switch(this.fileTypeRead) {
|
||
case Garmin.DeviceControl.FILE_TYPES.gpx:
|
||
case Garmin.DeviceControl.FILE_TYPES.gpxDir:
|
||
case Garmin.DeviceControl.FILE_TYPES.tcx:
|
||
case Garmin.DeviceControl.FILE_TYPES.crs:
|
||
case Garmin.DeviceControl.FILE_TYPES.tcxDir:
|
||
case Garmin.DeviceControl.FILE_TYPES.crsDir:
|
||
case Garmin.DeviceControl.FILE_TYPES.fitDir:
|
||
this.deviceSelectInput.disabled = true;
|
||
if( this.advancedUploadMode) {
|
||
// Advanced upload
|
||
if(this.loadingContentElement != null) {
|
||
this.loadingContentElement.hide();
|
||
}
|
||
} else {
|
||
// Simple upload
|
||
this.progressBar.hide();
|
||
this.uploadProgressBar.show();
|
||
this.readFilteredActivities();
|
||
}
|
||
break;
|
||
case Garmin.DeviceControl.FILE_TYPES.gpxDetail:
|
||
case Garmin.DeviceControl.FILE_TYPES.tcxDetail:
|
||
case Garmin.DeviceControl.FILE_TYPES.crsDetail:
|
||
case Garmin.DeviceControl.FILE_TYPES.fit:
|
||
case Garmin.DeviceControl.FILE_TYPES.binary:
|
||
this.readSelectedButton.disabled = false;
|
||
if( this.options.useLinks) {
|
||
this.readSelectedButton.show();
|
||
}
|
||
this.readSelectedButton.disabled = false;
|
||
if(this.checkAllBox != null) {
|
||
this.checkAllBox.disabled = false;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// pass data to the user if they want it
|
||
if (this.options.afterFinishReadFromDevice) {
|
||
var dataString = this.factory != null ? this.factory.produceString(this.activities) : json.controller.gpsDataString;
|
||
var dataDoc = this.factory != null ? Garmin.XmlConverter.toDocument(dataString): json.controller.gpsData;
|
||
this.options.afterFinishReadFromDevice(dataString, dataDoc, json.controller.gpsDataType, this.activities, this);
|
||
}
|
||
}
|
||
},
|
||
|
||
/** As uploads continue processing, this method will be called. This is called once
|
||
* per upload item. This does not track byte-progress of a single upload.
|
||
* @event
|
||
* @param {JSON} json the progress report in JSON format
|
||
* @see Garmin.DeviceDisplay.onFinishUploads
|
||
*/
|
||
onProgressUpload: function(json) {
|
||
if(this.options.showProgressBar) {
|
||
this.updateProgressBar(this.uploadProgressBarDisplay, json.progress.percentage);
|
||
this.updateProgressBarText(this.uploadProgressBarText, json.progress.text);
|
||
} else {
|
||
this.setStatus(json.progress);
|
||
}
|
||
},
|
||
|
||
/** Returns the current status of the upload progress based on the activity queue.
|
||
* If there is no upload in progress, all values will be 0.
|
||
* @returns {JSON} json object with report values and current DeviceDisplay instance
|
||
* @returns json.progress
|
||
* @returns {String} json.progress.current the current upload index from the activity queue
|
||
* @returns {String} json.progress.total whole number of uploads finished
|
||
* @returns {String} json.progress.percentage percentage value of uploads finished
|
||
* @returns {String} json.progress.text upload progress text to display to user
|
||
* @returns {Garmin.DeviceDisplay} json.display the current DeviceDisplay instance for UI purposes
|
||
*/
|
||
getUploadProgressJson: function() {
|
||
|
||
var currentVal;
|
||
var totalVal;
|
||
var percentageVal;
|
||
|
||
if( this.numQueuedActivities == null || this.activityQueue == null || this.activityQueue.length == 0) {
|
||
currentVal = 0;
|
||
totalVal = 0;
|
||
percentageVal = 0;
|
||
} else {
|
||
currentVal = this.numQueuedActivities - this.activityQueue.length;
|
||
totalVal = this.numQueuedActivities;
|
||
percentageVal = currentVal / totalVal * 100;
|
||
}
|
||
|
||
return {
|
||
progress: {
|
||
current: currentVal,
|
||
total: totalVal,
|
||
percentage: percentageVal,
|
||
text: this.evaluateTemplate(this.options.uploadProgressStatusText, {currentUpload: currentVal, totalUploads: totalVal})
|
||
},
|
||
display: this
|
||
};
|
||
},
|
||
|
||
/* Reads the user-selected activities from the device by using the activity queue.
|
||
*/
|
||
_readNextSelected: function() {
|
||
// Look at the next selected activity on the queue. (The queue only holds selected activities)
|
||
this._displayProcessingForCurrentActivity(this.activityQueue.last());
|
||
this.setStatus(this.options.uploadingActivities);
|
||
|
||
var currentActivityId = $(this.currentActivity).value;
|
||
|
||
if( this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.tcxDetail ) {
|
||
this.garminController.readDetailFromDevice(this.fileTypeRead, currentActivityId);
|
||
} else if( this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.fit ) {
|
||
var deviceNumber = this.getCurrentDevice().getNumber();
|
||
this.garminController.getBinaryFile(deviceNumber, this.activityDirectory.getEntry(currentActivityId).path);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Displays the processing icon for a given activity
|
||
*
|
||
* @param activity - the activity that will have the processing icon
|
||
*/
|
||
_displayProcessingForCurrentActivity: function(activity) {
|
||
this.currentActivity = activity;
|
||
|
||
// Display 'processing' image next to corresponding activity in table
|
||
var statusCellIdElement = $(this.currentActivity.replace(/Checkbox/, "Status"));
|
||
statusCellIdElement.innerHTML = this.options.statusCellProcessingImg;
|
||
},
|
||
|
||
/** Stop uploading activities in the queue, and go on to finished screen.
|
||
* Useful for certain error cases.
|
||
* @see Garmin.DeviceDisplay.onFinishUploads
|
||
*/
|
||
stopQueuedUploads: function() {
|
||
this.clearActivityQueue();
|
||
// Broadcast all uploads finished
|
||
this.getController()._broadcaster.dispatch("onFinishUploads", { display: this });
|
||
},
|
||
|
||
/* Posts the last read activity data from the activityQueue. See {@link this.options.sendDataUrl},
|
||
* {@link this.options.sendDataOptions} for designating the server and options for the AJAX request.
|
||
*
|
||
* A custom handler is also possible by defining {@link this.options.postActivityHandler}. Defining
|
||
* this method will override the default Send Data implementation provided by this API.
|
||
*
|
||
* @param String dataString - the data string to post to server
|
||
* @see Garmin.DeviceDisplayDefaultOptions.postActivityHandler
|
||
*/
|
||
_postActivityToServer: function(dataString) {
|
||
if( this.options.sendDataUrl == null && this.options.postActivityHandler == null ) {
|
||
throw new Error("Need to define either sendDataUrl or the postActivityHandler in display" +
|
||
" options, depending on desired behavior.");
|
||
}
|
||
else {
|
||
// nested function
|
||
var finishPostProcessing = function() {
|
||
// Exceptions are handled in postToServer. Even if errors occur, doesn't necessarily mean
|
||
// that the rest of the uploads should be stopped. The uploadQueue needs to be cleared
|
||
// if that is the desired behavior.
|
||
this.activityQueue.pop();
|
||
|
||
// Broadcast upload progress
|
||
this.getController()._broadcaster.dispatch("onProgressUpload", this.getUploadProgressJson());
|
||
|
||
// TODO: This doesn't quite belong here, but it's the only way to ensure synchronization.
|
||
if(this.activityQueue.length > 0) {
|
||
// Read what's left in the queue
|
||
this._readNextSelected();
|
||
} else {
|
||
// Broadcast all uploads finished
|
||
this.getController()._broadcaster.dispatch("onFinishUploads", { display: this });
|
||
}
|
||
}.bind(this);
|
||
|
||
if( this.options.sendDataUrl != null ) {
|
||
// post the activity and then read the next one
|
||
this.postToServer(finishPostProcessing);
|
||
}
|
||
else if( this.options.postActivityHandler != null) {
|
||
this.options.postActivityHandler(dataString, this);
|
||
finishPostProcessing();
|
||
}
|
||
}
|
||
},
|
||
|
||
/** Callback when all uploads are finished. The display is passed in as the single param.
|
||
* @event
|
||
* @param {Garmin.DeviceDisplay} the current DeviceDisplay instance
|
||
* @see Garmin.DeviceDisplayDefaultOptions.afterFinishUploads
|
||
*/
|
||
onFinishUploads: function(display) {
|
||
|
||
// Activities were uploaded
|
||
if( this.numQueuedActivities > 0) {
|
||
this.loadingContentElement.hide();
|
||
}
|
||
// Nothing to upload, so show it
|
||
else if( !this.advancedUploadMode ) {
|
||
this.loadingContentElement.className = 'shortStatus';
|
||
this.activityTable.hide();
|
||
this._updateLoadingContent('No new activities to upload.');
|
||
}
|
||
// Show the directory for results
|
||
this.activityDirectoryElement.show();
|
||
this.uploadProgressBar.hide();
|
||
this.findDevicesElement.hide();
|
||
this.readSelectedButton.hide();
|
||
this.deviceBrowserElement.hide();
|
||
|
||
if( this.options.afterFinishUploads ) {
|
||
this.options.afterFinishUploads.call(this, this);
|
||
}
|
||
},
|
||
|
||
_clearActivityTable: function() {
|
||
//clear previous data, if any, including the header
|
||
while(this.activityTableHeader.rows.length > 0) {
|
||
this.activityTableHeader.deleteRow(0);
|
||
}
|
||
while(this.activityTable.rows.length > 0) {
|
||
this.activityTable.deleteRow(0);
|
||
}
|
||
},
|
||
|
||
/** Creates the activity directory of all activities (activity IDs) on the device
|
||
* of the user-selected type. Most recent entries are first.
|
||
* @param listType String type of directory described by the list
|
||
* @param list Array list of directory entries, of any type. Currently expects activities (tcx) or files (fit)
|
||
* @private
|
||
*/
|
||
_createActivityDirectory: function(listType, list) {
|
||
|
||
if( this.advancedUploadMode ) {
|
||
this.activityDirectoryElement.show();
|
||
}
|
||
this.activityQueue = new Array(); // Initialized here so that we can detect activity selection read status
|
||
|
||
this.activityDirectory = new Garmin.ActivityDirectory();
|
||
|
||
for( var jj = 0; jj < list.length; jj++) {
|
||
var id;
|
||
var name;
|
||
var duration;
|
||
var entry;
|
||
|
||
if( listType == Garmin.DeviceControl.FILE_TYPES.tcxDir) {
|
||
// list of Garmin.Activity
|
||
var activity = list[jj];
|
||
id = activity.getAttribute(Garmin.Activity.ATTRIBUTE_KEYS.activityName);
|
||
name = activity.getSummaryValue(Garmin.Activity.SUMMARY_KEYS.startTime).getValue().getTimeString();
|
||
duration = activity.getStartTime().getDurationTo(activity.getEndTime()); // Correct time zone
|
||
|
||
entry = this.activityDirectory.addEntry(id, name, duration, null);
|
||
|
||
} else if( listType == Garmin.DeviceControl.FILE_TYPES.fitDir) {
|
||
// list of Garmin.File
|
||
var file = list[jj];
|
||
id = file.getIdValue(Garmin.FileId.KEYS.id);
|
||
name = file.getAttribute(Garmin.File.ATTRIBUTE_KEYS.creationTime).getTimeString();
|
||
this.activityDirectory.addEntry(id, name, null, null);
|
||
this.activityDirectory.getEntry(id).path = file.getAttribute(Garmin.File.ATTRIBUTE_KEYS.path);
|
||
} else if (listType == Garmin.DeviceControl.FILE_TYPES.gpxDir) {
|
||
// list of Garmin.Activity
|
||
var activity = list[jj];
|
||
var summaryValue = activity.getSummaryValue(Garmin.Activity.SUMMARY_KEYS.startTime);
|
||
var attribute = activity.getAttribute(Garmin.Activity.ATTRIBUTE_KEYS.activityName);
|
||
|
||
// Make sure these are not null or else we will skip
|
||
if (summaryValue != null && attribute != null)
|
||
{
|
||
id = summaryValue.getValue().getXsdString();
|
||
name = attribute;
|
||
|
||
this.activityDirectory.addEntry(id, name, null, null);
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
/* Creates the activity queue of selected activities to read in detail from device.
|
||
* Called after the user has finished selecting activities and also after the API
|
||
* does its synchronization thing). The queue is an Array that is constructed and
|
||
* then reversed to simulate a queue.
|
||
*/
|
||
_populateActivityQueue: function() {
|
||
var checkBoxName = "activityItemCheckbox";
|
||
// TODO Create a class for the activity queue
|
||
for( var jj = 0; jj < this.activityDirectory.size(); jj++) {
|
||
var checkBoxElementId = checkBoxName + jj;
|
||
|
||
if($(checkBoxElementId).checked == true){
|
||
this.activityQueue.push(checkBoxElementId);
|
||
}
|
||
|
||
var activityId = this.activityDirectory.getIds()[jj];
|
||
this.activityDirectory.getEntry(activityId).displayElementId = checkBoxElementId;
|
||
}
|
||
// Reverse the array to turn it into a queue
|
||
this.activityQueue.reverse();
|
||
|
||
// Save the original size for status reporting
|
||
this.numQueuedActivities = this.activityQueue.length;
|
||
},
|
||
|
||
/** Empties the activity queue if it has any entries.
|
||
*/
|
||
clearActivityQueue: function() {
|
||
for( var i=0; i < this.activityQueue.length; i++) {
|
||
this.activityQueue.pop();
|
||
}
|
||
},
|
||
|
||
/* The activityTable object is the HTML table element on the demo page. This function
|
||
* adds the necessary row to the table with the activity data.
|
||
* @param int index - the internal index assigned to the activity value in order
|
||
* to update the table status
|
||
* @param {Garmin.ActivityDirectory.Entry} entry - entry to add to the table
|
||
* @see afterTableInsert
|
||
*/
|
||
_addToActivityTable: function(index, entry) {
|
||
|
||
var tableIndex = 0;
|
||
|
||
var activityId = entry.id;
|
||
|
||
var row = this.activityTable.insertRow(this.activityTable.rows.length); // append a new row to the table
|
||
// Color odd rows
|
||
if( (index+2) % 2 != 0) {
|
||
row.setAttribute('bgcolor', '#f3f3f3');
|
||
}
|
||
|
||
var selectCell = row.insertCell(tableIndex++);
|
||
selectCell.width = '40'; // Set widths to match header
|
||
selectCell.align = 'right';
|
||
|
||
var checkbox = document.createElement("input");
|
||
Element.extend(checkbox);
|
||
checkbox.id = "activityItemCheckbox" + index;
|
||
checkbox.type = "checkbox";
|
||
checkbox.value = activityId;
|
||
|
||
// When checkbox is clicked, pass last 2 args to callback method, which is bounded to the display object
|
||
// TODO pass the entire directory object and handle appropriately
|
||
checkbox.observe('click', this.onActivitySelect.bind(this, checkbox.id, this.activityDirectory.getIds()));
|
||
selectCell.appendChild(checkbox);
|
||
|
||
var nameCell = row.insertCell(tableIndex++);
|
||
nameCell.width = '220';
|
||
|
||
if( entry.duration != null) {
|
||
var durationCell = row.insertCell(tableIndex++);
|
||
durationCell.width = '210';
|
||
}
|
||
|
||
var statusCell = row.insertCell(tableIndex++);
|
||
statusCell.id = "activityItemStatus" + index;
|
||
|
||
// Name and duration cells
|
||
if( this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.tcxDir ||
|
||
this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.fitDir ||
|
||
this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.gpxDir) {
|
||
nameCell.innerHTML = entry.name;
|
||
|
||
if( durationCell != null) {
|
||
durationCell.innerHTML = entry.duration;
|
||
}
|
||
}
|
||
else if( this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.crsDir ) {
|
||
nameCell.innerHTML = activityId;
|
||
}
|
||
|
||
if( this.options.afterTableInsert ) {
|
||
this.options.afterTableInsert.call(this, index, entry, statusCell, checkbox, row, this.activityMatcher);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Adds the single row to the activity table header. The columns in the table
|
||
* are determined by the data available in the directory, using an all or nothing
|
||
* check.
|
||
* @param directory {Garmin.ActivityDirectory} the activity directory to build the table header off of
|
||
*/
|
||
_addToActivityTableHeader: function(directory) {
|
||
|
||
var tableIndex = 0;
|
||
|
||
var row = this.activityTableHeader.insertRow(0); // append a new row to the table
|
||
|
||
var selectCell = row.insertCell(tableIndex++);
|
||
selectCell.id = 'selectAllHeader';
|
||
selectCell.width = '40';
|
||
selectCell.align = 'left';
|
||
|
||
var nameCell = row.insertCell(tableIndex++);
|
||
nameCell.id = 'nameHeader';
|
||
nameCell.width = '220';
|
||
nameCell.align = 'left';
|
||
nameCell.innerHTML = this.options.getActivityDirectoryHeaderIdLabel.call(this);
|
||
|
||
if( directory.getFirstEntry().duration != null) {
|
||
var durationCell = row.insertCell(tableIndex++);
|
||
durationCell.id = 'durationHeader';
|
||
durationCell.width = '210';
|
||
durationCell.align = 'left';
|
||
durationCell.innerHTML = this.options.activityDirectoryHeaderDuration;
|
||
}
|
||
|
||
var statusCell = row.insertCell(tableIndex++);
|
||
statusCell.innerHTML = this.options.activityDirectoryHeaderStatus;
|
||
|
||
// Only display 'check all' box if there's no upload limit
|
||
if( this.options.uploadMaximum < 1) {
|
||
this.checkAllBox = document.createElement("input");
|
||
Element.extend(this.checkAllBox);
|
||
this.checkAllBox.id = "checkAllBox";
|
||
this.checkAllBox.type = "checkbox";
|
||
|
||
if( this.options.uploadMaximum == 0) {
|
||
this.checkAllBox.hide();
|
||
}
|
||
selectCell.appendChild(this.checkAllBox);
|
||
|
||
this.checkAllBox.onclick = function() { this._checkAllDirectory(); }.bind(this);
|
||
}
|
||
},
|
||
|
||
/** Callback for enforcing upload selection limit. Called each time the user modifies selection.
|
||
* @param String elementId - the ID of the input element selected to trigger this callback
|
||
* @param Array activityDirectory - array of all activity IDs listed in the activity directory
|
||
* @see uploadSelectionLimit
|
||
*/
|
||
onActivitySelect: function(elementId, activityDirectory) {
|
||
var selectedCount = 0;
|
||
for( var jj = 0; jj < activityDirectory.length; jj++) {
|
||
if( $("activityItemCheckbox" + jj).checked == true){
|
||
selectedCount++;
|
||
}
|
||
}
|
||
if( this.options.uploadMaximum > 0 ) {
|
||
if( selectedCount > this.options.uploadMaximum ) {
|
||
// Cancel the selection
|
||
$(elementId).checked = false;
|
||
alert( this.evaluateTemplate(this.options.uploadMaximumReached, {activities:this.options.uploadMaximum}) );
|
||
}
|
||
}
|
||
},
|
||
|
||
/* Selects all checkboxes in the activity directory, which selects all activities to be read from the device.
|
||
* uploadMaximum must be -1 or 0 (no limit) for this method to be called.
|
||
*/
|
||
_checkAllDirectory: function() {
|
||
for( var boxIndex=0; boxIndex < this.activityDirectory.size(); boxIndex++ ) {
|
||
$("activityItemCheckbox" + boxIndex).checked = this.checkAllBox.checked;
|
||
}
|
||
},
|
||
|
||
/* Checks if any activities in directory listing are selected. Returns true if so, false otherwise.
|
||
*/
|
||
_directoryHasSelected: function() {
|
||
for( var boxIndex=0; boxIndex < this.activityDirectory.size(); boxIndex++ ) {
|
||
if ( $("activityItemCheckbox" + boxIndex).checked == true) {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
},
|
||
|
||
/* Lists the directory and returns summary data (# of entries).
|
||
* @param entries {Garmin.ActivityDirectory}
|
||
*/
|
||
_listDirectory: function(activityDirectory) {
|
||
// clear existing entries
|
||
this._clearHtmlSelect(this.readTracksSelect);
|
||
|
||
this._addToActivityTableHeader(activityDirectory);
|
||
|
||
// loop through each entry
|
||
var entries = activityDirectory.getEntries();
|
||
for (var i = 0; i < activityDirectory.size(); i++) {
|
||
var entry = entries[i];
|
||
|
||
// Directory entry
|
||
if(this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.tcxDir
|
||
|| this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.crsDir
|
||
|| this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.fitDir
|
||
|| this.fileTypeRead == Garmin.DeviceControl.FILE_TYPES.gpxDir ){
|
||
this._addToActivityTable(i, entry);
|
||
}
|
||
}
|
||
|
||
return {tracks: activityDirectory.size()};
|
||
},
|
||
|
||
_listActivities: function(activities) {
|
||
var numOfRoutes = 0;
|
||
var numOfTracks = 0;
|
||
var numOfWaypoints = 0;
|
||
|
||
// clear existing entries
|
||
this._clearHtmlSelect(this.readRoutesSelect);
|
||
this._clearHtmlSelect(this.readTracksSelect);
|
||
this._clearHtmlSelect(this.readWaypointsSelect);
|
||
|
||
// loop through each activity
|
||
for (var i = 0; i < activities.length; i++) {
|
||
var activity = activities[i];
|
||
var series = activity.getSeries();
|
||
// loop through each series in the activity
|
||
for (var j = 0; j < series.length; j++) {
|
||
var curSeries = series[j];
|
||
if (curSeries.getSeriesType() == Garmin.Series.TYPES.history) {
|
||
// activity contains a series of type history, list the track
|
||
this._listTrack(activity, curSeries, i, j);
|
||
numOfTracks++;
|
||
} else if (curSeries.getSeriesType() == Garmin.Series.TYPES.route) {
|
||
// activity contains a series of type route, list the route
|
||
this._listRoute(activity, curSeries, i, j);
|
||
numOfRoutes++;
|
||
} else if (curSeries.getSeriesType() == Garmin.Series.TYPES.waypoint) {
|
||
// activity contains a series of type waypoint, list the waypoint
|
||
this._listWaypoint(activity, curSeries, i, j);
|
||
numOfWaypoints++;
|
||
}
|
||
}
|
||
}
|
||
if (this.options.showReadRoutesSelect) {
|
||
if(numOfRoutes > 0) {
|
||
Element.show(this.readRoutesElement);
|
||
this.readRoutesSelect.disabled = false;
|
||
this.displayTrack( this._seriesFromSelect(this.readRoutesSelect) );
|
||
} else {
|
||
Element.hide(this.readRoutesElement);
|
||
this.readRoutesSelect.disabled = true;
|
||
}
|
||
}
|
||
if (this.options.showReadTracksSelect) {
|
||
if(numOfTracks > 0) {
|
||
Element.show(this.readTracksElement);
|
||
this.readTracksSelect.disabled = false;
|
||
this.displayTrack( this._seriesFromSelect(this.readTracksSelect) );
|
||
} else {
|
||
Element.hide(this.readTracksElement);
|
||
this.readTracksSelect.disabled = true;
|
||
}
|
||
}
|
||
if (this.options.showReadWaypointsSelect) {
|
||
if(numOfWaypoints > 0) {
|
||
Element.show(this.readWaypointsElement);
|
||
this.readWaypointsSelect.disabled = false;
|
||
this.displayWaypoint( this._seriesFromSelect(this.readWaypointsSelect) );
|
||
} else {
|
||
Element.hide(this.readWaypointsElement);
|
||
this.readWaypointsSelect.disabled = true;
|
||
}
|
||
}
|
||
return {routes: numOfRoutes, tracks: numOfTracks, waypoints: numOfWaypoints};
|
||
},
|
||
|
||
/* Load route names into select UI component.
|
||
*
|
||
*/
|
||
_listRoute: function(activity, series, activityIndex, seriesIndex) {
|
||
// make sure the select component exists
|
||
if (this.readRoutesSelect) {
|
||
var routeName = activity.getAttribute(Garmin.Activity.ATTRIBUTE_KEYS.activityName);
|
||
this.readRoutesSelect.options[this.readRoutesSelect.length] = new Option(routeName, activityIndex + "," + seriesIndex);
|
||
}
|
||
},
|
||
|
||
/* Load track name into select UI component.
|
||
*
|
||
*/
|
||
_listTrack: function(activity, series, activityIndex, seriesIndex) {
|
||
// make sure the select component exists
|
||
if (this.readTracksSelect) {
|
||
var startDate = activity.getSummaryValue(Garmin.Activity.SUMMARY_KEYS.startTime).getValue();
|
||
var endDate = activity.getSummaryValue(Garmin.Activity.SUMMARY_KEYS.endTime).getValue();
|
||
var values = {date:startDate.getDateString(), duration:startDate.getDurationTo(endDate)}
|
||
var trackName = this.evaluateTemplate(this.options.trackListing, values)
|
||
this.readTracksSelect.options[this.readTracksSelect.length] = new Option(trackName, activityIndex + "," + seriesIndex);
|
||
}
|
||
},
|
||
|
||
/* Load waypoint name into select UI component.
|
||
*
|
||
*/
|
||
_listWaypoint: function(activity, series, activityIndex, seriesIndex) {
|
||
// make sure the select component exists
|
||
if (this.readWaypointsSelect) {
|
||
var wptName = activity.getAttribute(Garmin.Activity.ATTRIBUTE_KEYS.activityName);
|
||
this.readWaypointsSelect.options[this.readWaypointsSelect.length] = new Option(wptName, activityIndex + "," + seriesIndex);
|
||
}
|
||
},
|
||
|
||
|
||
/* Retreive the two index string value from the selected index.
|
||
* Activities are stored in Select objects as strings with 2
|
||
* numbers: "(index of array), (index of series)", for example: "1,1".
|
||
* @param Select select - the Select DOM instance
|
||
* @type Garmin.Series
|
||
* @return a Series instance
|
||
*/
|
||
_seriesFromSelect: function(select) {
|
||
var indexesStr = select.options[select.selectedIndex].value;
|
||
var indexes = indexesStr.split(",", 2);
|
||
var activity = this.activities[parseInt(indexes[0])];
|
||
var series = activity.getSeries()[parseInt(indexes[1])];
|
||
return series;
|
||
},
|
||
|
||
|
||
/** Draws a simple line on the map using the Garmin.MapController.
|
||
*
|
||
* @param Garmin.Series series - that contains a track.
|
||
*/
|
||
displayTrack: function(series) {
|
||
if(this.options.showReadGoogleMap) {
|
||
this.readMapController.map.clearOverlays();
|
||
this.readMapController.drawTrack(series);
|
||
}
|
||
},
|
||
|
||
/** Draws a point (usualy as a thumb tack) on the map using the Garmin.MapController.
|
||
* @param Garmin.Series series - that contains the lat/lon position of the point.
|
||
*/
|
||
displayWaypoint: function(series) {
|
||
if(this.options.showReadGoogleMap) {
|
||
this.readMapController.map.clearOverlays();
|
||
this.readMapController.drawWaypoint(series);
|
||
}
|
||
},
|
||
|
||
/** Clears overlays from map.
|
||
*
|
||
*/
|
||
clearMapDisplay: function() {
|
||
if(this.options.showReadGoogleMap) {
|
||
this.readMapController.map.clearOverlays();
|
||
}
|
||
},
|
||
|
||
|
||
////////////////////////////// WRITE METHODS //////////////////////////////
|
||
|
||
/** Writes any supported data to the device.
|
||
*
|
||
* Requires that the option writeDataType field be set correctly to any of the following values
|
||
* located in Garmin.DeviceControl.FILE_TYPES:
|
||
*
|
||
* gpx, crs, binary, goals
|
||
*
|
||
* @throws InvalidTypeException
|
||
* @see Garmin.DeviceDisplayDefaultOptions.writeDataType
|
||
* @see Garmin.DeviceControl.FILE_TYPES
|
||
*/
|
||
writeToDevice: function() {
|
||
var dataType = null;
|
||
var supported = null;
|
||
|
||
// TODO remove this later
|
||
// Backwards compatability for deprecated method
|
||
if( this.options.writeDataType != null ) {
|
||
this.options.writeDataTypes = new Array();
|
||
this.options.writeDataTypes[0] = this.options.writeDataType;
|
||
}
|
||
|
||
for (var i = 0; i < this.options.writeDataTypes.length; i++) {
|
||
dataType = this.options.writeDataTypes[i];
|
||
var deviceWriteSupport = this.getController().checkDeviceWriteSupport(dataType);
|
||
//var deviceWriteSupport = this.getController().checkDeviceWriteSupport(this.options.writeDataType);
|
||
|
||
if (supported == null && deviceWriteSupport == true) {
|
||
supported = dataType;
|
||
|
||
switch (dataType) {
|
||
case Garmin.DeviceControl.FILE_TYPES.goals:
|
||
case Garmin.DeviceControl.FILE_TYPES.gpx:
|
||
case Garmin.DeviceControl.FILE_TYPES.crs:
|
||
case Garmin.DeviceControl.FILE_TYPES.wkt:
|
||
case Garmin.DeviceControl.FILE_TYPES.tcxProfile:
|
||
case Garmin.DeviceControl.FILE_TYPES.nlf:
|
||
var writeData = this.options.getWriteData();
|
||
var writeDataFileName = this.options.getWriteDataFileName();
|
||
this.getController().writeDataToDevice(dataType, writeData, writeDataFileName);
|
||
break;
|
||
// TODO Deprecated. Delete this fella.
|
||
case Garmin.DeviceControl.FILE_TYPES.gpi:
|
||
var xmlDescription = Garmin.GpiUtil.buildMultipleDeviceDownloadsXML(this.options.getGpiWriteDescription());
|
||
this.getController().downloadToDevice(xmlDescription);
|
||
break;
|
||
case Garmin.DeviceControl.FILE_TYPES.fitSettings:
|
||
case Garmin.DeviceControl.FILE_TYPES.fitSport:
|
||
case Garmin.DeviceControl.FILE_TYPES.fitCourse:
|
||
case Garmin.DeviceControl.FILE_TYPES.binary:
|
||
var xmlDescription = Garmin.GpiUtil.buildMultipleDeviceDownloadsXML(this.options.getBinaryWriteDescription());
|
||
this.getController().downloadToDevice(xmlDescription);
|
||
break;
|
||
default:
|
||
var error = new Error(Garmin.DeviceControl.MESSAGES.invalidFileType + dataType);
|
||
error.name = "InvalidTypeException";
|
||
this.handleException(error);
|
||
}
|
||
}
|
||
}
|
||
|
||
// No supported types found, throw error
|
||
if (deviceWriteSupport == false) {
|
||
var error = new Error(this.evaluateTemplate(this.options.unsupportedWriteDataType, {dataType: dataType}));
|
||
error.name = "UnsupportedDataTypeException";
|
||
this.handleException(error);
|
||
}
|
||
},
|
||
|
||
/** Call-back triggered before writing to a device.
|
||
* @see Garmin.DeviceControl
|
||
* @event
|
||
*/
|
||
onStartWriteToDevice: function(json) {
|
||
this.setStatus(this.options.writingToDevice);
|
||
},
|
||
|
||
/** Call-back triggered when write has been cancelled.
|
||
* @see Garmin.DeviceControl
|
||
* @event
|
||
*/
|
||
onCancelWriteToDevice: function(json) {
|
||
this.setStatus(this.options.writingCancelled);
|
||
},
|
||
|
||
/** 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
|
||
* @see Garmin.DeviceControl
|
||
* @event
|
||
*/
|
||
onWaitingWriteToDevice: function(json) {
|
||
if(confirm(json.message.getText())) {
|
||
this.setStatus(this.options.overwritingFile);
|
||
json.controller.respondToMessageBox(true);
|
||
} else {
|
||
this.setStatus(this.options.notOverwritingFile);
|
||
json.controller.respondToMessageBox(false);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @event
|
||
*/
|
||
onProgressWriteToDevice: function(json) {
|
||
if(this.options.showProgressBar) {
|
||
this.updateProgressBar(this.progressBarDisplay, json.progress.getPercentage());
|
||
}
|
||
this.setStatus( json.progress.percentage==100 ? this.options.dataDownloadProcessing : json.progress );
|
||
},
|
||
|
||
/**
|
||
* @event
|
||
*/
|
||
onFinishWriteToDevice: function(json) {
|
||
this.setStatus(this.options.writtenToDevice);
|
||
this.resetUI();
|
||
if (this.options.afterFinishWriteToDevice) {
|
||
this.options.afterFinishWriteToDevice.call(this, json.success);
|
||
}
|
||
},
|
||
|
||
////////////////////////////// UTILITY METHODS //////////////////////////////
|
||
|
||
/** Sets up the device control which handles most of the work that isn't user
|
||
* interface related. The controller is lazy loaded the first time it is called.
|
||
* Early calls must specify the unlock parameter, but read and write methods should
|
||
* not because they should follow a call to findDevice.
|
||
*
|
||
* Also initializes the RemoteTransfer object used to transfer data to remote servers.
|
||
*
|
||
* @param Boolean optional request to unlock the plugin if not already done.
|
||
*/
|
||
getController: function(unlock) {
|
||
if (!this.garminController) {
|
||
try {
|
||
this.garminController = new Garmin.DeviceControl();
|
||
this.garminController.register(this);
|
||
this.garminController.setPluginRequiredVersion(this.options.pluginRequiredVersion);
|
||
this.garminController.setPluginLatestVersion(this.options.pluginLatestVersion);
|
||
this.garminController.validatePlugin();
|
||
} catch (e) {
|
||
this.handleException(e);
|
||
return null;
|
||
}
|
||
}
|
||
if (!this.error && unlock && !this.isUnlocked()) {
|
||
if(this.garminController.unlock(this.options.pathKeyPairsArray)) {
|
||
this.setStatus(this.options.pluginUnlocked);
|
||
} else {
|
||
this.setStatus(this.options.pluginNotUnlocked);
|
||
}
|
||
this.garminRemoteTransfer = new Garmin.RemoteTransfer();
|
||
}
|
||
return this.garminController;
|
||
},
|
||
|
||
/** Plugin unlock status
|
||
* @returns Boolean
|
||
*/
|
||
isUnlocked: function() {
|
||
return (this.garminController && this.garminController.isUnlocked());
|
||
},
|
||
|
||
/** Sets options for this display object. Any options that are set will override
|
||
* the default values.
|
||
*
|
||
* @see Garmin.DeviceDisplayDefaultOptions for possible options and default values.
|
||
* @throws InvalidOptionException
|
||
* @param Object options - Object with options.
|
||
*/
|
||
setOptions: function(options) {
|
||
for(key in options || {}) {
|
||
if ( ! (key in Garmin.DeviceDisplayDefaultOptions) ) {
|
||
var err = new Error(key+" is not a valid option name, see Garmin.DeviceDisplayDefaultOptions");
|
||
err.name = "InvalidOptionException";
|
||
throw err;
|
||
}
|
||
}
|
||
this.options = Object.extend(Garmin.DeviceDisplayDefaultOptions, options || {});
|
||
},
|
||
|
||
/*Sets the size of the select to zero which essentially clears it from
|
||
* any values.
|
||
*
|
||
* @param {HTMLElement} select DOM element
|
||
*/
|
||
_clearHtmlSelect: function(select) {
|
||
if(select) {
|
||
select.length = 0;
|
||
}
|
||
},
|
||
|
||
/** Set status text if showStatusElement is visible.
|
||
* @param String text to display.
|
||
*/
|
||
setStatus: function(statusText) {
|
||
if(statusText == null) {
|
||
statusText = '';
|
||
}
|
||
if(this.options.showStatusElement) {
|
||
|
||
var statusDisplayString;
|
||
|
||
if( this.options.showDetailedStatus) {
|
||
statusDisplayString = this._buildDescriptiveStatusString(statusText);
|
||
} else {
|
||
if( statusText.getText ) {
|
||
|
||
var text = statusText.getText();
|
||
if( text instanceof Array) {
|
||
if( text.length == 0) {
|
||
statusDisplayString = '';
|
||
} else {
|
||
statusDisplayString = statusText.getText()[0];
|
||
}
|
||
}
|
||
} else {
|
||
statusDisplayString = statusText;
|
||
}
|
||
}
|
||
this.statusText.innerHTML = statusDisplayString;
|
||
}
|
||
},
|
||
|
||
/** Builds a descriptive string from the status response (typically JSON, but also can be a plain ol' string).
|
||
*/
|
||
_buildDescriptiveStatusString: function(statusText) {
|
||
if(statusText == null) {
|
||
statusText = '';
|
||
}
|
||
var resultString = statusText;
|
||
|
||
if( statusText.getTitle ) {
|
||
resultString = statusText.getTitle() + "<br />";
|
||
}
|
||
if( statusText.getText ) {
|
||
resultString += statusText.getText();
|
||
}
|
||
|
||
return resultString;
|
||
},
|
||
|
||
/** Helper method. Evaluates Prototype's Template object, and returns the evaluated string.
|
||
*
|
||
* Templates should contain fields as in the following format: 'My cow has #{numSpots}' spots!'
|
||
* Fields are in the following format: {numSpots: 22}
|
||
*
|
||
* @param String template - the template to evaluate
|
||
* @param Hash fields - the fields referenced in template
|
||
* @return the evaluated template string
|
||
*/
|
||
// TODO Move out to js-util
|
||
evaluateTemplate: function(template, fields) {
|
||
return new Template(template).evaluate(fields);
|
||
},
|
||
|
||
/** Makes progress bar visible.
|
||
* @deprecated
|
||
*/
|
||
showProgressBar: function() {
|
||
if(this.options.showStatusElement && this.options.showProgressBar) {
|
||
Element.show(this.progressBar);
|
||
}
|
||
},
|
||
|
||
/** Hides progress bar.
|
||
*/
|
||
hideProgressBar: function() {
|
||
if(this.options.showStatusElement && this.options.showProgressBar) {
|
||
Element.hide(this.progressBar);
|
||
}
|
||
},
|
||
|
||
/** Update percentage representation of progress bar.
|
||
* The progress bar starts out as full and then resets to empty. This is to
|
||
* take care of extremely short transfers.
|
||
* @param {Element} progressBarDisplay - the progress bar display DOM element to update.
|
||
* @param int percentage completion: 0-100
|
||
*/
|
||
updateProgressBar: function(progressBarDisplay, value) {
|
||
if(this.options.showStatusElement && this.options.showProgressBar && value) {
|
||
var percent = (0 < value && value <= 100) ? value : 100;
|
||
progressBarDisplay.style.width = percent + "%";
|
||
}
|
||
},
|
||
|
||
/** Update the progress text of the progress bar.
|
||
* @param {Element} progressBarText - the progress bar text DOM element to update.
|
||
* @param String the progress text to display near the progress bar
|
||
*/
|
||
updateProgressBarText: function(progressBarText, text) {
|
||
if(this.options.showStatusElement && this.options.showProgressBar && text) {
|
||
progressBarText.innerHTML = text;
|
||
}
|
||
},
|
||
|
||
/** Call-back for asynchronous method exceptions.
|
||
* @see Garmin.DeviceControl
|
||
* @event
|
||
*/
|
||
onException: function(json) {
|
||
this.handleException(json.msg);
|
||
},
|
||
|
||
/** Central exception dispatch method will delegate to options.customExceptionHandler
|
||
* if defined or call defaultExceptionHandler otherwise.
|
||
* @param {Error} error to process.
|
||
*/
|
||
handleException: function(error) {
|
||
this.error = true;
|
||
//console.debug("Display.handleException error="+error)
|
||
if (this.options.customExceptionHandler) {
|
||
this.options.customExceptionHandler.call(this, error);
|
||
} else {
|
||
this.defaultExceptionHandler(error);
|
||
}
|
||
},
|
||
/** Default exception handler method handles plug-in support/downloads/upgrades.
|
||
* If options.showStatusElement is true then put error messages in the status div otherwise
|
||
* put it in a alert popup.
|
||
* @param {Error} error - error to process.
|
||
*/
|
||
defaultExceptionHandler: function(error) {
|
||
var errorStatus;
|
||
var hideFromBrowser = false;
|
||
if(error.name == "BrowserNotSupportedException") {
|
||
errorStatus = error.message;
|
||
if (this.options.hideIfBrowserNotSupported) {
|
||
hideFromBrowser = true;
|
||
}
|
||
} else if (error.name == "PluginNotInstalledException" || error.name == "OutOfDatePluginException") {
|
||
errorStatus = error.message;
|
||
errorStatus += " <a href=\""+Garmin.DeviceDisplay.LINKS.pluginDownload+"\" target=\"_blank\">" + this.options.downloadAndInstall + "</a>";
|
||
} else if (Garmin.PluginUtils.isDeviceErrorXml(error)) {
|
||
errorStatus = Garmin.PluginUtils.getDeviceErrorMessage(error);
|
||
} else if (error.name == "UnsupportedDeviceException" || error.name == "UnsupportedDataTypeException" || error.name == "RemoteTransferException") {
|
||
errorStatus = error.message;
|
||
} else {
|
||
errorStatus = error.name + ": " + error.message;
|
||
}
|
||
|
||
this.setStatus(errorStatus);
|
||
this.resetUI();
|
||
//if no status UI div is defined, make sure the user sees the error
|
||
if (!this.options.showStatusElement && !hideFromBrowser) {
|
||
if (error.name == "PluginNotInstalledException" || error.name == "OutOfDatePluginException") {
|
||
if (window.confirm(error.message+"\n" + this.options.installNow)) {
|
||
window.open(Garmin.DeviceDisplay.LINKS.pluginDownload, "_blank");
|
||
}
|
||
} else {
|
||
alert(errorStatus);
|
||
}
|
||
}
|
||
}
|
||
|
||
};
|
||
|
||
/** Constant defining links referenced in the DeviceDisplay
|
||
*
|
||
* @struct {public} Garmin.DeviceDisplay.LINKS
|
||
*/
|
||
Garmin.DeviceDisplay.LINKS = {
|
||
pluginDownload: "http://www.garmin.com/products/communicator/",
|
||
/** Function that returns a direct download link based on the user's OS.
|
||
*/
|
||
// TODO Use this when we have a way of getting the latest version all the time.
|
||
pluginDownloadDetectOs: function() {
|
||
if( BrowserDetect.OS == "Windows") return "http://www.garmin.com/products/communicator/";
|
||
else if( BrowserDetect.OS == "Mac") return "http://www.garmin.com/products/communicator/";
|
||
else return "http://www.garmin.com/products/communicator/"
|
||
}
|
||
};
|
||
|
||
/** A queue of filters to be applied to activities after data is
|
||
* obtained from the device. Also used by display to determine
|
||
* if the filtering process is finished;
|
||
*/
|
||
var garminFilterQueue = new Array();
|
||
|
||
/** The default display options for the generated plug-in elements including
|
||
* booleans for which sub-items to show. Override specific option values by
|
||
* calling setOptions(optionsHash) on your instance of Garmin.DeviceDisplay
|
||
* to customize your display options.
|
||
*
|
||
* @class Garmin.DeviceDisplayDefaultOptions
|
||
* @name Garmin.DeviceDisplayDefaultOptions
|
||
*/
|
||
Garmin.DeviceDisplayDefaultOptions =
|
||
/** @lends Garmin.DeviceDisplayDefaultOptions */
|
||
{
|
||
// ================== Plugin unlock ======================
|
||
|
||
/**Unlock plugin when user lands on containing page which may result in security or
|
||
* plugin-not-installed messages. Set to false to supress plugin acivity
|
||
* until user initiates an action.
|
||
*
|
||
* @default true
|
||
* @type Boolean
|
||
*/
|
||
unlockOnPageLoad: true,
|
||
|
||
/**The array of strings that contain the unlock codes for the plugin.
|
||
* @example [URL1,KEY1,URL2,KEY2]
|
||
* Add as many url/key pairs as you'd like.
|
||
* @type String[]
|
||
*/
|
||
pathKeyPairsArray: ["file:///C:/dev/", "bd04dc1f5e97a6ff1ea76c564d133b7e"],
|
||
|
||
|
||
// ================== Global Options ======================
|
||
/**The class name used by various parts of the display to make
|
||
* CSS styling easier.
|
||
*
|
||
* @see Garmin.DeviceDisplayDefaultOptions.statusElementId
|
||
* @see Garmin.DeviceDisplayDefaultOptions.readDataElementId
|
||
* @see Garmin.DeviceDisplayDefaultOptions.findDevicesElementId
|
||
* @default "pluginElement"
|
||
* @type String
|
||
*/
|
||
elementClassName: "pluginElement",
|
||
|
||
/**Display link instead of buttons. Currently this only applies to the 'Find Devices' button.
|
||
* @default false
|
||
* @type Boolean
|
||
*/
|
||
useLinks: false,
|
||
|
||
/**Action to take if browser is not supported:
|
||
* if true on't display the application,
|
||
* else if status bar is visible, display message, otherwise popup an alert dialog
|
||
* @default false
|
||
* @type Boolean
|
||
*/
|
||
hideIfBrowserNotSupported: false,
|
||
|
||
/**The function called when an error occurs. This is here to allow
|
||
* custom error handling logic. <br/>
|
||
* <br/>
|
||
* The function should accept an arguement of type Error (Javascript
|
||
* Error Object) and a second arguement for the DeviceDisplay instance.<br/>
|
||
* <br/>
|
||
* <br/>Error.name - the type of the error in a String format.
|
||
* <br/>Error.message - the detailed message of the error.<br/>
|
||
*<br/>
|
||
* Some Errors:<br/>
|
||
* PluginNotInstalledException - the plugin is not installed<br/>
|
||
* OutOfDatePluginException - the plugin is out of date<br/>
|
||
* BrowserNotSupportedException - the browser is not support by the site<br/>
|
||
* @example
|
||
* function(error, display){ display.defaultExceptionHandler(error); }
|
||
* @type function
|
||
* @function
|
||
*/
|
||
customExceptionHandler: null,
|
||
|
||
/**Class name to add for all buttons/links that perform an action
|
||
* @default "actionButton"
|
||
* @type String
|
||
*/
|
||
actionButtonClassName: "actionButton",
|
||
|
||
/**DEPRECATED - Use autoHideUnusedElements instead!
|
||
* Auto-hides read elements when they are not in use. This is currently used for the Upload Activities use case.
|
||
* @default false
|
||
* @type String
|
||
*/
|
||
autoHideUnusedReadElements: false,
|
||
|
||
/**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.
|
||
* @default false
|
||
* @type String
|
||
*/
|
||
autoHideUnusedElements: false,
|
||
|
||
/**The choice to display the about ('Powered by...') element
|
||
* @default true
|
||
* @type Boolean
|
||
*/
|
||
showAboutElement: true,
|
||
|
||
/** Optional - Restricts transfer of data to specifically listed devices ONLY. This is an array list of
|
||
* device part numbers, which can be found in the GarminDevice.XML.
|
||
* @example ["006-B1018-00"] // Forerunner 310 only
|
||
* @type Array
|
||
*/
|
||
restrictByDevice: [],
|
||
|
||
/** Sets the required version of the Communicator Plugin that is compatible with the given application.
|
||
* This value is set using an array of the major and minor build numbers. *
|
||
* @example version 2.2.0.1 is given by [2,2,0,1].
|
||
* @default [2,2,0,1] (for version 2.2.0.1)
|
||
* @type Array
|
||
*/
|
||
pluginRequiredVersion: [2,2,0,1],
|
||
|
||
/** Sets the latest plugin version number. This represents the latest version available for download at Garmin.
|
||
* We will attempt to keep the default value of this up to date with each API release, but this is not guaranteed,
|
||
* so set this to be safe or if you don't want to upgrade to the latest API.
|
||
*
|
||
* @param reqVersionArray Array The latest version to set to. In the format [versionMajor, versionMinor, buildMajor, buildMinor]
|
||
* i.e. [2,2,0,1]
|
||
* @default [2,5,1,0] (for version 2.5.1.0)
|
||
* @type Array
|
||
*/
|
||
pluginLatestVersion: [2,5,2,0],
|
||
|
||
// ================== Status Element Options ======================
|
||
/**The choice to display the feedback regarding the communications
|
||
* with the device.
|
||
*
|
||
* @default true
|
||
* @type Boolean
|
||
*/
|
||
showStatusElement: true,
|
||
|
||
/** The choice to display more detailed status when transferring data to and from device.
|
||
* @default false
|
||
* @type Boolean
|
||
*/
|
||
showDetailedStatus: false,
|
||
|
||
/**The id of the HTML element where the statusBox is to be rendered.
|
||
* @type String
|
||
* @default "statusBox"
|
||
*/
|
||
statusElementId: "statusBox",
|
||
|
||
/**The id of the HTML element where the status text messages are to be displayed.
|
||
* @default "statusText"
|
||
* @type String
|
||
*/
|
||
statusTextId: "statusText",
|
||
|
||
/**The progress bar is a graphical percentage bar used to display
|
||
* the amount of reading/writing is complete.
|
||
* @default true
|
||
* @type Boolean
|
||
*/
|
||
showProgressBar: true,
|
||
|
||
/** The id of the HTML element where the progress status text is to be displayed.
|
||
* This element will be a child of the statusText element.
|
||
* @default "progressText"
|
||
* @type String
|
||
*/
|
||
progressTextId: "progressText",
|
||
|
||
/** The class name for the progress status text.
|
||
* This element will be a child of the statusText element.
|
||
* @type String
|
||
*/
|
||
progressTextClass: "progressTextClass",
|
||
|
||
/**The class name for the progress bar container element.
|
||
* @default 'progressBarClass'
|
||
* @type String
|
||
*/
|
||
progressBarClass: "progressBarClass",
|
||
|
||
/** The background of the progress bar. This stays static during transfer.
|
||
* @default 'progressBarBackClass'
|
||
* @type String
|
||
*/
|
||
progressBarBackClass: "progressBarBackClass",
|
||
|
||
/** The class name for the dynamic progress bar that is overlaid
|
||
* on top of the progressBar element. This controls
|
||
* the part that 'moves'.
|
||
* @default 'progressBarDisplayClass'
|
||
* @type String
|
||
*/
|
||
progressBarDisplayClass: "progressBarDisplayClass",
|
||
|
||
/**The container for the progress bar.
|
||
* @default 'progressBar'
|
||
* @type String
|
||
*/
|
||
progressBarId: "progressBar",
|
||
|
||
/** The background of the progress bar. This stays static during transfer.
|
||
* @default 'progressBarBack'
|
||
* @type String
|
||
*/
|
||
progressBarBackId: "progressBarBack",
|
||
|
||
/**The id of the displayed progress element. This is dynamic during transfer.
|
||
* @default 'progressBarDisplay'
|
||
* @type String
|
||
*/
|
||
progressBarDisplayId: "progressBarDisplay",
|
||
|
||
/** The class name for the progress bar text.
|
||
* @default progressBarTextClass
|
||
* @type String
|
||
*/
|
||
progressBarTextClass: "progressBarTextClass",
|
||
|
||
/** The id of the progress bar text. This is generally the percentage
|
||
* value text displayed, but could also be in the context of what is being transferred
|
||
* (i.e. 1 of 5 activities).
|
||
* @default progressBarText
|
||
* @type String
|
||
*/
|
||
progressBarTextId: "progressBarText",
|
||
|
||
// Upload Progress bar
|
||
|
||
/** The id of the HTML element where the upload progress status text is to be displayed.
|
||
* This element will be a child of the statusText element.
|
||
* @type String
|
||
* @default "uploadProgressText"
|
||
*/
|
||
uploadProgressTextId: "uploadProgressText",
|
||
|
||
/** The class name for the uploadProgress status text.
|
||
* This element will be a child of the statusText element.
|
||
* @type String
|
||
* @default "uploadProgressTextClass"
|
||
*/
|
||
uploadProgressTextClass: "uploadProgressTextClass",
|
||
|
||
/**The class name for the uploadProgress bar container element.
|
||
* @default 'uploadProgressBarClass'
|
||
* @type String
|
||
*/
|
||
uploadProgressBarClass: "uploadProgressBarClass",
|
||
|
||
/** The background of the uploadProgress bar. This stays static during transfer.
|
||
* @default 'uploadProgressBarBackClass'
|
||
* @type String
|
||
*/
|
||
uploadProgressBarBackClass: "uploadProgressBarBackClass",
|
||
|
||
/** The class name for the dynamic uploadProgress bar that is overlaid
|
||
* on top of the uploadProgressBar element. This controls
|
||
* the part that 'moves'.
|
||
* @default 'uploadProgressBarDisplayClass'
|
||
* @type String
|
||
*/
|
||
uploadProgressBarDisplayClass: "uploadProgressBarDisplayClass",
|
||
|
||
/**The container for the uploadProgress bar.
|
||
* @default 'uploadProgressBar'
|
||
* @type String
|
||
*/
|
||
uploadProgressBarId: "uploadProgressBar",
|
||
|
||
/** The background of the uploadProgress bar. This stays static during transfer.
|
||
* @default 'uploadProgressBarBack'
|
||
* @type String
|
||
*/
|
||
uploadProgressBarBackId: "uploadProgressBarBack",
|
||
|
||
/**The id of the displayed uploadProgress element. This is dynamic during transfer.
|
||
* @default 'uploadProgressBarDisplay'
|
||
* @type String
|
||
*/
|
||
uploadProgressBarDisplayId: "uploadProgressBarDisplay",
|
||
|
||
/** The class name for the uploadProgress bar text.
|
||
* @default uploadProgressBarTextClass
|
||
* @type String
|
||
*/
|
||
uploadProgressBarTextClass: "uploadProgressBarTextClass",
|
||
|
||
/** The id of the uploadProgress bar text. This is generally the percentage
|
||
* value text displayed, but could also be in the context of what is being transferred
|
||
* (i.e. 1 of 5 activities).
|
||
* @default uploadProgressBarText
|
||
* @type String
|
||
*/
|
||
uploadProgressBarTextId: "uploadProgressBarText",
|
||
|
||
/** The text to display above the upload progress bar. This is a static string.
|
||
* @default "Uploading activities..."
|
||
* @type String
|
||
*/
|
||
uploadingStatusText: "Uploading activities...",
|
||
|
||
/** Templated string used to display the upload progress status.
|
||
* The template parameters currentUpload and totalUploads must be included
|
||
* to work properly, as the default value does.
|
||
*
|
||
* @default #{currentUpload} of #{totalUploads} completed.
|
||
* @type String
|
||
*/
|
||
uploadProgressStatusText: '#{currentUpload} of #{totalUploads} completed.',
|
||
|
||
//=================== Find Devices Element Options ===============
|
||
|
||
/**Choice to display the find devices area that will search for connected devices.
|
||
* @type Boolean
|
||
* @default true
|
||
*/
|
||
showFindDevicesElement: true,
|
||
|
||
/**Choice to display the find devices area that will search for connected devices when the page loads.
|
||
* @type Boolean
|
||
* @default true
|
||
*/
|
||
showFindDevicesElementOnLoad: true,
|
||
|
||
/**Looks for devices as soon as the page is loaded and the plugin unlocked.
|
||
* This might be particularly annoying in many situations since the plugin
|
||
* requires the user to authorize access to device information via a
|
||
* dialog box.
|
||
* @type Boolean
|
||
* @default false
|
||
*/
|
||
autoFindDevices: false,
|
||
|
||
/**Controls the view of the buttons related to find devices (find & cancel) if
|
||
* based on if the plugin finds one or more devices.
|
||
* When set to <b>false</b> and
|
||
* used with {@link showDeviceButtonsOnLoad} =false
|
||
* and {@link autoFindDevices} =true these buttons will only
|
||
* show up if a device is not found (minimizing confusion for the user).
|
||
* <p>
|
||
* More granular control is provided on each of the device buttons
|
||
* {@link showFindDevicesButton} and {@link showCancelFindDevicesButton} .
|
||
* </p>
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showFindDevicesElement
|
||
* @type Boolean
|
||
* @default true
|
||
*/
|
||
showDeviceButtonsOnFound: true,
|
||
|
||
/**If true the buttons will show when the page is rendered.
|
||
* If false, the buttons will not be displayed until the plugin detects that a device is not found.
|
||
* If you choose not to see the buttons at all (regardless if device is found or not) then
|
||
* {@link showFindDevicesElement} should be set to false.
|
||
*
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showDeviceButtonsOnFound
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showFindDevicesElement
|
||
* @type Boolean
|
||
* @default true
|
||
*/
|
||
showDeviceButtonsOnLoad: true,
|
||
|
||
/**Allows granular control to hide the find devices button independent
|
||
* of the {@link showCancelFindDevicesButton} cancel button contol.
|
||
* @type Boolean
|
||
* @default true
|
||
*/
|
||
showFindDevicesButton: true,
|
||
|
||
/**The id referencing the HTML container around the find devices buttons.
|
||
* This is useful for CSS customizations.
|
||
* <p>
|
||
* @default deviceBox
|
||
* </p>
|
||
* @type String
|
||
* @default "deviceBox"
|
||
*/
|
||
findDevicesElementId: "deviceBox",
|
||
|
||
/**The id referencing the find devices button. This is useful for
|
||
* CSS customizations.
|
||
*
|
||
* @type String
|
||
* @default findDevicesButton
|
||
*/
|
||
findDevicesButtonId: "findDevicesButton",
|
||
|
||
/**The text for the find device button.
|
||
* @type String
|
||
* @default "Find Devices"
|
||
*/
|
||
findDevicesButtonText: "Find Devices",
|
||
|
||
/**Controls the view of the cancel find devices button. When
|
||
* set to <b>false</b> the button will never show. When
|
||
* set to <b>true</b> the button's behavior will depend on other
|
||
* settings such as {@link showFindDevicesButton} ,
|
||
* {@link showDeviceButtonsOnFound} , {@link showDeviceButtonsOnLoad} ,
|
||
* and {@link showFindDevicesElement} .
|
||
* @default false
|
||
* @type Boolean
|
||
*/
|
||
showCancelFindDevicesButton: false,
|
||
|
||
/**The id referencing the cancel find devices button. This is useful for
|
||
* CSS customizations.
|
||
* @default cancelFindDevicesButton
|
||
* @type String
|
||
*/
|
||
cancelFindDevicesButtonId: "cancelFindDevicesButton",
|
||
|
||
/**The text for the cancel find device button.
|
||
* @type String
|
||
* @default "Cancel Find Devices"
|
||
*/
|
||
cancelFindDevicesButtonText: "Cancel Find Devices",
|
||
|
||
/**Controls the view of the device select box.
|
||
* When set to <b>true</b> the select device box will show even when only
|
||
* one device is found.
|
||
* When set to <b>false</b> the select device box will hide when only
|
||
* one device is found.
|
||
* When {@link showFindDevicesElement} is set to false, the device select
|
||
* box will never show.
|
||
* @default false
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showDeviceSelectNoDevice
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showDeviceSelectOnLoad
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showFindDevicesElement
|
||
* @type Boolean
|
||
*/
|
||
showDeviceSelectOnSingle: false,
|
||
|
||
/**Controls the view of the device select box.
|
||
* When set to <b>true</b> the select device box will show even when
|
||
* no device is found.
|
||
* When set to <b>false</b> the select device box will hide when
|
||
* no device is found.
|
||
* When {@link showFindDevicesElement} is set to false, the device select
|
||
* box will never show.
|
||
*
|
||
* @default true
|
||
*
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showDeviceSelectOnSingle
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showDeviceSelectOnLoad
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showFindDevicesElement
|
||
* @type Boolean
|
||
*/
|
||
showDeviceSelectNoDevice: false,
|
||
|
||
/**Controls the view of the device select box.
|
||
* When set to <b>true</b> the select device box will show when
|
||
* the display loads.
|
||
* When set to <b>false</b> the select device box will never be visible.
|
||
* When {@link showFindDevicesElement} is set to false, the device select
|
||
* box will never show.
|
||
*
|
||
* @default true
|
||
*
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showDeviceSelectOnSingle
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showDeviceSelectNoDevice
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showFindDevicesElement
|
||
* @type Boolean
|
||
*/
|
||
showDeviceSelectOnLoad: true,
|
||
|
||
/**When more than one device is detected automaticly pick the first device.
|
||
* This allows single button interfaces to avoid having to ask the user to
|
||
* choose the device and keeps the deviceSelect hidden.
|
||
*
|
||
* @default false
|
||
*
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showDeviceSelectOnSingle
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showDeviceSelectNoDevice
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showFindDevicesElement
|
||
* @type Boolean
|
||
*/
|
||
autoSelectFirstDevice: false,
|
||
|
||
//=================== Upload UI Options ===============
|
||
|
||
/**The id referencing the device select box. This is useful for
|
||
* CSS customizations.
|
||
*
|
||
* @default deviceSelectBox
|
||
* @type String
|
||
*/
|
||
deviceSelectElementId: "deviceSelectBox",
|
||
|
||
/**The label for the device select box. Shows up next to the
|
||
* device select box.
|
||
* @type String
|
||
* @default "Devices: "
|
||
*/
|
||
deviceSelectLabel: "Devices: ",
|
||
|
||
/**The id referencing the device select box label. This is useful for
|
||
* CSS customizations.
|
||
*
|
||
* @default deviceSelectLabel
|
||
* @type String
|
||
*/
|
||
deviceSelectLabelId: "deviceSelectLabel",
|
||
|
||
/** The class name referencing the device select element. This is useful for CSS customizations.
|
||
*
|
||
* @default deviceSelectClass
|
||
* @type String
|
||
*/
|
||
deviceSelectClass: "deviceSelectClass",
|
||
|
||
/**The id referencing the device select element. This is useful for
|
||
* CSS customizations.
|
||
*
|
||
* @default deviceSelect
|
||
* @type String
|
||
*/
|
||
deviceSelectId: "deviceSelect",
|
||
|
||
/**The id referencing the element that displays what device was selected. This is useful for CSS customizations.
|
||
*
|
||
* @default deviceSelected
|
||
* @type String
|
||
*/
|
||
deviceSelectedElementId: "deviceSelected",
|
||
|
||
/**The label for the device selected element. This label preceeds the device selected element. This is useful for CSS customizations.
|
||
*
|
||
* @default "Previewing "
|
||
* @type String
|
||
*/
|
||
deviceSelectedLabel: "Previewing ",
|
||
|
||
/**The id referencing the label for the element that displays what device was selected. This is useful for CSS customizations.
|
||
*
|
||
* @default deviceSelectedLabel
|
||
* @type String
|
||
*/
|
||
deviceSelectedLabelId: "deviceSelectedLabel",
|
||
|
||
/**The status text that is displayed when no devices are found. The Find Devices button is
|
||
* displayed to allow the user to try again. To change the button text, set findDevicesButtonText.
|
||
*
|
||
* @type String
|
||
* @default "No devices found."
|
||
*/
|
||
noDeviceDetectedStatusText: "No devices found.",
|
||
/**The status text that prepends itself to the device name when a single device is found.
|
||
*
|
||
* @type String
|
||
* @default "Found "
|
||
*/
|
||
singleDeviceDetectedStatusText: "Found ",
|
||
|
||
/**The function called when device search completes successfully or unsuccessfully.
|
||
* The function should have two arguments:
|
||
* devices {Array<Garmin.Device>} - an array of device descriptors or an empty array in none were found.
|
||
* display {Garmin.DeviceDisplay} - the current instance of the DeveiceDisplay
|
||
* @example function(devices){...}
|
||
* @type function
|
||
* @function
|
||
*/
|
||
afterFinishFindDevices: null,
|
||
|
||
/** The function called after all item uploads complete successfully or unsuccessfully.
|
||
* The function will have one argument:
|
||
* display {Garmin.DeviceDisplay - the current instance of the DeviceDisplay
|
||
* @example
|
||
* function(display) {...}
|
||
* @type function
|
||
* @function
|
||
*/
|
||
afterFinishUploads: null,
|
||
|
||
// ================== Read Element Options ======================
|
||
/**Start reading data from the device when one or more device(s)
|
||
* is found.
|
||
*
|
||
* @default false
|
||
*
|
||
* @see Garmin.DeviceDisplayDefaultOptions.autoFindDevices
|
||
* @see Garmin.DeviceDisplayDefaultOptions.autoWriteData
|
||
* @type Boolean
|
||
*/
|
||
autoReadData: false,
|
||
|
||
/**Display the user interface associated with reading from
|
||
* a connected device.
|
||
*
|
||
* @default true
|
||
* @type Boolean
|
||
*/
|
||
showReadDataElement: true,
|
||
|
||
/**Controls the view of the read data button. When
|
||
* set to <b>false</b> the button will never show. When
|
||
* set to <b>true</b> the button's behavior will depend on other
|
||
* settings such as {@link showReadDataElement} .
|
||
*
|
||
* @default true
|
||
* @type Boolean
|
||
*/
|
||
showReadDataButton: true,
|
||
|
||
/**Controls the view of the read data element. When
|
||
* set to <b>true</b> the element will only show after a
|
||
* device has been found. When set to <b>false</b> the
|
||
* element will show on page load.
|
||
* Behavior will depend on other settings such as
|
||
* and {@link showReadDataElement} .
|
||
*
|
||
* @default false
|
||
* @type Boolean
|
||
*/
|
||
showReadDataElementOnDeviceFound: false,
|
||
|
||
/**The id referencing the box containing read elements. This is
|
||
* useful for CSS customizations.
|
||
*
|
||
* @default readBox
|
||
* @type String
|
||
*/
|
||
readDataElementId: "readBox",
|
||
|
||
/**The id referencing the read data button. This is useful for
|
||
* CSS customizations.
|
||
*
|
||
* @default readDataButton
|
||
* @type String
|
||
*/
|
||
readDataButtonId: "readDataButton",
|
||
|
||
/**The text on the read button.
|
||
*
|
||
* @type String
|
||
*/
|
||
readDataButtonText: "Get Data",
|
||
|
||
/**Controls the view of the cancel read data button. When
|
||
* set to <b>false</b> the button will never show. When
|
||
* set to <b>true</b> the button's behavior will depend on other
|
||
* settings such as {@link showReadDataButton} ,
|
||
* and {@link showReadDataElement} .
|
||
*
|
||
* @default true
|
||
* @type Boolean
|
||
*/
|
||
showCancelReadDataButton: true,
|
||
|
||
/**The id referencing the cancel read data button. This is
|
||
* useful for CSS customizations.
|
||
*
|
||
* @default cancelReadDataButton
|
||
* @type String
|
||
*/
|
||
cancelReadDataButtonId: "cancelReadDataButton",
|
||
|
||
/**The text on the cancel read button.
|
||
*
|
||
* @type String
|
||
*/
|
||
cancelReadDataButtonText: "Cancel Get Data",
|
||
|
||
/**The status text that is displayed when user cancels the
|
||
* read progress.
|
||
*
|
||
* @type String
|
||
*/
|
||
cancelReadStatusText: "Read cancelled",
|
||
|
||
/**Controls the view of the device select box.
|
||
* When set to <b>true</b> the select device box will show when
|
||
* the display loads.
|
||
* When set to <b>false</b> the select device box will hide when
|
||
* the display loads.
|
||
* When {@link showReadDataElement} is set to false, the results select
|
||
* box will never show.
|
||
*
|
||
* @default false
|
||
*
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showReadDataElement
|
||
* @type Boolean
|
||
*/
|
||
showReadResultsSelectOnLoad: false,
|
||
|
||
/**The class to set for select lists that are displaying results
|
||
* from a read operation. This is useful for CSS customizations.
|
||
*
|
||
* @default readResultsSelect
|
||
* @type String
|
||
*/
|
||
readResultsSelectClass: "readResultsSelect",
|
||
|
||
/**The class to set for results elements. This is useful for CSS customizations.
|
||
*
|
||
* @default readResultsElement
|
||
* @type String
|
||
*/
|
||
readResultsElementClass: "readResultsElement",
|
||
|
||
/**Display the route select dropdown. When
|
||
* <@link showReadDataElement> is set to false, the select
|
||
* track dropdown will not show.
|
||
*
|
||
* @default true
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showReadDataElement
|
||
* @type Boolean
|
||
*/
|
||
showReadRoutesSelect: true,
|
||
|
||
/**The id referencing the read routes element. This is
|
||
* useful for CSS customizations.
|
||
*
|
||
* @default readRoutesElement
|
||
* @type String
|
||
*/
|
||
readRoutesElementId : "readRoutesElement",
|
||
|
||
/**The id referencing the route select dropdown. This is
|
||
* useful for CSS customizations.
|
||
*
|
||
* @default readRoutesSelect
|
||
* @type String
|
||
*/
|
||
readRoutesSelectId: "readRoutesSelect",
|
||
|
||
/**The label for the read routes select box. Shows up next to the
|
||
* read routes select box.
|
||
*
|
||
* @type String
|
||
*/
|
||
readRoutesSelectLabel: "Routes: ",
|
||
|
||
/**The id referencing the read routes select box label. This is useful for
|
||
* CSS customizations.
|
||
*
|
||
* @default readRoutesSelectLabel
|
||
* @type String
|
||
*/
|
||
readRoutesSelectLabelId: "readRoutesSelectLabel",
|
||
|
||
/** The id referencing the button for uploading selected activities button. This is useful for CSS customizations.
|
||
*
|
||
* @default readSelectedButton
|
||
* @type String
|
||
*/
|
||
readSelectedButtonId: "readSelectedButton",
|
||
|
||
/** The text label for the upload selected data button. This is useful for CSS customizations.
|
||
*
|
||
* @default Upload Selected
|
||
* @type String
|
||
*/
|
||
readSelectedButtonText: "Upload Selected",
|
||
|
||
/**Display the track select dropdown. When
|
||
* <@link showReadDataElement> is set to false, the select
|
||
* track dropdown will not show.
|
||
*
|
||
* @default true
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showReadDataElement
|
||
* @type Boolean
|
||
*/
|
||
showReadTracksSelect: true,
|
||
|
||
/**The id referencing the read tracks element. This is
|
||
* useful for CSS customizations.
|
||
*
|
||
* @default readTracksElement
|
||
* @type String
|
||
*/
|
||
readTracksElementId: "readTracksElement",
|
||
|
||
/**The id referencing the track select dropdown. This is
|
||
* useful for CSS customizations.
|
||
*
|
||
* @default readTracksSelect
|
||
* @type String
|
||
*/
|
||
readTracksSelectId: "readTracksSelect",
|
||
|
||
/**The label for the read tracks select box. Shows up next to the
|
||
* read tracks select box.
|
||
*
|
||
* @type String
|
||
*/
|
||
readTracksSelectLabel: "Tracks: ",
|
||
|
||
/**The id referencing the read tracks select box label. This is useful for
|
||
* CSS customizations.
|
||
*
|
||
* @default deviceSelectLabel
|
||
* @type String
|
||
*/
|
||
readTracksSelectLabelId: "readTracksSelectLabel",
|
||
|
||
/**The id referencing the read tracks element. This is
|
||
* useful for CSS customizations.
|
||
*
|
||
* @default readTracksElement
|
||
* @type String
|
||
*/
|
||
readWaypointsElementId: "readWaypointsElement",
|
||
|
||
/**Display the waypoint select dropdown. When
|
||
* <@link showReadDataElement> is set to false, the select
|
||
* waypoint dropdown will not show.
|
||
*
|
||
* @default true
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showReadDataElement
|
||
* @type Boolean
|
||
*/
|
||
showReadWaypointsSelect: true,
|
||
|
||
/**The id referencing the waypoint select dropdown. This is
|
||
* useful for CSS customizations.
|
||
*
|
||
* @default readWaypointsSelect
|
||
* @type String
|
||
*/
|
||
readWaypointsSelectId: "readWaypointsSelect",
|
||
|
||
/**The label for the read waypoints select box. Shows up next to the
|
||
* read tracks select box.
|
||
*
|
||
* @type String
|
||
*/
|
||
readWaypointsSelectLabel: "Waypoints: ",
|
||
|
||
/**The id referencing the read waypoints select box label. This is useful for
|
||
* CSS customizations.
|
||
*
|
||
* @default readWaypointsSelectLabel
|
||
* @type String
|
||
*/
|
||
readWaypointsSelectLabelId: "readWaypointsSelectLabel",
|
||
|
||
/**Display Google map to show tracks and laps that have been read. When <@link showReadDataElement> is
|
||
* set to false, the Google map will not show.
|
||
*
|
||
* @default false
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showReadDataElement
|
||
* @type Boolean
|
||
*/
|
||
showReadGoogleMap: false,
|
||
|
||
/**The id referencing the google map display. This is
|
||
* useful for CSS customizations.
|
||
*
|
||
* @default readMap
|
||
* @type String
|
||
*/
|
||
readGoogleMapId: "readMap",
|
||
|
||
/**DEPRECATED - Use {@link Garmin.DeviceDisplayDefaultOptions.readDataTypes} Tells the plug-in what data type to read from the device. Options for this
|
||
* are currently constants listed in {@link Garmin.DeviceControl.FILE_TYPES} ,
|
||
* and the values are: crs, gpx, gpi, or null to skip this option altogether and get the default data type from
|
||
* the device.
|
||
* <p>
|
||
* This property works in conjunction with the following functions, based on the datatype:
|
||
* <p>
|
||
* For CRS and GPX: Define the getWriteData() and getWriteDataFileName() functions in your options section.
|
||
* <p>
|
||
* For GPI: Define the getWriteData() and getWriteDataFileName() functions in your options section.
|
||
* The getGpiWriteDescription() function replaces getWriteData().
|
||
* <p>
|
||
* @default Garmin.DeviceControl.FILE_TYPES.gpx
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showReadDataElement
|
||
* @see Garmin.DeviceControl.FILE_TYPES
|
||
* @see Garmin.DeviceDisplayDefaultOptions.readDataTypes
|
||
* @type String
|
||
* @deprecated
|
||
*/
|
||
readDataType: null,
|
||
|
||
/** OVERRIDES readDataType!
|
||
*
|
||
* Tells the plug-in what data types to read from the device, in order of preference. Options for this
|
||
* are currently constants listed in {@link Garmin.DeviceControl.FILE_TYPES}.
|
||
*
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showReadDataElement
|
||
* @see Garmin.DeviceControl.FILE_TYPES
|
||
* @default TCX, GPX
|
||
* @example ["FitnessHistory", "GPSData"]
|
||
* @type Array
|
||
*/
|
||
readDataTypes: ["GPSData"],
|
||
|
||
/**Display the dropdown select box for selecting what type
|
||
* of data to read from the device. When
|
||
* <@link showReadDataElement> is set to false,
|
||
* this device type select will not show.
|
||
*
|
||
* @default false
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showReadDataElement
|
||
* @type Boolean
|
||
*/
|
||
showReadDataTypesSelect: false,
|
||
|
||
/**The id referencing the data type select. This is
|
||
* useful for CSS customizations.
|
||
*
|
||
* @default readDataTypesSelect
|
||
* @type String
|
||
*/
|
||
readDataTypesSelectId: "readDataTypesSelect",
|
||
|
||
/**The function called when data is successfully read from
|
||
* the device. The function should have three arguements:<br/>
|
||
* <br/>
|
||
* dataString - the xml received from the device in String format<br/>
|
||
* dataDoc - the xml received from the device in Document format<br/>
|
||
* extension - the file type extension of the data, used to determine
|
||
* the type of data received.<br/>
|
||
* activities - list of <@link Garmin.Activity> parsed from the xml.<br/>
|
||
* display - the display object<br/>
|
||
* @see Garmin.DeviceDisplayDefaultOptions.Garmin.Activity
|
||
* @example function(dataString, dataDoc, extension, activities, display){...}
|
||
* @type function
|
||
* @function
|
||
*/
|
||
afterFinishReadFromDevice: null,
|
||
|
||
/**Load tracks even if they don't have a timestamp (technically these are
|
||
* routes). Set to false if you need to do synchronization with existing
|
||
* track log database (like MotionBased does).
|
||
*
|
||
* @default true
|
||
* @see Garmin.DeviceDisplayDefaultOptions._listTracks
|
||
* @type Boolean
|
||
*/
|
||
loadTracksWithoutATimestamp: true,
|
||
|
||
// ================== Write Element Options ======================
|
||
|
||
/**Start writing data to the device when one or more device(s)
|
||
* is found.
|
||
*
|
||
* @default false
|
||
*
|
||
* @see Garmin.DeviceDisplayDefaultOptions.autoFindDevices
|
||
* @see Garmin.DeviceDisplayDefaultOptions.autoReadData
|
||
* @type Boolean
|
||
*/
|
||
autoWriteData: false,
|
||
|
||
/**Display the user interface associated with writing to
|
||
* a connected device.
|
||
*
|
||
* @default false
|
||
* @type Boolean
|
||
*/
|
||
showWriteDataElement: false,
|
||
|
||
/**Controls the view of the write data element. When
|
||
* set to <b>true</b> the element will only show after a
|
||
* device has been found. When set to <b>false</b> the
|
||
* element will show on page load.
|
||
* Behavior will depend on other settings such as
|
||
* and {@link showWriteDataElement} .
|
||
*
|
||
* @default false
|
||
* @type Boolean
|
||
*/
|
||
showWriteDataElementOnDeviceFound: false,
|
||
|
||
/**The id referencing the box containing write elements. This is
|
||
* useful for CSS customizations.
|
||
*
|
||
* @default writeBox
|
||
* @type String
|
||
*/
|
||
writeDataElementId: "writeBox",
|
||
|
||
/**The id referencing the write data button. This is
|
||
* useful for CSS customizations.
|
||
*
|
||
* @default writeDataButton
|
||
* @type String
|
||
*/
|
||
writeDataButtonId: "writeDataButton",
|
||
|
||
/**The text on the write button.
|
||
*
|
||
* @type String
|
||
*/
|
||
writeDataButtonText: "Write",
|
||
|
||
/**Controls the view of the cancel write data button. When
|
||
* set to <b>false</b> the button will never show. When
|
||
* set to <b>true</b> the button's behavior will depend on other
|
||
* settings such as {@link showWriteDataButton} ,
|
||
* and {@link showWriteDataElement} .
|
||
*
|
||
* @default true
|
||
* @type Boolean
|
||
*/
|
||
showCancelWriteDataButton: true,
|
||
|
||
/**The id referencing the cancel write data button. This is
|
||
* useful for CSS customizations.
|
||
*
|
||
* @default cancelWriteDataButton
|
||
* @type String
|
||
*/
|
||
cancelWriteDataButtonId: "cancelWriteDataButton",
|
||
|
||
/**The text on the cancel write button.
|
||
*
|
||
* @type String
|
||
*/
|
||
cancelWriteDataButtonText: "Cancel Write",
|
||
|
||
/**The function called when data is successfully written to
|
||
* the device. This method takes two parameters:
|
||
* success Boolean - true if data was written
|
||
* display {Garmin.DeviceDisplay} - the current instance of the DeviceDisplay
|
||
* @type function
|
||
* @example function(success, display) {...}
|
||
* @function
|
||
*/
|
||
afterFinishWriteToDevice: null,
|
||
|
||
/**Array of filters to sequencialy apply to activities before being sent or displayed.
|
||
*
|
||
* @see Garmin.FILTERS
|
||
* @type Array dataFilters
|
||
* @example [Garmin.FILTERS.historyOnly]
|
||
*/
|
||
dataFilters: [],
|
||
|
||
/**The function called by the display in order to acquire the data
|
||
* that will be written to the device during the writing operation.
|
||
*
|
||
* This function should return a String.
|
||
*
|
||
* @see Garmin.DeviceDisplayDefaultOptions.getWriteDataFileName
|
||
* @type function
|
||
* @example function() { return $("myTextAreaId").value; }
|
||
* @function
|
||
*/
|
||
getWriteData: null,
|
||
|
||
/**The function called by the display in order to acquire the filename
|
||
* of the data that will be written to the device during the writing
|
||
* operation.
|
||
*
|
||
* This function should return a String.
|
||
* @default function(){ return "myData.gpx"; }
|
||
* @see Garmin.DeviceDisplayDefaultOptions.getWriteData
|
||
* @type function
|
||
*/
|
||
getWriteDataFileName: function(){ return "myData.gpx"; } ,
|
||
|
||
/**DEPRECATED (see {@link getBinaryWriteDescription}) - The function called by the display in order to acquire the data
|
||
* that will be written to the device during the writing operation.
|
||
*
|
||
* This function should return an array of strings where adjacent items
|
||
* indicate the source (URL) of the gpi to be written and the destination
|
||
* (device path and filename) to write to the device.
|
||
*
|
||
* e.g.: [SOURCE,DESTINATION,SOURCE2,DESTINATION2] add as many source/destination
|
||
* pairs as you'd like.
|
||
*
|
||
* @example function() { return ["http://connect.garmin.com/SampleGpi.gpi", "Garmin\\POI\\Test.gpi"] }
|
||
* @type function
|
||
* @function
|
||
*/
|
||
getGpiWriteDescription: null,
|
||
|
||
/**The function called by the display in order to acquire the data
|
||
* that will be written to the device during the writing operation.
|
||
*
|
||
* This function should return an array of strings where adjacent items
|
||
* indicate the source (URL) of the binary data to be written and the destination
|
||
* (device path and filename) to write to the device.
|
||
*
|
||
* e.g.: [SOURCE,DESTINATION,SOURCE2,DESTINATION2] add as many source/destination
|
||
* pairs as you'd like.
|
||
*
|
||
* @example function() { return ["http://connect.garmin.com/SampleGpi.gpi", "Garmin\\POI\\Test.gpi"] }
|
||
* @type function
|
||
* @function
|
||
*/
|
||
getBinaryWriteDescription: null,
|
||
|
||
/**DEPRECATED - Use {@link Garmin.DeviceDisplayDefaultOptions.writeDataTypes}
|
||
* Tells the plug-in what data type to write to the device.
|
||
* Options are "gpx" which will use {@link getWriteData} to get the data
|
||
* or "gpi" which will use {@link getGpiWriteDescription} to get the data to
|
||
* save to the device.
|
||
*
|
||
* @default Garmin.DeviceControl.FILE_TYPES.gpx
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showWriteDataElement
|
||
* @see Garmin.DeviceDisplayDefaultOptions.getWriteData
|
||
* @see Garmin.DeviceDisplayDefaultOptions.getGpiWriteDescription
|
||
* @see Garmin.DeviceDisplayDefaultOptions.writeDataTypes
|
||
* @type String
|
||
* @deprecated
|
||
*/
|
||
writeDataType: null,
|
||
|
||
/** OVERRIDES writeDataType!
|
||
*
|
||
* Tells the plug-in what data type to write to the device.
|
||
* Options are "gpx" which will use {@link getWriteData} to get the data
|
||
* or "gpi" which will use {@link getGpiWriteDescription} to get the data to
|
||
* save to the device.
|
||
*
|
||
* @default Garmin.DeviceControl.FILE_TYPES.gpx
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showWriteDataElement
|
||
* @see Garmin.DeviceDisplayDefaultOptions.getWriteData
|
||
* @see Garmin.DeviceDisplayDefaultOptions.getGpiWriteDescription
|
||
* @type Array
|
||
*/
|
||
writeDataTypes: ["GPSData"],
|
||
|
||
//=================== Activity Directory Element Options ===============
|
||
|
||
/** Displays the activity directory table, which essentially
|
||
* allows users to select individual activities to read from
|
||
* the device. showReadDataElement must be true for this
|
||
* to display.
|
||
*
|
||
* @default true
|
||
*
|
||
* @see Garmin.DeviceDisplayDefaultOptions.showReadDataElement
|
||
* @type Boolean
|
||
*/
|
||
showActivityDirectoryElement: true,
|
||
|
||
/**
|
||
* The classname referencing the element that lists individual activities. This element includes the
|
||
* data table as well as the related buttons (such as for uploading data). This is useful for CSS customizations.
|
||
* @default activityDirectoryClass
|
||
* @type String
|
||
*/
|
||
activityDirectoryClass: "activityDirectoryClass",
|
||
|
||
/** The id referencing the table that holds the activity directory data. This is useful for CSS customizations.
|
||
*
|
||
* @default activityDirectoryData
|
||
* @type String
|
||
*/
|
||
activityDirectoryDataId: "activityDirectoryData",
|
||
|
||
/** The id referencing the element that lists individual activities. This element includes the
|
||
* data table as well as the related buttons (such as for uploading data). This is useful for CSS customizations.
|
||
*
|
||
* @default activityDirectory
|
||
* @type String
|
||
*/
|
||
activityDirectoryElementId: "activityDirectory",
|
||
|
||
/** The id referencing the header of the activity table that lists individual activities.
|
||
* This is useful for CSS customizations.
|
||
* @default activityTableHeader
|
||
* @type String
|
||
*/
|
||
activityTableHeaderId: "activityTableHeader",
|
||
|
||
/** The id referencing the activity table that lists individual activities. This table contains
|
||
* the activity data. This is useful for CSS customizations.
|
||
*
|
||
* @default activityTable
|
||
* @type String
|
||
*/
|
||
activityTableId: "activityTable",
|
||
|
||
/** The function called after an activity entry is added to the activity listing table.
|
||
* Useful for updating the status cell in a unique way.
|
||
* @param Boolean index - the index in the table where the activity was added
|
||
* @param {Garmin.Activity} activity
|
||
* @param {Element} statusCell - the status td element associated with the activity
|
||
* @param {Element} checkbox - the checkbox input element associated with the activity
|
||
* @param Object row - the table row element associated with the activity
|
||
* @param {Garmin.ActivityMatcher} activityMatcher - the activity matcher object, if available.
|
||
* @param {Garmin.DeviceDisplay} display - the current instance of the display object
|
||
* @type function
|
||
* @function
|
||
* @example function(index, activity, statusCell, checkbox, row, this.activityMatcher, this) {...}
|
||
*/
|
||
afterTableInsert: null,
|
||
|
||
//=================== Upload Options ===============
|
||
|
||
/** Class name for the cancel upload button.
|
||
* Useful for CSS customizations.
|
||
* @default cancelUploadButtonClass
|
||
* @type String
|
||
*/
|
||
cancelUploadButtonClass: 'cancelUploadButtonClass',
|
||
|
||
/** Element ID for the cancel upload button.
|
||
* @default 'cancelUploadButton'
|
||
* @type String
|
||
*/
|
||
cancelUploadButtonId: 'cancelUploadButton',
|
||
|
||
/** Text to display on Cancel upload button.
|
||
* @default '(Cancel)'
|
||
* @type String
|
||
*/
|
||
cancelUploadButtonText: '(Cancel)',
|
||
|
||
/**Display the user interface associated with uploading new activities from a connected device.
|
||
*
|
||
* @default true
|
||
* @type Boolean
|
||
*/
|
||
showUploadNewButton: false,
|
||
|
||
/** The id referencing the button for uploading data to a server. This is useful for CSS customizations.
|
||
*
|
||
* @default uploadNewButton
|
||
* @type String
|
||
*/
|
||
uploadNewButtonId: "uploadNewButton",
|
||
|
||
/** The text label for the upload data button. This is useful for CSS customizations.
|
||
*
|
||
* @default activityDirectory
|
||
* @type String
|
||
*/
|
||
uploadNewButtonText: "Upload new activities",
|
||
|
||
/** Select activities to upload, rather than all activities read off the device.
|
||
*
|
||
* @default false
|
||
* @type Boolean
|
||
*/
|
||
uploadSelectedActivities: false,
|
||
|
||
/** Upload compressed data. Compressed data is gzip base 64 encoded. Compression is supported
|
||
* for fitness history activities only.
|
||
*
|
||
* @default false
|
||
* @type Boolean
|
||
*/
|
||
uploadCompressedData: false,
|
||
|
||
/** Maximum number of activities allowed for upload selection. Users are notified during the selection
|
||
* process if they try to exceed this value. uploadSelectedActivities must be set to true
|
||
* for this to work. Note that if this value is > 0, the 'select all activities' feature will not be available.
|
||
*
|
||
* Set this value to 0 for no limit with NO 'select all activities' checkbox.
|
||
*
|
||
* @default -1 (no limit with 'select all' checkbox)
|
||
* @type int
|
||
* @see Garmin.DeviceDisplayDefaultOptions.uploadSelectedActivities
|
||
*/
|
||
uploadMaximum: -1,
|
||
|
||
// ================== Post to Server ======================
|
||
|
||
/** The function called when a single activity is finished reading, and the data is ready to post.
|
||
* Use this if you need a custom way of uploading data to your server (advanced users).<br/>
|
||
* <br/>
|
||
* Otherwise, if you just need an AJAX call, use Send Data. (See {@link this.options.sendDataUrl}
|
||
* and {@link this.options.sendDataOptions}.)
|
||
*
|
||
* Parameters to this function:<br/>
|
||
* <br/>datastring String - The XML datastring of the activity read from the device.<br/>
|
||
* <br/>display {GarminDeviceDisplay} - the display object
|
||
* @example function(datastring, display){...}
|
||
* @function
|
||
* @type function
|
||
*/
|
||
postActivityHandler: null,
|
||
|
||
/** Show the element to send data to a remote server.
|
||
*
|
||
* @type Boolean
|
||
*/
|
||
showSendDataElement: false,
|
||
|
||
/**Controls the view of the send data element. When
|
||
* set to <b>true</b> the element will only show after a
|
||
* device has been found. When set to <b>false</b> the
|
||
* element will show on page load.
|
||
* Behavior will depend on other settings such as
|
||
* and {@link showSendDataElement} .
|
||
*
|
||
* @default false
|
||
* @type Boolean
|
||
*/
|
||
showSendDataElementOnDeviceFound: false,
|
||
|
||
/** The callback function to set the request options when hitting the remote server.
|
||
* This function is passed these parameters:
|
||
*
|
||
* options - The options object. Use this object to set the property values.
|
||
* Some may already be set by sendDataOptions. getSendOptions will overwrite any existing ones.
|
||
* deviceXml - the active device's XML
|
||
* data - read data from the device, if any
|
||
*
|
||
* Don't forget to return the options object!
|
||
*
|
||
* @type function
|
||
* @example function(options, deviceXml, data) {}
|
||
* @function
|
||
*/
|
||
getSendOptions: null,
|
||
|
||
/** The URL to send the data to.
|
||
*
|
||
* @type String
|
||
*/
|
||
sendDataUrl: null,
|
||
|
||
/** The AJAX request options to use for sending data to a server. To be used in conjunction with {@link sendDataUrl} .
|
||
*
|
||
* See the <a href="http://www.prototypejs.org/api/ajax/options">AJAX options page</a> for configurable options and default values.
|
||
*
|
||
* @type Object
|
||
*/
|
||
sendDataOptions: null,
|
||
|
||
/**The id referencing the box containing send elements. This is
|
||
* useful for CSS customizations.
|
||
*
|
||
* @default sendBox
|
||
* @type String
|
||
*/
|
||
sendDataElementId: "sendBox",
|
||
|
||
/**The id referencing the send data button. This is useful for
|
||
* CSS customizations.
|
||
*
|
||
* @default sendDataButton
|
||
* @type String
|
||
*/
|
||
sendDataButtonId: "sendDataButton",
|
||
|
||
/**The text on the read button.
|
||
*
|
||
* @type String
|
||
*/
|
||
sendDataButtonText: "Send Data",
|
||
|
||
/** The callback function that will be passed the AJAX response
|
||
* after making the request. The display object is passed in, so you can
|
||
* make follow-up read or write calls if so desired.
|
||
*
|
||
* @see Garmin.DeviceDisplayDefaultOptions.sendDataUrl
|
||
* @see Garmin.DeviceDisplayDefaultOptions.getSendOptions
|
||
* @param response object (see <a href="http://www.prototypejs.org/api/ajax/response">Ajax.Response</a> for response attributes)
|
||
* @default null
|
||
* @type function
|
||
* @function
|
||
* @example function(response){}
|
||
*/
|
||
afterFinishSendData: null,
|
||
|
||
//=================== Device Browser Element Options ===============
|
||
|
||
/** The callback function that will be called when the user selects a
|
||
* device from the device browser.
|
||
*
|
||
* @default null
|
||
* @param deviceNum the device number of the selected device
|
||
* @param devices an array of the detected devices
|
||
* @param deviceXml the device xml of the selected device
|
||
* @type function
|
||
* @function
|
||
* @example function(deviceXml){...}
|
||
*/
|
||
afterSelectDevice: null,
|
||
|
||
/** Show the device browser list. Currently the browser list is only available
|
||
* when activity directory reading is on (readDataType = Garmin.DeviceControl.FILE_TYPES.tcxDir).
|
||
*
|
||
* @default true
|
||
*
|
||
* @see Garmin.DeviceDisplayDefaultOptions.uploadSelectedActivities
|
||
* @type Boolean
|
||
*/
|
||
useDeviceBrowser: true,
|
||
|
||
/**Display list instead of select drop down.
|
||
* @default false
|
||
* @type Boolean
|
||
*/
|
||
useDeviceSelectList: false,
|
||
|
||
/** The classname for the device browser element. This is useful for custom CSS.
|
||
* @default deviceBrowserBoxClass
|
||
* @type String
|
||
*/
|
||
deviceBrowserElementClass: "deviceBrowserBoxClass",
|
||
|
||
/** The id referencing the device browser element. This is useful for custom CSS.
|
||
* @default deviceBrowserList
|
||
* @type String
|
||
*/
|
||
deviceBrowserElementId: "deviceBrowserBox",
|
||
|
||
/** The classname for the device browser text label. This is useful for custom CSS.
|
||
* @default deviceBrowserLabelClass
|
||
* @type String
|
||
*/
|
||
deviceBrowserLabelClass: "deviceBrowserLabelClass",
|
||
|
||
/** The id referencing the device browser text label. This is useful for custom CSS.
|
||
* @default deviceBrowserLabelId
|
||
* @type String
|
||
*/
|
||
deviceBrowserLabelId: "deviceBrowserLabel",
|
||
|
||
/** The text label to display above the device browser list. This is useful for custom CSS.
|
||
* @default 'Browse devices:'
|
||
* @type String
|
||
*/
|
||
deviceBrowserLabel: "Browse devices:",
|
||
|
||
/** The id referencing the device browser list. This is useful for
|
||
* CSS customizations.
|
||
*
|
||
* @default deviceBrowserList
|
||
* @type String
|
||
*/
|
||
deviceBrowserListId: "deviceBrowserList",
|
||
|
||
/** The classname referencing the button that displays the UI for browsing the user's
|
||
* file system.
|
||
* @default browseComputerButtonClass
|
||
* @type String
|
||
*/
|
||
browseComputerButtonClass: "browseComputerButtonClass",
|
||
|
||
/** The id referencing the button that displays the UI for browsing the user's
|
||
* file system.
|
||
* @default browseComputerButton
|
||
* @type String
|
||
*/
|
||
browseComputerButtonId: "browseComputerButton",
|
||
|
||
/** The text for the button that displays the UI for browsing the user's file system.
|
||
* @default "Browse Computer"
|
||
* @type String
|
||
*/
|
||
browseComputerButtonText: "Browse Computer",
|
||
|
||
/** The classname for the browse computer container.
|
||
* @type String
|
||
*/
|
||
browseComputerElementClass: "browseComputerElementClass",
|
||
|
||
/** The id for the browse computer container.
|
||
* @type String
|
||
*/
|
||
browseComputerElementId: "browseComputerElement",
|
||
|
||
/** The url of the iframe to load into the browse computer container.
|
||
* The iframe object is generated by the API. See browseComputerElementClass
|
||
* to change the dimensions of the iframe.
|
||
* @type String
|
||
*/
|
||
browseComputerContentUrl: "about:blank",
|
||
|
||
/** The text label to display in the browser list for browsing the computer.
|
||
* @default My Computer
|
||
* @type String
|
||
*/
|
||
browseComputerLabel: "My Computer",
|
||
|
||
/** The text to display on the loading content screen. This is useful for internationalization.
|
||
* @default 'Loading content...'
|
||
* @type String
|
||
*/
|
||
loadingContentText: "Loading content from #{deviceName}, please wait...",
|
||
|
||
/** The text to display to change the device. This is useful for internationalization.
|
||
* @default '(Change)'
|
||
* @type String
|
||
*/
|
||
changeDeviceButtonText: "(Change)",
|
||
|
||
/** The classname referencing the change device element, which is a link that allows
|
||
* the user to change device in list mode.
|
||
* @default changeDevice
|
||
* @type String
|
||
*/
|
||
changeDeviceClass: "change",
|
||
|
||
/** The id referencing the change device element, which is a link that allows
|
||
* the user to change device in list mode.
|
||
* @default changeDevice
|
||
* @type String
|
||
*/
|
||
changeDeviceElementId: "changeDevice",
|
||
|
||
/** The class name referencing the connected devices label displayed when using
|
||
* the device select list.
|
||
* @default connectedDevicesClass
|
||
* @type String
|
||
*/
|
||
connectedDevicesClass: "connectedDevicesClass",
|
||
|
||
/** The image to display in the connected devices label.
|
||
* @type String
|
||
*/
|
||
connectedDevicesImg: null,
|
||
|
||
/** The label for connected devices, displayed when using the device select list.
|
||
* @default 'Connected devices:'
|
||
* @type String
|
||
*/
|
||
connectedDevicesLabel: "Connected devices:",
|
||
|
||
/** The classname referencing the preview device element, which displays an image
|
||
* depending on the device selected.
|
||
*
|
||
* @default previewDevice
|
||
* @see Garmin.DeviceDisplayDefaultOptions.previewDeviceDefaultImg
|
||
* @type String
|
||
*/
|
||
previewDeviceClass: "preview",
|
||
|
||
/** The id referencing the change device element, which is a link that allows
|
||
* the user to change device in list mode.
|
||
* @default changeDevice
|
||
* @type String
|
||
*/
|
||
previewDeviceElementId: "previewDevice",
|
||
|
||
/** The default image URL to display for any selected device.
|
||
* @see Garmin.DeviceDisplayDefaultOptions.previewDeviceId
|
||
* @see Garmin.DeviceDisplayDefaultOptions.useDeviceSelectList
|
||
* @type String
|
||
*/
|
||
previewDeviceDefaultImg: "../../../theme/upload/images/icon-edge.png",
|
||
|
||
/**The maximum number of characters to display for a device name.
|
||
*@type int
|
||
*/
|
||
deviceLabelMaxSize: 20,
|
||
|
||
/** Allows the user to browse their file system for upload.
|
||
* {@link uploadSelectedActivities} must be set to true for this
|
||
* option to take effect.
|
||
* @default false
|
||
* @type Boolean
|
||
*/
|
||
showBrowseComputer: false,
|
||
|
||
// ================== Synchronization ======================
|
||
|
||
/**Detect new activities in the activity listing by comparing the device list to a server list.
|
||
* @default true
|
||
* @type Boolean
|
||
*/
|
||
detectNewActivities: false,
|
||
|
||
/**
|
||
* The URL to make the sync request to. To be used in conjuntion with {@link syncDataOptions}.
|
||
* {@link detectNewActivities} must be set to true.
|
||
* @type String
|
||
*/
|
||
syncDataUrl: null,
|
||
|
||
/** The AJAX request options to use for posting the parameters to a server. To be used in conjunction with {@link syncDataUrl} .
|
||
*
|
||
* See the <a href="http://www.prototypejs.org/api/ajax/options">AJAX options page</a> for configurable options and default values.
|
||
*
|
||
* @type Object
|
||
*/
|
||
syncDataOptions: null,
|
||
|
||
// ================== Internationalization ======================
|
||
/** Status message exposed for internationalization. @type String */
|
||
pluginUnlocked: "Plug-in initialized. Find some devices to get started.",
|
||
/** Status message exposed for internationalization. @type String */
|
||
pluginNotUnlocked: "The plug-in was not unlocked successfully",
|
||
/** Read data selection option exposed for internationalization. @type String */
|
||
gpsData: "GPS Data",
|
||
/** Read data selection option exposed for internationalization. @type String */
|
||
trainingData: "Training Data",
|
||
/** Status message exposed for internationalization. Prepended to the device name after user selects which device to use.
|
||
* i.e. "Using Diana's Forerunner 305"
|
||
* @default "Using "
|
||
* @type String
|
||
*/
|
||
usingDevice: "Using #{deviceName}",
|
||
/** Track list box item template exposed for internationalization. @type String */
|
||
trackListing: "#{date} (Duration: #{duration} )",
|
||
/** Status message template exposed for internationalization. @type String */
|
||
dataFound: "#{routes} routes, #{tracks} tracks and #{waypoints} waypoints found",
|
||
/** Status message exposed for internationalization. @type String */
|
||
writingToDevice: "Writing data to the device",
|
||
/** Status message exposed for internationalization. @type String */
|
||
writtenToDevice: "Data written to the device",
|
||
/** Status message exposed for internationalization. @type String */
|
||
writingCancelled: "Writing cancelled",
|
||
/** Status message exposed for internationalization. @type String */
|
||
overwritingFile: "Overwriting file",
|
||
/** Status message exposed for internationalization. @type String */
|
||
notOverwritingFile: "Will not be overwriting file",
|
||
/** Status message exposed for internationalization. @type String */
|
||
lookingForDevices: "Looking for connected devices...",
|
||
/** Status template exposed for internationalization. When single device is found. @type String */
|
||
foundDevice: "Found #{deviceName} ",
|
||
/** Status template exposed for internationalization. When multiple devices are found. @type String */
|
||
foundDevices: "Found #{deviceCount} devices",
|
||
/** Status message exposed for internationalization. When user cancels Find Devices.@type String */
|
||
findCancelled: "Find cancelled",
|
||
/** Status message exposed for internationalization. When reading data from the device. @type String */
|
||
dataReadProcessing: "Data read from device. Processing...",
|
||
/** Status message exposed for internationalization. When large files are being written to device. @type String */
|
||
dataDownloadProcessing: "Processing data to write... ",
|
||
/** Status message exposed for internationalization. When uploads have completed. @type String */
|
||
uploadsFinished: "Transfer Complete!",
|
||
/** Error message exposed for internationalization. @type String */
|
||
noParseSupportForType: "The plugin does not have parsing support for file type ",
|
||
/** Request message exposed for internationalization. @type String */
|
||
installNow: "Install now?",
|
||
/** Request message exposed for internationalization. @type String */
|
||
downloadAndInstall: "Download and install now",
|
||
/** Powered-by message. Required for plugin license agreement. @type String */
|
||
poweredByGarmin: "Powered by <a href='http://www.garmin.com/products/communicator/' target='_new'>Garmin Communicator</a>",
|
||
/** 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 */
|
||
unsupportedDevice: "Your device is not supported by this application.",
|
||
/** Error message to display when user attempts to upload 0 activities. @type String */
|
||
errorActivitySelect: "No selected or new activities to upload.",
|
||
/** DEPRECATED Column header for the Date fields in the activity directory. Useful for CSS customizations and internationalization. @type String @deprecated @see Garmin.DeviceDisplayDefaultOptions.getActivityDirectoryHeaderIdLabel */
|
||
activityDirectoryHeaderDate: "<b>Date</b>",
|
||
/** 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 */
|
||
getActivityDirectoryHeaderIdLabel: function () { return "<b>Date</b>"; },
|
||
/** Column header for the Duration fields in the activity directory. Useful for CSS customizations and internationalization. @type String */
|
||
activityDirectoryHeaderDuration: "<b>Duration</b>",
|
||
/** Column header for the Status fields in the activity directory. Useful for CSS customizations and internationalization. @type String */
|
||
activityDirectoryHeaderStatus: "<b>Status</b>",
|
||
/** 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 */
|
||
unsupportedReadDataType: "Your device does not support reading of the type #{dataType}.",
|
||
/** 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 */
|
||
unsupportedWriteDataType: "Your device does not support writing of the type #{dataType}.",
|
||
/** Status message to display when activities are being uploaded. @type String */
|
||
uploadingActivities: "Uploading activities...",
|
||
/** Error message exposed for internationalization. When maximum selection for upload is reached. @type String */
|
||
uploadMaximumReached: "Maximum upload selection of #{activities} activities reached.",
|
||
/** 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.
|
||
*
|
||
* @default '<img src="style/ajax-loader.gif" />'
|
||
*
|
||
* which is an animated loader image. You can customize this to be text instead of an image by not using the image tags.
|
||
* @type String */
|
||
statusCellProcessingImg: '<img src="style/processing-arrows.gif" width="15" height="15" />',
|
||
/** Status text to display when the plugin is sending data to a remote server. @type String */
|
||
sendingDataToServer: "Sending data from #{deviceName} to server...",
|
||
/** Error message to display when there is an error getting the HTTP response back from the HTTP request. @type String */
|
||
errorHttpResponse: "Unable to get valid response from HTTP request object. Ensure that your options are set correctly and try again.",
|
||
/** Status text to display when none of the activities from the device meet the filter requirements. @type String */
|
||
noFilteredActivities: "No new activities found on device.",
|
||
noActivitiesOnDevice: "No activities found on selected device."
|
||
} ;
|
||
|
||
|
||
/*
|
||
* DisplayBootstrap - not sure what form this should take: class or global var
|
||
* It should probably be in the Garmin namesapce.
|
||
*
|
||
* Dynamic include of required libraries and check for Prototype
|
||
* Code taken from scriptaculous (http://script.aculo.us/) - thanks guys!
|
||
*/
|
||
var GarminDeviceDisplay = {
|
||
require: function(libraryName) {
|
||
// inserting via DOM fails in Safari 2.0, so brute force approach
|
||
document.write('<script type="text/javascript" src="'+libraryName+'"></script>');
|
||
},
|
||
|
||
load: function() {
|
||
if((typeof Prototype=='undefined') ||
|
||
(typeof Element == 'undefined') ||
|
||
(typeof Element.Methods=='undefined') ||
|
||
parseFloat(Prototype.Version.split(".")[0] + "." +
|
||
Prototype.Version.split(".")[1]) < 1.5) {
|
||
throw("GarminDeviceDisplay requires the Prototype JavaScript framework >= 1.5.0");
|
||
}
|
||
|
||
$A(document.getElementsByTagName("script"))
|
||
.findAll(
|
||
function(s) {
|
||
return (s.src && s.src.match(/GarminDeviceDisplay\.js(\?.*)?$/))
|
||
}
|
||
)
|
||
.each(
|
||
function(s) {
|
||
var path = s.src.replace(/GarminDeviceDisplay\.js(\?.*)?$/,'../../');
|
||
var includes = s.src.match(/\?.*load=([a-z,]*)/);
|
||
var dependencies = 'garmin/device/GarminDeviceControl' +
|
||
',garmin/device/GarminDevicePlugin' +
|
||
',garmin/device/GarminGpsDataStructures' +
|
||
',garmin/device/GoogleMapController' +
|
||
',garmin/device/GarminDevice' +
|
||
',garmin/device/GarminPluginUtils' +
|
||
',garmin/api/GarminRemoteTransfer' +
|
||
',garmin/util/Util-XmlConverter' +
|
||
',garmin/util/Util-Broadcaster' +
|
||
',garmin/util/Util-DateTimeFormat' +
|
||
',garmin/util/Util-BrowserDetect' +
|
||
',garmin/util/Util-PluginDetect' +
|
||
',garmin/device/GarminObjectGenerator' +
|
||
',garmin/activity/GarminMeasurement' +
|
||
',garmin/activity/GarminSample' +
|
||
',garmin/activity/GarminSeries' +
|
||
',garmin/activity/GarminActivity' +
|
||
',garmin/activity/GarminActivityDirectory' +
|
||
',garmin/activity/GarminActivityFilter' +
|
||
',garmin/activity/GarminActivityMatcher' +
|
||
',garmin/activity/TcxActivityFactory' +
|
||
',garmin/activity/GpxActivityFactory'+
|
||
',garmin/directory/GarminDirectoryFactory'+
|
||
',garmin/directory/GarminFile';
|
||
(includes ? includes[1] : dependencies).split(',').each(
|
||
function(include) {
|
||
GarminDeviceDisplay.require(path+include+'.js')
|
||
}
|
||
);
|
||
}
|
||
);
|
||
}
|
||
}
|
||
|
||
GarminDeviceDisplay.load();
|