Using globally-resolvable Syncbase instances

     Adding client option for syncbase launch; admin mounts to global MT
     Removing localhost mounttable for admin syncbase instances
     Reintroduced hoverintent

Change-Id: I001e1088b765762d5dc5d062275ce1eed3deae9f
diff --git a/package.json b/package.json
index 4166d93..ef22340 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
     "es6-promisify": "^2.0.0",
     "es6-shim": "^0.33.0",
     "global": "^4.3.0",
+    "hoverintent-jqplugin": "^0.1.2",
     "htmlencode": "^0.0.4",
     "jquery": "^2.1.4",
     "lodash": "^3.10.1",
diff --git a/src/syncgroup-manager.js b/src/syncgroup-manager.js
index 35ce0c6..e471537 100644
--- a/src/syncgroup-manager.js
+++ b/src/syncgroup-manager.js
@@ -14,25 +14,23 @@
     createSyncGroup: function(name, prefixes) {
       var self = this;
 
-      return this.prereq.then(function() {
-        var sg = self.syncbaseWrapper.syncGroup(self.sgAdmin, name);
+      var sg = self.syncbaseWrapper.syncGroup(self.sgAdmin, name);
 
-        var mgmt = vanadium.naming.join(self.mountNames.app, 'sgmt');
-        var spec = sg.buildSpec(prefixes, [mgmt]);
+      var mgmt = vanadium.naming.join(self.mountNames.app, 'sgmt');
+      var spec = sg.buildSpec(prefixes, [mgmt]);
 
-        /* TODO(rosswang): Right now, duplicate Syncbase creates on
-         * different Syncbase instances results in siloed SyncGroups.
-         * Revisit this logic once it merges properly. */
-        return sg.joinOrCreate(spec).then(function() {
-          // TODO(rosswang): this is a hack to make the syncgroup joinable
-          return self.vanadiumWrapper.setPermissions(mgmt, new Map([
-            ['Admin', {in: ['...']}],
-            ['Read', {in: ['...']}],
-            ['Resolve', {in: ['...']}]
-          ]));
-        }).then(function() {
-          return sg;
-        });
+      /* TODO(rosswang): Right now, duplicate Syncbase creates on
+       * different Syncbase instances results in siloed SyncGroups.
+       * Revisit this logic once it merges properly. */
+      return sg.joinOrCreate(spec).then(function() {
+        // TODO(rosswang): this is a hack to make the SyncGroup joinable
+        return self.vanadiumWrapper.setPermissions(mgmt, new Map([
+          ['Admin', {in: ['...']}],
+          ['Read', {in: ['...']}],
+          ['Resolve', {in: ['...']}]
+        ]));
+      }).then(function() {
+        return sg;
       });
     },
 
@@ -49,8 +47,6 @@
 
   privates: {
     advertise: function() {
-      var self = this;
-
       var basicPerms = new Map([
         ['Admin', {in: [this.identity.account]}],
         ['Read', {in: ['...']}],
@@ -62,11 +58,7 @@
          * mount names on ns.dev.v.io don't yet default to Resolve in [...].
          */
         this.vanadiumWrapper.setPermissions(this.mountNames.user, basicPerms),
-        this.vanadiumWrapper.setPermissions(this.mountNames.app, basicPerms),
-        this.prereq.then(function() {
-          // TODO(rosswang): This seems wrong too.
-          return self.vanadiumWrapper.setPermissions(self.sgAdmin, basicPerms);
-        })
+        this.vanadiumWrapper.setPermissions(this.mountNames.app, basicPerms)
       ]);
     }
   },
@@ -85,12 +77,6 @@
 
     this.sgAdmin = vanadium.naming.join(mountNames.app, 'sgadmin');
 
-    /* TODO(rosswang): Once Vanadium supports global SyncGroup admin
-     * creation, remove this. For now, use the first local Syncbase
-     * instance to administrate. */
-    this.prereq = vanadiumWrapper.mount(this.sgAdmin, syncbaseWrapper.mountName,
-      vanadiumWrapper.multiMount.FAIL);
-
     this.advertise().catch(this.onError);
   }
 });
diff --git a/src/travel.js b/src/travel.js
index 197d3e1..7dc275d 100644
--- a/src/travel.js
+++ b/src/travel.js
@@ -4,6 +4,7 @@
 
 require('es6-shim');
 
+var queryString = require('query-string');
 var raf = require('raf');
 
 var $ = require('./util/jquery');
@@ -424,10 +425,16 @@
         };
       });
 
+    var sbName = opts.syncbase ||
+      queryString.parse(location.search).syncbase || 4000;
+    if ($.isNumeric(sbName)) {
+      sbName = '/localhost:' + sbName;
+    }
+
     var sync = this.sync = new TravelSync(vanadiumStartup, {
       maps: maps,
       placesService: map.createPlacesService()
-    });
+    }, sbName);
     sync.bindDestinations(destinations);
 
     this.info(strings['Connecting...'], sync.startup
@@ -515,8 +522,8 @@
     var $toggleTimeline = this.$toggleTimeline = $('<div>')
       .addClass('toggle-timeline no-select collapsed')
       .text(strings['Timeline'])
-      .mouseenter(this.showTimeline)
       .click(this.collapseTimeline);
+    $toggleTimeline.hoverintent(this.showTimeline, $.noop);
 
     map.addControls(maps.ControlPosition.TOP_CENTER, messages.$);
     map.addControls(maps.ControlPosition.LEFT_TOP, $miniPanel);
diff --git a/src/travelsync.js b/src/travelsync.js
index 4c87344..d2d363e 100644
--- a/src/travelsync.js
+++ b/src/travelsync.js
@@ -5,7 +5,6 @@
 require('es6-shim');
 
 var _ = require('lodash');
-var queryString = require('query-string');
 var uuid = require('uuid');
 var vanadium = require('vanadium');
 
@@ -637,14 +636,9 @@
       var self = this;
       var vanadiumWrapper = args.vanadiumWrapper;
 
-      var sbName = queryString.parse(location.search).syncbase || 4000;
-      if ($.isNumeric(sbName)) {
-        sbName = '/localhost:' + sbName;
-      }
-
       this.status.syncbase = 'starting';
       return vanadiumWrapper
-        .syncbase(sbName)
+        .syncbase(this.syncbaseName)
         .then(function(syncbase) {
           syncbase.onError.add(self.onError);
           syncbase.onUpdate.add(self.processUpdates);
@@ -713,11 +707,13 @@
    * @mapsDependencies an object with the following keys:
    *  maps
    *  placesService
+   * @syncbaseName name of the local SyncBase endpoint.
    */
-  init: function(prereqs, mapsDependencies) {
+  init: function(prereqs, mapsDependencies, syncbaseName) {
     var self = this;
 
     this.mapsDeps = mapsDependencies;
+    this.syncbaseName = syncbaseName;
 
     this.tripStatus = {};
     this.messages = {};
diff --git a/src/util/jquery.js b/src/util/jquery.js
index 4f35251..55429f5 100644
--- a/src/util/jquery.js
+++ b/src/util/jquery.js
@@ -14,4 +14,6 @@
   $ = jq(window);
 }
 
+require('hoverintent-jqplugin')($);
+
 module.exports = $;
\ No newline at end of file
diff --git a/test/travel.js b/test/travel.js
index 48a936d..ab8bd84 100644
--- a/test/travel.js
+++ b/test/travel.js
@@ -23,7 +23,8 @@
   new Travel({
     maps: mockMaps,
     vanadiumWrapper: mockVanadiumWrapper,
-    domRoot: root
+    domRoot: root,
+    syncbase: 'dummy'
   });
   /* jshint +W031 */
 
@@ -36,7 +37,8 @@
 test('messages', function(t) {
   var travel = new Travel({
     maps: mockMaps,
-    vanadiumWrapper: mockVanadiumWrapper
+    vanadiumWrapper: mockVanadiumWrapper,
+    syncbase: 'dummy'
   });
 
   var $messages = $('.messages ul');
diff --git a/tools/start_services.sh b/tools/start_services.sh
index b8e74a5..7803c80 100644
--- a/tools/start_services.sh
+++ b/tools/start_services.sh
@@ -29,19 +29,35 @@
   local -r TMP=tmp
   local -r CREDS=./tmp/creds/${creds-}
   local -r PORT=${port-4000}
-  local -r MOUNTTABLED_ADDR=":$((PORT+1))"
   local -r SYNCBASED_ADDR=":$((PORT))"
   local -r BLESSINGS=`principal dump --v23.credentials=${CREDS} -s=true`
+
+  if [ ${client-} ]; then
+    local -r MOUNTTABLED_ADDR=":$((PORT+1))"
+    mounttabled \
+      --v23.tcp.address=${MOUNTTABLED_ADDR} \
+      --v23.credentials=${CREDS} &
+
+    local -r SG_NAME=syncbase
+    local -r NS_ROOT=/${MOUNTTABLED_ADDR}
+  else
+    local -r RE="dev\.v\.io/u/(.*)"
+    if [[ ${BLESSINGS} =~ ${RE} ]]; then
+      local -r V_USER=${BASH_REMATCH[1]}
+    fi
+    local -r SG_NAME=users/${V_USER}/travel/sgadmin
+    local -r NS_ROOT=/ns.dev.v.io:8101
+  fi
+
+  echo "Starting syncbased on ${SYNCBASED_ADDR} mounted at ${NS_ROOT}/${SG_NAME}"
+
   mkdir -p $TMP
-  mounttabled \
-    --v23.tcp.address=${MOUNTTABLED_ADDR} \
-    --v23.credentials=${CREDS} &
   ./bin/syncbased \
     --v=5 \
     --alsologtostderr=false \
     --root-dir=${TMP}/syncbase_${PORT} \
-    --name=syncbase \
-    --v23.namespace.root=/${MOUNTTABLED_ADDR} \
+    --name=${SG_NAME} \
+    --v23.namespace.root=${NS_ROOT} \
     --v23.tcp.address=${SYNCBASED_ADDR} \
     --v23.credentials=${CREDS} \
     --v23.permissions.literal="{\"Admin\":{\"In\":[\"${BLESSINGS}\"]},\"Write\":{\"In\":[\"${BLESSINGS}\"]},\"Read\":{\"In\":[\"${BLESSINGS}\"]},\"Resolve\":{\"In\":[\"${BLESSINGS}\"]},\"Debug\":{\"In\":[\"...\"]}}"