1 if(Garmin == undefined){
  2     /**
  3     *@namespace Garmin The Garmin namespace object.
  4     */
  5     var Garmin = {};
  6 }
  7 /** Copyright © 2007-2011 Garmin Ltd. or its subsidiaries.
  8  *
  9  * Licensed under the Apache License, Version 2.0 (the 'License')
 10  * you may not use this file except in compliance with the License.
 11  * You may obtain a copy of the License at
 12  *
 13  *    http://www.apache.org/licenses/LICENSE-2.0
 14  *
 15  * Unless required by applicable law or agreed to in writing, software
 16  * distributed under the License is distributed on an 'AS IS' BASIS,
 17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 18  * See the License for the specific language governing permissions and
 19  * limitations under the License.
 20  * 
 21  * @fileOverview Garmin.DevicePlugin wraps the Garmin ActiveX/Netscape plugin that should be installed on your machine in order to talk to a Garmin Device.<br/>
 22  * The plugin is available for download from http://www.garmin.com/support/download_details.jsp?id=3608
 23  * More information is available about this plugin from http://www.garmin.com/products/communicator/
 24  * @version 1.10
 25  */
 26 
 27 /**
 28  * Wraps the ActiveX/Netscape plugin that should be installed on your machine in order to talk to a Garmin Device.
 29  * The Garmin Communicator Plugin is available for download <a href="http://www.garmin.com/support/download_details.jsp?id=3608">here</a><br/>
 30  * More information is available about the plugin <a href="http://www.garmin.com/products/communicator/">here</a><br/><br/>
 31  * This api provides a set of functions to accomplish the following tasks with a Garmin Device:
 32  * <br/>
 33  * <br/>  1) Unlocking devices allowing them to be found and accessed.
 34  * <br/>  2) Finding avaliable devices plugged into this machine.
 35  * <br/>  3) Reading from the device.
 36  * <br/>  4) Writing to the device.
 37  * <br/>  5) Getting messages, getting transfer status/progress and version information from the device.
 38  * <br/><br/>
 39  * Note that XML schemas <a href=http://www.garmin.com/xmlschemas/GarminPluginAPIV1.xsd>GarminPluginAPIV1</a> and
 40  * <a href=http://www.garmin.com/xmlschemas/DeviceDownloadV1.xsd>DeviceDownloadV1</a> define XML input and output throughout this API.<br/>
 41  * 
 42  * @class Garmin.DevicePlugin
 43  * @constructor
 44  * @param pluginElement element that references the Garmin GPS Control Web Plugin that should be installed.
 45  * @return a new Garmin.DevicePlugin
 46  *
 47  * @requires Prototype
 48  */
 49 
 50 Garmin.DevicePlugin = function(pluginElement){};  //just here for jsdoc
 51 Garmin.DevicePlugin = Class.create();
 52 Garmin.DevicePlugin.prototype = {
 53 
 54     /** Constructor.
 55      * @private
 56      */
 57 	initialize: function(pluginElement) {        
 58 	    this.plugin = pluginElement;
 59 	    this.unlocked = false;
 60 	    //console.debug("DevicePlugin constructor supportsFitnessWrite="+this.supportsFitnessWrite)
 61 	},
 62 	
 63 	/** Unlocks the GpsControl object to be used at the given web address.  
 64      * More than one set of path-key pairs my be passed in, for example:
 65      * ['http://myDomain.com/', 'xxx','http://www.myDomain.com/', 'yyy']
 66      * See documentation site for more info on getting a key. <br/>
 67      * <br/>
 68      * Minimum plugin version 2.0.0.4
 69      * 
 70      * @param pathKeyPairsArray {Array}- baseURL and key pairs.  
 71      * @type Boolean
 72      * @return true if successfully unlocked or undefined otherwise
 73      */
 74 	unlock: function(pathKeyPairsArray) {
 75 	    var len = pathKeyPairsArray ? pathKeyPairsArray.length / 2 : 0;
 76 	    for(var i=0;i<len;i++) {
 77 	    	if (this.plugin.Unlock(pathKeyPairsArray[i*2], pathKeyPairsArray[i*2+1])){
 78 	    		this.unlocked = true;
 79 	    		return this.unlocked;
 80 	    	}
 81 	    }
 82 	    
 83 	    // Unlock codes for local development
 84 	    this.tryUnlock = this.plugin.Unlock("file:///","cb1492ae040612408d87cc53e3f7ff3c")
 85         	|| this.plugin.Unlock("http://localhost","45517b532362fc3149e4211ade14c9b2")
 86         	|| this.plugin.Unlock("http://127.0.0.1","40cd4860f7988c53b15b8491693de133");
 87         
 88         this.unlocked = !this.plugin.Locked;
 89         	
 90 	    return this.unlocked;
 91 	},
 92 	
 93 	/** Returns true if the plug-in is unlocked.
 94 	 */
 95 	isUnlocked: function() {
 96 		return this.unlocked;
 97 	},
 98 	
 99 	/**
100 	* Check to see if a property (function or field) is defined for an object.
101 	* This function is reliable for native and host objects.
102 	* @param object {Object} - the object to test.
103 	* @param propertyName {String} - name of the property to test.
104     * @type Boolean
105 	* @return true - if property is defined,  False otherwise.
106 	*/
107 	_propertyExists: function( object, property ) {
108 	    //tweaked version of Michaux's detection function:
109         //http://peter.michaux.ca/articles/feature-detection-state-of-the-art-browser-scripting
110         var isProperty = false;
111         var theType = typeof object[property];
112         if( theType != 'undefined' ) {
113             isProperty = true;
114         }
115         else {
116             try {
117                 // This test would error for ActiveX but
118                 // those test will have returned above 
119                 // because typeof result will have been 
120                 // 'unknown'.
121                 if( object[property] ) {
122                     isProperty = true;
123                 }
124             }
125             catch (e) {}
126         }
127         return isProperty;
128 	},
129 	
130 	/** Lazy-logic accessor to fitness write support var.
131 	 * This is used to detect whether the user's installed plugin supports fitness writing.
132 	 * Fitness writing capability has a minimum requirement of plugin version 2.2.0.1.
133 	 * This should NOT be called until the plug-in has been unlocked.
134 	 */
135 	getSupportsFitnessWrite: function() {
136 	    return this._propertyExists(this.plugin, "StartWriteFitnessData"); 
137 	},
138 	
139 	/** Lazy-logic accessor to fitness write support var.
140 	 * This is used to detect whether the user's installed plugin supports fitness directory reading,
141 	 * which has a minimum requirement of plugin version 2.2.0.2.
142 	 * This should NOT be called until the plug-in has been unlocked.
143 	 */
144 	getSupportsFitnessDirectoryRead: function() {	
145 		return this._propertyExists(this.plugin, "StartReadFitnessDirectory");
146 	},
147 
148 	/** Lazy-logic accessor to FIT read support var.
149 	 * This is used to detect whether the user's installed plugin supports FIT directory reading,
150 	 * which has a minimum requirement of plugin version 2.8.1.0.
151 	 * This should NOT be called until the plug-in has been unlocked.
152 	 */
153 	getSupportsFitDirectoryRead: function() {
154         return this._propertyExists(this.plugin, "StartReadFITDirectory");
155 	},
156 	
157 	/** Lazy-logic accessor to fitness read compressed support var.
158 	 * This is used to detect whether the user's installed plugin supports fitness reading in compressed format,
159 	 * which has a minimum requirement of plugin version 2.2.0.2.
160 	 * This should NOT be called until the plug-in has been unlocked.
161 	 */
162 	getSupportsFitnessReadCompressed: function() {
163 	    return this._propertyExists(this.plugin, "TcdXmlz" );
164 	},
165 
166 	/** This is used to detect whether the user's installed plugin supports readable file listing.
167 	 * Readable file listing has a minimum requirement of plugin version 2.8.1.0.
168 	 * This should NOT be called until the plug-in has been unlocked.
169 	 */
170 	getSupportsReadableFileListing: function() {		
171         return this._propertyExists(this.plugin, "StartReadableFileListing");
172 	},
173 	
174 	/** Initiates a find Gps devices action on the plugin. 
175 	 * Poll with finishFindDevices to determine when the plugin has completed this action.
176 	 * Use getDeviceXmlString to inspect xml contents for and array of Device nodes.<br/>
177 	 * <br/>
178 	 * Minimum plugin version 2.0.0.4
179 	 * 
180 	 * @see #finishFindDevices
181 	 * @see #cancelFindDevices
182 	 */
183 	startFindDevices: function() {
184 		this.plugin.StartFindDevices();
185 	},
186 
187 	/** Cancels the current find devices interaction. <br/>
188 	 * <br/>
189 	 * Minimum plugin version 2.0.0.4
190 	 * 
191 	 * @see #startFindDevices
192 	 * @see #finishFindDevices
193 	 */
194 	cancelFindDevices: function() {
195         this.plugin.CancelFindDevices();
196 	},
197 
198 	/** Poll - with this function to determine completion of startFindDevices. Used after 
199 	 * the call to startFindDevices(). <br/>
200 	 * <br/>
201 	 * Minimum plugin version 2.0.0.4
202 	 * 
203 	 * @type Boolean
204 	 * @return Returns true if completed finding devices otherwise false.
205 	 * @see #startFindDevices
206 	 * @see #cancelFindDevices
207 	 */
208 	finishFindDevices: function() {
209     	return this.plugin.FinishFindDevices();
210 	},
211 	
212 	/** Returns information about the number of devices connected to this machine as 
213 	 * well as the names of those devices. Refer to the Devices_t element in the
214 	 * <a href="http://www.garmin.com/xmlschemas/GarminPluginAPIV1.xsd">GarminPluginAPIV1 schema</a>
215 	 * for what is included.
216 	 * The xml returned should contain a 'Device' element with 'DisplayName' and 'Number'
217 	 * if there is a device actually connected. <br/>
218 	 * <br/>
219 	 * Minimum plugin version 2.0.0.4
220 	 * 
221 	 * @type String
222 	 * @return Xml string with detailed device info
223 	 * @see #getDeviceDescriptionXml
224 	 */
225 	getDevicesXml: function(){
226 		return this.plugin.DevicesXmlString();
227 	},
228 
229 	/** Returns information about the specified Device indicated by the device Number. 
230 	 * See the getDevicesXml function to get the actual deviceNumber assigned.
231 	 * Refer to the 
232 	 * <a href="http://developer.garmin.com/schemas/device/v2/xmlspy/index.html#Link04DDFE88">Devices_t</a>
233 	 * element in the Device XML schema for what is included in the XML. <br/>
234 	 * <br/>
235 	 * Minimum plugin version 2.0.0.4
236 	 * 
237 	 * @param deviceNumber {Number} Assigned by the plugin, see getDevicesXml for 
238 	 * assignment of that number.
239 	 * @type String
240 	 * @return Xml string with detailed device info
241 	 * @see #getDevicesXml for device number assignment
242 	 */
243 	getDeviceDescriptionXml: function(deviceNumber){
244 		return this.plugin.DeviceDescription(deviceNumber);
245 	},
246 	
247 	/**Returns the number, assigned by plugin, of the parent device.<br/> 
248      * If the device has no parent, -1 is returned.<br/>
249 	 * Minimum plugin version 2.9.2.5
250 	 * 
251 	 * @param deviceNumber {Number} Assigned by the plugin
252      * @returns {Number} Parent device's assigned number. -1 if the device has no parent.
253 	 * @see #getDevicesXml for device number assignment
254 	 */
255 	getParentDevice: function(deviceNumber) {
256 		if( !this.checkPluginVersionSupport([2,9,2,5]) ) 
257 		{
258 			throw new Error("Your Communicator Plug-in version (" + this.getPluginVersionString() + ") does not support device hierarchy (getParentDevice)");
259 		}
260 		return this.plugin.ParentDevice(deviceNumber);
261 	},
262 	
263 	// Read Methods
264 	
265 	/** Initiates the read from the gps device conneted. Use finishReadFromGps and getGpsProgressXml to 
266 	 * determine when the plugin is done with this operation. Also, use getGpsXml to extract the
267 	 * actual data from the device. <br/>
268 	 * <br/>
269 	 * Minimum plugin version 2.0.0.4
270 	 * 
271 	 * @param deviceNumber {Number} assigned by the plugin, see getDevicesXml for 
272 	 * assignment of that number.
273 	 * @see #finishReadFromGps
274 	 * @see #cancelReadFromGps
275 	 * @see #getDevicesXml for device number assignment
276 	 */
277 	startReadFromGps: function(deviceNumber) {
278 		 this.plugin.StartReadFromGps( deviceNumber );
279 	},
280 
281 	/** Indicates the status of the read process. It will return an integer
282 	 * know as the completion state.  The purpose is to show the 
283  	 * user information about what is happening to the plugin while it 
284  	 * is servicing your request. Used after startReadFromGps(). <br/>
285  	 * <br/>
286  	 * Minimum plugin version 2.0.0.4
287  	 * 
288 	 * @type Number
289 	 * @return Completion state - The completion state can be one of the following: <br/>
290 	 *  <br/>
291 	 *	0 = idle <br/>
292  	 * 	1 = working <br/>
293  	 * 	2 = waiting <br/>
294  	 * 	3 = finished <br/>
295  	 * @see #startReadFromGps
296 	 * @see #cancelReadFromGps
297 	 */
298 	finishReadFromGps: function() {
299 		return this.plugin.FinishReadFromGps();
300 	},
301 	
302 	/** Cancels the current read from the device. <br/>
303 	 * <br/>
304 	 * Minimum plugin version 2.0.0.4
305 	 * @see #startReadFromGps
306 	 * @see #finishReadFromGps
307      */	
308 	cancelReadFromGps: function() {
309 		this.plugin.CancelReadFromGps();
310 	},
311 	
312 	/** Start the asynchronous ReadFitnessData operation. <br/>
313 	 * <br/>
314 	 * Minimum plugin version 2.1.0.3 for FitnessHistory type<br/>
315      * Minimum plugin version 2.2.0.1 for FitnessWorkouts, FitnessUserProfile, FitnessCourses
316 	 * 
317 	 * @param deviceNumber {Number} assigned by the plugin.
318      * @param dataTypeName {String} a fitness datatype from the 
319 	 * <a href="http://developer.garmin.com/schemas/device/v2">Garmin Device XML</a> 
320 	 * retrieved with getDeviceDescriptionXml
321 	 * @see #finishReadFitnessData  
322 	 * @see #cancelReadFitnessData
323 	 * @see #getDeviceDescriptionXml
324 	 * @see #getDevicesXml for device number assignment
325 	 * @see Garmin.DeviceControl#FILE_TYPES
326 	 */
327 	startReadFitnessData: function(deviceNumber, dataTypeName) {
328 		if( !this.checkPluginVersionSupport([2,1,0,3]) ) {
329 			throw new Error("Your Communicator Plug-in version (" + this.getPluginVersionString() + ") does not support reading this type of fitness data.");
330 		}
331 
332 		 this.plugin.StartReadFitnessData( deviceNumber, dataTypeName );
333 	},
334 
335 	/** Poll for completion of the asynchronous ReadFitnessData operation. <br/>
336      * <br/>
337      * If the CompletionState is eMessageWaiting, call MessageBoxXml
338      * to get a description of the message box to be displayed to
339      * the user, and then call RespondToMessageBox with the value of the
340      * selected button to resume operation.<br/>
341      * <br/>
342      * Minimum plugin version 2.1.0.3 for FitnessHistory type <br/>
343      * Minimum plugin version 2.2.0.1 for FitnessWorkouts, FitnessUserProfile, FitnessCourses
344 	 * 
345 	 * @type Number
346 	 * @return Completion state - The completion state can be one of the following: <br/>
347 	 *  <br/>
348 	 *	0 = idle <br/>
349  	 * 	1 = working <br/>
350  	 * 	2 = waiting <br/>
351  	 * 	3 = finished <br/>
352  	 * @see #startReadFitnessData  
353 	 * @see #cancelReadFitnessData
354 	 */
355 	finishReadFitnessData: function() {
356 	 	 return  this.plugin.FinishReadFitnessData();
357 	},
358 	
359 	/** Cancel the asynchronous ReadFitnessData operation. <br/>
360 	 * <br/>
361 	 * Minimum plugin version 2.1.0.3 for FitnessHistory type <br/>
362      * Minimum plugin version 2.2.0.1 for FitnessWorkouts, FitnessUserProfile, FitnessCourses
363      * 
364      * @see #startReadFitnessData  
365 	 * @see #finishReadFitnessData
366      */	
367 	cancelReadFitnessData: function() {
368 		this.plugin.CancelReadFitnessData();
369 	},
370 	
371 	/**
372 	 * List all of the FIT files on the device. Starts an asynchronous directory listing operation for the device.
373 	 * Poll for finished with FinishReadFitDirectory. The result can be retrieved with {@link #getDirectoryXml}.
374 	 * 
375 	 * Minimum plugin version 2.7.2.0
376 	 * @see #finishReadFitDirectory
377 	 */
378 	startReadFitDirectory: function(deviceNumber) {
379 	    if( !this.getSupportsFitDirectoryRead() ) {
380 			throw new Error("Your Communicator Plug-in version (" + this.getPluginVersionString() + ") does not support FIT directory listing data.");
381 		}
382 	    this.plugin.StartReadFITDirectory(deviceNumber);
383 	},
384 	
385 	/** Poll for completion of the asynchronous startReadFitDirectory operation. <br/>
386      * <br/>
387 	 * Minimum plugin version 2.7.2.0
388 	 * 
389 	 * @type Number
390 	 * @return Completion state - The completion state can be one of the following: <br/>
391 	 *  <br/>
392 	 *	0 = idle <br/>
393  	 * 	1 = working <br/>
394  	 * 	2 = waiting <br/>
395  	 * 	3 = finished <br/>
396 	 * 
397 	 * @see #startReadFitDirectory
398 	 * @see #cancelReadFitDirectory
399 	 * @see #getMessageBoxXml
400 	 * @see #respondToMessageBox
401 	 */
402 	finishReadFitDirectory: function() {
403 		return this.plugin.FinishReadFITDirectory();
404 	},
405 	
406 	/** Start the asynchronous ReadFitnessDirectory operation. <br/>
407 	 * <br/>
408 	 * Minimum plugin version 2.2.0.2
409 	 * 
410 	 * @param deviceNumber {Number} assigned by the plugin
411      * @param dataTypeName a Fitness DataType from the GarminDevice.xml retrieved with DeviceDescription
412 	 * @see #finishReadFitnessDirectory
413 	 * @see #cancelReadFitnessDirectory
414 	 * @see #getDevicesXml for device number assignment
415 	 * @see Garmin.DeviceControl#FILE_TYPES
416 	 */
417 	startReadFitnessDirectory: function(deviceNumber, dataTypeName) {
418 		if( !this.getSupportsFitnessDirectoryRead() ) {
419 			throw new Error("Your Communicator Plug-in version (" + this.getPluginVersionString() + ") does not support reading fitness directory data.");
420 		}
421 		this.plugin.StartReadFitnessDirectory( deviceNumber, dataTypeName);
422 	},
423 	
424 	/** Poll for completion of the asynchronous ReadFitnessDirectory operation. <br/>
425      * <br/>
426      * If the CompletionState is eMessageWaiting, call getMessageBoxXml
427      * to get a description of the message box to be displayed to
428      * the user, and then call respondToMessageBox with the value of the
429      * selected button to resume operation.<br/>
430 	 * <br/>
431 	 * Minimum plugin version 2.2.0.2
432 	 * 
433 	 * @type Number
434 	 * @return Completion state - The completion state can be one of the following: <br/>
435 	 *  <br/>
436 	 *	0 = idle <br/>
437  	 * 	1 = working <br/>
438  	 * 	2 = waiting <br/>
439  	 * 	3 = finished <br/>
440 	 * 
441 	 * @see #startReadFitnessDirectory
442 	 * @see #cancelReadFitnessDirectory
443 	 * @see #getMessageBoxXml
444 	 * @see #respondToMessageBox
445 	 */
446 	finishReadFitnessDirectory: function() {
447 		return this.plugin.FinishReadFitnessDirectory();
448 	},
449 	
450 	/** Cancel the asynchronous ReadFitnessDirectory operation. <br/>
451 	 * <br/>
452 	 * Minimum plugin version 2.2.0.2
453 	 * 
454 	 * @see #startReadFitnessDirectory
455 	 * @see #finishReadFitnessDirectory
456      */	
457 	cancelReadFitnessDirectory: function() {
458 		this.plugin.CancelReadFitnessDirectory();
459 	},
460 
461 	/** Cancel the asynchronous ReadFitDirectory operation. <br/>
462 	 * <br/>
463 	 * Minimum plugin version 2.7.2.0
464 	 * 
465 	 * @see #startReadFitDirectory
466 	 * @see #finishReadFitDirectory
467      */	
468 	cancelReadFitDirectory: function() {
469 		this.plugin.CancelReadFitDirectory();
470 	},
471 	
472 	/** Start the asynchronous ReadFitnessDetail operation. <br/>
473 	 * <br/>
474 	 * Minimum plugin version 2.2.0.2
475 	 * 
476 	 * @param deviceNumber assigned by the plugin
477      * @param dataTypeName a Fitness DataType from the GarminDevice.xml retrieved with DeviceDescription
478 	 * @see #finishReadFitnessDetail
479 	 * @see #cancelReadFitnessDetail
480      * @see #getDevicesXml for device number assignment
481 	 * @see Garmin.DeviceControl#FILE_TYPES
482 	 */
483 	startReadFitnessDetail: function(deviceNumber, dataTypeName, dataId) {
484 		if( !this.checkPluginVersionSupport([2,2,0,2]) ) {
485 			throw new Error("Your Communicator Plug-in version (" + this.getPluginVersionString() + ") does not support reading fitness detail.");
486 		}
487 		
488 		this.plugin.StartReadFitnessDetail(deviceNumber, dataTypeName, dataId);
489 	},
490 	
491 	/** Poll for completion of the asynchronous ReadFitnessDetail operation. <br/>
492      * <br/>
493      * If the CompletionState is eMessageWaiting, call MessageBoxXml
494      * to get a description of the message box to be displayed to
495      * the user, and then call RespondToMessageBox with the value of the
496      * selected button to resume operation.<br/>
497      * <br/>
498      * Minimum plugin version 2.2.0.2
499 	 * 
500 	 * @type Number
501 	 * @return Completion state - The completion state can be one of the following: <br/>
502 	 *  <br/>
503 	 *	0 = idle <br/>
504  	 * 	1 = working <br/>
505  	 * 	2 = waiting <br/>
506  	 * 	3 = finished <br/>
507 	 * 
508 	 */
509 	finishReadFitnessDetail: function() {
510 		return this.plugin.FinishReadFitnessDetail();
511 	},
512 	
513 	/** Cancel the asynchronous ReadFitnessDirectory operation. <br/>
514 	 * <br/>
515 	 * Minimum version 2.2.0.2
516 	 * 
517 	 * @see #startReadFitnessDetail
518 	 * @see #finishReadFitnessDetail
519      */	
520 	cancelReadFitnessDetail: function() {
521 		this.plugin.CancelReadFitnessDetail();
522 	},
523 
524 
525    /** Starts an asynchronous file listing operation for a Mass Storage mode device. <br/>
526 	* Only files that are output from the device are listed. </br>
527 	* The result can be retrieved with {@link #getDirectoryXml}.
528 	* Minimum plugin version 2.8.1.0 <br/>
529     *
530 	* @param {Number} deviceNumber assigned by the plugin
531 	* @param {String} dataTypeName a DataType from GarminDevice.xml retrieved with DeviceDescription
532 	* @param {String} fileTypeName a Specification Identifier for a File in dataTypeName from GarminDevice.xml
533     * @param {Boolean} computeMD5 If true, the plug-in will generate an MD5 checksum for each readable file. 
534 	*
535 	* @see #finishReadableFileListing
536 	* @see #cancelReadableFileListing
537     * @see #getDevicesXml for device number assignment
538 	* @see Garmin.DeviceControl.FILE_TYPES
539 	*/
540 	startReadableFileListing: function( deviceNumber, dataTypeName, fileTypeName, computeMD5 ) 
541 	{
542 		if( !this.checkPluginVersionSupport([2,8,1,0]) ) 
543 		{
544 			throw new Error("Your Communicator Plug-in version (" + this.getPluginVersionString() + ") does not support listing readable files.");
545 		}
546 		
547 		this.plugin.StartReadableFileListing(deviceNumber, dataTypeName, fileTypeName, computeMD5);
548 	},
549 	
550    /** Cancel the asynchronous ReadableFileListing operation <br/>
551     * Minimum version 2.8.1.0 <br/>
552     *
553  	* @see #startReadableFileListing
554  	* @see #finishReadableFileListing
555  	*/
556 	cancelReadableFileListing: function() 
557 	{
558 		this.plugin.CancelReadableFileListing();	
559 	},
560 
561 	/** Poll for completion of the asynchronous ReadableFileListing operation. <br/>
562      * <br/>
563      * If the CompletionState is eMessageWaiting, call MessageBoxXml
564      * to get a description of the message box to be displayed to
565      * the user, and then call RespondToMessageBox with the value of the
566      * selected button to resume operation.<br/>
567      * <br/>
568      * Minimum version 2.8.1.0 <br/>
569 	 * 
570 	 * @type Number
571 	 * @return Completion state - The completion state can be one of the following: <br/>
572 	 *  <br/>
573 	 *	0 = idle <br/>
574  	 * 	1 = working <br/>
575  	 * 	2 = waiting <br/>
576  	 * 	3 = finished <br/>
577 	 * 
578 	 */
579 	finishReadableFileListing: function()
580 	{
581 		return this.plugin.FinishReadableFileListing();
582 	},
583 
584 	// Write Methods
585 	
586 	/** Initates writing the gpsXml to the device specified by deviceNumber with a filename set by filename.
587 	 * The gpsXml is typically in GPX fomat and the filename is only the name without the extension. The 
588 	 * plugin will append the .gpx extension automatically.<br/>
589 	 * <br/>
590 	 * Use finishWriteToGps to poll when the write operation/plugin is complete.<br/>
591 	 * <br/>
592 	 * Uses the helper functions to set the xml info and the filename.  <br/>
593 	 * <br/>
594 	 * Minimum plugin version 2.0.0.4<br/>
595      * Minimum plugin version 2.2.0.1 for writes of GPX to SD Card
596 	 * 
597 	 * @param gpsXml {String} the gps/gpx information that should be transferred to the device.
598 	 * @param filename {String} the desired filename for the gpsXml that shall end up on the device.
599 	 * @param deviceNumber {Number} the device number assigned by the plugin.
600 	 * @see #finishWriteToGps
601 	 * @see #cancelWriteToGps  
602 	 */
603 	startWriteToGps: function(gpsXml, filename, deviceNumber) {
604 		this._setWriteGpsXml(gpsXml);
605 		this._setWriteFilename(filename);
606 	    this.plugin.StartWriteToGps(deviceNumber);
607 	},
608 
609 	/** Sets the gps xml content that will end up on the device once the transfer is complete.
610 	 * Use in conjunction with startWriteToGps to initiate the actual write.
611 	 *
612 	 * @private 
613 	 * @param gpsXml {String} xml data that is to be written to the device. Must be in GPX format.
614 	 */
615 	_setWriteGpsXml: function(gpsXml) {
616     	this.plugin.GpsXml = gpsXml;
617 	},
618 
619 	/** This the filename that wil contain the gps xml once the transfer is complete. Use with 
620 	 * setWriteGpsXml to set what the file contents will be. Also, use startWriteToGps to 
621 	 * actually make the write happen.
622 	 * 
623 	 * @private
624 	 * @param filename {String} the actual filename that will end up on the device. Should only be the
625 	 * name and not the extension. The plugin will append the extension portion to the file name--typically .gpx.
626 	 * @see #setWriteGpsXml, #startWriteToGps, #startWriteFitnessData
627 	 */
628 	_setWriteFilename: function(filename) {
629     	this.plugin.FileName = filename;
630 	},
631 
632 	/** This is used to indicate the status of the write process. It will return an integer
633 	 * know as the completion state.  The purpose is to show the 
634  	 * user information about what is happening to the plugin while it 
635  	 * is servicing your request. <br/>
636  	 * <br/>
637  	 * Minimum plugin version 2.0.0.4<br/>
638      * Minimum plugin version 2.2.0.1 for writes of GPX to SD Card 
639  	 * 
640 	 * @type Number
641 	 * @return Completion state - The completion state can be one of the following: <br/>
642 	 *  <br/>
643 	 *	0 = idle <br/>
644  	 * 	1 = working <br/>
645  	 * 	2 = waiting <br/>
646  	 * 	3 = finished <br/>
647  	 * @see #startWriteToGps
648 	 * @see #cancelWriteToGps  
649  	 */
650 	finishWriteToGps: function() {
651 		//console.debug("Plugin.finishWriteToGps");
652 	   	return  this.plugin.FinishWriteToGps();
653 	},
654     
655 	/** Cancels the current write operation to the gps device. <br/>
656 	 * <br/>
657 	 * Minimum plugin version 2.0.0.4<br/>
658      * Minimum plugin version 2.2.0.1 for writes of GPX to SD Card
659      * 
660      * @see #startWriteToGps
661 	 * @see #finishWriteToGps  
662      */	
663 	cancelWriteToGps: function() {
664 		this.plugin.CancelWriteToGps();
665 	},
666 
667 	/** Start the asynchronous StartWriteFitnessData operation. <br/>
668      * For binary files such as FIT, use {@link #startDownloadData}
669 	 * <br/>
670 	 * Minimum plugin version 2.2.0.1
671 	 * 
672 	 * @param tcdXml {String} XML of TCD data
673 	 * @param deviceNumber {Number} the device number, assigned by the plugin.
674 	 * @param filename {String} the filename to write to on the device.
675 	 * @param dataTypeName {String} a Fitness DataType from the GarminDevice.xml retrieved with DeviceDescription
676 	 * @see #finishWriteFitnessData  
677 	 * @see #cancelWriteFitnessData
678 	 * @see #getDevicesXml for device number assignment
679 	 * @see Garmin.DeviceControl#FILE_TYPES
680 	 */
681 	startWriteFitnessData: function(tcdXml, deviceNumber, filename, dataTypeName) {	
682 		if( !this.checkPluginVersionSupport([2,2,0,1]) ) {
683 			throw new Error("Your Communicator Plug-in version (" + this.getPluginVersionString() + ") does not support writing fitness data.");
684 		}
685 		
686 		this._setWriteTcdXml(tcdXml);
687 		this._setWriteFilename(filename);
688 		this.plugin.StartWriteFitnessData(deviceNumber, dataTypeName);
689 	},
690 	
691 	/** This is used to indicate the status of the write process for fitness data. It will return an integer
692 	 * know as the completion state.  The purpose is to show the 
693  	 * user information about what is happening to the plugin while it 
694  	 * is servicing your request. <br/>
695  	 * <br/>
696  	 * Minimum plugin version 2.2.0.1
697  	 * 
698 	 * @type Number
699 	 * @return Completion state - The completion state can be one of the following: <br/>
700 	 *  <br/>
701 	 *	0 = idle <br/>
702  	 * 	1 = working <br/>
703  	 * 	2 = waiting <br/>
704  	 * 	3 = finished <br/>
705  	 * @see #startWriteFitnessData  
706 	 * @see #cancelWriteFitnessData
707 	 */
708 	finishWriteFitnessData: function() {
709 	 	return  this.plugin.FinishWriteFitnessData();
710 	},
711 	
712 	/** Cancel the asynchronous ReadFitnessData operation. <br/>
713 	 * <br/>
714 	 * Minimum plugin version 2.2.0.1
715 	 * 
716 	 * @see #startWriteFitnessData  
717 	 * @see #finishWriteFitnessData
718      */	
719 	cancelWriteFitnessData: function() {
720 		this.plugin.CancelWriteFitnessData();
721 	},
722 	
723 	/** Sets the tcd xml content that will end up on the device once the transfer is complete.
724 	 * Use in conjunction with startWriteFitnessData to initiate the actual write.
725 	 *
726 	 * @private 
727 	 * @param tcdXml {String} xml data that is to be written to the device. Must be in TCX format.
728 	 */
729 	_setWriteTcdXml: function(tcdXml) {
730     	this.plugin.TcdXml = tcdXml;
731 	},
732 	
733 	/**
734 	 * Determine the amount of space available on a Mass Storage Mode Device Volume.
735 	 * 
736 	 * @param {Number} deviceNumber - the device number assigned by the plugin.
737 	 * @param {String} relativeFilePath - if a file is being replaced, set to relative path on device, otherwise set to empty string.
738 	 * @return -1 for non-mass storage mode devices.  
739 	 * @see #getDevicesXml for device number assignment
740 	 */
741 	bytesAvailable: function(deviceNumber, relativeFilePath) {
742 	    return this.plugin.BytesAvailable(deviceNumber, relativeFilePath);
743 	},
744 
745 	/**
746 	 * Determine if device is file-based. <br/>
747 	 * File-based devices include Mass Storage Mode Devices such as Nuvi, Oregon, Edge 705,
748 	 * as well as ANT Proxy Devices.<br/>
749 	 * 
750 	 * Minimum plugin version 2.8.1.0 <br/>
751 	 * @param {Number} deviceNumber the device number assigned by the plugin.
752 	 * @returns {Boolean} true for file based devices, false otherwise
753 	 * @see #getDevicesXml for device number assignment
754 	 */
755 	isDeviceFileBased: function(deviceNumber) {
756         var theReturn = true;
757         //do a dummy file listing
758         try {
759             this.startReadableFileListing( deviceNumber, 'FileBasedTest', 'FileBasedTest', false );
760 	        while( this.finishReadableFileListing() == 1 ) {
761             //wait until done. Only safe to do because fxn returns quickly because of test name and id!
762             }
763         } catch(e) {
764             theReturn = false;
765         }
766         return theReturn;
767 	},
768 
769     /** Responds to a message box on the device. <br/>
770      * <br/>
771      * Minimum plugin version 2.0.0.4
772      *   
773      * @param response should be an int which corresponds to a button value from this.plugin.MessageBoxXml
774      */
775     respondToMessageBox: function(response) {
776         this.plugin.RespondToMessageBox(response);
777     },
778 
779 	/** Downloads and writes files asynchronously to the specified device. <br/>
780 	 * Use {@link #finishDownloadData} to poll when the write operation/plugin is complete.<br/>
781 	 * <br/>
782 	 * Minimum plugin version 2.0.0.4
783 	 *  
784      * @param xmlDownloadDescription {String} XML string containing information about the files to be downloaded onto the device.<br/>
785 	 * This xml must conform to one of two schemas:<br/>
786 	 * <a href=http://www.garmin.com/xmlschemas/GarminPluginAPIV1.xsd>GarminPluginAPIV1</a> <br/>
787 	 * <a href=http://www.garmin.com/xmlschemas/DeviceDownloadV1.xsd>DeviceDownloadV1</a> <strong>Recommended</strong> <br/>
788 	 * @param deviceNumber {Number} the device number assigned by the plugin. 
789 	 * @see #finishDownloadData  
790 	 * @see #cancelDownloadData
791      * @see #Garmin.GpiUtil
792 	 */
793 	startDownloadData: function(xmlDownloadDescription, deviceNumber) {
794 		this.plugin.StartDownloadData(xmlDownloadDescription, deviceNumber);
795 	},
796 
797 	/** This is used to indicate the status of the download process. It will return an integer
798 	 * know as the completion state.  The purpose is to show the 
799  	 * user information about what is happening to the plugin while it 
800  	 * is servicing your request.<br/>
801 	 * <br/>
802 	 * Minimum plugin version 2.0.0.4
803 	 * 
804 	 * @type Number
805 	 * @return Completion state - The completion state can be one of the following: <br/>
806 	 *  <br/>
807 	 *	0 = idle <br/>
808  	 * 	1 = working <br/>
809  	 * 	2 = waiting <br/>
810  	 * 	3 = finished <br/>
811  	 * @see #startDownloadData  
812 	 * @see #cancelDownloadData
813 	 */
814 	finishDownloadData: function() {
815 		//console.debug("Plugin.finishDownloadData");
816 		return this.plugin.FinishDownloadData();
817 	},
818 
819 	/** Cancel the asynchronous Download Data operation. <br/>
820 	 * <br/>
821 	 * Minimum plugin version 2.0.0.4
822 	 * 
823 	 * @see #startDownloadData  
824 	 * @see #finishDownloadData
825 	 */
826 	cancelDownloadData: function() {
827 		this.plugin.CancelDownloadData();
828 	},
829 
830     /** Indicates success of StartDownloadData operation. <br/>
831      * <br/>
832      * Minimum plugin version 2.0.0.4
833      * 
834      * @type Boolean
835      * @return True if the last StartDownloadData operation was successful
836      */
837     downloadDataSucceeded: function() {
838 		return this.plugin.DownloadDataSucceeded;
839     },
840 
841     /** Download and install a list of unit software updates.  Start the asynchronous 
842      * StartUnitSoftwareUpdate operation.
843      * 
844      * Check for completion with the FinishUnitSoftwareUpdate() method.  After
845      * completion check the DownloadDataSucceeded property to make sure that all of the downloads 
846      * were successfully placed on the device. 
847      * 
848      * See the Schema UnitSoftwareUpdatev3.xsd for the format of the UpdateResponsesXml description
849      *
850      * @see Garmin.DevicePlugin.finishUnitSoftwareUpdate
851      * @see Garmin.DevicePlugin.cancelUnitSoftwareUpdate
852      * @see Garmin.DevicePlugin.downloadDataSucceeded
853      * @version plugin v2.6.2.0
854      */
855     startUnitSoftwareUpdate: function(updateResponsesXml, deviceNumber) {
856         this.plugin.StartUnitSoftwareUpdate(updateResponsesXml, deviceNumber);
857     },
858     
859     /** Poll for completion of the asynchronous Unit Software Update operation. It will return an integer
860 	 * know as the completion state.  The purpose is to show the 
861  	 * user information about what is happening to the plugin while it 
862  	 * is servicing your request.<br/>
863  	 * @type Number 
864      * @version plugin v2.6.2.0
865      * @return Completion state - The completion state can be one of the following: <br/>
866 	 *  <br/>
867 	 *	0 = idle <br/>
868  	 * 	1 = working <br/>
869  	 * 	2 = waiting <br/>
870  	 * 	3 = finished <br/>
871  	 * @see Garmin.DevicePlugin.startUnitSoftwareUpdate
872  	 * @see Garmin.DevicePlugin.cancelUnitSoftwareUpdate
873      */
874     finishUnitSoftwareUpdate: function() {
875         return this.plugin.FinishUnitSoftwareUpdate();  
876     },
877     
878     /** Cancel the asynchrous Download Data operation
879      * @version plugin v2.6.2.0
880      */
881     cancelUnitSoftwareUpdate: function() {
882         this.plugin.CancelUnitSoftwareUpdate();
883     },
884     
885     /** Get the UnitSoftwareUpdateRequests for a given device.
886      * This request retrieves the main system software (system region only.)
887      * @param deviceNumber {Number} the device number to retrieve unit software information for. 
888      * @return {String} XML string of the document format in the namespace below, or
889      * the most current version of that xms namespace
890      * http://www.garmin.com/xmlschemas/UnitSoftwareUpdate/v3
891      * @version plugin v2.6.2.0
892      * @see Garmin.DevicePlugin.getAdditionalSoftwareUpdateRequests
893      */
894 //    getUnitSoftwareUpdateRequests: function(deviceNumber) {
895 //        return this.plugin.UnitSoftwareUpdateRequests(deviceNumber);
896 //    },
897     
898     /** Get the AdditionalSoftwareUpdateRequests for a given device.
899      * This request retrieves the additional system software (all software except for system region.)
900      * @param deviceNumber {Number} the device number to retrieve unit software information for.
901      * @return {String} XML string of the document format in the namespace below, or
902      * the most current version of that xms namespace
903      * http://www.garmin.com/xmlschemas/UnitSoftwareUpdate/v3
904      * @version plugin v2.6.2.0
905      * @see Garmin.DevicePlugin.getUnitSoftwareUpdateRequests
906      */
907 //    getAdditionalSoftwareUpdateRequests: function(deviceNumber) {
908 //        return this.plugin.AdditionalSoftwareUpdateRequests(deviceNumber);
909 //    },
910     
911     /** Indicates success of WriteToGps operation. <br/>
912      * <br/>
913      * Minimum plugin version 2.0.0.4
914      * 
915      * @type Boolean
916      * @return True if the last ReadFromGps or WriteToGps operation was successful
917      */
918     gpsTransferSucceeded: function() {
919 		return this.plugin.GpsTransferSucceeded;
920     },
921 
922     /** Indicates success of ReadFitnessData or WriteFitnessData operation. <br/>
923      * <br/>
924      * Minimum plugin version 2.1.0.3
925      * 
926      * @type Boolean
927      * @return True if the last ReadFitnessData or WriteFitnessData operation succeeded
928      */
929     fitnessTransferSucceeded: function() {
930 		return this.plugin.FitnessTransferSucceeded;
931     },
932     
933     /** Return the specified file as a UU-Encoded string
934      * <br/>
935      * Minimum version 2.6.3.1
936      * 
937      * If the file is known to be compressed, compressed should be
938      * set to false. Otherwise, set compressed to true to retrieve a
939      * gzipped and uuencoded file.
940      * 
941      * @param relativeFilePath {String} path relative to the Garmin folder on the device
942      */
943     getBinaryFile: function(deviceNumber, relativeFilePath, compressed) {
944         return this.plugin.GetBinaryFile(deviceNumber, relativeFilePath, compressed);
945     },
946     
947     /** This is the GpsXml information from the device. Typically called after a read operation.
948      * 
949      * @see #finishReadFromGps
950      */
951 	getGpsXml: function(){
952 		return this.plugin.GpsXml;
953 	},
954 
955     /** This is the fitness data Xml information from the device. Typically called after a ReadFitnessData operation. <br/>
956 	 * <br/>
957      * Schemas for the TrainingCenterDatabase format are available at
958      * <a href="http://developer.garmin.com/schemas/tcx/v2/">http://developer.garmin.com/schemas/tcx/v2/</a><br/>
959      * <br/>
960      * Minimum plugin version 2.1.0.3
961      * 
962      * @see #finishReadFitnessData
963      * @see #finishReadFitnessDirectory
964      * @see #finishReadFitnessDetail
965      */
966 	getTcdXml: function(){
967 		return this.plugin.TcdXml;
968 	},
969 	
970 	 /** Returns last read fitness xml data in compressed format.  The xml is compressed as gzp and base64 expanded. <br/>
971 	  * <br/>
972 	  * Minimum plugin version 2.2.0.2
973 	  * 
974 	  * @return The read xml data in compressed gzp and base64 expanded format.
975 	  * @see #finishReadFitnessData
976       * @see #finishReadFitnessDirectory
977       * @see #finishReadFitnessDetail
978 	  */
979 	getTcdXmlz: function() {
980 		return this.plugin.TcdXmlz;
981 	},
982 
983 	 /** Returns last read directory xml data.<br/>
984 	  * <br/>
985 	  * 
986 	  * @return The directory xml data
987 	  * @see #finishReadFitDirectory
988 	  */
989 	getDirectoryXml: function() {
990 		return this.plugin.DirectoryListingXml;
991 	},
992 	
993     /** Returns the xml describing the message when the plug-in is waiting for input from the user.
994      * @type String
995      * @return The xml describing the message when the plug-in is waiting for input from the user.
996      */
997 	getMessageBoxXml: function(){
998 		return this.plugin.MessageBoxXml;
999 	},
1000     
1001 	/** Get the status/progress of the current state or transfer.
1002      * @type String
1003      * @return The xml describing the current progress state of the plug-in.
1004      */	
1005 	getProgressXml: function() {
1006 		return this.plugin.ProgressXml;
1007 	},
1008 
1009 	/** Returns metadata information about the plugin version. 
1010      * @type String
1011      * @return The xml describing the user's version of the plug-in.
1012 	 */
1013 	getVersionXml: function() {
1014 		return this.plugin.VersionXml;
1015 	},
1016 	
1017 	/** Gets a string of the version number for the plugin the user has currently installed.
1018      * @type String 
1019      * @return A string of the format "versionMajor.versionMinor.buildMajor.buildMinor", ex: "2.0.0.4"
1020      */	
1021 	getPluginVersionString: function() {
1022 		var versionArray = this.getPluginVersion();
1023 	
1024 		var versionString = versionArray[0] + "." + versionArray[1] + "." + versionArray[2] + "." + versionArray[3];
1025 	    return versionString;
1026 	},
1027 	
1028 	/** Gets the version number for the plugin the user has currently installed.
1029      * @type Array 
1030      * @return An array of the format: [versionMajor, versionMinor, buildMajor, buildMinor].
1031      */	
1032 	getPluginVersion: function() {
1033     	var versionMajor = parseInt(this._getElementValue(this.getVersionXml(), "VersionMajor"));
1034     	var versionMinor = parseInt(this._getElementValue(this.getVersionXml(), "VersionMinor"));
1035     	var buildMajor = parseInt(this._getElementValue(this.getVersionXml(), "BuildMajor"));
1036     	var buildMinor = parseInt(this._getElementValue(this.getVersionXml(), "BuildMinor"));
1037 
1038 	    var versionArray = [versionMajor, versionMinor, buildMajor, buildMinor];
1039 	    return versionArray;
1040 	},
1041 	
1042 	/** Sets the required plugin version number for the application.
1043 	 * @param reqVersionArray {Array} The required version to set to.  In the format [versionMajor, versionMinor, buildMajor, buildMinor]
1044 	 * 			i.e. [2,2,0,1]
1045 	 */
1046 	setPluginRequiredVersion: function(reqVersionArray) {
1047 		Garmin.DevicePlugin.REQUIRED_VERSION.versionMajor = reqVersionArray[0];
1048 		Garmin.DevicePlugin.REQUIRED_VERSION.versionMinor = reqVersionArray[1];
1049 		Garmin.DevicePlugin.REQUIRED_VERSION.buildMajor = reqVersionArray[2];
1050 		Garmin.DevicePlugin.REQUIRED_VERSION.buildMinor = reqVersionArray[3];
1051 	},
1052 	
1053 	/** Sets the latest plugin version number.  This represents the latest version available for download at Garmin.
1054 	 * We will attempt to keep the default value of this up to date with each API release, but this is not guaranteed,
1055 	 * so set this to be safe or if you don't want to upgrade to the latest API.
1056 	 * 
1057 	 * @param reqVersionArray {Array} The latest version to set to.  In the format [versionMajor, versionMinor, buildMajor, buildMinor]
1058 	 * 			i.e. [2,2,0,1]
1059 	 */
1060 	setPluginLatestVersion: function(reqVersionArray) {
1061 		Garmin.DevicePlugin.LATEST_VERSION.versionMajor = reqVersionArray[0];
1062 		Garmin.DevicePlugin.LATEST_VERSION.versionMinor = reqVersionArray[1];
1063 		Garmin.DevicePlugin.LATEST_VERSION.buildMajor = reqVersionArray[2];
1064 		Garmin.DevicePlugin.LATEST_VERSION.buildMinor = reqVersionArray[3];
1065 	},
1066 	
1067 	/** Used to check if the user's installed plugin version meets the required version for feature support purposes.
1068 	 *  
1069 	 * @param {Array} reqVersionArray An array representing the required version, in the format: [versionMajor, versionMinor, buildMajor, buildMinor]. 
1070 	 * @return {boolean} true if the passed in required version is met by the user's plugin version (user's version is equal to or greater), false otherwise.
1071 	 * @see setPluginRequiredVersion
1072 	 */
1073 	checkPluginVersionSupport: function(reqVersionArray) {
1074 		
1075 		var pVersion = this._versionToNumber(this.getPluginVersion());
1076    		var rVersion = this._versionToNumber(reqVersionArray);
1077         return (pVersion >= rVersion);
1078 	},
1079 	
1080 	/**
1081 	 * @private
1082 	 */
1083 	_versionToNumber: function(versionArray) {
1084 		if (versionArray[1] > 99 || versionArray[2] > 99 || versionArray[3] > 99)
1085 			throw new Error("version segment is greater than 99: "+versionArray);
1086 		return 1000000*versionArray[0] + 10000*versionArray[1] + 100*versionArray[2] + versionArray[3];
1087 	},
1088 	
1089 	/** Determines if the Garmin plugin is at least the required version for the application.
1090      * @type Boolean
1091      * @see setPluginRequiredVersion
1092 	 */
1093 	isPluginOutOfDate: function() {
1094     	var pVersion = this._versionToNumber(this.getPluginVersion());
1095    		var rVersion = this._versionToNumber(Garmin.DevicePlugin.REQUIRED_VERSION.toArray());
1096         return (pVersion < rVersion);
1097 	},
1098 	
1099 	/** Checks if plugin is the most recent version released, for those that want the latest and greatest.
1100      */
1101     isUpdateAvailable: function() {
1102     	var pVersion = this._versionToNumber(this.getPluginVersion());
1103    		var cVersion = this._versionToNumber(Garmin.DevicePlugin.LATEST_VERSION.toArray());
1104         return (pVersion < cVersion);
1105     },
1106 	
1107 	/** Pulls value from xml given an element name or null if no tag exists with that name.
1108 	 * @private
1109 	 */
1110 	_getElementValue: function(xml, tagName) {
1111 		var start = xml.indexOf("<"+tagName+">");
1112 		if (start == -1)
1113 			return null;
1114 		start += tagName.length+2;
1115 		var end = xml.indexOf("</"+tagName+">");
1116 		var result = xml.substring(start, end);
1117 		return result;
1118 	}
1119 	
1120 };
1121 
1122 /** Latest version (not required) of the Garmin Communicator Plugin, and a complementary toString function to print it out with
1123  */
1124 Garmin.DevicePlugin.LATEST_VERSION = {
1125     versionMajor: 3,
1126     versionMinor: 0,
1127     buildMajor: 1,
1128     buildMinor: 0,
1129     
1130     toString: function() {
1131         return this.versionMajor + "." + this.versionMinor + "." + this.buildMajor + "." + this.buildMinor;
1132     },
1133     
1134     toArray: function() {
1135         return [this.versionMajor, this.versionMinor, this.buildMajor, this.buildMinor];
1136     }	
1137 }; 
1138  
1139  
1140 /** Latest required version of the Garmin Communicator Plugin, and a complementary toString function to print it out with. 
1141  */
1142 Garmin.DevicePlugin.REQUIRED_VERSION = {
1143     versionMajor: 3,
1144     versionMinor: 0,
1145     buildMajor: 0,
1146     buildMinor: 0,
1147     
1148     toString: function() {
1149         return this.versionMajor + "." + this.versionMinor + "." + this.buildMajor + "." + this.buildMinor;
1150     },
1151     
1152     toArray: function() {
1153         return [this.versionMajor, this.versionMinor, this.buildMajor, this.buildMinor];
1154     }	
1155 };