From 2cd4de54d33f888c5803658a1870b6773bbc29f9 Mon Sep 17 00:00:00 2001 From: Markus Birth Date: Sat, 22 Nov 2014 22:55:54 +0100 Subject: [PATCH] Switched to own API files. --- .../ocstyle/js/GarminDeviceDisplay.js | 4547 ----------------- .../resource2/ocstyle/js/GarminPrototype.js | 4221 --------------- htdocs/templates2/ocstyle/sys_main.tpl | 4 +- 3 files changed, 2 insertions(+), 8770 deletions(-) delete mode 100644 htdocs/resource2/ocstyle/js/GarminDeviceDisplay.js delete mode 100644 htdocs/resource2/ocstyle/js/GarminPrototype.js diff --git a/htdocs/resource2/ocstyle/js/GarminDeviceDisplay.js b/htdocs/resource2/ocstyle/js/GarminDeviceDisplay.js deleted file mode 100644 index ac03a170a..000000000 --- a/htdocs/resource2/ocstyle/js/GarminDeviceDisplay.js +++ /dev/null @@ -1,4547 +0,0 @@ -if (Garmin == undefined) var Garmin = {}; -/** Copyright � 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 = ''; - 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 = ''; - 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 = ''; - 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 = ''; - 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(''+this.options.cancelUploadButtonText+''); - } 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 = ''+this.options.findDevicesButtonText+''; - } 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 = ''+this.options.cancelFindDevicesButtonText+''; - } 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 = '
' + this.options.deviceSelectLabel + '
'; - 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 = ''+this.options.browseComputerButtonText+''; - } 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 = ''+this.options.sendDataButtonText+''; - } 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 = ''+this.options.readDataButtonText+''; - } 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 = ''+this.options.cancelReadDataButtonText+''; - } 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 = ''+this.options.uploadNewButtonText+''; - } 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 = "" + this.options.readRoutesSelectLabel + ""; - - 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 = "" + this.options.readTracksSelectLabel + ""; - - 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 = "" + this.options.readWaypointsSelectLabel + ""; - - 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 = ''+this.options.readSelectedButtonText+''; - } 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 = ''+this.options.writeDataButtonText+''; - } 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 = ''+this.options.cancelWriteDataButtonText+''; - } 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 = ''+this.options.changeDeviceButtonText+''; - 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 = '

'+this.getShortDeviceName(this.getCurrentDevice())+'

'; - 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('' + 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 = '

'+this.getShortDeviceName(this.devices[deviceNumber])+'

'; - 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.
- *
- * 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.
- *
- * Fitness detail reading (one specific activity) is not supported by this read method, refer to - * readDetailFromDevice for that.
- * - * @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() + "
"; - } - 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 += " " + this.options.downloadAndInstall + ""; - } 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.
- *
- * The function should accept an arguement of type Error (Javascript - * Error Object) and a second arguement for the DeviceDisplay instance.
- *
- *
Error.name - the type of the error in a String format. - *
Error.message - the detailed message of the error.
- *
- * Some Errors:
- * PluginNotInstalledException - the plugin is not installed
- * OutOfDatePluginException - the plugin is out of date
- * BrowserNotSupportedException - the browser is not support by the site
- * @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 false 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). - *

- * More granular control is provided on each of the device buttons - * {@link showFindDevicesButton} and {@link showCancelFindDevicesButton} . - *

- * @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. - *

- * @default deviceBox - *

- * @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 false the button will never show. When - * set to true 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 true the select device box will show even when only - * one device is found. - * When set to false 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 true the select device box will show even when - * no device is found. - * When set to false 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 true the select device box will show when - * the display loads. - * When set to false 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} - 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 false the button will never show. When - * set to true 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 true the element will only show after a - * device has been found. When set to false 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 false the button will never show. When - * set to true 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 true the select device box will show when - * the display loads. - * When set to false 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. - *

- * This property works in conjunction with the following functions, based on the datatype: - *

- * For CRS and GPX: Define the getWriteData() and getWriteDataFileName() functions in your options section. - *

- * For GPI: Define the getWriteData() and getWriteDataFileName() functions in your options section. - * The getGpiWriteDescription() function replaces getWriteData(). - *

- * @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:
- *
- * dataString - the xml received from the device in String format
- * dataDoc - the xml received from the device in Document format
- * extension - the file type extension of the data, used to determine - * the type of data received.
- * activities - list of <@link Garmin.Activity> parsed from the xml.
- * display - the display object
- * @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 true the element will only show after a - * device has been found. When set to false 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 false the button will never show. When - * set to true 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).
- *
- * 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:
- *
datastring String - The XML datastring of the activity read from the device.
- *
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 true the element will only show after a - * device has been found. When set to false 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 AJAX options page 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 Ajax.Response 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 AJAX options page 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 Garmin Communicator", - /** 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: "Date", - /** 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 "Date"; }, - /** Column header for the Duration fields in the activity directory. Useful for CSS customizations and internationalization. @type String */ - activityDirectoryHeaderDuration: "Duration", - /** Column header for the Status fields in the activity directory. Useful for CSS customizations and internationalization. @type String */ - activityDirectoryHeaderStatus: "Status", - /** 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 '' - * - * 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: '', - /** 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(''); - }, - - 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(); diff --git a/htdocs/resource2/ocstyle/js/GarminPrototype.js b/htdocs/resource2/ocstyle/js/GarminPrototype.js deleted file mode 100644 index bd67dc9bb..000000000 --- a/htdocs/resource2/ocstyle/js/GarminPrototype.js +++ /dev/null @@ -1,4221 +0,0 @@ -/* Prototype JavaScript framework, version 1.6.0.2 - * (c) 2005-2008 Sam Stephenson - * - * Prototype is freely distributable under the terms of an MIT-style license. - * For details, see the Prototype web site: http://www.prototypejs.org/ - * - *--------------------------------------------------------------------------*/ - -var Prototype = { - Version: '1.6.0.2', - - Browser: { - IE: !!(window.attachEvent && !window.opera), - Opera: !!window.opera, - WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1, - Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1, - MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/) - }, - - BrowserFeatures: { - XPath: !!document.evaluate, - ElementExtensions: !!window.HTMLElement, - SpecificElementExtensions: - document.createElement('div').__proto__ && - document.createElement('div').__proto__ !== - document.createElement('form').__proto__ - }, - - ScriptFragment: ']*>([\\S\\s]*?)<\/script>', - JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, - - emptyFunction: function() { }, - K: function(x) { return x } -}; - -if (Prototype.Browser.MobileSafari) - Prototype.BrowserFeatures.SpecificElementExtensions = false; - - -/* Based on Alex Arnell's inheritance implementation. */ -var Class = { - create: function() { - var parent = null, properties = $A(arguments); - if (Object.isFunction(properties[0])) - parent = properties.shift(); - - function klass() { - this.initialize.apply(this, arguments); - } - - Object.extend(klass, Class.Methods); - klass.superclass = parent; - klass.subclasses = []; - - if (parent) { - var subclass = function() { }; - subclass.prototype = parent.prototype; - klass.prototype = new subclass; - parent.subclasses.push(klass); - } - - for (var i = 0; i < properties.length; i++) - klass.addMethods(properties[i]); - - if (!klass.prototype.initialize) - klass.prototype.initialize = Prototype.emptyFunction; - - klass.prototype.constructor = klass; - - return klass; - } -}; - -Class.Methods = { - addMethods: function(source) { - var ancestor = this.superclass && this.superclass.prototype; - var properties = Object.keys(source); - - if (!Object.keys({ toString: true }).length) - properties.push("toString", "valueOf"); - - for (var i = 0, length = properties.length; i < length; i++) { - var property = properties[i], value = source[property]; - if (ancestor && Object.isFunction(value) && - value.argumentNames().first() == "$super") { - var method = value, value = Object.extend((function(m) { - return function() { return ancestor[m].apply(this, arguments) }; - })(property).wrap(method), { - valueOf: function() { return method }, - toString: function() { return method.toString() } - }); - } - this.prototype[property] = value; - } - - return this; - } -}; - -var Abstract = { }; - -Object.extend = function(destination, source) { - for (var property in source) - destination[property] = source[property]; - return destination; -}; - -Object.extend(Object, { - inspect: function(object) { - try { - if (Object.isUndefined(object)) return 'undefined'; - if (object === null) return 'null'; - return object.inspect ? object.inspect() : String(object); - } catch (e) { - if (e instanceof RangeError) return '...'; - throw e; - } - }, - - toJSON: function(object) { - var type = typeof object; - switch (type) { - case 'undefined': - case 'function': - case 'unknown': return; - case 'boolean': return object.toString(); - } - - if (object === null) return 'null'; - if (object.toJSON) return object.toJSON(); - if (Object.isElement(object)) return; - - var results = []; - for (var property in object) { - var value = Object.toJSON(object[property]); - if (!Object.isUndefined(value)) - results.push(property.toJSON() + ': ' + value); - } - - return '{' + results.join(', ') + '}'; - }, - - toQueryString: function(object) { - return $H(object).toQueryString(); - }, - - toHTML: function(object) { - return object && object.toHTML ? object.toHTML() : String.interpret(object); - }, - - keys: function(object) { - var keys = []; - for (var property in object) - keys.push(property); - return keys; - }, - - values: function(object) { - var values = []; - for (var property in object) - values.push(object[property]); - return values; - }, - - clone: function(object) { - return Object.extend({ }, object); - }, - - isElement: function(object) { - return object && object.nodeType == 1; - }, - - isArray: function(object) { - return object != null && typeof object == "object" && - 'splice' in object && 'join' in object; - }, - - isHash: function(object) { - return object instanceof Hash; - }, - - isFunction: function(object) { - return typeof object == "function"; - }, - - isString: function(object) { - return typeof object == "string"; - }, - - isNumber: function(object) { - return typeof object == "number"; - }, - - isUndefined: function(object) { - return typeof object == "undefined"; - } -}); - -Object.extend(Function.prototype, { - argumentNames: function() { - var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip"); - return names.length == 1 && !names[0] ? [] : names; - }, - - bind: function() { - if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; - var __method = this, args = $A(arguments), object = args.shift(); - return function() { - return __method.apply(object, args.concat($A(arguments))); - } - }, - - bindAsEventListener: function() { - var __method = this, args = $A(arguments), object = args.shift(); - return function(event) { - return __method.apply(object, [event || window.event].concat(args)); - } - }, - - curry: function() { - if (!arguments.length) return this; - var __method = this, args = $A(arguments); - return function() { - return __method.apply(this, args.concat($A(arguments))); - } - }, - - delay: function() { - var __method = this, args = $A(arguments), timeout = args.shift() * 1000; - return window.setTimeout(function() { - return __method.apply(__method, args); - }, timeout); - }, - - wrap: function(wrapper) { - var __method = this; - return function() { - return wrapper.apply(this, [__method.bind(this)].concat($A(arguments))); - } - }, - - methodize: function() { - if (this._methodized) return this._methodized; - var __method = this; - return this._methodized = function() { - return __method.apply(null, [this].concat($A(arguments))); - }; - } -}); - -Function.prototype.defer = Function.prototype.delay.curry(0.01); - -Date.prototype.toJSON = function() { - return '"' + this.getUTCFullYear() + '-' + - (this.getUTCMonth() + 1).toPaddedString(2) + '-' + - this.getUTCDate().toPaddedString(2) + 'T' + - this.getUTCHours().toPaddedString(2) + ':' + - this.getUTCMinutes().toPaddedString(2) + ':' + - this.getUTCSeconds().toPaddedString(2) + 'Z"'; -}; - -var Try = { - these: function() { - var returnValue; - - for (var i = 0, length = arguments.length; i < length; i++) { - var lambda = arguments[i]; - try { - returnValue = lambda(); - break; - } catch (e) { } - } - - return returnValue; - } -}; - -RegExp.prototype.match = RegExp.prototype.test; - -RegExp.escape = function(str) { - return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); -}; - -/*--------------------------------------------------------------------------*/ - -var PeriodicalExecuter = Class.create({ - initialize: function(callback, frequency) { - this.callback = callback; - this.frequency = frequency; - this.currentlyExecuting = false; - - this.registerCallback(); - }, - - registerCallback: function() { - this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); - }, - - execute: function() { - this.callback(this); - }, - - stop: function() { - if (!this.timer) return; - clearInterval(this.timer); - this.timer = null; - }, - - onTimerEvent: function() { - if (!this.currentlyExecuting) { - try { - this.currentlyExecuting = true; - this.execute(); - } finally { - this.currentlyExecuting = false; - } - } - } -}); -Object.extend(String, { - interpret: function(value) { - return value == null ? '' : String(value); - }, - specialChar: { - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '\\': '\\\\' - } -}); - -Object.extend(String.prototype, { - gsub: function(pattern, replacement) { - var result = '', source = this, match; - replacement = arguments.callee.prepareReplacement(replacement); - - while (source.length > 0) { - if (match = source.match(pattern)) { - result += source.slice(0, match.index); - result += String.interpret(replacement(match)); - source = source.slice(match.index + match[0].length); - } else { - result += source, source = ''; - } - } - return result; - }, - - sub: function(pattern, replacement, count) { - replacement = this.gsub.prepareReplacement(replacement); - count = Object.isUndefined(count) ? 1 : count; - - return this.gsub(pattern, function(match) { - if (--count < 0) return match[0]; - return replacement(match); - }); - }, - - scan: function(pattern, iterator) { - this.gsub(pattern, iterator); - return String(this); - }, - - truncate: function(length, truncation) { - length = length || 30; - truncation = Object.isUndefined(truncation) ? '...' : truncation; - return this.length > length ? - this.slice(0, length - truncation.length) + truncation : String(this); - }, - - strip: function() { - return this.replace(/^\s+/, '').replace(/\s+$/, ''); - }, - - stripTags: function() { - return this.replace(/<\/?[^>]+>/gi, ''); - }, - - stripScripts: function() { - return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); - }, - - extractScripts: function() { - var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); - var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); - return (this.match(matchAll) || []).map(function(scriptTag) { - return (scriptTag.match(matchOne) || ['', ''])[1]; - }); - }, - - evalScripts: function() { - return this.extractScripts().map(function(script) { return eval(script) }); - }, - - escapeHTML: function() { - var self = arguments.callee; - self.text.data = this; - return self.div.innerHTML; - }, - - unescapeHTML: function() { - var div = new Element('div'); - div.innerHTML = this.stripTags(); - return div.childNodes[0] ? (div.childNodes.length > 1 ? - $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) : - div.childNodes[0].nodeValue) : ''; - }, - - toQueryParams: function(separator) { - var match = this.strip().match(/([^?#]*)(#.*)?$/); - if (!match) return { }; - - return match[1].split(separator || '&').inject({ }, function(hash, pair) { - if ((pair = pair.split('='))[0]) { - var key = decodeURIComponent(pair.shift()); - var value = pair.length > 1 ? pair.join('=') : pair[0]; - if (value != undefined) value = decodeURIComponent(value); - - if (key in hash) { - if (!Object.isArray(hash[key])) hash[key] = [hash[key]]; - hash[key].push(value); - } - else hash[key] = value; - } - return hash; - }); - }, - - toArray: function() { - return this.split(''); - }, - - succ: function() { - return this.slice(0, this.length - 1) + - String.fromCharCode(this.charCodeAt(this.length - 1) + 1); - }, - - times: function(count) { - return count < 1 ? '' : new Array(count + 1).join(this); - }, - - camelize: function() { - var parts = this.split('-'), len = parts.length; - if (len == 1) return parts[0]; - - var camelized = this.charAt(0) == '-' - ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) - : parts[0]; - - for (var i = 1; i < len; i++) - camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); - - return camelized; - }, - - capitalize: function() { - return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); - }, - - underscore: function() { - return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase(); - }, - - dasherize: function() { - return this.gsub(/_/,'-'); - }, - - inspect: function(useDoubleQuotes) { - var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) { - var character = String.specialChar[match[0]]; - return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16); - }); - if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; - return "'" + escapedString.replace(/'/g, '\\\'') + "'"; - }, - - toJSON: function() { - return this.inspect(true); - }, - - unfilterJSON: function(filter) { - return this.sub(filter || Prototype.JSONFilter, '#{1}'); - }, - - isJSON: function() { - var str = this; - if (str.blank()) return false; - str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''); - return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str); - }, - - evalJSON: function(sanitize) { - var json = this.unfilterJSON(); - try { - if (!sanitize || json.isJSON()) return eval('(' + json + ')'); - } catch (e) { } - throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); - }, - - include: function(pattern) { - return this.indexOf(pattern) > -1; - }, - - startsWith: function(pattern) { - return this.indexOf(pattern) === 0; - }, - - endsWith: function(pattern) { - var d = this.length - pattern.length; - return d >= 0 && this.lastIndexOf(pattern) === d; - }, - - empty: function() { - return this == ''; - }, - - blank: function() { - return /^\s*$/.test(this); - }, - - interpolate: function(object, pattern) { - return new Template(this, pattern).evaluate(object); - } -}); - -if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, { - escapeHTML: function() { - return this.replace(/&/g,'&').replace(//g,'>'); - }, - unescapeHTML: function() { - return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); - } -}); - -String.prototype.gsub.prepareReplacement = function(replacement) { - if (Object.isFunction(replacement)) return replacement; - var template = new Template(replacement); - return function(match) { return template.evaluate(match) }; -}; - -String.prototype.parseQuery = String.prototype.toQueryParams; - -Object.extend(String.prototype.escapeHTML, { - div: document.createElement('div'), - text: document.createTextNode('') -}); - -with (String.prototype.escapeHTML) div.appendChild(text); - -var Template = Class.create({ - initialize: function(template, pattern) { - this.template = template.toString(); - this.pattern = pattern || Template.Pattern; - }, - - evaluate: function(object) { - if (Object.isFunction(object.toTemplateReplacements)) - object = object.toTemplateReplacements(); - - return this.template.gsub(this.pattern, function(match) { - if (object == null) return ''; - - var before = match[1] || ''; - if (before == '\\') return match[2]; - - var ctx = object, expr = match[3]; - var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; - match = pattern.exec(expr); - if (match == null) return before; - - while (match != null) { - var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1]; - ctx = ctx[comp]; - if (null == ctx || '' == match[3]) break; - expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); - match = pattern.exec(expr); - } - - return before + String.interpret(ctx); - }); - } -}); -Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; - -var $break = { }; - -var Enumerable = { - each: function(iterator, context) { - var index = 0; - iterator = iterator.bind(context); - try { - this._each(function(value) { - iterator(value, index++); - }); - } catch (e) { - if (e != $break) throw e; - } - return this; - }, - - eachSlice: function(number, iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var index = -number, slices = [], array = this.toArray(); - while ((index += number) < array.length) - slices.push(array.slice(index, index+number)); - return slices.collect(iterator, context); - }, - - all: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var result = true; - this.each(function(value, index) { - result = result && !!iterator(value, index); - if (!result) throw $break; - }); - return result; - }, - - any: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var result = false; - this.each(function(value, index) { - if (result = !!iterator(value, index)) - throw $break; - }); - return result; - }, - - collect: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var results = []; - this.each(function(value, index) { - results.push(iterator(value, index)); - }); - return results; - }, - - detect: function(iterator, context) { - iterator = iterator.bind(context); - var result; - this.each(function(value, index) { - if (iterator(value, index)) { - result = value; - throw $break; - } - }); - return result; - }, - - findAll: function(iterator, context) { - iterator = iterator.bind(context); - var results = []; - this.each(function(value, index) { - if (iterator(value, index)) - results.push(value); - }); - return results; - }, - - grep: function(filter, iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var results = []; - - if (Object.isString(filter)) - filter = new RegExp(filter); - - this.each(function(value, index) { - if (filter.match(value)) - results.push(iterator(value, index)); - }); - return results; - }, - - include: function(object) { - if (Object.isFunction(this.indexOf)) - if (this.indexOf(object) != -1) return true; - - var found = false; - this.each(function(value) { - if (value == object) { - found = true; - throw $break; - } - }); - return found; - }, - - inGroupsOf: function(number, fillWith) { - fillWith = Object.isUndefined(fillWith) ? null : fillWith; - return this.eachSlice(number, function(slice) { - while(slice.length < number) slice.push(fillWith); - return slice; - }); - }, - - inject: function(memo, iterator, context) { - iterator = iterator.bind(context); - this.each(function(value, index) { - memo = iterator(memo, value, index); - }); - return memo; - }, - - invoke: function(method) { - var args = $A(arguments).slice(1); - return this.map(function(value) { - return value[method].apply(value, args); - }); - }, - - max: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var result; - this.each(function(value, index) { - value = iterator(value, index); - if (result == null || value >= result) - result = value; - }); - return result; - }, - - min: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var result; - this.each(function(value, index) { - value = iterator(value, index); - if (result == null || value < result) - result = value; - }); - return result; - }, - - partition: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var trues = [], falses = []; - this.each(function(value, index) { - (iterator(value, index) ? - trues : falses).push(value); - }); - return [trues, falses]; - }, - - pluck: function(property) { - var results = []; - this.each(function(value) { - results.push(value[property]); - }); - return results; - }, - - reject: function(iterator, context) { - iterator = iterator.bind(context); - var results = []; - this.each(function(value, index) { - if (!iterator(value, index)) - results.push(value); - }); - return results; - }, - - sortBy: function(iterator, context) { - iterator = iterator.bind(context); - return this.map(function(value, index) { - return {value: value, criteria: iterator(value, index)}; - }).sort(function(left, right) { - var a = left.criteria, b = right.criteria; - return a < b ? -1 : a > b ? 1 : 0; - }).pluck('value'); - }, - - toArray: function() { - return this.map(); - }, - - zip: function() { - var iterator = Prototype.K, args = $A(arguments); - if (Object.isFunction(args.last())) - iterator = args.pop(); - - var collections = [this].concat(args).map($A); - return this.map(function(value, index) { - return iterator(collections.pluck(index)); - }); - }, - - size: function() { - return this.toArray().length; - }, - - inspect: function() { - return '#'; - } -}; - -Object.extend(Enumerable, { - map: Enumerable.collect, - find: Enumerable.detect, - select: Enumerable.findAll, - filter: Enumerable.findAll, - member: Enumerable.include, - entries: Enumerable.toArray, - every: Enumerable.all, - some: Enumerable.any -}); -function $A(iterable) { - if (!iterable) return []; - if (iterable.toArray) return iterable.toArray(); - var length = iterable.length || 0, results = new Array(length); - while (length--) results[length] = iterable[length]; - return results; -} - -if (Prototype.Browser.WebKit) { - $A = function(iterable) { - if (!iterable) return []; - if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') && - iterable.toArray) return iterable.toArray(); - var length = iterable.length || 0, results = new Array(length); - while (length--) results[length] = iterable[length]; - return results; - }; -} - -Array.from = $A; - -Object.extend(Array.prototype, Enumerable); - -if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse; - -Object.extend(Array.prototype, { - _each: function(iterator) { - for (var i = 0, length = this.length; i < length; i++) - iterator(this[i]); - }, - - clear: function() { - this.length = 0; - return this; - }, - - first: function() { - return this[0]; - }, - - last: function() { - return this[this.length - 1]; - }, - - compact: function() { - return this.select(function(value) { - return value != null; - }); - }, - - flatten: function() { - return this.inject([], function(array, value) { - return array.concat(Object.isArray(value) ? - value.flatten() : [value]); - }); - }, - - without: function() { - var values = $A(arguments); - return this.select(function(value) { - return !values.include(value); - }); - }, - - reverse: function(inline) { - return (inline !== false ? this : this.toArray())._reverse(); - }, - - reduce: function() { - return this.length > 1 ? this : this[0]; - }, - - uniq: function(sorted) { - return this.inject([], function(array, value, index) { - if (0 == index || (sorted ? array.last() != value : !array.include(value))) - array.push(value); - return array; - }); - }, - - intersect: function(array) { - return this.uniq().findAll(function(item) { - return array.detect(function(value) { return item === value }); - }); - }, - - clone: function() { - return [].concat(this); - }, - - size: function() { - return this.length; - }, - - inspect: function() { - return '[' + this.map(Object.inspect).join(', ') + ']'; - }, - - toJSON: function() { - var results = []; - this.each(function(object) { - var value = Object.toJSON(object); - if (!Object.isUndefined(value)) results.push(value); - }); - return '[' + results.join(', ') + ']'; - } -}); - -// use native browser JS 1.6 implementation if available -if (Object.isFunction(Array.prototype.forEach)) - Array.prototype._each = Array.prototype.forEach; - -if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) { - i || (i = 0); - var length = this.length; - if (i < 0) i = length + i; - for (; i < length; i++) - if (this[i] === item) return i; - return -1; -}; - -if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) { - i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1; - var n = this.slice(0, i).reverse().indexOf(item); - return (n < 0) ? n : i - n - 1; -}; - -Array.prototype.toArray = Array.prototype.clone; - -function $w(string) { - if (!Object.isString(string)) return []; - string = string.strip(); - return string ? string.split(/\s+/) : []; -} - -if (Prototype.Browser.Opera){ - Array.prototype.concat = function() { - var array = []; - for (var i = 0, length = this.length; i < length; i++) array.push(this[i]); - for (var i = 0, length = arguments.length; i < length; i++) { - if (Object.isArray(arguments[i])) { - for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++) - array.push(arguments[i][j]); - } else { - array.push(arguments[i]); - } - } - return array; - }; -} -Object.extend(Number.prototype, { - toColorPart: function() { - return this.toPaddedString(2, 16); - }, - - succ: function() { - return this + 1; - }, - - times: function(iterator) { - $R(0, this, true).each(iterator); - return this; - }, - - toPaddedString: function(length, radix) { - var string = this.toString(radix || 10); - return '0'.times(length - string.length) + string; - }, - - toJSON: function() { - return isFinite(this) ? this.toString() : 'null'; - } -}); - -$w('abs round ceil floor').each(function(method){ - Number.prototype[method] = Math[method].methodize(); -}); -function $H(object) { - return new Hash(object); -}; - -var Hash = Class.create(Enumerable, (function() { - - function toQueryPair(key, value) { - if (Object.isUndefined(value)) return key; - return key + '=' + encodeURIComponent(String.interpret(value)); - } - - return { - initialize: function(object) { - this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); - }, - - _each: function(iterator) { - for (var key in this._object) { - var value = this._object[key], pair = [key, value]; - pair.key = key; - pair.value = value; - iterator(pair); - } - }, - - set: function(key, value) { - return this._object[key] = value; - }, - - get: function(key) { - return this._object[key]; - }, - - unset: function(key) { - var value = this._object[key]; - delete this._object[key]; - return value; - }, - - toObject: function() { - return Object.clone(this._object); - }, - - keys: function() { - return this.pluck('key'); - }, - - values: function() { - return this.pluck('value'); - }, - - index: function(value) { - var match = this.detect(function(pair) { - return pair.value === value; - }); - return match && match.key; - }, - - merge: function(object) { - return this.clone().update(object); - }, - - update: function(object) { - return new Hash(object).inject(this, function(result, pair) { - result.set(pair.key, pair.value); - return result; - }); - }, - - toQueryString: function() { - return this.map(function(pair) { - var key = encodeURIComponent(pair.key), values = pair.value; - - if (values && typeof values == 'object') { - if (Object.isArray(values)) - return values.map(toQueryPair.curry(key)).join('&'); - } - return toQueryPair(key, values); - }).join('&'); - }, - - inspect: function() { - return '#'; - }, - - toJSON: function() { - return Object.toJSON(this.toObject()); - }, - - clone: function() { - return new Hash(this); - } - } -})()); - -Hash.prototype.toTemplateReplacements = Hash.prototype.toObject; -Hash.from = $H; -var ObjectRange = Class.create(Enumerable, { - initialize: function(start, end, exclusive) { - this.start = start; - this.end = end; - this.exclusive = exclusive; - }, - - _each: function(iterator) { - var value = this.start; - while (this.include(value)) { - iterator(value); - value = value.succ(); - } - }, - - include: function(value) { - if (value < this.start) - return false; - if (this.exclusive) - return value < this.end; - return value <= this.end; - } -}); - -var $R = function(start, end, exclusive) { - return new ObjectRange(start, end, exclusive); -}; - -var Ajax = { - getTransport: function() { - return Try.these( - function() {return new XMLHttpRequest()}, - function() {return new ActiveXObject('Msxml2.XMLHTTP')}, - function() {return new ActiveXObject('Microsoft.XMLHTTP')} - ) || false; - }, - - activeRequestCount: 0 -}; - -Ajax.Responders = { - responders: [], - - _each: function(iterator) { - this.responders._each(iterator); - }, - - register: function(responder) { - if (!this.include(responder)) - this.responders.push(responder); - }, - - unregister: function(responder) { - this.responders = this.responders.without(responder); - }, - - dispatch: function(callback, request, transport, json) { - this.each(function(responder) { - if (Object.isFunction(responder[callback])) { - try { - responder[callback].apply(responder, [request, transport, json]); - } catch (e) { } - } - }); - } -}; - -Object.extend(Ajax.Responders, Enumerable); - -Ajax.Responders.register({ - onCreate: function() { Ajax.activeRequestCount++ }, - onComplete: function() { Ajax.activeRequestCount-- } -}); - -Ajax.Base = Class.create({ - initialize: function(options) { - this.options = { - method: 'post', - asynchronous: true, - contentType: 'application/x-www-form-urlencoded', - encoding: 'UTF-8', - parameters: '', - evalJSON: true, - evalJS: true - }; - Object.extend(this.options, options || { }); - - this.options.method = this.options.method.toLowerCase(); - - if (Object.isString(this.options.parameters)) - this.options.parameters = this.options.parameters.toQueryParams(); - else if (Object.isHash(this.options.parameters)) - this.options.parameters = this.options.parameters.toObject(); - } -}); - -Ajax.Request = Class.create(Ajax.Base, { - _complete: false, - - initialize: function($super, url, options) { - $super(options); - this.transport = Ajax.getTransport(); - this.request(url); - }, - - request: function(url) { - this.url = url; - this.method = this.options.method; - var params = Object.clone(this.options.parameters); - - if (!['get', 'post'].include(this.method)) { - // simulate other verbs over post - params['_method'] = this.method; - this.method = 'post'; - } - - this.parameters = params; - - if (params = Object.toQueryString(params)) { - // when GET, append parameters to URL - if (this.method == 'get') - this.url += (this.url.include('?') ? '&' : '?') + params; - else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) - params += '&_='; - } - - try { - var response = new Ajax.Response(this); - if (this.options.onCreate) this.options.onCreate(response); - Ajax.Responders.dispatch('onCreate', this, response); - - this.transport.open(this.method.toUpperCase(), this.url, - this.options.asynchronous); - - if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); - - this.transport.onreadystatechange = this.onStateChange.bind(this); - this.setRequestHeaders(); - - this.body = this.method == 'post' ? (this.options.postBody || params) : null; - this.transport.send(this.body); - - /* Force Firefox to handle ready state 4 for synchronous requests */ - if (!this.options.asynchronous && this.transport.overrideMimeType) - this.onStateChange(); - - } - catch (e) { - this.dispatchException(e); - } - }, - - onStateChange: function() { - var readyState = this.transport.readyState; - if (readyState > 1 && !((readyState == 4) && this._complete)) - this.respondToReadyState(this.transport.readyState); - }, - - setRequestHeaders: function() { - var headers = { - 'X-Requested-With': 'XMLHttpRequest', - 'X-Prototype-Version': Prototype.Version, - 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' - }; - - if (this.method == 'post') { - headers['Content-type'] = this.options.contentType + - (this.options.encoding ? '; charset=' + this.options.encoding : ''); - - /* Force "Connection: close" for older Mozilla browsers to work - * around a bug where XMLHttpRequest sends an incorrect - * Content-length header. See Mozilla Bugzilla #246651. - */ - if (this.transport.overrideMimeType && - (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) - headers['Connection'] = 'close'; - } - - // user-defined headers - if (typeof this.options.requestHeaders == 'object') { - var extras = this.options.requestHeaders; - - if (Object.isFunction(extras.push)) - for (var i = 0, length = extras.length; i < length; i += 2) - headers[extras[i]] = extras[i+1]; - else - $H(extras).each(function(pair) { headers[pair.key] = pair.value }); - } - - for (var name in headers) - this.transport.setRequestHeader(name, headers[name]); - }, - - success: function() { - var status = this.getStatus(); - return !status || (status >= 200 && status < 300); - }, - - getStatus: function() { - try { - return this.transport.status || 0; - } catch (e) { return 0 } - }, - - respondToReadyState: function(readyState) { - var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); - - if (state == 'Complete') { - try { - this._complete = true; - (this.options['on' + response.status] - || this.options['on' + (this.success() ? 'Success' : 'Failure')] - || Prototype.emptyFunction)(response, response.headerJSON); - } catch (e) { - this.dispatchException(e); - } - - var contentType = response.getHeader('Content-type'); - if (this.options.evalJS == 'force' - || (this.options.evalJS && this.isSameOrigin() && contentType - && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) - this.evalResponse(); - } - - try { - (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); - Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); - } catch (e) { - this.dispatchException(e); - } - - if (state == 'Complete') { - // avoid memory leak in MSIE: clean up - this.transport.onreadystatechange = Prototype.emptyFunction; - } - }, - - isSameOrigin: function() { - var m = this.url.match(/^\s*https?:\/\/[^\/]*/); - return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ - protocol: location.protocol, - domain: document.domain, - port: location.port ? ':' + location.port : '' - })); - }, - - getHeader: function(name) { - try { - return this.transport.getResponseHeader(name) || null; - } catch (e) { return null } - }, - - evalResponse: function() { - try { - return eval((this.transport.responseText || '').unfilterJSON()); - } catch (e) { - this.dispatchException(e); - } - }, - - dispatchException: function(exception) { - (this.options.onException || Prototype.emptyFunction)(this, exception); - Ajax.Responders.dispatch('onException', this, exception); - } -}); - -Ajax.Request.Events = - ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; - -Ajax.Response = Class.create({ - initialize: function(request){ - this.request = request; - var transport = this.transport = request.transport, - readyState = this.readyState = transport.readyState; - - if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { - this.status = this.getStatus(); - this.statusText = this.getStatusText(); - this.responseText = String.interpret(transport.responseText); - this.headerJSON = this._getHeaderJSON(); - } - - if(readyState == 4) { - var xml = transport.responseXML; - this.responseXML = Object.isUndefined(xml) ? null : xml; - this.responseJSON = this._getResponseJSON(); - } - }, - - status: 0, - statusText: '', - - getStatus: Ajax.Request.prototype.getStatus, - - getStatusText: function() { - try { - return this.transport.statusText || ''; - } catch (e) { return '' } - }, - - getHeader: Ajax.Request.prototype.getHeader, - - getAllHeaders: function() { - try { - return this.getAllResponseHeaders(); - } catch (e) { return null } - }, - - getResponseHeader: function(name) { - return this.transport.getResponseHeader(name); - }, - - getAllResponseHeaders: function() { - return this.transport.getAllResponseHeaders(); - }, - - _getHeaderJSON: function() { - var json = this.getHeader('X-JSON'); - if (!json) return null; - json = decodeURIComponent(escape(json)); - try { - return json.evalJSON(this.request.options.sanitizeJSON || - !this.request.isSameOrigin()); - } catch (e) { - this.request.dispatchException(e); - } - }, - - _getResponseJSON: function() { - var options = this.request.options; - if (!options.evalJSON || (options.evalJSON != 'force' && - !(this.getHeader('Content-type') || '').include('application/json')) || - this.responseText.blank()) - return null; - try { - return this.responseText.evalJSON(options.sanitizeJSON || - !this.request.isSameOrigin()); - } catch (e) { - this.request.dispatchException(e); - } - } -}); - -Ajax.Updater = Class.create(Ajax.Request, { - initialize: function($super, container, url, options) { - this.container = { - success: (container.success || container), - failure: (container.failure || (container.success ? null : container)) - }; - - options = Object.clone(options); - var onComplete = options.onComplete; - options.onComplete = (function(response, json) { - this.updateContent(response.responseText); - if (Object.isFunction(onComplete)) onComplete(response, json); - }).bind(this); - - $super(url, options); - }, - - updateContent: function(responseText) { - var receiver = this.container[this.success() ? 'success' : 'failure'], - options = this.options; - - if (!options.evalScripts) responseText = responseText.stripScripts(); - - if (receiver = $(receiver)) { - if (options.insertion) { - if (Object.isString(options.insertion)) { - var insertion = { }; insertion[options.insertion] = responseText; - receiver.insert(insertion); - } - else options.insertion(receiver, responseText); - } - else receiver.update(responseText); - } - } -}); - -Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { - initialize: function($super, container, url, options) { - $super(options); - this.onComplete = this.options.onComplete; - - this.frequency = (this.options.frequency || 2); - this.decay = (this.options.decay || 1); - - this.updater = { }; - this.container = container; - this.url = url; - - this.start(); - }, - - start: function() { - this.options.onComplete = this.updateComplete.bind(this); - this.onTimerEvent(); - }, - - stop: function() { - this.updater.options.onComplete = undefined; - clearTimeout(this.timer); - (this.onComplete || Prototype.emptyFunction).apply(this, arguments); - }, - - updateComplete: function(response) { - if (this.options.decay) { - this.decay = (response.responseText == this.lastText ? - this.decay * this.options.decay : 1); - - this.lastText = response.responseText; - } - this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); - }, - - onTimerEvent: function() { - this.updater = new Ajax.Updater(this.container, this.url, this.options); - } -}); -function $(element) { - if (arguments.length > 1) { - for (var i = 0, elements = [], length = arguments.length; i < length; i++) - elements.push($(arguments[i])); - return elements; - } - if (Object.isString(element)) - element = document.getElementById(element); - return Element.extend(element); -} - -if (Prototype.BrowserFeatures.XPath) { - document._getElementsByXPath = function(expression, parentElement) { - var results = []; - var query = document.evaluate(expression, $(parentElement) || document, - null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); - for (var i = 0, length = query.snapshotLength; i < length; i++) - results.push(Element.extend(query.snapshotItem(i))); - return results; - }; -} - -/*--------------------------------------------------------------------------*/ - -if (!window.Node) var Node = { }; - -if (!Node.ELEMENT_NODE) { - // DOM level 2 ECMAScript Language Binding - Object.extend(Node, { - ELEMENT_NODE: 1, - ATTRIBUTE_NODE: 2, - TEXT_NODE: 3, - CDATA_SECTION_NODE: 4, - ENTITY_REFERENCE_NODE: 5, - ENTITY_NODE: 6, - PROCESSING_INSTRUCTION_NODE: 7, - COMMENT_NODE: 8, - DOCUMENT_NODE: 9, - DOCUMENT_TYPE_NODE: 10, - DOCUMENT_FRAGMENT_NODE: 11, - NOTATION_NODE: 12 - }); -} - -(function() { - var element = this.Element; - this.Element = function(tagName, attributes) { - attributes = attributes || { }; - tagName = tagName.toLowerCase(); - var cache = Element.cache; - if (Prototype.Browser.IE && attributes.name) { - tagName = '<' + tagName + ' name="' + attributes.name + '">'; - delete attributes.name; - return Element.writeAttribute(document.createElement(tagName), attributes); - } - if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName)); - return Element.writeAttribute(cache[tagName].cloneNode(false), attributes); - }; - Object.extend(this.Element, element || { }); -}).call(window); - -Element.cache = { }; - -Element.Methods = { - visible: function(element) { - return $(element).style.display != 'none'; - }, - - toggle: function(element) { - element = $(element); - Element[Element.visible(element) ? 'hide' : 'show'](element); - return element; - }, - - hide: function(element) { - $(element).style.display = 'none'; - return element; - }, - - show: function(element) { - $(element).style.display = ''; - return element; - }, - - remove: function(element) { - element = $(element); - element.parentNode.removeChild(element); - return element; - }, - - update: function(element, content) { - element = $(element); - if (content && content.toElement) content = content.toElement(); - if (Object.isElement(content)) return element.update().insert(content); - content = Object.toHTML(content); - element.innerHTML = content.stripScripts(); - content.evalScripts.bind(content).defer(); - return element; - }, - - replace: function(element, content) { - element = $(element); - if (content && content.toElement) content = content.toElement(); - else if (!Object.isElement(content)) { - content = Object.toHTML(content); - var range = element.ownerDocument.createRange(); - range.selectNode(element); - content.evalScripts.bind(content).defer(); - content = range.createContextualFragment(content.stripScripts()); - } - element.parentNode.replaceChild(content, element); - return element; - }, - - insert: function(element, insertions) { - element = $(element); - - if (Object.isString(insertions) || Object.isNumber(insertions) || - Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML))) - insertions = {bottom:insertions}; - - var content, insert, tagName, childNodes; - - for (var position in insertions) { - content = insertions[position]; - position = position.toLowerCase(); - insert = Element._insertionTranslations[position]; - - if (content && content.toElement) content = content.toElement(); - if (Object.isElement(content)) { - insert(element, content); - continue; - } - - content = Object.toHTML(content); - - tagName = ((position == 'before' || position == 'after') - ? element.parentNode : element).tagName.toUpperCase(); - - childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); - - if (position == 'top' || position == 'after') childNodes.reverse(); - childNodes.each(insert.curry(element)); - - content.evalScripts.bind(content).defer(); - } - - return element; - }, - - wrap: function(element, wrapper, attributes) { - element = $(element); - if (Object.isElement(wrapper)) - $(wrapper).writeAttribute(attributes || { }); - else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes); - else wrapper = new Element('div', wrapper); - if (element.parentNode) - element.parentNode.replaceChild(wrapper, element); - wrapper.appendChild(element); - return wrapper; - }, - - inspect: function(element) { - element = $(element); - var result = '<' + element.tagName.toLowerCase(); - $H({'id': 'id', 'className': 'class'}).each(function(pair) { - var property = pair.first(), attribute = pair.last(); - var value = (element[property] || '').toString(); - if (value) result += ' ' + attribute + '=' + value.inspect(true); - }); - return result + '>'; - }, - - recursivelyCollect: function(element, property) { - element = $(element); - var elements = []; - while (element = element[property]) - if (element.nodeType == 1) - elements.push(Element.extend(element)); - return elements; - }, - - ancestors: function(element) { - return $(element).recursivelyCollect('parentNode'); - }, - - descendants: function(element) { - return $(element).select("*"); - }, - - firstDescendant: function(element) { - element = $(element).firstChild; - while (element && element.nodeType != 1) element = element.nextSibling; - return $(element); - }, - - immediateDescendants: function(element) { - if (!(element = $(element).firstChild)) return []; - while (element && element.nodeType != 1) element = element.nextSibling; - if (element) return [element].concat($(element).nextSiblings()); - return []; - }, - - previousSiblings: function(element) { - return $(element).recursivelyCollect('previousSibling'); - }, - - nextSiblings: function(element) { - return $(element).recursivelyCollect('nextSibling'); - }, - - siblings: function(element) { - element = $(element); - return element.previousSiblings().reverse().concat(element.nextSiblings()); - }, - - match: function(element, selector) { - if (Object.isString(selector)) - selector = new Selector(selector); - return selector.match($(element)); - }, - - up: function(element, expression, index) { - element = $(element); - if (arguments.length == 1) return $(element.parentNode); - var ancestors = element.ancestors(); - return Object.isNumber(expression) ? ancestors[expression] : - Selector.findElement(ancestors, expression, index); - }, - - down: function(element, expression, index) { - element = $(element); - if (arguments.length == 1) return element.firstDescendant(); - return Object.isNumber(expression) ? element.descendants()[expression] : - element.select(expression)[index || 0]; - }, - - previous: function(element, expression, index) { - element = $(element); - if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); - var previousSiblings = element.previousSiblings(); - return Object.isNumber(expression) ? previousSiblings[expression] : - Selector.findElement(previousSiblings, expression, index); - }, - - next: function(element, expression, index) { - element = $(element); - if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); - var nextSiblings = element.nextSiblings(); - return Object.isNumber(expression) ? nextSiblings[expression] : - Selector.findElement(nextSiblings, expression, index); - }, - - select: function() { - var args = $A(arguments), element = $(args.shift()); - return Selector.findChildElements(element, args); - }, - - adjacent: function() { - var args = $A(arguments), element = $(args.shift()); - return Selector.findChildElements(element.parentNode, args).without(element); - }, - - identify: function(element) { - element = $(element); - var id = element.readAttribute('id'), self = arguments.callee; - if (id) return id; - do { id = 'anonymous_element_' + self.counter++ } while ($(id)); - element.writeAttribute('id', id); - return id; - }, - - readAttribute: function(element, name) { - element = $(element); - if (Prototype.Browser.IE) { - var t = Element._attributeTranslations.read; - if (t.values[name]) return t.values[name](element, name); - if (t.names[name]) name = t.names[name]; - if (name.include(':')) { - return (!element.attributes || !element.attributes[name]) ? null : - element.attributes[name].value; - } - } - return element.getAttribute(name); - }, - - writeAttribute: function(element, name, value) { - element = $(element); - var attributes = { }, t = Element._attributeTranslations.write; - - if (typeof name == 'object') attributes = name; - else attributes[name] = Object.isUndefined(value) ? true : value; - - for (var attr in attributes) { - name = t.names[attr] || attr; - value = attributes[attr]; - if (t.values[attr]) name = t.values[attr](element, value); - if (value === false || value === null) - element.removeAttribute(name); - else if (value === true) - element.setAttribute(name, name); - else element.setAttribute(name, value); - } - return element; - }, - - getHeight: function(element) { - return $(element).getDimensions().height; - }, - - getWidth: function(element) { - return $(element).getDimensions().width; - }, - - classNames: function(element) { - return new Element.ClassNames(element); - }, - - hasClassName: function(element, className) { - if (!(element = $(element))) return; - var elementClassName = element.className; - return (elementClassName.length > 0 && (elementClassName == className || - new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName))); - }, - - addClassName: function(element, className) { - if (!(element = $(element))) return; - if (!element.hasClassName(className)) - element.className += (element.className ? ' ' : '') + className; - return element; - }, - - removeClassName: function(element, className) { - if (!(element = $(element))) return; - element.className = element.className.replace( - new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip(); - return element; - }, - - toggleClassName: function(element, className) { - if (!(element = $(element))) return; - return element[element.hasClassName(className) ? - 'removeClassName' : 'addClassName'](className); - }, - - // removes whitespace-only text node children - cleanWhitespace: function(element) { - element = $(element); - var node = element.firstChild; - while (node) { - var nextNode = node.nextSibling; - if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) - element.removeChild(node); - node = nextNode; - } - return element; - }, - - empty: function(element) { - return $(element).innerHTML.blank(); - }, - - descendantOf: function(element, ancestor) { - element = $(element), ancestor = $(ancestor); - var originalAncestor = ancestor; - - if (element.compareDocumentPosition) - return (element.compareDocumentPosition(ancestor) & 8) === 8; - - if (element.sourceIndex && !Prototype.Browser.Opera) { - var e = element.sourceIndex, a = ancestor.sourceIndex, - nextAncestor = ancestor.nextSibling; - if (!nextAncestor) { - do { ancestor = ancestor.parentNode; } - while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode); - } - if (nextAncestor && nextAncestor.sourceIndex) - return (e > a && e < nextAncestor.sourceIndex); - } - - while (element = element.parentNode) - if (element == originalAncestor) return true; - return false; - }, - - scrollTo: function(element) { - element = $(element); - var pos = element.cumulativeOffset(); - window.scrollTo(pos[0], pos[1]); - return element; - }, - - getStyle: function(element, style) { - element = $(element); - style = style == 'float' ? 'cssFloat' : style.camelize(); - var value = element.style[style]; - if (!value) { - var css = document.defaultView.getComputedStyle(element, null); - value = css ? css[style] : null; - } - if (style == 'opacity') return value ? parseFloat(value) : 1.0; - return value == 'auto' ? null : value; - }, - - getOpacity: function(element) { - return $(element).getStyle('opacity'); - }, - - setStyle: function(element, styles) { - element = $(element); - var elementStyle = element.style, match; - if (Object.isString(styles)) { - element.style.cssText += ';' + styles; - return styles.include('opacity') ? - element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element; - } - for (var property in styles) - if (property == 'opacity') element.setOpacity(styles[property]); - else - elementStyle[(property == 'float' || property == 'cssFloat') ? - (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') : - property] = styles[property]; - - return element; - }, - - setOpacity: function(element, value) { - element = $(element); - element.style.opacity = (value == 1 || value === '') ? '' : - (value < 0.00001) ? 0 : value; - return element; - }, - - getDimensions: function(element) { - element = $(element); - var display = $(element).getStyle('display'); - if (display != 'none' && display != null) // Safari bug - return {width: element.offsetWidth, height: element.offsetHeight}; - - // All *Width and *Height properties give 0 on elements with display none, - // so enable the element temporarily - var els = element.style; - var originalVisibility = els.visibility; - var originalPosition = els.position; - var originalDisplay = els.display; - els.visibility = 'hidden'; - els.position = 'absolute'; - els.display = 'block'; - var originalWidth = element.clientWidth; - var originalHeight = element.clientHeight; - els.display = originalDisplay; - els.position = originalPosition; - els.visibility = originalVisibility; - return {width: originalWidth, height: originalHeight}; - }, - - makePositioned: function(element) { - element = $(element); - var pos = Element.getStyle(element, 'position'); - if (pos == 'static' || !pos) { - element._madePositioned = true; - element.style.position = 'relative'; - // Opera returns the offset relative to the positioning context, when an - // element is position relative but top and left have not been defined - if (window.opera) { - element.style.top = 0; - element.style.left = 0; - } - } - return element; - }, - - undoPositioned: function(element) { - element = $(element); - if (element._madePositioned) { - element._madePositioned = undefined; - element.style.position = - element.style.top = - element.style.left = - element.style.bottom = - element.style.right = ''; - } - return element; - }, - - makeClipping: function(element) { - element = $(element); - if (element._overflow) return element; - element._overflow = Element.getStyle(element, 'overflow') || 'auto'; - if (element._overflow !== 'hidden') - element.style.overflow = 'hidden'; - return element; - }, - - undoClipping: function(element) { - element = $(element); - if (!element._overflow) return element; - element.style.overflow = element._overflow == 'auto' ? '' : element._overflow; - element._overflow = null; - return element; - }, - - cumulativeOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - element = element.offsetParent; - } while (element); - return Element._returnOffset(valueL, valueT); - }, - - positionedOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - element = element.offsetParent; - if (element) { - if (element.tagName == 'BODY') break; - var p = Element.getStyle(element, 'position'); - if (p !== 'static') break; - } - } while (element); - return Element._returnOffset(valueL, valueT); - }, - - absolutize: function(element) { - element = $(element); - if (element.getStyle('position') == 'absolute') return; - // Position.prepare(); // To be done manually by Scripty when it needs it. - - var offsets = element.positionedOffset(); - var top = offsets[1]; - var left = offsets[0]; - var width = element.clientWidth; - var height = element.clientHeight; - - element._originalLeft = left - parseFloat(element.style.left || 0); - element._originalTop = top - parseFloat(element.style.top || 0); - element._originalWidth = element.style.width; - element._originalHeight = element.style.height; - - element.style.position = 'absolute'; - element.style.top = top + 'px'; - element.style.left = left + 'px'; - element.style.width = width + 'px'; - element.style.height = height + 'px'; - return element; - }, - - relativize: function(element) { - element = $(element); - if (element.getStyle('position') == 'relative') return; - // Position.prepare(); // To be done manually by Scripty when it needs it. - - element.style.position = 'relative'; - var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); - var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); - - element.style.top = top + 'px'; - element.style.left = left + 'px'; - element.style.height = element._originalHeight; - element.style.width = element._originalWidth; - return element; - }, - - cumulativeScrollOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.scrollTop || 0; - valueL += element.scrollLeft || 0; - element = element.parentNode; - } while (element); - return Element._returnOffset(valueL, valueT); - }, - - getOffsetParent: function(element) { - if (element.offsetParent) return $(element.offsetParent); - if (element == document.body) return $(element); - - while ((element = element.parentNode) && element != document.body) - if (Element.getStyle(element, 'position') != 'static') - return $(element); - - return $(document.body); - }, - - viewportOffset: function(forElement) { - var valueT = 0, valueL = 0; - - var element = forElement; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - - // Safari fix - if (element.offsetParent == document.body && - Element.getStyle(element, 'position') == 'absolute') break; - - } while (element = element.offsetParent); - - element = forElement; - do { - if (!Prototype.Browser.Opera || element.tagName == 'BODY') { - valueT -= element.scrollTop || 0; - valueL -= element.scrollLeft || 0; - } - } while (element = element.parentNode); - - return Element._returnOffset(valueL, valueT); - }, - - clonePosition: function(element, source) { - var options = Object.extend({ - setLeft: true, - setTop: true, - setWidth: true, - setHeight: true, - offsetTop: 0, - offsetLeft: 0 - }, arguments[2] || { }); - - // find page position of source - source = $(source); - var p = source.viewportOffset(); - - // find coordinate system to use - element = $(element); - var delta = [0, 0]; - var parent = null; - // delta [0,0] will do fine with position: fixed elements, - // position:absolute needs offsetParent deltas - if (Element.getStyle(element, 'position') == 'absolute') { - parent = element.getOffsetParent(); - delta = parent.viewportOffset(); - } - - // correct by body offsets (fixes Safari) - if (parent == document.body) { - delta[0] -= document.body.offsetLeft; - delta[1] -= document.body.offsetTop; - } - - // set position - if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; - if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; - if (options.setWidth) element.style.width = source.offsetWidth + 'px'; - if (options.setHeight) element.style.height = source.offsetHeight + 'px'; - return element; - } -}; - -Element.Methods.identify.counter = 1; - -Object.extend(Element.Methods, { - getElementsBySelector: Element.Methods.select, - childElements: Element.Methods.immediateDescendants -}); - -Element._attributeTranslations = { - write: { - names: { - className: 'class', - htmlFor: 'for' - }, - values: { } - } -}; - -if (Prototype.Browser.Opera) { - Element.Methods.getStyle = Element.Methods.getStyle.wrap( - function(proceed, element, style) { - switch (style) { - case 'left': case 'top': case 'right': case 'bottom': - if (proceed(element, 'position') === 'static') return null; - case 'height': case 'width': - // returns '0px' for hidden elements; we want it to return null - if (!Element.visible(element)) return null; - - // returns the border-box dimensions rather than the content-box - // dimensions, so we subtract padding and borders from the value - var dim = parseInt(proceed(element, style), 10); - - if (dim !== element['offset' + style.capitalize()]) - return dim + 'px'; - - var properties; - if (style === 'height') { - properties = ['border-top-width', 'padding-top', - 'padding-bottom', 'border-bottom-width']; - } - else { - properties = ['border-left-width', 'padding-left', - 'padding-right', 'border-right-width']; - } - return properties.inject(dim, function(memo, property) { - var val = proceed(element, property); - return val === null ? memo : memo - parseInt(val, 10); - }) + 'px'; - default: return proceed(element, style); - } - } - ); - - Element.Methods.readAttribute = Element.Methods.readAttribute.wrap( - function(proceed, element, attribute) { - if (attribute === 'title') return element.title; - return proceed(element, attribute); - } - ); -} - -else if (Prototype.Browser.IE) { - // IE doesn't report offsets correctly for static elements, so we change them - // to "relative" to get the values, then change them back. - Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( - function(proceed, element) { - element = $(element); - var position = element.getStyle('position'); - if (position !== 'static') return proceed(element); - element.setStyle({ position: 'relative' }); - var value = proceed(element); - element.setStyle({ position: position }); - return value; - } - ); - - $w('positionedOffset viewportOffset').each(function(method) { - Element.Methods[method] = Element.Methods[method].wrap( - function(proceed, element) { - element = $(element); - var position = element.getStyle('position'); - if (position !== 'static') return proceed(element); - // Trigger hasLayout on the offset parent so that IE6 reports - // accurate offsetTop and offsetLeft values for position: fixed. - var offsetParent = element.getOffsetParent(); - if (offsetParent && offsetParent.getStyle('position') === 'fixed') - offsetParent.setStyle({ zoom: 1 }); - element.setStyle({ position: 'relative' }); - var value = proceed(element); - element.setStyle({ position: position }); - return value; - } - ); - }); - - Element.Methods.getStyle = function(element, style) { - element = $(element); - style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize(); - var value = element.style[style]; - if (!value && element.currentStyle) value = element.currentStyle[style]; - - if (style == 'opacity') { - if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/)) - if (value[1]) return parseFloat(value[1]) / 100; - return 1.0; - } - - if (value == 'auto') { - if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none')) - return element['offset' + style.capitalize()] + 'px'; - return null; - } - return value; - }; - - Element.Methods.setOpacity = function(element, value) { - function stripAlpha(filter){ - return filter.replace(/alpha\([^\)]*\)/gi,''); - } - element = $(element); - var currentStyle = element.currentStyle; - if ((currentStyle && !currentStyle.hasLayout) || - (!currentStyle && element.style.zoom == 'normal')) - element.style.zoom = 1; - - var filter = element.getStyle('filter'), style = element.style; - if (value == 1 || value === '') { - (filter = stripAlpha(filter)) ? - style.filter = filter : style.removeAttribute('filter'); - return element; - } else if (value < 0.00001) value = 0; - style.filter = stripAlpha(filter) + - 'alpha(opacity=' + (value * 100) + ')'; - return element; - }; - - Element._attributeTranslations = { - read: { - names: { - 'class': 'className', - 'for': 'htmlFor' - }, - values: { - _getAttr: function(element, attribute) { - return element.getAttribute(attribute, 2); - }, - _getAttrNode: function(element, attribute) { - var node = element.getAttributeNode(attribute); - return node ? node.value : ""; - }, - _getEv: function(element, attribute) { - attribute = element.getAttribute(attribute); - return attribute ? attribute.toString().slice(23, -2) : null; - }, - _flag: function(element, attribute) { - return $(element).hasAttribute(attribute) ? attribute : null; - }, - style: function(element) { - return element.style.cssText.toLowerCase(); - }, - title: function(element) { - return element.title; - } - } - } - }; - - Element._attributeTranslations.write = { - names: Object.extend({ - cellpadding: 'cellPadding', - cellspacing: 'cellSpacing' - }, Element._attributeTranslations.read.names), - values: { - checked: function(element, value) { - element.checked = !!value; - }, - - style: function(element, value) { - element.style.cssText = value ? value : ''; - } - } - }; - - Element._attributeTranslations.has = {}; - - $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' + - 'encType maxLength readOnly longDesc').each(function(attr) { - Element._attributeTranslations.write.names[attr.toLowerCase()] = attr; - Element._attributeTranslations.has[attr.toLowerCase()] = attr; - }); - - (function(v) { - Object.extend(v, { - href: v._getAttr, - src: v._getAttr, - type: v._getAttr, - action: v._getAttrNode, - disabled: v._flag, - checked: v._flag, - readonly: v._flag, - multiple: v._flag, - onload: v._getEv, - onunload: v._getEv, - onclick: v._getEv, - ondblclick: v._getEv, - onmousedown: v._getEv, - onmouseup: v._getEv, - onmouseover: v._getEv, - onmousemove: v._getEv, - onmouseout: v._getEv, - onfocus: v._getEv, - onblur: v._getEv, - onkeypress: v._getEv, - onkeydown: v._getEv, - onkeyup: v._getEv, - onsubmit: v._getEv, - onreset: v._getEv, - onselect: v._getEv, - onchange: v._getEv - }); - })(Element._attributeTranslations.read.values); -} - -else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) { - Element.Methods.setOpacity = function(element, value) { - element = $(element); - element.style.opacity = (value == 1) ? 0.999999 : - (value === '') ? '' : (value < 0.00001) ? 0 : value; - return element; - }; -} - -else if (Prototype.Browser.WebKit) { - Element.Methods.setOpacity = function(element, value) { - element = $(element); - element.style.opacity = (value == 1 || value === '') ? '' : - (value < 0.00001) ? 0 : value; - - if (value == 1) - if(element.tagName == 'IMG' && element.width) { - element.width++; element.width--; - } else try { - var n = document.createTextNode(' '); - element.appendChild(n); - element.removeChild(n); - } catch (e) { } - - return element; - }; - - // Safari returns margins on body which is incorrect if the child is absolutely - // positioned. For performance reasons, redefine Element#cumulativeOffset for - // KHTML/WebKit only. - Element.Methods.cumulativeOffset = function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - if (element.offsetParent == document.body) - if (Element.getStyle(element, 'position') == 'absolute') break; - - element = element.offsetParent; - } while (element); - - return Element._returnOffset(valueL, valueT); - }; -} - -if (Prototype.Browser.IE || Prototype.Browser.Opera) { - // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements - Element.Methods.update = function(element, content) { - element = $(element); - - if (content && content.toElement) content = content.toElement(); - if (Object.isElement(content)) return element.update().insert(content); - - content = Object.toHTML(content); - var tagName = element.tagName.toUpperCase(); - - if (tagName in Element._insertionTranslations.tags) { - $A(element.childNodes).each(function(node) { element.removeChild(node) }); - Element._getContentFromAnonymousElement(tagName, content.stripScripts()) - .each(function(node) { element.appendChild(node) }); - } - else element.innerHTML = content.stripScripts(); - - content.evalScripts.bind(content).defer(); - return element; - }; -} - -if ('outerHTML' in document.createElement('div')) { - Element.Methods.replace = function(element, content) { - element = $(element); - - if (content && content.toElement) content = content.toElement(); - if (Object.isElement(content)) { - element.parentNode.replaceChild(content, element); - return element; - } - - content = Object.toHTML(content); - var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); - - if (Element._insertionTranslations.tags[tagName]) { - var nextSibling = element.next(); - var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); - parent.removeChild(element); - if (nextSibling) - fragments.each(function(node) { parent.insertBefore(node, nextSibling) }); - else - fragments.each(function(node) { parent.appendChild(node) }); - } - else element.outerHTML = content.stripScripts(); - - content.evalScripts.bind(content).defer(); - return element; - }; -} - -Element._returnOffset = function(l, t) { - var result = [l, t]; - result.left = l; - result.top = t; - return result; -}; - -Element._getContentFromAnonymousElement = function(tagName, html) { - var div = new Element('div'), t = Element._insertionTranslations.tags[tagName]; - if (t) { - div.innerHTML = t[0] + html + t[1]; - t[2].times(function() { div = div.firstChild }); - } else div.innerHTML = html; - return $A(div.childNodes); -}; - -Element._insertionTranslations = { - before: function(element, node) { - element.parentNode.insertBefore(node, element); - }, - top: function(element, node) { - element.insertBefore(node, element.firstChild); - }, - bottom: function(element, node) { - element.appendChild(node); - }, - after: function(element, node) { - element.parentNode.insertBefore(node, element.nextSibling); - }, - tags: { - TABLE: ['', '
', 1], - TBODY: ['', '
', 2], - TR: ['', '
', 3], - TD: ['
', '
', 4], - SELECT: ['', 1] - } -}; - -(function() { - Object.extend(this.tags, { - THEAD: this.tags.TBODY, - TFOOT: this.tags.TBODY, - TH: this.tags.TD - }); -}).call(Element._insertionTranslations); - -Element.Methods.Simulated = { - hasAttribute: function(element, attribute) { - attribute = Element._attributeTranslations.has[attribute] || attribute; - var node = $(element).getAttributeNode(attribute); - return node && node.specified; - } -}; - -Element.Methods.ByTag = { }; - -Object.extend(Element, Element.Methods); - -if (!Prototype.BrowserFeatures.ElementExtensions && - document.createElement('div').__proto__) { - window.HTMLElement = { }; - window.HTMLElement.prototype = document.createElement('div').__proto__; - Prototype.BrowserFeatures.ElementExtensions = true; -} - -Element.extend = (function() { - if (Prototype.BrowserFeatures.SpecificElementExtensions) - return Prototype.K; - - var Methods = { }, ByTag = Element.Methods.ByTag; - - var extend = Object.extend(function(element) { - if (!element || element._extendedByPrototype || - element.nodeType != 1 || element == window) return element; - - var methods = Object.clone(Methods), - tagName = element.tagName, property, value; - - // extend methods for specific tags - if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); - - for (property in methods) { - value = methods[property]; - if (Object.isFunction(value) && !(property in element)) - element[property] = value.methodize(); - } - - element._extendedByPrototype = Prototype.emptyFunction; - return element; - - }, { - refresh: function() { - // extend methods for all tags (Safari doesn't need this) - if (!Prototype.BrowserFeatures.ElementExtensions) { - Object.extend(Methods, Element.Methods); - Object.extend(Methods, Element.Methods.Simulated); - } - } - }); - - extend.refresh(); - return extend; -})(); - -Element.hasAttribute = function(element, attribute) { - if (element.hasAttribute) return element.hasAttribute(attribute); - return Element.Methods.Simulated.hasAttribute(element, attribute); -}; - -Element.addMethods = function(methods) { - var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag; - - if (!methods) { - Object.extend(Form, Form.Methods); - Object.extend(Form.Element, Form.Element.Methods); - Object.extend(Element.Methods.ByTag, { - "FORM": Object.clone(Form.Methods), - "INPUT": Object.clone(Form.Element.Methods), - "SELECT": Object.clone(Form.Element.Methods), - "TEXTAREA": Object.clone(Form.Element.Methods) - }); - } - - if (arguments.length == 2) { - var tagName = methods; - methods = arguments[1]; - } - - if (!tagName) Object.extend(Element.Methods, methods || { }); - else { - if (Object.isArray(tagName)) tagName.each(extend); - else extend(tagName); - } - - function extend(tagName) { - tagName = tagName.toUpperCase(); - if (!Element.Methods.ByTag[tagName]) - Element.Methods.ByTag[tagName] = { }; - Object.extend(Element.Methods.ByTag[tagName], methods); - } - - function copy(methods, destination, onlyIfAbsent) { - onlyIfAbsent = onlyIfAbsent || false; - for (var property in methods) { - var value = methods[property]; - if (!Object.isFunction(value)) continue; - if (!onlyIfAbsent || !(property in destination)) - destination[property] = value.methodize(); - } - } - - function findDOMClass(tagName) { - var klass; - var trans = { - "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph", - "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList", - "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading", - "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote", - "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION": - "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD": - "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR": - "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET": - "FrameSet", "IFRAME": "IFrame" - }; - if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element'; - if (window[klass]) return window[klass]; - klass = 'HTML' + tagName + 'Element'; - if (window[klass]) return window[klass]; - klass = 'HTML' + tagName.capitalize() + 'Element'; - if (window[klass]) return window[klass]; - - window[klass] = { }; - window[klass].prototype = document.createElement(tagName).__proto__; - return window[klass]; - } - - if (F.ElementExtensions) { - copy(Element.Methods, HTMLElement.prototype); - copy(Element.Methods.Simulated, HTMLElement.prototype, true); - } - - if (F.SpecificElementExtensions) { - for (var tag in Element.Methods.ByTag) { - var klass = findDOMClass(tag); - if (Object.isUndefined(klass)) continue; - copy(T[tag], klass.prototype); - } - } - - Object.extend(Element, Element.Methods); - delete Element.ByTag; - - if (Element.extend.refresh) Element.extend.refresh(); - Element.cache = { }; -}; - -document.viewport = { - getDimensions: function() { - var dimensions = { }; - var B = Prototype.Browser; - $w('width height').each(function(d) { - var D = d.capitalize(); - dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] : - (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D]; - }); - return dimensions; - }, - - getWidth: function() { - return this.getDimensions().width; - }, - - getHeight: function() { - return this.getDimensions().height; - }, - - getScrollOffsets: function() { - return Element._returnOffset( - window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft, - window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop); - } -}; -/* Portions of the Selector class are derived from Jack Slocum’s DomQuery, - * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style - * license. Please see http://www.yui-ext.com/ for more information. */ - -var Selector = Class.create({ - initialize: function(expression) { - this.expression = expression.strip(); - this.compileMatcher(); - }, - - shouldUseXPath: function() { - if (!Prototype.BrowserFeatures.XPath) return false; - - var e = this.expression; - - // Safari 3 chokes on :*-of-type and :empty - if (Prototype.Browser.WebKit && - (e.include("-of-type") || e.include(":empty"))) - return false; - - // XPath can't do namespaced attributes, nor can it read - // the "checked" property from DOM nodes - if ((/(\[[\w-]*?:|:checked)/).test(this.expression)) - return false; - - return true; - }, - - compileMatcher: function() { - if (this.shouldUseXPath()) - return this.compileXPathMatcher(); - - var e = this.expression, ps = Selector.patterns, h = Selector.handlers, - c = Selector.criteria, le, p, m; - - if (Selector._cache[e]) { - this.matcher = Selector._cache[e]; - return; - } - - this.matcher = ["this.matcher = function(root) {", - "var r = root, h = Selector.handlers, c = false, n;"]; - - while (e && le != e && (/\S/).test(e)) { - le = e; - for (var i in ps) { - p = ps[i]; - if (m = e.match(p)) { - this.matcher.push(Object.isFunction(c[i]) ? c[i](m) : - new Template(c[i]).evaluate(m)); - e = e.replace(m[0], ''); - break; - } - } - } - - this.matcher.push("return h.unique(n);\n}"); - eval(this.matcher.join('\n')); - Selector._cache[this.expression] = this.matcher; - }, - - compileXPathMatcher: function() { - var e = this.expression, ps = Selector.patterns, - x = Selector.xpath, le, m; - - if (Selector._cache[e]) { - this.xpath = Selector._cache[e]; return; - } - - this.matcher = ['.//*']; - while (e && le != e && (/\S/).test(e)) { - le = e; - for (var i in ps) { - if (m = e.match(ps[i])) { - this.matcher.push(Object.isFunction(x[i]) ? x[i](m) : - new Template(x[i]).evaluate(m)); - e = e.replace(m[0], ''); - break; - } - } - } - - this.xpath = this.matcher.join(''); - Selector._cache[this.expression] = this.xpath; - }, - - findElements: function(root) { - root = root || document; - if (this.xpath) return document._getElementsByXPath(this.xpath, root); - return this.matcher(root); - }, - - match: function(element) { - this.tokens = []; - - var e = this.expression, ps = Selector.patterns, as = Selector.assertions; - var le, p, m; - - while (e && le !== e && (/\S/).test(e)) { - le = e; - for (var i in ps) { - p = ps[i]; - if (m = e.match(p)) { - // use the Selector.assertions methods unless the selector - // is too complex. - if (as[i]) { - this.tokens.push([i, Object.clone(m)]); - e = e.replace(m[0], ''); - } else { - // reluctantly do a document-wide search - // and look for a match in the array - return this.findElements(document).include(element); - } - } - } - } - - var match = true, name, matches; - for (var i = 0, token; token = this.tokens[i]; i++) { - name = token[0], matches = token[1]; - if (!Selector.assertions[name](element, matches)) { - match = false; break; - } - } - - return match; - }, - - toString: function() { - return this.expression; - }, - - inspect: function() { - return "#"; - } -}); - -Object.extend(Selector, { - _cache: { }, - - xpath: { - descendant: "//*", - child: "/*", - adjacent: "/following-sibling::*[1]", - laterSibling: '/following-sibling::*', - tagName: function(m) { - if (m[1] == '*') return ''; - return "[local-name()='" + m[1].toLowerCase() + - "' or local-name()='" + m[1].toUpperCase() + "']"; - }, - className: "[contains(concat(' ', @class, ' '), ' #{1} ')]", - id: "[@id='#{1}']", - attrPresence: function(m) { - m[1] = m[1].toLowerCase(); - return new Template("[@#{1}]").evaluate(m); - }, - attr: function(m) { - m[1] = m[1].toLowerCase(); - m[3] = m[5] || m[6]; - return new Template(Selector.xpath.operators[m[2]]).evaluate(m); - }, - pseudo: function(m) { - var h = Selector.xpath.pseudos[m[1]]; - if (!h) return ''; - if (Object.isFunction(h)) return h(m); - return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m); - }, - operators: { - '=': "[@#{1}='#{3}']", - '!=': "[@#{1}!='#{3}']", - '^=': "[starts-with(@#{1}, '#{3}')]", - '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']", - '*=': "[contains(@#{1}, '#{3}')]", - '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]", - '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]" - }, - pseudos: { - 'first-child': '[not(preceding-sibling::*)]', - 'last-child': '[not(following-sibling::*)]', - 'only-child': '[not(preceding-sibling::* or following-sibling::*)]', - 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]", - 'checked': "[@checked]", - 'disabled': "[@disabled]", - 'enabled': "[not(@disabled)]", - 'not': function(m) { - var e = m[6], p = Selector.patterns, - x = Selector.xpath, le, v; - - var exclusion = []; - while (e && le != e && (/\S/).test(e)) { - le = e; - for (var i in p) { - if (m = e.match(p[i])) { - v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m); - exclusion.push("(" + v.substring(1, v.length - 1) + ")"); - e = e.replace(m[0], ''); - break; - } - } - } - return "[not(" + exclusion.join(" and ") + ")]"; - }, - 'nth-child': function(m) { - return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m); - }, - 'nth-last-child': function(m) { - return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m); - }, - 'nth-of-type': function(m) { - return Selector.xpath.pseudos.nth("position() ", m); - }, - 'nth-last-of-type': function(m) { - return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m); - }, - 'first-of-type': function(m) { - m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m); - }, - 'last-of-type': function(m) { - m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m); - }, - 'only-of-type': function(m) { - var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m); - }, - nth: function(fragment, m) { - var mm, formula = m[6], predicate; - if (formula == 'even') formula = '2n+0'; - if (formula == 'odd') formula = '2n+1'; - if (mm = formula.match(/^(\d+)$/)) // digit only - return '[' + fragment + "= " + mm[1] + ']'; - if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b - if (mm[1] == "-") mm[1] = -1; - var a = mm[1] ? Number(mm[1]) : 1; - var b = mm[2] ? Number(mm[2]) : 0; - predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " + - "((#{fragment} - #{b}) div #{a} >= 0)]"; - return new Template(predicate).evaluate({ - fragment: fragment, a: a, b: b }); - } - } - } - }, - - criteria: { - tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;', - className: 'n = h.className(n, r, "#{1}", c); c = false;', - id: 'n = h.id(n, r, "#{1}", c); c = false;', - attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;', - attr: function(m) { - m[3] = (m[5] || m[6]); - return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m); - }, - pseudo: function(m) { - if (m[6]) m[6] = m[6].replace(/"/g, '\\"'); - return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m); - }, - descendant: 'c = "descendant";', - child: 'c = "child";', - adjacent: 'c = "adjacent";', - laterSibling: 'c = "laterSibling";' - }, - - patterns: { - // combinators must be listed first - // (and descendant needs to be last combinator) - laterSibling: /^\s*~\s*/, - child: /^\s*>\s*/, - adjacent: /^\s*\+\s*/, - descendant: /^\s/, - - // selectors follow - tagName: /^\s*(\*|[\w\-]+)(\b|$)?/, - id: /^#([\w\-\*]+)(\b|$)/, - className: /^\.([\w\-\*]+)(\b|$)/, - pseudo: -/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/, - attrPresence: /^\[([\w]+)\]/, - attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ - }, - - // for Selector.match and Element#match - assertions: { - tagName: function(element, matches) { - return matches[1].toUpperCase() == element.tagName.toUpperCase(); - }, - - className: function(element, matches) { - return Element.hasClassName(element, matches[1]); - }, - - id: function(element, matches) { - return element.id === matches[1]; - }, - - attrPresence: function(element, matches) { - return Element.hasAttribute(element, matches[1]); - }, - - attr: function(element, matches) { - var nodeValue = Element.readAttribute(element, matches[1]); - return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]); - } - }, - - handlers: { - // UTILITY FUNCTIONS - // joins two collections - concat: function(a, b) { - for (var i = 0, node; node = b[i]; i++) - a.push(node); - return a; - }, - - // marks an array of nodes for counting - mark: function(nodes) { - var _true = Prototype.emptyFunction; - for (var i = 0, node; node = nodes[i]; i++) - node._countedByPrototype = _true; - return nodes; - }, - - unmark: function(nodes) { - for (var i = 0, node; node = nodes[i]; i++) - node._countedByPrototype = undefined; - return nodes; - }, - - // mark each child node with its position (for nth calls) - // "ofType" flag indicates whether we're indexing for nth-of-type - // rather than nth-child - index: function(parentNode, reverse, ofType) { - parentNode._countedByPrototype = Prototype.emptyFunction; - if (reverse) { - for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { - var node = nodes[i]; - if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; - } - } else { - for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) - if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; - } - }, - - // filters out duplicates and extends all nodes - unique: function(nodes) { - if (nodes.length == 0) return nodes; - var results = [], n; - for (var i = 0, l = nodes.length; i < l; i++) - if (!(n = nodes[i])._countedByPrototype) { - n._countedByPrototype = Prototype.emptyFunction; - results.push(Element.extend(n)); - } - return Selector.handlers.unmark(results); - }, - - // COMBINATOR FUNCTIONS - descendant: function(nodes) { - var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) - h.concat(results, node.getElementsByTagName('*')); - return results; - }, - - child: function(nodes) { - var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) { - for (var j = 0, child; child = node.childNodes[j]; j++) - if (child.nodeType == 1 && child.tagName != '!') results.push(child); - } - return results; - }, - - adjacent: function(nodes) { - for (var i = 0, results = [], node; node = nodes[i]; i++) { - var next = this.nextElementSibling(node); - if (next) results.push(next); - } - return results; - }, - - laterSibling: function(nodes) { - var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) - h.concat(results, Element.nextSiblings(node)); - return results; - }, - - nextElementSibling: function(node) { - while (node = node.nextSibling) - if (node.nodeType == 1) return node; - return null; - }, - - previousElementSibling: function(node) { - while (node = node.previousSibling) - if (node.nodeType == 1) return node; - return null; - }, - - // TOKEN FUNCTIONS - tagName: function(nodes, root, tagName, combinator) { - var uTagName = tagName.toUpperCase(); - var results = [], h = Selector.handlers; - if (nodes) { - if (combinator) { - // fastlane for ordinary descendant combinators - if (combinator == "descendant") { - for (var i = 0, node; node = nodes[i]; i++) - h.concat(results, node.getElementsByTagName(tagName)); - return results; - } else nodes = this[combinator](nodes); - if (tagName == "*") return nodes; - } - for (var i = 0, node; node = nodes[i]; i++) - if (node.tagName.toUpperCase() === uTagName) results.push(node); - return results; - } else return root.getElementsByTagName(tagName); - }, - - id: function(nodes, root, id, combinator) { - var targetNode = $(id), h = Selector.handlers; - if (!targetNode) return []; - if (!nodes && root == document) return [targetNode]; - if (nodes) { - if (combinator) { - if (combinator == 'child') { - for (var i = 0, node; node = nodes[i]; i++) - if (targetNode.parentNode == node) return [targetNode]; - } else if (combinator == 'descendant') { - for (var i = 0, node; node = nodes[i]; i++) - if (Element.descendantOf(targetNode, node)) return [targetNode]; - } else if (combinator == 'adjacent') { - for (var i = 0, node; node = nodes[i]; i++) - if (Selector.handlers.previousElementSibling(targetNode) == node) - return [targetNode]; - } else nodes = h[combinator](nodes); - } - for (var i = 0, node; node = nodes[i]; i++) - if (node == targetNode) return [targetNode]; - return []; - } - return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : []; - }, - - className: function(nodes, root, className, combinator) { - if (nodes && combinator) nodes = this[combinator](nodes); - return Selector.handlers.byClassName(nodes, root, className); - }, - - byClassName: function(nodes, root, className) { - if (!nodes) nodes = Selector.handlers.descendant([root]); - var needle = ' ' + className + ' '; - for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) { - nodeClassName = node.className; - if (nodeClassName.length == 0) continue; - if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle)) - results.push(node); - } - return results; - }, - - attrPresence: function(nodes, root, attr, combinator) { - if (!nodes) nodes = root.getElementsByTagName("*"); - if (nodes && combinator) nodes = this[combinator](nodes); - var results = []; - for (var i = 0, node; node = nodes[i]; i++) - if (Element.hasAttribute(node, attr)) results.push(node); - return results; - }, - - attr: function(nodes, root, attr, value, operator, combinator) { - if (!nodes) nodes = root.getElementsByTagName("*"); - if (nodes && combinator) nodes = this[combinator](nodes); - var handler = Selector.operators[operator], results = []; - for (var i = 0, node; node = nodes[i]; i++) { - var nodeValue = Element.readAttribute(node, attr); - if (nodeValue === null) continue; - if (handler(nodeValue, value)) results.push(node); - } - return results; - }, - - pseudo: function(nodes, name, value, root, combinator) { - if (nodes && combinator) nodes = this[combinator](nodes); - if (!nodes) nodes = root.getElementsByTagName("*"); - return Selector.pseudos[name](nodes, value, root); - } - }, - - pseudos: { - 'first-child': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) { - if (Selector.handlers.previousElementSibling(node)) continue; - results.push(node); - } - return results; - }, - 'last-child': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) { - if (Selector.handlers.nextElementSibling(node)) continue; - results.push(node); - } - return results; - }, - 'only-child': function(nodes, value, root) { - var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (!h.previousElementSibling(node) && !h.nextElementSibling(node)) - results.push(node); - return results; - }, - 'nth-child': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, formula, root); - }, - 'nth-last-child': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, formula, root, true); - }, - 'nth-of-type': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, formula, root, false, true); - }, - 'nth-last-of-type': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, formula, root, true, true); - }, - 'first-of-type': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, "1", root, false, true); - }, - 'last-of-type': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, "1", root, true, true); - }, - 'only-of-type': function(nodes, formula, root) { - var p = Selector.pseudos; - return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root); - }, - - // handles the an+b logic - getIndices: function(a, b, total) { - if (a == 0) return b > 0 ? [b] : []; - return $R(1, total).inject([], function(memo, i) { - if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i); - return memo; - }); - }, - - // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type - nth: function(nodes, formula, root, reverse, ofType) { - if (nodes.length == 0) return []; - if (formula == 'even') formula = '2n+0'; - if (formula == 'odd') formula = '2n+1'; - var h = Selector.handlers, results = [], indexed = [], m; - h.mark(nodes); - for (var i = 0, node; node = nodes[i]; i++) { - if (!node.parentNode._countedByPrototype) { - h.index(node.parentNode, reverse, ofType); - indexed.push(node.parentNode); - } - } - if (formula.match(/^\d+$/)) { // just a number - formula = Number(formula); - for (var i = 0, node; node = nodes[i]; i++) - if (node.nodeIndex == formula) results.push(node); - } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b - if (m[1] == "-") m[1] = -1; - var a = m[1] ? Number(m[1]) : 1; - var b = m[2] ? Number(m[2]) : 0; - var indices = Selector.pseudos.getIndices(a, b, nodes.length); - for (var i = 0, node, l = indices.length; node = nodes[i]; i++) { - for (var j = 0; j < l; j++) - if (node.nodeIndex == indices[j]) results.push(node); - } - } - h.unmark(nodes); - h.unmark(indexed); - return results; - }, - - 'empty': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) { - // IE treats comments as element nodes - if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue; - results.push(node); - } - return results; - }, - - 'not': function(nodes, selector, root) { - var h = Selector.handlers, selectorType, m; - var exclusions = new Selector(selector).findElements(root); - h.mark(exclusions); - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (!node._countedByPrototype) results.push(node); - h.unmark(exclusions); - return results; - }, - - 'enabled': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (!node.disabled) results.push(node); - return results; - }, - - 'disabled': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (node.disabled) results.push(node); - return results; - }, - - 'checked': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (node.checked) results.push(node); - return results; - } - }, - - operators: { - '=': function(nv, v) { return nv == v; }, - '!=': function(nv, v) { return nv != v; }, - '^=': function(nv, v) { return nv.startsWith(v); }, - '$=': function(nv, v) { return nv.endsWith(v); }, - '*=': function(nv, v) { return nv.include(v); }, - '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); }, - '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); } - }, - - split: function(expression) { - var expressions = []; - expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { - expressions.push(m[1].strip()); - }); - return expressions; - }, - - matchElements: function(elements, expression) { - var matches = $$(expression), h = Selector.handlers; - h.mark(matches); - for (var i = 0, results = [], element; element = elements[i]; i++) - if (element._countedByPrototype) results.push(element); - h.unmark(matches); - return results; - }, - - findElement: function(elements, expression, index) { - if (Object.isNumber(expression)) { - index = expression; expression = false; - } - return Selector.matchElements(elements, expression || '*')[index || 0]; - }, - - findChildElements: function(element, expressions) { - expressions = Selector.split(expressions.join(',')); - var results = [], h = Selector.handlers; - for (var i = 0, l = expressions.length, selector; i < l; i++) { - selector = new Selector(expressions[i].strip()); - h.concat(results, selector.findElements(element)); - } - return (l > 1) ? h.unique(results) : results; - } -}); - -if (Prototype.Browser.IE) { - Object.extend(Selector.handlers, { - // IE returns comment nodes on getElementsByTagName("*"). - // Filter them out. - concat: function(a, b) { - for (var i = 0, node; node = b[i]; i++) - if (node.tagName !== "!") a.push(node); - return a; - }, - - // IE improperly serializes _countedByPrototype in (inner|outer)HTML. - unmark: function(nodes) { - for (var i = 0, node; node = nodes[i]; i++) - node.removeAttribute('_countedByPrototype'); - return nodes; - } - }); -} - -function $$() { - return Selector.findChildElements(document, $A(arguments)); -} -var Form = { - reset: function(form) { - $(form).reset(); - return form; - }, - - serializeElements: function(elements, options) { - if (typeof options != 'object') options = { hash: !!options }; - else if (Object.isUndefined(options.hash)) options.hash = true; - var key, value, submitted = false, submit = options.submit; - - var data = elements.inject({ }, function(result, element) { - if (!element.disabled && element.name) { - key = element.name; value = $(element).getValue(); - if (value != null && (element.type != 'submit' || (!submitted && - submit !== false && (!submit || key == submit) && (submitted = true)))) { - if (key in result) { - // a key is already present; construct an array of values - if (!Object.isArray(result[key])) result[key] = [result[key]]; - result[key].push(value); - } - else result[key] = value; - } - } - return result; - }); - - return options.hash ? data : Object.toQueryString(data); - } -}; - -Form.Methods = { - serialize: function(form, options) { - return Form.serializeElements(Form.getElements(form), options); - }, - - getElements: function(form) { - return $A($(form).getElementsByTagName('*')).inject([], - function(elements, child) { - if (Form.Element.Serializers[child.tagName.toLowerCase()]) - elements.push(Element.extend(child)); - return elements; - } - ); - }, - - getInputs: function(form, typeName, name) { - form = $(form); - var inputs = form.getElementsByTagName('input'); - - if (!typeName && !name) return $A(inputs).map(Element.extend); - - for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { - var input = inputs[i]; - if ((typeName && input.type != typeName) || (name && input.name != name)) - continue; - matchingInputs.push(Element.extend(input)); - } - - return matchingInputs; - }, - - disable: function(form) { - form = $(form); - Form.getElements(form).invoke('disable'); - return form; - }, - - enable: function(form) { - form = $(form); - Form.getElements(form).invoke('enable'); - return form; - }, - - findFirstElement: function(form) { - var elements = $(form).getElements().findAll(function(element) { - return 'hidden' != element.type && !element.disabled; - }); - var firstByIndex = elements.findAll(function(element) { - return element.hasAttribute('tabIndex') && element.tabIndex >= 0; - }).sortBy(function(element) { return element.tabIndex }).first(); - - return firstByIndex ? firstByIndex : elements.find(function(element) { - return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); - }); - }, - - focusFirstElement: function(form) { - form = $(form); - form.findFirstElement().activate(); - return form; - }, - - request: function(form, options) { - form = $(form), options = Object.clone(options || { }); - - var params = options.parameters, action = form.readAttribute('action') || ''; - if (action.blank()) action = window.location.href; - options.parameters = form.serialize(true); - - if (params) { - if (Object.isString(params)) params = params.toQueryParams(); - Object.extend(options.parameters, params); - } - - if (form.hasAttribute('method') && !options.method) - options.method = form.method; - - return new Ajax.Request(action, options); - } -}; - -/*--------------------------------------------------------------------------*/ - -Form.Element = { - focus: function(element) { - $(element).focus(); - return element; - }, - - select: function(element) { - $(element).select(); - return element; - } -}; - -Form.Element.Methods = { - serialize: function(element) { - element = $(element); - if (!element.disabled && element.name) { - var value = element.getValue(); - if (value != undefined) { - var pair = { }; - pair[element.name] = value; - return Object.toQueryString(pair); - } - } - return ''; - }, - - getValue: function(element) { - element = $(element); - var method = element.tagName.toLowerCase(); - return Form.Element.Serializers[method](element); - }, - - setValue: function(element, value) { - element = $(element); - var method = element.tagName.toLowerCase(); - Form.Element.Serializers[method](element, value); - return element; - }, - - clear: function(element) { - $(element).value = ''; - return element; - }, - - present: function(element) { - return $(element).value != ''; - }, - - activate: function(element) { - element = $(element); - try { - element.focus(); - if (element.select && (element.tagName.toLowerCase() != 'input' || - !['button', 'reset', 'submit'].include(element.type))) - element.select(); - } catch (e) { } - return element; - }, - - disable: function(element) { - element = $(element); - element.blur(); - element.disabled = true; - return element; - }, - - enable: function(element) { - element = $(element); - element.disabled = false; - return element; - } -}; - -/*--------------------------------------------------------------------------*/ - -var Field = Form.Element; -var $F = Form.Element.Methods.getValue; - -/*--------------------------------------------------------------------------*/ - -Form.Element.Serializers = { - input: function(element, value) { - switch (element.type.toLowerCase()) { - case 'checkbox': - case 'radio': - return Form.Element.Serializers.inputSelector(element, value); - default: - return Form.Element.Serializers.textarea(element, value); - } - }, - - inputSelector: function(element, value) { - if (Object.isUndefined(value)) return element.checked ? element.value : null; - else element.checked = !!value; - }, - - textarea: function(element, value) { - if (Object.isUndefined(value)) return element.value; - else element.value = value; - }, - - select: function(element, index) { - if (Object.isUndefined(index)) - return this[element.type == 'select-one' ? - 'selectOne' : 'selectMany'](element); - else { - var opt, value, single = !Object.isArray(index); - for (var i = 0, length = element.length; i < length; i++) { - opt = element.options[i]; - value = this.optionValue(opt); - if (single) { - if (value == index) { - opt.selected = true; - return; - } - } - else opt.selected = index.include(value); - } - } - }, - - selectOne: function(element) { - var index = element.selectedIndex; - return index >= 0 ? this.optionValue(element.options[index]) : null; - }, - - selectMany: function(element) { - var values, length = element.length; - if (!length) return null; - - for (var i = 0, values = []; i < length; i++) { - var opt = element.options[i]; - if (opt.selected) values.push(this.optionValue(opt)); - } - return values; - }, - - optionValue: function(opt) { - // extend element because hasAttribute may not be native - return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; - } -}; - -/*--------------------------------------------------------------------------*/ - -Abstract.TimedObserver = Class.create(PeriodicalExecuter, { - initialize: function($super, element, frequency, callback) { - $super(callback, frequency); - this.element = $(element); - this.lastValue = this.getValue(); - }, - - execute: function() { - var value = this.getValue(); - if (Object.isString(this.lastValue) && Object.isString(value) ? - this.lastValue != value : String(this.lastValue) != String(value)) { - this.callback(this.element, value); - this.lastValue = value; - } - } -}); - -Form.Element.Observer = Class.create(Abstract.TimedObserver, { - getValue: function() { - return Form.Element.getValue(this.element); - } -}); - -Form.Observer = Class.create(Abstract.TimedObserver, { - getValue: function() { - return Form.serialize(this.element); - } -}); - -/*--------------------------------------------------------------------------*/ - -Abstract.EventObserver = Class.create({ - initialize: function(element, callback) { - this.element = $(element); - this.callback = callback; - - this.lastValue = this.getValue(); - if (this.element.tagName.toLowerCase() == 'form') - this.registerFormCallbacks(); - else - this.registerCallback(this.element); - }, - - onElementEvent: function() { - var value = this.getValue(); - if (this.lastValue != value) { - this.callback(this.element, value); - this.lastValue = value; - } - }, - - registerFormCallbacks: function() { - Form.getElements(this.element).each(this.registerCallback, this); - }, - - registerCallback: function(element) { - if (element.type) { - switch (element.type.toLowerCase()) { - case 'checkbox': - case 'radio': - Event.observe(element, 'click', this.onElementEvent.bind(this)); - break; - default: - Event.observe(element, 'change', this.onElementEvent.bind(this)); - break; - } - } - } -}); - -Form.Element.EventObserver = Class.create(Abstract.EventObserver, { - getValue: function() { - return Form.Element.getValue(this.element); - } -}); - -Form.EventObserver = Class.create(Abstract.EventObserver, { - getValue: function() { - return Form.serialize(this.element); - } -}); -if (!window.Event) var Event = { }; - -Object.extend(Event, { - KEY_BACKSPACE: 8, - KEY_TAB: 9, - KEY_RETURN: 13, - KEY_ESC: 27, - KEY_LEFT: 37, - KEY_UP: 38, - KEY_RIGHT: 39, - KEY_DOWN: 40, - KEY_DELETE: 46, - KEY_HOME: 36, - KEY_END: 35, - KEY_PAGEUP: 33, - KEY_PAGEDOWN: 34, - KEY_INSERT: 45, - - cache: { }, - - relatedTarget: function(event) { - var element; - switch(event.type) { - case 'mouseover': element = event.fromElement; break; - case 'mouseout': element = event.toElement; break; - default: return null; - } - return Element.extend(element); - } -}); - -Event.Methods = (function() { - var isButton; - - if (Prototype.Browser.IE) { - var buttonMap = { 0: 1, 1: 4, 2: 2 }; - isButton = function(event, code) { - return event.button == buttonMap[code]; - }; - - } else if (Prototype.Browser.WebKit) { - isButton = function(event, code) { - switch (code) { - case 0: return event.which == 1 && !event.metaKey; - case 1: return event.which == 1 && event.metaKey; - default: return false; - } - }; - - } else { - isButton = function(event, code) { - return event.which ? (event.which === code + 1) : (event.button === code); - }; - } - - return { - isLeftClick: function(event) { return isButton(event, 0) }, - isMiddleClick: function(event) { return isButton(event, 1) }, - isRightClick: function(event) { return isButton(event, 2) }, - - element: function(event) { - var node = Event.extend(event).target; - return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node); - }, - - findElement: function(event, expression) { - var element = Event.element(event); - if (!expression) return element; - var elements = [element].concat(element.ancestors()); - return Selector.findElement(elements, expression, 0); - }, - - pointer: function(event) { - return { - x: event.pageX || (event.clientX + - (document.documentElement.scrollLeft || document.body.scrollLeft)), - y: event.pageY || (event.clientY + - (document.documentElement.scrollTop || document.body.scrollTop)) - }; - }, - - pointerX: function(event) { return Event.pointer(event).x }, - pointerY: function(event) { return Event.pointer(event).y }, - - stop: function(event) { - Event.extend(event); - event.preventDefault(); - event.stopPropagation(); - event.stopped = true; - } - }; -})(); - -Event.extend = (function() { - var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { - m[name] = Event.Methods[name].methodize(); - return m; - }); - - if (Prototype.Browser.IE) { - Object.extend(methods, { - stopPropagation: function() { this.cancelBubble = true }, - preventDefault: function() { this.returnValue = false }, - inspect: function() { return "[object Event]" } - }); - - return function(event) { - if (!event) return false; - if (event._extendedByPrototype) return event; - - event._extendedByPrototype = Prototype.emptyFunction; - var pointer = Event.pointer(event); - Object.extend(event, { - target: event.srcElement, - relatedTarget: Event.relatedTarget(event), - pageX: pointer.x, - pageY: pointer.y - }); - return Object.extend(event, methods); - }; - - } else { - Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__; - Object.extend(Event.prototype, methods); - return Prototype.K; - } -})(); - -Object.extend(Event, (function() { - var cache = Event.cache; - - function getEventID(element) { - if (element._prototypeEventID) return element._prototypeEventID[0]; - arguments.callee.id = arguments.callee.id || 1; - return element._prototypeEventID = [++arguments.callee.id]; - } - - function getDOMEventName(eventName) { - if (eventName && eventName.include(':')) return "dataavailable"; - return eventName; - } - - function getCacheForID(id) { - return cache[id] = cache[id] || { }; - } - - function getWrappersForEventName(id, eventName) { - var c = getCacheForID(id); - return c[eventName] = c[eventName] || []; - } - - function createWrapper(element, eventName, handler) { - var id = getEventID(element); - var c = getWrappersForEventName(id, eventName); - if (c.pluck("handler").include(handler)) return false; - - var wrapper = function(event) { - if (!Event || !Event.extend || - (event.eventName && event.eventName != eventName)) - return false; - - Event.extend(event); - handler.call(element, event); - }; - - wrapper.handler = handler; - c.push(wrapper); - return wrapper; - } - - function findWrapper(id, eventName, handler) { - var c = getWrappersForEventName(id, eventName); - return c.find(function(wrapper) { return wrapper.handler == handler }); - } - - function destroyWrapper(id, eventName, handler) { - var c = getCacheForID(id); - if (!c[eventName]) return false; - c[eventName] = c[eventName].without(findWrapper(id, eventName, handler)); - } - - function destroyCache() { - for (var id in cache) - for (var eventName in cache[id]) - cache[id][eventName] = null; - } - - if (window.attachEvent) { - window.attachEvent("onunload", destroyCache); - } - - return { - observe: function(element, eventName, handler) { - element = $(element); - var name = getDOMEventName(eventName); - - var wrapper = createWrapper(element, eventName, handler); - if (!wrapper) return element; - - if (element.addEventListener) { - element.addEventListener(name, wrapper, false); - } else { - element.attachEvent("on" + name, wrapper); - } - - return element; - }, - - stopObserving: function(element, eventName, handler) { - element = $(element); - var id = getEventID(element), name = getDOMEventName(eventName); - - if (!handler && eventName) { - getWrappersForEventName(id, eventName).each(function(wrapper) { - element.stopObserving(eventName, wrapper.handler); - }); - return element; - - } else if (!eventName) { - Object.keys(getCacheForID(id)).each(function(eventName) { - element.stopObserving(eventName); - }); - return element; - } - - var wrapper = findWrapper(id, eventName, handler); - if (!wrapper) return element; - - if (element.removeEventListener) { - element.removeEventListener(name, wrapper, false); - } else { - element.detachEvent("on" + name, wrapper); - } - - destroyWrapper(id, eventName, handler); - - return element; - }, - - fire: function(element, eventName, memo) { - element = $(element); - if (element == document && document.createEvent && !element.dispatchEvent) - element = document.documentElement; - - var event; - if (document.createEvent) { - event = document.createEvent("HTMLEvents"); - event.initEvent("dataavailable", true, true); - } else { - event = document.createEventObject(); - event.eventType = "ondataavailable"; - } - - event.eventName = eventName; - event.memo = memo || { }; - - if (document.createEvent) { - element.dispatchEvent(event); - } else { - element.fireEvent(event.eventType, event); - } - - return Event.extend(event); - } - }; -})()); - -Object.extend(Event, Event.Methods); - -Element.addMethods({ - fire: Event.fire, - observe: Event.observe, - stopObserving: Event.stopObserving -}); - -Object.extend(document, { - fire: Element.Methods.fire.methodize(), - observe: Element.Methods.observe.methodize(), - stopObserving: Element.Methods.stopObserving.methodize(), - loaded: false -}); - -(function() { - /* Support for the DOMContentLoaded event is based on work by Dan Webb, - Matthias Miller, Dean Edwards and John Resig. */ - - var timer; - - function fireContentLoadedEvent() { - if (document.loaded) return; - if (timer) window.clearInterval(timer); - document.fire("dom:loaded"); - document.loaded = true; - } - - if (document.addEventListener) { - if (Prototype.Browser.WebKit) { - timer = window.setInterval(function() { - if (/loaded|complete/.test(document.readyState)) - fireContentLoadedEvent(); - }, 0); - - Event.observe(window, "load", fireContentLoadedEvent); - - } else { - document.addEventListener("DOMContentLoaded", - fireContentLoadedEvent, false); - } - - } else { - document.write(" {/if} {if $garmin==true} - - + + {* *}