Commits

Matthew Schinckel committed 1aa4290

Remove signal-based API.
Now, only use callbacks.

  • Participants
  • Parent commits 5944c1a

Comments (0)

Files changed (2)

 
 ## Usage
 
-### Delegate
-
-    <script>
-      $(function(){
-        // Put the listeners you want in this.
-        var delegate = {};
-        var g = Garmin.Communicator(delegate, true);
-      });
-    </script>
-
-This will inject the plugin into the page if it isn't already there, create a new instance of the `Communicator` controller, and (if the second argument is true), start looking for devices right away.
-
-Your delegate's attributes that match the various events are then called when an event occurs. You can also manually set listeners for the events like `$(document).on('onFinishReadFromDevice', handler)`. The handler will recieve a jQuery Event object, and depending upon the event, there may also be a `data` object. Event.data contains a reference to the controller object.
-
-### Callback
-
     <script>
       $(function() {
         var g = Garmin.Communicator();
       });
     </script>
     
-This usage is new: there may still be some bugs (like things happening before the devices are loaded), as I'm still looking into the bare constructor.
+This will inject the plugin into the page if it isn't already there, create a new instance of the `Communicator` controller, and start looking for devices right away.
 
-## Events
+If there is already an instance of the controller, it will just return that.
 
-To be finalised. Source code contains some info about this, but may change.
+You may pass an object to the constructor which can have a property `unlockCodes`, used to pass in the garmin unlock codes for your site:
 
-### onStartFindDevices
+    var g = Garmin.Communicator({unlockCodes: ['foo', 'bar']});
 
-Called before the search for devices occurs.
+There are only a handful of operations that are exposed:
 
+### findDevices
+
+Tell the plugin to search for devices. Accepts a callback function that is passed the list of devices that were found.
+
+    plugin.findDevices(function(deviceList) {
+      // Do something here with the deviceList.
+    });
+
+### selectDevice
+
+Make a device active for communication. There may only be one active device at a time. Argument is the index of the device from `findDevices` above.
+
+    plugin.selectDevice(0);
+
+TODO: allow for passing in the device object to `selectDevice()`.
+
+### activeDevice
     Profile: ['FitnessUserProfile', 'FitnessData']
   };
   
-  // == Events ==
-  // 
-  // List of events that are triggered by this controller.
-  //
-  //  {{{initialisationError}}}   {message: "string"}
-  Garmin.EVENTS = [
-    "initialisationError",
-    "deviceListUpdate",
-    "deviceSelected",
-    "startInteractionWithDevice",
-    "endInteractionWithDevice",
-    "progressInterationWithDevice",
-    "interactionError"
-  ];
-  
-  _.each(Garmin.FITNESS_TYPES, function(data, name){
-    Garmin.EVENTS.push('read' + name + 'Complete');
-    Garmin.EVENTS.push('write' + name + 'Complete');
-  });
-  
-  /* 
-  
-  Things we need to publish.
-  
-    * Initialisation errors         {message: "string"}
-    * deviceListUpdate              {devices: [device, device]}
-    * deviceSelected                {device: device, number: <number>}
+  // == Communicator ==
+  Garmin.Communicator = function Communicator(options) {
+    options = options || {};
     
-    * Transfer status
-      * startInteractionWithDevice  {type:"string", device:device}
-      * progressUpdate              {device:device, progress:{}, status:STATUS}
-      * endInteractionWithDevice    {device:device, status:STATUS, message:"string"}
-      * interactionError            {device:device, message:"string"}
-    
-    * Specific fitness events
-      * readActivitiesComplete      {device:device, data: data}
-      * writeActivitiesComplete     {device:device}
-      * <which><Whatever>Complete
-  
-  */
-  var EVENTS = [
-  ];
-  
-  // == Communicator ==
-  Garmin.Communicator = function Communicator(delegate, autostart) {
-    
-    // We actually want to be  singleton, and each call will just add
-    // the delegate to the list of delegates we call handlers on.
+    // This should be a singleton.
     if (arguments.callee._singletonInstance) {
-      if (delegate) {
-        Garmin.Communicator.delegates.push(delegate);
+      // We have already been executed. If we are not yet unlocked, then look for
+      // new unlock codes, and attempt to unlock ourself again.
+      if (plugin.Locked) {
+        unlock(options.unlockCodes || []);
       }
       return arguments.callee._singletonInstance;
     } else {
+      // Set the singleton instance for future calls.
       arguments.callee._singletonInstance = this;
     }
     
     // We need to have a flag so we can't try to have two things happening at once.
     var inUse = false;
-    var delegates = Garmin.Communicator.delegates = [delegate];
     var currentDevice = null;
-    var plugin = new Garmin.Plugin();
+    var plugin = this.plugin = new Garmin.Plugin();
     var $plugin = $(plugin);
-    
     var unlockCodes = [
       "file:///","cb1492ae040612408d87cc53e3f7ff3c",
       "http://localhost","45517b532362fc3149e4211ade14c9b2",
     
     //************** Helpers **************//
     
-    var send = function(eventType, data) {
-      _.each(delegates, function(delegate){
-        if (delegate[eventType]) {
-          delegate[eventType](data);
-        }        
-      });
-      $plugin.trigger(eventType, data);
-    };
-    
     var parseXML = function(xml) {
       return $($.parseXML(xml));
     };
     
     var checkUnlocked = function() {
       if (plugin.Locked) {
-        var message = "Plugin is not unlocked.";
-        send("initialisationError", {message: message});
-        throw new Error(message);
+        throw new Error("Plugin is not unlocked.");
       }
     };
     
         
     //************** Handlers **************//
     
-    this.findDevices = function() {
+    this.findDevices = function(callback) {
       checkUnlocked();
-      inUse = "findDevices";
-      plugin.StartFindDevices();
+      if (inUse) {
+        // communication in progress
+        if (inUse !== "findDevices") {
+          throw new Error("Already attempting to " + inUse);
+        }
+      } else {
+        inUse = "findDevices";
+        plugin.StartFindDevices();        
+      }
+      
+      var finishFindDevices = function() {
+        if (plugin.FinishFindDevices()) {
+          var data = parseXML(plugin.DevicesXmlString());
+          devices = [];
+          _.each(data.find('Device'), function(el,i) {
+            devices.push(new Device(i));
+          });
+          inUse = false;
+          if (callback) {
+            callback(devices);
+          }
+        } else {
+          // Keep checking every .1 sec until we finish.
+          setTimeout(finishFindDevices, 100);
+        }        
+      };
+      
       // Check in 100 ms if we have finished.
       setTimeout(finishFindDevices, 100);
     };
     
     this.cancelFindDevices = plugin.CancelFindDevices;
     
-    var finishFindDevices = function() {
-      if (plugin.FinishFindDevices()) {
-        var data = parseXML(plugin.DevicesXmlString());
-        devices = [];
-        _.each(data.find('Device'), function(el,i) {
-          devices.push(new Device(i));
-        });
-        send('deviceListUpdate', {devices: devices});
-        inUse = false;
-      } else {
-        // Keep checking every .1 sec until we finish.
-        setTimeout(finishFindDevices, 100);
-      }        
-    };
-    
-    // Simple RO accessor.
-    this.getDevices = function() {
-      return devices;
-    };
-    
     this.selectDevice = function(d) {
       // Ensure device number is 0..devices.length;
       if (isNaN(d) || d < 0 || d >= devices.length) {
         // Different error type?
-        send('interactionError', {message: "Invalid device number selected"});
         currentDevice = null;
-        return;
+        throw new Error("Invalid device number");
       }
       currentDevice = d;
-      send('deviceSelected', {device: devices[d], number: d});
       return devices[d];
     };
     
       return devices[currentDevice];
     };
             
-    var baseHandler = function(readWrite, dataType, pluginMethod, name, callback) {
+    var baseHandler = function(readWrite, dataType, pluginMethod, name, callback, progress) {
       if (devices.length === 0) {
-        send('interactionError', {message: "No devices found."});
+        throw new Error("No devices found");
       }
       if (currentDevice === null) {
-        send('interactionError', {message: "No device selected."});
+        throw new Error("No device selected");
         return;
       }
       if (inUse) {
-        send('interactionError', {message: "Process '" + inUse + "' already in progress."});
+        throw new Error("Process '" + inUse + "' already in progress.");
         return;
       }
       var device = devices[currentDevice];
         readWrite = "Read";
       }
       inUse = readWrite.toLowerCase() + name;
-      send('startInteractionWithDevice', {device:device, type:dataType});
       plugin['Start' + readWrite + pluginMethod](currentDevice, dataType);
       
       // This handler will set a timeout and execute again if an interaction is in progress.
         var status = plugin['Finish' + readWrite + pluginMethod]();
         switch (status) {
           case Garmin.STATUS.working:
-            send('progressInterationWithDevice', {
-              device: device, 
-              progress: getProgress(), 
-              status: status
-            });
+            if (progress) {
+              progress(getProgress());
+            };
             setTimeout(finishHandler, 200);
             break;
           case Garmin.STATUS.finished:
-            send('endInteractionWithDevice', {device:device, status: status});
-            send(readWrite.toLowerCase() + name + 'Complete', {device:device, data:plugin.TcdXml});
             inUse = false;
+            // For safety, when we have done a write, we want to empty the buffer.
+            if (readWrite == "Write") {
+              plugin.TcdXml = "";
+            }
             // If we were passed in a callback function, we call it with either the data
             // we got from the plugin, or true (on a write).
             if (callback) {
             }
             break;
           case Garmin.STATUS.waiting:
-            send('awaitingUserInput', {});
+            // Not sure how to handle this!
             break;
           case Garmin.STATUS.idle:
             inUse = false;
             // This could happen if we were idle: perhaps the process was cancelled. That's cool, we'll just not set a Timeout.
             break;
           default:
-            send('interactionError', {message: "Received an unknown status: " + status});
+            throw new Error("Received an unknown status: " + status);
             break;
         }
       };
     };
     
     var writeHandler = function(dataType, pluginMethod, type) {
-      return function(data, filename, callback) {
+      return function(data, callback) {
         // Strip out any newlines, as they break things.
         plugin.TcdXml = data.replace(/[\n\r]+/gm, '');
-        plugin.FileName = filename;
+        plugin.FileName = "";
         baseHandler('Write', dataType, pluginMethod, type, callback);
       };
     };
         
     // See if the plugin is actually installed.
     if (!plugin.Unlock) {
-      send('interactionError', {message: "Could not find Communicator Plugin"});
+      throw new Error("Could not find Communicator Plugin");
       // We could have a default way of dealing with this...
       return;
     }
     
     // Unlock the plugin (or die trying)
-    unlock(_.union(unlockCodes, delegate.unlockCodes || []));
+    unlock(_.union(unlockCodes, options.unlockCodes || []));
     
     // wait for other initialisation to finish, then search for devices.
-    if (autostart || !delegate) {
-      setTimeout(this.findDevices, 0);
-    }
+    setTimeout(this.findDevices, 0);
   };
 })(jQuery);