feature discovery in remotestorage.js
authorggrin <ggrin@lavabit.com>
Sat, 23 Nov 2013 13:52:29 +0000 (14:52 +0100)
committerggrin <ggrin@lavabit.com>
Sat, 23 Nov 2013 13:52:29 +0000 (14:52 +0100)
src/remotestorage.js

index 2b29d78..8f79d09 100644 (file)
@@ -1,5 +1,5 @@
 (function(global) {
-  function emitUnauthorized(status){
+  function emitUnauthorized(status) {
     var args = Array.prototype.slice.call(arguments);
     if (status === 403  || status === 401) {
       this._emit('error', new RemoteStorage.Unauthorized());
         this._emit('error', new RemoteStorage.DiscoveryError("user adress doesn't contain an @"));
         return;
       }
-      this.remote.configure(userAddress);
       this._emit('connecting');
-      
+      this.remote.configure(userAddress);
       RemoteStorage.Discover(userAddress,function(href, storageApi, authURL){
         if (!href){
+
           this._emit('error', new RemoteStorage.DiscoveryError('failed to contact storage server'));
           return;
         }
     /**
      ** FEATURE DETECTION
      **/
-
-    _detectFeatures: function() {
-      // determine availability
+    _loadFeatures: function(callback) {
       var features = [
         'WireClient',
         'Dropbox',
         'Widget',
         'IndexedDB',
         'LocalStorage',
+        'InMemoryStorage',
         'Sync',
         'BaseClient'
-      ].map(function(featureName) {
-        var impl = RemoteStorage[featureName];
-        return {
-          name: featureName,
-          init: (impl && impl._rs_init),
-          supported: impl && (impl._rs_supported ? impl._rs_supported() : true),
-          cleanup: ( impl && impl._rs_cleanup )
-        };
-      }).filter(function(feature) {
-        var supported = !! (feature.init && feature.supported);
-        this.log("[FEATURE " + feature.name + "] " + (supported ? '' : 'not ') + 'supported.');
-        return supported;
-      }.bind(this));
-
-      features.forEach(function(feature) {
-        if (feature.name === 'IndexedDB') {
-          features.local = RemoteStorage.IndexedDB;
-        } else if (feature.name === 'LocalStorage' && ! features.local) {
-          features.local = RemoteStorage.LocalStorage;
-        }
-      });
-      features.caching = !!RemoteStorage.Caching;
-      features.sync = !!RemoteStorage.Sync;
-
-      this.features = features;
-
-      return features;
-    },
-
-    _loadFeatures: function(callback) {
-      var features = this._detectFeatures();
+      ];
+      var theFeatures = [];
       var n = features.length, i = 0;
       var self = this;
+      function doneNow() {
+        i++;
+        if(i == n)
+          setTimeout(function() {
+            theFeatures.caching = !!RemoteStorage.Caching;
+            theFeatures.sync = !!RemoteStorage.Sync;
+            [
+              'IndexedDB',
+              'LocalStorage',
+              'InMemoryStorage'
+            ].some(function(cachingLayer) {
+              if ( theFeatures.some( function(feature) {
+                return feature.name === cachingLayer;
+              } ) 
+                 ) {
+                theFeatures.local = RemoteStorage[cachingLayer];
+                return true;
+              }
+            });
+            self.features = theFeatures;
+            callback.apply(self, [theFeatures]);
+          }, 0);
+      }
       function featureDoneCb(name) {
         return function() {
-          i++;
-          self.log("[FEATURE " + name + "] initialized. (" + i + "/" + n + ")");
-          if (i === n) {
-            setTimeout(function() {
-              callback.apply(self, [features]);
-            }, 0);
+          self.log("[FEATURE " + name + "] initialized. (" + (i+1) + "/" + n + ")");
+          theFeatures.push( {
+            name : name,
+            init :  RemoteStorage[name]._rs_init,
+            supported : true,
+            cleanup : RemoteStorage[name]._rs_cleanup
+          } );
+          doneNow();
+        };
+      }
+      function featureFailedCb(name) {
+        return function(err) {
+          self.log("[FEATURE "+name+"] initialization failed ( "+err+")");
+          //self.features
+          doneNow();
+        };
+      }
+      function featureSupportedCb(name) {
+        return function( success ) {
+          self.log("[FEATURE "+name+"]" + success ? "":" not"+" supported");
+          if(!success) {
+            doneNow();
           }
         };
       }
-      features.forEach(function(feature) {
-        self.log("[FEATURE " + feature.name + "] initializing...");
-        var initResult = feature.init(self);
-        var cb = featureDoneCb(feature.name);
-        if (typeof(initResult) === 'object' && typeof(initResult.then) === 'function') {
-          initResult.then(cb);
+      features.forEach(function(featureName) {
+        self.log("[FEATURE " + featureName + "] initializing...");
+        var impl = RemoteStorage[featureName];
+        var cb = featureDoneCb(featureName);
+        var failedCb = featureFailedCb(featureName);
+        var supportedCb = featureSupportedCb(featureName);
+        if( impl && (
+            ( impl._rs_supported && impl._rs_supported() ) || 
+            ( !impl._rs_supported )) ) {
+          supportedCb(true);
+          var initResult;
+          try {
+            initResult = impl._rs_init(self);
+          } catch(e) {
+            failedCb(e);
+            return;
+          }
+          if(typeof(initResult) == 'object' && typeof(initResult.then) == 'function') {
+            initResult.then(cb,failedCb);
+          } else {
+            cb();
+          }
         } else {
-          cb();
+          supportedCb(false);
         }
       });
-      if (features.length === 0) {
-        self.log("[NO FEATURES DETECTED] done");
-        callback.apply(self, [[]]);
-      }
     },
 
     /**
     _dispatchEvent: function(eventName, event) {
       for(var path in this._pathHandlers[eventName]) {
         var pl = path.length;
+        var self = this;
         this._pathHandlers[eventName][path].forEach(function(handler) {
           if (event.path.substr(0, pl) === path) {
             var ev = {};
               handler(ev);
             } catch(e) {
               console.error("'change' handler failed: ", e, e.stack);
-              this._emit('error', e);
+              self._emit('error', e);
             }
           }
-        }.bind(this));
+        });
       }
     }
   };