1 if (Garmin == undefined) var Garmin = {};
  2 /**
  3  * Copyright � 2007 Garmin Ltd. or its subsidiaries.
  4  *
  5  * Licensed under the Apache License, Version 2.0 (the 'License')
  6  * you may not use this file except in compliance with the License.
  7  * You may obtain a copy of the License at
  8  *
  9  *    http://www.apache.org/licenses/LICENSE-2.0
 10  *
 11  * Unless required by applicable law or agreed to in writing, software
 12  * distributed under the License is distributed on an 'AS IS' BASIS,
 13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  * See the License for the specific language governing permissions and
 15  * limitations under the License.
 16  * 
 17  * @fileoverview Garmin.Device A place-holder for Garmin device information. <br/>
 18  * Source: 
 19  * <a href="http://developer.garmin.com/web/communicator-api/garmin/device/GarminDevice.js">Hosted Distribution</a>  
 20  * <a href="https://svn.garmindeveloper.com/web/trunk/communicator/communicator-api/src/main/webapp/garmin/device/GarminDevice.js">Source Control</a><br/>
 21  * 
 22  * @author Richard Easterling developer.connect.at.garmin.com
 23  * @version 1.0
 24  */
 25  
 26 /** Plugin-specific utility functions.
 27  *
 28  * @class Garmin.PluginUtils
 29  * @constructor 
 30  */
 31 Garmin.PluginUtils = function(){}; //just here for jsdoc
 32 Garmin.PluginUtils = {
 33 //Garmin.PluginUtils.prototype = {
 34 
 35 	initialize: function() {
 36 	},
 37 	
 38 	/** Parse device xml string into device objects.  
 39 	 * 
 40 	 * Each device object contains the following:
 41 	 * 1) device display name
 42 	 * 2) device number
 43 	 * 3) device XML as an XML document
 44 	 * 
 45 	 * This function returns an array of the described Device objects.
 46 	 * 
 47 	 * @param garminPlugin - the GarminDevicePlugin object having access to the device XML data.
 48 	 * @param getDetailedDeviceData - boolean indicating if you want to get the entire device XML 
 49 	 *  as an XML document (rather than the few essentials)
 50 	 */ 
 51 	parseDeviceXml: function(garminPlugin, getDetailedDeviceData) {
 52         var xmlDevicesString = garminPlugin.getDevicesXml();
 53         var xmlDevicesDoc = Garmin.XmlConverter.toDocument(xmlDevicesString); 
 54         
 55         var deviceList = xmlDevicesDoc.getElementsByTagName("Device");
 56         var devices = new Array();
 57         var numDevices = deviceList.length;
 58         
 59     	for( var i=0; i < numDevices; i++ ) {
 60 			var displayName = deviceList[i].getAttribute("DisplayName");        		
 61     		var deviceNumber = parseInt( deviceList[i].getAttribute("Number") );
 62     		var deviceDescriptionDoc = null;
 63     		if (getDetailedDeviceData) {
 64 				var deviceDescriptionXml = garminPlugin.getDeviceDescriptionXml(deviceNumber);
 65 				deviceDescriptionDoc = Garmin.XmlConverter.toDocument(deviceDescriptionXml);    
 66     		}
 67     		devices.push(Garmin.PluginUtils._createDeviceFromXml(displayName, deviceNumber, deviceDescriptionDoc));              		  		
 68     	}
 69     	return devices;
 70 	},
 71 	
 72 	/** Create a Garmin.Device instance for each connected device found.
 73 	 * @private
 74 	 */
 75 	_createDeviceFromXml: function(displayName, deviceNumber, deviceDescriptionDoc) {
 76    		var device = new Garmin.Device(displayName, deviceNumber);
 77 
 78    		if(deviceDescriptionDoc) {						
 79 			var partNumber = deviceDescriptionDoc.getElementsByTagName("PartNumber")[0].childNodes[0].nodeValue;
 80 			var softwareVersion = deviceDescriptionDoc.getElementsByTagName("SoftwareVersion")[0].childNodes[0].nodeValue;
 81 			var description = deviceDescriptionDoc.getElementsByTagName("Description")[0].childNodes[0].nodeValue;
 82 			var id = deviceDescriptionDoc.getElementsByTagName("Id")[0].childNodes[0].nodeValue;
 83 			
 84 			device.setPartNumber(partNumber);
 85 			device.setSoftwareVersion(softwareVersion);
 86 			device.setDescription(description);
 87 			device.setId(id);
 88 			
 89 //			var dataTypeList = deviceDescriptionDoc.getElementsByTagName("DataType");
 90 			var dataTypeList = deviceDescriptionDoc.getElementsByTagName("MassStorageMode")[0].getElementsByTagName("DataType");
 91 			var numOfDataTypes = dataTypeList.length;
 92 	
 93 			for ( var j = 0; j < numOfDataTypes; j++ ) {
 94 				var dataName = dataTypeList[j].getElementsByTagName("Name")[0].childNodes[0].nodeValue;					
 95 				var dataExt = dataTypeList[j].getElementsByTagName("FileExtension")[0].childNodes[0].nodeValue;
 96 				
 97 				var dataType = new Garmin.DeviceDataType(dataName, dataExt);
 98 				var fileList = dataTypeList[j].getElementsByTagName("File");
 99 				
100 				var numOfFiles = fileList.length;
101 				
102 				for ( var k = 0; k < numOfFiles; k++ ) {
103 					// Path is an optional element in the schema
104 					var pathList = fileList[k].getElementsByTagName("Path");
105 					var transferDir = fileList[k].getElementsByTagName("TransferDirection")[0].childNodes[0].nodeValue;											
106 					
107 					if ((transferDir == Garmin.DeviceControl.TRANSFER_DIRECTIONS.read)) {
108 						dataType.setReadAccess(true);
109 						
110 						if (pathList.length > 0) {
111 						    var filePath = pathList[0].childNodes[0].nodeValue;
112 						    dataType.setReadFilePath(filePath);							
113 						}
114 					} else if ((transferDir == Garmin.DeviceControl.TRANSFER_DIRECTIONS.write)) {			
115 						dataType.setWriteAccess(true);
116 						
117 						if (pathList.length > 0) {
118                             var filePath = pathList[0].childNodes[0].nodeValue;
119                             dataType.setWriteFilePath(filePath);                         
120                         }
121 					} else if ((transferDir == Garmin.DeviceControl.TRANSFER_DIRECTIONS.both)) {		
122 						dataType.setReadAccess(true);
123 						dataType.setWriteAccess(true);
124 						
125 						if (pathList.length > 0) {
126                             var filePath = pathList[0].childNodes[0].nodeValue;
127                             dataType.setReadFilePath(filePath);
128                             dataType.setWriteFilePath(filePath);                         
129                         }
130 					}
131 
132                     // Deprecated! Need to be removed at some point.
133 					if( pathList.length > 0) {
134 						var filePath = pathList[0].childNodes[0].nodeValue;
135 						dataType.setFilePath(filePath);
136 					}
137 					
138 					// Identifier is optional
139 					var identifierList = fileList[k].getElementsByTagName("Identifier");
140 					if( identifierList.length > 0) {
141 						var identifier = identifierList[0].childNodes[0].nodeValue;
142 						dataType.setIdentifier(identifier);
143 					}
144 				}			
145 				device.addDeviceDataType(dataType);
146 			}   			
147    		}
148 		return device;
149 	},
150 	
151 	/** Is this a device XML error message.
152 	 * @param {String} xml string or Error instance with embedded xml
153 	 * @type Boolean
154 	 * @return true if error is device-generared error
155 	 */
156 	isDeviceErrorXml: function(error) {
157 		var msg = (typeof(error)=="string") ? error : error.name + ": " + error.message;
158 		return ( (msg.indexOf("<ErrorReport") > 0) );
159 	},
160 	
161 	/** Best effort to convert XML error message to a String.
162 	 * @param {String} xml string or Error instance with embedded xml
163 	 * @type String
164 	 * @return Human readable interpretation of XML message
165 	 */
166 	getDeviceErrorMessage: function(error) {
167 		var msg = (typeof(error)=="string") ? error : error.name + ": " + error.message;
168 		var startPos = msg.indexOf("<ErrorReport");
169 		if (startPos>0) { //strip off any text surrounding the xml
170 			var endPos = msg.indexOf("</ErrorReport>") + "</ErrorReport>".length;
171 			msg = msg.substring(startPos, endPos);
172 		}
173         var xmlDoc = Garmin.XmlConverter.toDocument(msg); 
174         var errorMessage = Garmin.PluginUtils._getElementValue(xmlDoc, "Extra");
175         var sourceFileName = Garmin.PluginUtils._getElementValue(xmlDoc, "SourceFileName");
176         var sourceFileLine = Garmin.PluginUtils._getElementValue(xmlDoc, "SourceFileLine");
177         var msg = "";
178         if (errorMessage) {
179         	msg = errorMessage;
180         } else { // gota show something :-(
181         	msg = "Plugin error: ";
182 	        if (sourceFileName)
183 	        	msg += "source: "+sourceFileName;
184 	        if (sourceFileLine)
185 	        	msg += ", line: "+sourceFileLine;
186         }
187 		return msg;
188 	},
189 
190 	/** Get the value of a document element
191 	 * @param doc - the document that the element is contained in
192 	 * @param elementName - the name of the element to get the value from
193 	 * @return the value of the element identified by elementName 
194 	 */	
195 	_getElementValue: function(doc, elementName) {
196         var elementNameNodes = doc.getElementsByTagName(elementName);
197         var value = (elementNameNodes && elementNameNodes.length>0) ? elementNameNodes[0].childNodes[0].nodeValue : null;
198  		return value;		
199 	}
200 };
201 
202 
203 /** GPI XML generation utility.
204  *
205  * @class Garmin.PluginUtils
206  * @constructor 
207  **/
208 Garmin.GpiUtil = function(){};
209 Garmin.GpiUtil = {
210 	
211 	/** Build a single DeviceDownload XML for multiple file downloads.  
212 	 * 
213 	 * @param descriptionArray - Even sized array with matching source and destination pairs.
214 	 * @param regionId - Optional parameter designating RegionId attribute of File.  For now, this single
215 	 * regionId will be applied to all files in the descriptionArray if provided at all.    
216 	 * 
217 	 */
218 	buildMultipleDeviceDownloadsXML: function(descriptionArray) {
219 		if(descriptionArray.length % 2 != 0) {
220 			throw new Error("buildMultipleDeviceDownloadsXML expects even sized array with matching source and destination pairs");
221 		}
222 		var xml =
223 		'<?xml version="1.0" encoding="UTF-8"?>\n' +
224 		'<DeviceDownload xmlns="http://www.garmin.com/xmlschemas/PluginAPI/v1"\n' +
225 		' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n' +
226 		' xsi:schemaLocation="http://www.garmin.com/xmlschemas/PluginAPI/v1 http://www.garmin.com/xmlschemas/GarminPluginAPIV1.xsd">\n';
227 
228 		for(var i=0;i<descriptionArray.length;i+=2) {
229 			var source = descriptionArray[i];
230 			var destination = descriptionArray[i+1];
231 		
232 //			if(!Garmin.GpiUtil.isDestinationValid(destinationArray[i])) {
233 //				throw new Error("Destination filename contains invalid characters: [" + destinationArray[i] + "]");
234 //			}
235 			xml += ' <File Source="'+source+'" Destination="'+destination+'" RegionId="46" />\n';
236 		}
237 		xml += '</DeviceDownload>';
238 		return xml;
239 	},
240 	
241 	buildDeviceDownloadXML: function(source, destination) {
242 		return Garmin.GpiUtil.buildMultipleDeviceDownloadsXML([source, destination]);
243 	},
244 	
245 	isDestinationValid: function(destination) {
246 		var splitPath = destination.split("/");
247 		var filename = splitPath[splitPath.length-1];
248 
249 		var lengthBefore = filename.length;
250 		
251 		var stringAfter = Garmin.GpiUtil.cleanUpFilename(filename);
252 		
253 		return(lengthBefore == stringAfter.length);
254 	},
255 	
256 	cleanUpFilename: function(filename) {
257 		var result = filename;
258 
259 		var replacement = "";						// see http://www.asciitable.com/
260 		result = result.stripTags();
261 		result = result.replace(/&/, replacement);
262 		result = result.replace(/[\x21-\x2F]/g, replacement); 	// using range "!" through "/"
263 		result = result.replace(/[\x5B-\x60]/g, replacement);	// using range "[" through "`"
264 		result = result.replace(/[\x3A-\x40]/g, replacement);	// using range ":" through "@"
265 		result = result.strip();
266 		
267 		return result;
268 	}
269 };