1 if (Garmin == undefined) var Garmin = {};
  2 /** Copyright 2007 Garmin Ltd. or its subsidiaries.
  3  *
  4  * Licensed under the Apache License, Version 2.0 (the 'License')
  5  * you may not use this file except in compliance with the License.
  6  * You may obtain a copy of the License at
  7  *
  8  *    http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an 'AS IS' BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  * 
 16  * @fileoverview Garmin.DeviceControl A mostly deprecated library of GPS track and waypoint data structures along with parsing tools.
 17  * @deprecated use Garmin.GpxActivityFactory instead
 18  * 
 19  * @author Developer developer.connect.at.garmin.com
 20  * @version 1.0
 21  */
 22 
 23 /** A waypoint represents a stored location.
 24  * Equivalent to a <wpt> in GPX format
 25  * Note: this class is only used by Garmin.Geocode but otherwise has been replaced by Garmin.Activity.
 26  * 
 27  * @class Garmin.WayPoint 
 28  * @constructor 
 29  * @param {Number} lat
 30  * @param {Number} lng
 31  * @param {Number} elev
 32  * @param {String} name
 33  */
 34 Garmin.WayPoint = function(lat, lng, elev, name, addrdetails, desc, sym, type, cmt){};
 35 Garmin.WayPoint = Class.create();
 36 Garmin.WayPoint.prototype = {
 37 
 38 	/** Prototype constructor
 39 	 */
 40 	initialize: function(lat, lng, elev, name, addrdetails, desc, sym, type, cmt) {
 41 		this.lat = lat;
 42 		this.lng = lng;
 43 		this.name = name;
 44 		this.addrdetails = addrdetails;
 45 		
 46 		// Get city, streetaddr, and zip data.
 47 		if( this.addrdetails ) {
 48 			this._initSubArea();
 49 		}
 50 		this.elev = elev;		
 51 		this.desc = desc;
 52 		this.sym = sym;
 53 		this.type = type;
 54 		this.cmt = cmt;
 55 		this.date = null;
 56 	},
 57 	
 58 	/** Initializes the subadministrative area, as designated by Google Maps API (see http://code.google.com/apis/maps/documentation/services.html#Geocoding_Structured).
 59 	 *  Apparently some locations have Locality while others don't, so this function takes care of both.
 60 	 */
 61 	_initSubArea: function() {
 62 		
 63 		if( this.addrdetails.Country ) {
 64             this.country = this.addrdetails.Country.CountryNameCode;
 65             if (this.addrdetails.Country.AdministrativeArea) {
 66                 this.state = this.addrdetails.Country.AdministrativeArea.AdministrativeAreaName;
 67 				
 68 				if( this.addrdetails.Country.AdministrativeArea.SubAdministrativeArea ) {
 69 	                if( this.addrdetails.Country.AdministrativeArea.SubAdministrativeArea.Locality) {
 70 	                    this.city = this.addrdetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.LocalityName;
 71 	                    if( this.addrdetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.Thoroughfare ) {
 72 	                        this.streetaddr = this.addrdetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.Thoroughfare.ThoroughfareName;
 73 	                    }
 74 	                    if( this.addrdetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.PostalCode ) {
 75 	                        this.zip = this.addrdetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.PostalCode.PostalCodeNumber;
 76 	                    }
 77 	                } else {
 78 	                   this.city = this.addrdetails.Country.AdministrativeArea.SubAdministrativeArea.SubAdministrativeAreaName; 
 79 	
 80 	                   if( this.addrdetails.Country.AdministrativeArea.SubAdministrativeArea.Thoroughfare ) {
 81 	                       this.streetaddr = this.addrdetails.Country.AdministrativeArea.SubAdministrativeArea.Thoroughfare.ThoroughfareName;
 82 	                   } 
 83 	
 84 	                   if( this.addrdetails.Country.AdministrativeArea.SubAdministrativeArea.PostalCode ) {
 85 	                       this.zip = this.addrdetails.Country.AdministrativeArea.SubAdministrativeArea.PostalCode.PostalCodeNumber;
 86 	                   } 
 87 	                }
 88 				} else {
 89 					if( this.addrdetails.Country.AdministrativeArea.Locality) {
 90 	                    this.city = this.addrdetails.Country.AdministrativeArea.Locality.LocalityName;
 91 	                    if( this.addrdetails.Country.AdministrativeArea.Locality.Thoroughfare ) {
 92 	                        this.streetaddr = this.addrdetails.Country.AdministrativeArea.Locality.Thoroughfare.ThoroughfareName;
 93 	                    }
 94 	                    if( this.addrdetails.Country.AdministrativeArea.Locality.PostalCode ) {
 95 	                        this.zip = this.addrdetails.Country.AdministrativeArea.Locality.PostalCode.PostalCodeNumber;
 96 	                    }
 97 	                } else {
 98 	                   this.city = this.addrdetails.Country.AdministrativeArea.SubAdministrativeAreaName; 
 99 	
100 	                   if( this.addrdetails.Country.AdministrativeArea.Thoroughfare ) {
101 	                       this.streetaddr = this.addrdetails.Country.AdministrativeArea.Thoroughfare.ThoroughfareName;
102 	                   } 
103 	
104 	                   if( this.addrdetails.Country.AdministrativeArea.PostalCode ) {
105 	                       this.zip = this.addrdetails.Country.AdministrativeArea.PostalCode.PostalCodeNumber;
106 	                   } 
107 	                }
108 				}
109             }
110         }
111 	},
112 	
113 	/** Get waypoint symbol usually associated with a display icon.
114 	 * @type String 
115 	 * @return The symbol of this waypoint
116 	 */
117 	getSymbol: function() {
118 		return this.sym;
119 	},
120 
121 	/** Get waypoint type.
122 	 * @type String 
123 	 * @return The type of this waypoint
124 	 */
125 	getType: function() {
126 		return this.type;
127 	},
128 
129 	/** Get waypoint name.
130 	 * @type String 
131 	 * @return The name of this waypoint
132 	 */
133 	getName: function() {
134 		return this.name;
135 	},
136 
137 	/** Get waypoint address XML string.  Uses the extension element to generate XML address format listed in the
138 	 * GPX Extensions v3 schema: http://www8.garmin.com/xmlschemas/GpxExtensions/v3/GpxExtensionsv3.xsd
139 	 *
140 	 * @type String 
141 	 * @return The address of this waypoint, as an XML string with the outermost element being gpxx::Address
142 	 */
143 	getAddress: function() {
144 		return (this.addrdetails != null) ? "<gpxx:Address><gpxx:StreetAddress>" 
145 				+ this.getStreetAddr() + "</gpxx:StreetAddress><gpxx:City>" 
146 				+ this.getCity() + "</gpxx:City><gpxx:State>" 
147 				+ this.getState() + "</gpxx:State><gpxx:PostalCode>"
148 				+ this.getZip() + "</gpxx:PostalCode></gpxx:Address>" : null;
149 	},
150 	
151 	/** Get country that the waypoint is located in, according to Google Maps API.
152 	 *  See http://www.google.com/apis/maps/ for details.
153 	 * @type String 
154 	 * @return The country the waypoint is located in.
155 	 */
156 	getCountry: function() {
157 		return this.country;
158 	},
159 
160 	/** Get state that the waypoint is located in, according to Google Maps API.
161 	 * 	See http://www.google.com/apis/maps/ for details.
162 	 * @type String 
163 	 * @return The state the waypoint is located in.
164 	 */
165 	getState: function() {
166 		return this.state;
167 	},
168 
169 	/** Get city that the waypoint is located in, according to Google Maps API.
170 	 * See http://www.google.com/apis/maps/ for details.
171 	 * @type String 
172 	 * @return The city the waypoint is located in.
173 	 */
174 	getCity: function() {
175 		return this.city;
176 	},
177 	
178 	/** Get street address that the waypoint is located in, according to Google Maps API.
179 	 * See http://www.google.com/apis/maps/ for details.
180 	 * @type String 
181 	 * @return The street address the waypoint is located in.
182 	 */
183 	getStreetAddr: function() {
184 		return this.streetaddr;
185 	},
186 	
187 	/** Get zip code that the waypoint is located in, according to Google Maps API.
188 	 * See http://www.google.com/apis/maps/ for details.
189 	 * @type String 
190 	 * @return The zip code the waypoint is located in.
191 	 */
192 	getZip: function() {
193 		return this.zip;
194 	},
195 	
196 	/** Get waypoint description.
197 	 * @type String 
198 	 * @return A description of this waypoint
199 	 */
200 	getDescription: function() {
201 		return this.desc;
202 	},
203 
204 	/** Shortcut for directly getting the lat value
205 	 * 
206 	 * @type Number
207 	 * @return The value of the latitude for this point
208 	 */
209 	getLat: function() {
210 		return this.lat;
211 	},
212 	
213 	/** Shortcut for directly getting the longitude value
214 	 * 
215 	 * @type Number
216 	 * @return The value of the longitude for this point
217 	 */
218 	getLng: function() {
219 		return this.lng;
220 	},
221 	
222 	/** Shortcut for directly getting the elevation value
223 	 * 
224 	 * @type Number
225 	 * @return The value of the elevation for this point
226 	 */
227 	getElev: function() {
228 		return this.elev;
229 	},
230 	
231 	/** Get comment.
232 	 * 
233 	 * @type String
234 	 * @return The value of the comment for this point
235 	 */
236 	getComment: function() {
237 		return this.cmt;
238 	},
239 	
240 	
241 	/** Shortcut for directly getting the date/time
242 	 * 
243 	 * @type Garmin.DateTimeFormat
244 	 * @return The DateTimeFormat object for this point
245 	 */
246 	getDate: function() {
247 		return this.date;
248 	},
249 	
250 	toString: function() {
251 		return "Waypoint: (" + this.getLat() + ", " +  this.getLng() + ")";
252 	}
253 };
254 
255 
256 /** TrackPoint class reprsents a point from a track.<br>
257  * A TrackPoint contains an associative array of measurements, which can be retrieved with a 
258  * #getMeasurement call passing in a string index.<br>
259  * Equivalent to a <trkpt> in GPX format  
260  * @deprecated use Garmin.Activity instead
261  * @class Garmin.TrackPoint
262  * @constructor 
263  */
264 Garmin.TrackPoint = function(){};
265 Garmin.TrackPoint = Class.create();
266 Garmin.TrackPoint.prototype = {
267     /** prototype constructor
268      */
269 	initialize: function() {
270 		this.measurements = null;
271 		this.date = null;
272 	},
273 
274 	/** Get a Measurement from this TrackPoint
275 	 * If the measurement does not exist - return null
276 	 * 
277 	 * @param {String} context of the measurement we would like to get
278 	 * @type Object
279 	 * @return A measurement object (important to remember it's value is in measurementObject.value!)
280 	 * 	or null if the measurement doesn't exist
281 	 */
282 	getMeasurement: function(context) {
283 		var meas = this.measurements[context];
284 		if(meas == undefined) {
285 		  meas = null;
286 		}
287 		return meas;
288 	},
289 
290 	/** Determines if this TrackPoint point is valid for determing location
291 	 * @type Boolean
292 	 * @return True if lat/lon exist, false otherwise
293 	 */
294     isValidLocation: function() {
295         return ( (this.getLat() != "null") && (this.getLat() != null) && (this.getLng() != "null") && (this.getLng() != null));
296     },
297 
298 	/** Shortcut for directly getting the lat value
299 	 * 
300 	 * @type Number
301 	 * @return The value of the latitude for this point
302 	 */
303 	getLat: function() {
304 	    var meas = this.getMeasurement( "latitude" );
305 	    if(meas == null) {
306 	    	return null;
307 	    } else {
308 	    	return meas.value;
309 	    }
310 	},
311 	
312 	/** Shortcut for directly getting the longitude value
313 	 * 
314 	 * @type Number
315 	 * @return The value of the longitude for this point
316 	 */
317 	getLng: function() {
318 		var meas = this.getMeasurement( "longitude" );
319 	    if(meas == null) {
320 	    	return null;
321 	    } else {
322 	    	return meas.value;
323 	    }	
324 	},
325 	
326 	/** Shortcut for directly getting the elevation value
327 	 * 
328 	 * @type Number
329 	 * @return The value of the elevation for this point
330 	 */
331 	getElev: function() {
332 		var meas = this.getMeasurement( "elevation" );
333 	    if(meas == null) {
334 	    	return null;
335 	    } else {
336 	    	return meas.value;
337 	    }	
338 	},
339 	
340 	/** Shortcut for directly getting the date/time
341 	 * 
342 	 * @type  Garmin.DateTimeFormat
343 	 * @return The time for this point
344 	 */
345 	getDate: function() {
346 		return this.date;
347 	},
348 	
349 	toString: function() {
350 		return "TrackPoint Point: (" + this.getLat() + ", " +  this.getLng() + ")";
351 	}
352 };
353 
354 
355 
356 /** Equivalent to a <trkseg> in GPX format
357  * 
358  * @deprecated use Garmin.Activity instead
359  * @class Garmin.TrackSegment
360  * @constructor
361  */
362 Garmin.TrackSegment = function(){};
363 Garmin.TrackSegment = Class.create();
364 Garmin.TrackSegment.prototype = {
365 
366     initialize: function() {
367         this.points = new Array();
368     },
369     
370     addTrackPoint: function(trackPointObject) {
371     	this.points.push(trackPointObject);
372     },
373     
374     /** Find the nearest valid point to the index given
375      * 
376      * @param index is the index
377      * @param incDirection is an int in the direction we'd like to look positive 
378      * 	nums are forward, negative nums are backwards
379      * 
380      * @type Garmin.TrackPoint 
381      * @return The nearest point (possibly the index) that has a validLocation
382      */ 
383     findNearestValidLocationPoint: function(index, incDirection) {
384         if( this.getPoint( index ).isValidLocation() ) {
385             return this.getPoint( index );
386         } else if( index >= this.getLength() ) {
387         	return this.findNearestValidLocationPoint(this.getLength()-1, -1);
388         } else {
389             return this.findNearestValidLocationPoint(index+incDirection, incDirection);
390         }
391     },
392 
393 	/** Get the point specified on the track
394 	 * If the number is negative, get's the first
395 	 * If it's larger than possible, get's the last
396 	 * Otherwise it gets the number requested
397 	 *
398 	 * @param {Number} index is the point we want
399      * @type Garmin.TrackPoint 
400 	 * @return A TrackPoint that fits the pattern described above 
401 	 */
402     getPoint: function(index) {
403         index = Math.floor(index);
404     
405         if(index >= this.getLength()) {
406             return this.getEnd();
407         }
408         if(index <= 0) {
409             return this.getStart();
410         }
411             
412         return this.points[index];
413     },
414 
415     /** Quick method to get the first point
416      * @type Garmin.TrackPoint 
417      * @return The first point of this track
418      */
419     getStart: function() {
420         return this.points[0];
421     },
422 
423     /** Quick method to get the last point
424      * @type Garmin.TrackPoint 
425      * @return The last point of this track
426      */
427     getEnd: function() {
428         return this.points[this.getLength()-1];
429     },
430 
431     /** Get the latitude for the start point of this segment
432      * @type Number
433 	 * @return Latitude of the first trackpoint
434      */
435     getStartLat: function() {
436     	return this.getStart().getLat();
437     },
438 
439     /** Get the longitude for the start point of this segment
440      * @type Number
441 	 * @return Longitude of the first trackpoint
442      */
443     getStartLng: function() {
444     	return this.getStart().getLng();
445     },
446 
447     /** Get the data/time for the start point of this segment
448      * @return date/time of the first trackpoint
449      * @type Garmin.DateTimeFormat
450      */
451     getStartDate: function() {
452     	return this.getStart().getDate();
453     },
454 
455     /** Get the data/time for the end point of this segment
456      * @type Garmin.DateTimeFormat
457      * @return Date/time of the last trackpoint
458      */
459     getEndDate: function() {
460     	return this.getEnd().getDate();
461     },
462     
463     /** Get the total duration for this track segment
464 	 * @type String
465 	 * @return String of Duration (hh:mm:ss)
466      */
467     getDuration: function() {
468     	return this.getStartDate().getDurationTo(this.getEndDate());
469     },
470 
471     /** Get the total number of trackpoints in this segment
472 	 * @type Number
473 	 * @return Total number of trackpoints in this segment
474      */
475     getLength: function() {
476         return this.points.length;
477     },
478 
479     /** String representation
480 	 * @type String
481      */
482     toString: function() {
483         return "Track Segment w/ " + this.getLength() + " points.";
484     }
485 };
486 
487 
488 /** A track is an ordered list of track segments.<br>
489  * Equivalent to a <trk> in GPX format.
490  * 
491  * @deprecated use Garmin.Activity instead
492  * @class Garmin.Track
493  * @constructor
494  */
495 Garmin.Track = function(){}; 
496 Garmin.Track = Class.create();
497 Garmin.Track.prototype = {
498     initialize: function() {
499         this.segments = new Array();
500     },
501     
502     /** Add a segment to this track
503 	 * @type Garmin.TrackPoint
504      */
505     addSegment: function(trackSegment) {
506     	this.segments.push(trackSegment);
507     },
508 
509 	/** Get the segment specified on the track
510 	 * If the number is negative, get's the first
511 	 * If it's larger than possible, get's the last
512 	 * Otherwise it gets the number requested
513 	 *
514 	 * @param {Number} index is the segment we want
515 	 * @type Garmin.TrackSegment
516 	 * @return A segment that fits the pattern described above 
517 	 */
518     getSegment: function(index) {
519         index = Math.floor(index);
520     
521         if(index >= this.getLastSegment()) {
522             return this.getEnd();
523         }
524         if(index <= 0) {
525             return this.getFirstSegment();
526         }
527             
528         return this.segments[index];
529     },
530 
531     /** Get the first segment
532 	 * @type Garmin.TrackSegment
533      * @return The first segment of this track
534      */
535     getFirstSegment: function() {
536         return this.segments[0];
537     },
538 
539     /** Get the last segment
540 	 * @type Garmin.TrackSegment
541      * @return The last segment of this track
542      */
543     getLastSegment: function() {
544         return this.segments[this.getNumSegments()-1];
545     },
546 
547     /** Get the total length of the track
548 	 * @type Number
549      */
550     getNumSegments: function() {
551         return this.segments.length;
552     },
553 
554     /** Get the start point for the track
555 	 * @type Garmin.TrackPoint
556      */
557     getStart: function() {
558     	return this.getFirstSegment().getStart();
559     },
560 
561     /** Get the latitude of the start point for the track
562 	 * @type Number
563      */
564     getStartLat: function() {
565     	return this.getFirstSegment().getStartLat();
566     },
567 
568     /** Get the lpngitude of the start point for the track
569 	 * @type Number
570      */
571     getStartLng: function() {
572     	return this.getFirstSegment().getStartLng();
573     },
574     
575     /** Get the DateTimeFormat object for the start of this track
576 	 * @type Garmin.DateTimeFormat
577      */
578     getStartDate: function() {
579     	return this.getFirstSegment().getStartDate();
580     },
581 
582     /** Get the end point for the track
583 	 * @type Garmin.TrackPoint
584      */
585     getEnd: function() {
586     	return this.getLastSegment().getEnd();
587     },
588 
589     /** Get the DateTimeFormat object for the end of this track
590 	 * @type Garmin.DateTimeFormat
591      */
592     getEndDate: function() {
593     	return this.getLastSegment().getEndDate();
594     },
595     
596     /** Get the total duration for this track
597 	 * @type String
598      */
599     getDuration: function() {
600     	return this.getStartDate().getDurationTo(this.getEndDate());
601     },
602 
603     /** Get the total number of trackpoints in this track
604 	 * @type Number
605      */
606     getLength: function() {
607 		var length = 0;
608 		for( var i=0; i < this.segments.length; i++ ) {
609 			length += this.segments[i].getLength();
610 		}
611         return length;
612     },
613 
614     /** Checks to see if the startDate is defined.  If not then this is
615      * technically a route and presents issues for some
616      * track log databases where timestamps are used to merge old
617      * and new entries.
618 	 * @type Boolean
619 	 * @return True if this track has a timestamp
620      */
621     isDrawable: function() {
622     	return (this.getStartDate() != null);
623     },
624 
625     toString: function() {
626         return "Track w/ " + this.getNumSegments() + " segments.";
627     }
628 };
629 
630 /* THIS CLASS NOT NEEDED YET
631  * @class Garmin.Address
632  * Address class reprsents a US postal address.<br>
633  * @constructor 
634 
635 Garmin.Address = function(){};
636 Garmin.Address = Class.create();
637 Garmin.Address.prototype = {
638 	initialize: function() {
639 		this.streetAddress = null;
640 		this.streetAddress2 = null;
641 		this.city = null;
642 		this.state = null;
643 		this.postalCode = null;
644 	},
645 
646 	toString: function() {
647 		return "Address: (" + this.streetAddress + ", " + 
648 			  (this.streetAddress2 ? this.streetAddress2+", " : "") + 
649 			  (this.city ? this.city+", " : "") + 
650 			  (this.state ? this.state+" " : "") + 
651 			  (this.postalCode ? this.postalCode : "") + 
652 			  ")";
653 	}
654 };
655  */
656 
657 
658 /** Used to parse track and/or waypoint data from a number of Xml formats.<br>
659  * Currently only supports tracks from a GPX file.
660  * 
661  * @deprecated use Garmin.GpxActivityFactory instead
662  * @class Garmin.GpsDataFactory
663  * @constructor
664  */
665 Garmin.GpsDataFactory = function(){}; 
666 Garmin.GpsDataFactory = Class.create();
667 Garmin.GpsDataFactory.prototype = {
668 
669     initialize: function() {
670     	this.tracks = new Array();
671     	this.waypoints = new Array();
672     },
673 
674     /** Get the tracks parsed by this factory
675 	 * @type Array<Garmin.Tracks>
676      */
677 	getTracks: function() {
678 		return this.tracks;
679 	},
680 
681     /** Get the waypoints parsed by this factory
682 	 * @type Array<Garmin.WayPoint>
683      */
684 	getWaypoints: function() {
685 		return this.waypoints;
686 	},
687 
688     /** Parse a gpx string and save the tracks found as objects
689 	 * @param {String} xml string in GPX format
690      */
691     parseGpxString: function(gpxString) {
692 		var gpxDocument = Garmin.XmlConverter.toDocument(gpxString);
693 		
694 		this.parseGpxDocument(gpxDocument);
695 	},
696 
697     /** Parse a gpx document and save the tracks and waypoints found
698 	 * @param {Document} xml document in GPX format
699      */
700     parseGpxDocument: function(gpxDocument) {
701 		this.parseGpxTracks(gpxDocument);
702 		this.parseGpxWaypoints(gpxDocument);
703     },
704 
705     /** Parse a GPX xml document for tracks
706 	 * @param {Document} xml document in GPX format
707      * @type Array<Garmin.Track>
708      */
709 	parseGpxTracks: function(gpxDocument) {
710 		var tracks = new Array();
711 
712     	var trackNodes = gpxDocument.getElementsByTagName("trk");
713 
714 		// triple for-loop fun
715 		for( var i=0; i < trackNodes.length; i++ ) {
716 			var trk = new Garmin.Track();
717 
718 			var trackSegments = trackNodes[i].getElementsByTagName("trkseg");
719 	
720 			for( var j=0; j < trackSegments.length; j++ ) {
721 				var trkseg = new Garmin.TrackSegment();
722 				
723 				var trackPoints = trackSegments[j].getElementsByTagName("trkpt");
724 		
725 				for( var k=0; k < trackPoints.length; k++ ) {
726 					var trkpt = new Garmin.TrackPoint();
727 
728 					var lat = trackPoints[k].getAttribute("lat");
729 					var lng = trackPoints[k].getAttribute("lon");
730 					var eleElement = trackPoints[k].getElementsByTagName("ele");
731 					var ele = (eleElement.length > 0) ? eleElement[0].childNodes[0].nodeValue : null;
732 					
733 					var timeNodes = trackPoints[k].getElementsByTagName("time");
734 					if(timeNodes.length > 0) {
735 						var time = timeNodes[0].childNodes[0].nodeValue;
736 						trkpt.date = (new Garmin.DateTimeFormat()).parseXsdDateTime(time);
737 					}
738 					
739 					trkpt.measurements = {
740 						latitude: {
741 							value: lat,
742 							context: "latitude"
743 						},
744 						longitude: {
745 							value: lng,
746 							context: "longitude"
747 						},
748 						elevation: {
749 							value: ele,
750 							context: "feet"
751 						}
752 					};
753 					
754 					trkseg.addTrackPoint(trkpt);
755 				}
756 				
757 				trk.addSegment(trkseg);
758 			}
759 
760 			tracks.push(trk);
761 		}
762 
763     	this.tracks = tracks;
764     	return tracks;
765 	},
766 
767     /** Parse a GPX xml waypoint into a Waypoint object.
768 	 * @param {Element} xml waypoint in GPX format
769      * @type Garmin.WayPoint
770      */
771 	parseGpxWaypoint: function(waypointNode) {
772 		var lat  = waypointNode.getAttribute("lat");
773 		var lng  = waypointNode.getAttribute("lon");
774 		var name = this._tagValue(waypointNode,"name");
775 		var desc = this._tagValue(waypointNode,"desc");
776 		var ele  = this._tagValue(waypointNode,"ele");
777 		var sym  = this._tagValue(waypointNode,"sym");
778 		var type  = this._tagValue(waypointNode,"type");
779 		var cmt  = this._tagValue(waypointNode,"cmt");
780 		
781 		var wpt  = new Garmin.WayPoint(lat, lng, ele, name, null, desc, sym, type, cmt);		
782    		return wpt;
783 	},
784 
785     /** Parse a GPX xml document for waypoints
786 	 * @param {Document} xml document in GPX format
787      * @type Array<Garmin.WayPoint>
788      */
789 	parseGpxWaypoints: function(gpxDocument) {
790 		var waypoints = new Array();
791     	var waypointNodes = gpxDocument.getElementsByTagName("wpt");
792 		for( var i=0; i < waypointNodes.length; i++ ) {
793 			var waypointNode = waypointNodes[i];
794 			var wpt = this.parseGpxWaypoint(waypointNode);
795 			waypoints.push(wpt);
796 		}
797     	this.waypoints = waypoints;
798     	return waypoints;
799 	},
800 
801     /** Take a list of tracks and waypoints and generate a GPX xml string
802 	 * @param {Array<Garmin.Track>} Tracks
803 	 * @param {Array<Garmin.WayPoint>} Waypoints
804      * @type String
805      */
806 	produceGpxString: function(tracks, waypoints) {
807 		gpxString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>";
808 	
809 		gpxString += "<gpx xmlns=\"http://www.topografix.com/GPX/1/1\" xmlns:gpxx=\"http://www.garmin.com/xmlschemas/GpxExtensions/v3\" creator=\"Garmin Communicator Plug-In API\" version=\"1.1\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensions/v3/GpxExtensionsv3.xsd\">";
810 		
811 		if(tracks != null) {
812 			for( var i=0; i < tracks.length; i++ ) {
813 				gpxString += this.produceTrackGpxString(tracks[i]);
814 			}
815 		}
816 
817 		if(waypoints != null) {		
818 			for( var i=0; i < waypoints.length; i++ ) {
819 				gpxString += this.produceWaypointGpxString(waypoints[i]);
820 			}
821 		}
822 		gpxString += "</gpx>";
823 	
824 		return gpxString;
825 	},
826 
827     /** Take a track object and generate a GPX xml string (without gpx or xml headers)
828 	 * @param {Garmin.Track} Track
829      * @type String
830      */
831 	produceTrackGpxString: function(track) {
832 		gpxString = "<trk>";
833 
834 		for( var i=0; i < track.getNumSegments(); i++ ) {
835 			var segment = track.getSegment(i);
836 			
837 			gpxString += "<trkseg>";
838 			for(var j=0; j < segment.getLength(); j++) {
839 				var point = segment.getPoint(j);
840 				
841 				gpxString += "<trkpt lat=\"" + point.getLat() + "\" lon=\"" + point.getLng() + "\">";
842 				if(point.getElev()) {
843 					gpxString += "<ele>" + point.getElev() + "</ele>";
844 				}
845 				if(point.getDate()) {
846 					gpxString += "<time>" + point.getDate().getXsdString() + "</time>";
847 				}
848 				gpxString += "</trkpt>";
849 			}
850 			gpxString += "</trkseg>";
851 
852 		}
853 		
854 		gpxString += "</trk>";
855 	
856 		return gpxString;
857 	},
858 
859     /** Take a waypoint object and generate a GPX xml string (without gpx or xml headers)
860 	 * @param {Garmin.WayPoint} WayPoint
861      * @type String
862      */
863 	produceWaypointGpxString: function(waypoint) {
864 		gpxString = "<wpt lat=\"" + waypoint.getLat() + "\" lon=\"" + waypoint.getLng() + "\">";
865 	
866 		if(waypoint.getElev()) {
867 			gpxString += "<ele>" + waypoint.getElev() + "</ele>";
868 		}
869 		if(waypoint.getName()) {
870 			gpxString += "<name>" + waypoint.getName() + "</name>";
871 		}
872 		if(waypoint.getComment()) {
873 			gpxString += "<cmt>" + waypoint.getComment() + "</cmt>";
874 		}
875 		if(waypoint.getDescription()) {
876 			gpxString += "<desc>" + waypoint.getDescription() + "</desc>";
877 		}
878 		if(waypoint.getSymbol()) {
879 			gpxString += "<sym>" + waypoint.getSymbol() + "</sym>";
880 		}
881 		if(waypoint.getAddress()) {
882 			gpxString += "<extensions><gpxx:WaypointExtension>" + waypoint.getAddress() + "</gpxx:WaypointExtension></extensions>";
883 		}
884 		if(waypoint.getType()) {
885 			gpxString += "<type>" + waypoint.getType() + "</type>";
886 		}
887 		gpxString += "</wpt>";
888 	
889 		return gpxString;
890 	},
891 
892     /** Utility method to get the value of a child element.
893      * @param {Node} parent DOM node
894      * @param {String} name of child element
895      * @type String value of child element
896      */
897 	_tagValue: function(parentNode, tagName) {
898 		var subNode = parentNode.getElementsByTagName(tagName);
899 		return subNode.length > 0 ? subNode[0].childNodes[0].nodeValue : null;
900 	},
901 	
902     /** String representation of instance.
903      * @type String
904      */
905     toString: function() {
906         return "GpsDataFactory.";
907     }
908 };
909