javascript/api/src/naming: Add a mounttable client library.

Also use it in the veyron_browser demo.  I've only implemented
the parts I needed.  It will be easy to add the rest.

Change-Id: I538249053601c383b1be9c23374f0f0a6ffe62c7
diff --git a/examples/veyron_browser/Makefile b/examples/veyron_browser/Makefile
index eb3c8a0..1c8bc3b 100644
--- a/examples/veyron_browser/Makefile
+++ b/examples/veyron_browser/Makefile
@@ -11,6 +11,7 @@
 VEYRON_IDENTITYD= ../../../../bin/identityd
 VEYRON_STORE= ../../../../bin/stored
 VEYRON_MOUNTTABLE_PORT= 5167
+VEYRON_MOUNTTABLE_PORT2= 5168
 VEYRON_PROXY_PORT= 5164
 VEYRON_PROXY_ADDR= 127.0.0.1:$(VEYRON_PROXY_PORT)
 VEYRON_WSPR_PORT= 5165
@@ -40,14 +41,17 @@
 
 	export VEYRON_IDENTITY=$(VEYRON_IDENTITY_PATH) ; \
 	$(VEYRON_IDENTITYD) --port=$(VEYRON_IDENTITY_PORT) & \
-	$(VEYRON_MOUNTTABLE) --address=:$(VEYRON_MOUNTTABLE_PORT) & \
-	export MOUNTTABLE_ROOT=/localhost:$(VEYRON_MOUNTTABLE_PORT)/mt ; \
+	$(VEYRON_MOUNTTABLE) --address=:$(VEYRON_MOUNTTABLE_PORT) --prefix= & \
+	export MOUNTTABLE_ROOT=/localhost:$(VEYRON_MOUNTTABLE_PORT) ; \
 	$(VEYRON_PROXY) -address=$(VEYRON_PROXY_ADDR) & \
 	$(VEYRON_WSPR) --v=3 -logtostderr=true -vproxy=$(VEYRON_PROXY_ADDR) --port $(VEYRON_WSPR_PORT) & \
+	$(VEYRON_MOUNTTABLE) --address=:$(VEYRON_MOUNTTABLE_PORT2) --name=global --prefix= & \
+	sleep 1 ; \
 	$(VEYRON_STORE) --address=:$(VEYRON_STORE_PORT) --name=global/$(USER)/store &
 
 killdaemons:
 	kill `lsof -t -i:$(VEYRON_MOUNTTABLE_PORT)`; \
+	kill `lsof -t -i:$(VEYRON_MOUNTTABLE_PORT2)`; \
 	kill `lsof -t -i:$(VEYRON_IDENTITY_PORT)`; \
 	kill `lsof -t -i:$(VEYRON_WSPR_PORT)`; \
 	kill `lsof -t -i:$(VEYRON_PROXY_PORT)`; \
diff --git a/examples/veyron_browser/browser/client/client.html b/examples/veyron_browser/browser/client/client.html
index 87cecf8..7bd1880 100644
--- a/examples/veyron_browser/browser/client/client.html
+++ b/examples/veyron_browser/browser/client/client.html
@@ -19,7 +19,7 @@
 </head>
 <body onload="init()">
   <div>
-    Enter mounttable: <input id="mt" type="text" value="/localhost:5167//mt"></input><input type="submit" value="connect" onclick="init()"></input>
+    Enter mounttable: <input id="mt" type="text" value=""></input><input type="submit" value="connect" onclick="init()"></input>
   </div>
   <div>
     Enter query: <input id="query" type="text" onkeyup="handleChange()" value="*"></input>
diff --git a/examples/veyron_browser/browser/client/client.js b/examples/veyron_browser/browser/client/client.js
index 5c8f933..4fc7c4d 100644
--- a/examples/veyron_browser/browser/client/client.js
+++ b/examples/veyron_browser/browser/client/client.js
@@ -1,81 +1,63 @@
 var veyron = new Veyron(veyronConfig);
+var mountTable = veyron.newMountTable();
 var client = veyron.newClient();
 var mp;
 
-//TODO(bprosnitz) Remove setup mount. This is used to populate the mount table.
-var setupMounts = function() {
-  return Promise.all([
-    mp.appendToPath('other').mount('/@2@tcp@[::]:111@e73f89469f6ec8252f9e0c7bbe5dd516@1@1@@/FAKE/ADDRESS'),
-    mp.appendToPath('recurse').mount(mp.addr),
-    mp.appendToPath('a/b/recurse').mount(mp.addr)]);
-}
-
-var promiseChain = null;
 var init = function() {
   var mtAddr = document.getElementById('mt').value;
-  mp = new MountPoint(client, mtAddr);
-  promiseChain = setupMounts();
-  handleChange();
+  mountTable.then(function(mt) {
+    mp = new MountPoint(client, mt, mtAddr);
+    handleChange();
+  });
 }
 
 var updateResults = function(query) {
   var results = document.getElementById('results');
   results.innerHTML = '<i>Loading results...</i>';
 
-  promiseChain.then(function() {
-    // perform query.
-    mp.glob(query).then(function(items) {
-      // generate the results list.
-      var list = document.createElement('ul');
-      for (var i in items) {
-        var item = items[i];
-        var li = document.createElement('li');
-        var a = document.createElement('a');
-
-        a.innerHTML = item.name + ' - ' + JSON.stringify(item);
-        (function(item, a) {
-          isMounttable(client, item).then(function(isMt) {
-            if (!isMt) {
-              a.className = "service";
+  // perform query.
+  mp.glob(query).then(function(items) {
+    // generate the results list.
+    var list = document.createElement('ul');
+    for (var i in items) {
+      var item = items[i];
+      var li = document.createElement('li');
+      var a = document.createElement('a');
+      
+      a.innerHTML = item.name + ' - ' + JSON.stringify(item);
+      (function(item, a) {
+        isMounttable(client, item).then(function(isMt) {
+          if (!isMt) {
+            a.className = "service";
+          } else {
+            a.href = '#';
+            a.onclick = function() {
+              mp = mp.appendToPath(item.name);
+              handleChange();
+            };
+            if (item.servers.length > 0) {
+              a.className = 'remote';
             } else {
-              a.href = '#';
-              if (item.servers.length > 0) {
-                // switch mounttable address if this is a remote server.
-                a.className = 'remote';
-                a.onclick = function() {
-                  mt = item.servers[0].server;
-                  document.getElementById('mt').value = mt;
-                  document.getElementById('query').value = '*';
-                  handleChange();
-                };
-              } else {
-                // if this is in the current mounttable, modify the query.
-                a.className = 'local';
-                a.onclick = function() {
-                  document.getElementById('query').value = item.name + '/*';
-                  handleChange();
-                };
-              }
+              a.className = 'local';
             }
-          }, function(reason) {
-            console.error('Error testing mounttable: ', reason);
-            a.className = "error";
-          });
-        })(item, a);
-
-        li.appendChild(a);
-        list.appendChild(li);
-      }
-      results.innerHTML = '';
-      results.appendChild(list);
-    }).catch(function(msg) {
-      console.error('Problem loading page: ' + msg);
-    });
-  }).catch(function(err) {
-    console.error(err);
+          }
+        }, function(reason) {
+          console.error('Error testing mounttable: ', reason);
+          a.className = "error";
+        });
+      })(item, a);
+      
+      li.appendChild(a);
+      list.appendChild(li);
+    }
+    results.innerHTML = '';
+    results.appendChild(list);
+  }).catch(function(msg) {
+    console.error('Problem loading page: ' + msg);
   });
 }
 
 var handleChange = function() {
+  document.getElementById('mt').value = mp.name;
   updateResults(document.getElementById('query').value);
 }
diff --git a/examples/veyron_browser/browser/client/mounttable.js b/examples/veyron_browser/browser/client/mounttable.js
index ad3bdbd..ad79a28 100644
--- a/examples/veyron_browser/browser/client/mounttable.js
+++ b/examples/veyron_browser/browser/client/mounttable.js
@@ -3,12 +3,30 @@
  * MountPoint handles manipulating and querying from
  * a mounttable.
  * @param {object} client A veyron client.
+ * @param {object} mountTable A veyron MountTable instance.
  * @param {...string} addressParts Parts of the address to join
  * @constructor
  */
-var MountPoint = function(client, addressParts) {
-  this.client = client
-  this.addr = Array.prototype.slice.call(arguments, 1).join('/');
+var MountPoint = function(client, mountTable, addressParts) {
+  this.client = client;
+  this.mountTable = mountTable;
+  this.name = Array.prototype.slice.call(arguments, 2).join('/');
+  this._terminalNames = null;
+}
+
+/**
+ * A helper method that returns the terminal names for this
+ * MountPoint and memoizes them.
+ * @return {Promise} A promise that resolves to a list of terminal names.
+ */
+MountPoint.prototype._getTerminalNames = function() {
+  // We resolve to a terminal name manually because veyron rpc calls
+  // wont usually resolve a name if it's to a mounttable.  We
+  // would like to interact with all kinds of servers.
+  if (!this._terminalNames) {
+    this._terminalNames = this.mountTable.resolveMaximally(this.name);
+  }
+  return this._terminalNames;
 }
 
 /**
@@ -19,8 +37,10 @@
  */
 MountPoint.prototype.appendToPath = function(toAdd) {
   var args = Array.prototype.slice.call(arguments);
-  args.unshift(this.addr);
-  return new MountPoint(this.client, args.join('/'));
+  if (this.name.length > 0) {
+    args.unshift(this.name);
+  }
+  return new MountPoint(this.client, this.mountTable, args.join('/'));
 }
 
 /**
@@ -29,8 +49,12 @@
  * @return {promise} a promise that completes when it is mounted
  */
 MountPoint.prototype.mount = function(target) {
-  return this.client.bind(this.addr).then(function(mtService) {
-    return mtService.mount(target, 0);
+  return this._getTerminalNames().then(function(terminalNames) {
+    // TODO(mattr): We should try all the names instead of just the first.
+    // Perhpas the library should allow me to pass a list of names.
+    return this.client.bind(terminalNames[0]).then(function(mtService) {
+      return mtService.mount(target, 0);
+    })
   });
 }
 
@@ -41,19 +65,23 @@
  */
 MountPoint.prototype.glob = function(expr) {
   var results = [];
-  return this.client.bind(this.addr).then(function(mtService) {
-    var promise = mtService.glob(expr);
-    var stream = promise.stream;
-
-    stream.on('readable', function() {
-      var val = stream.read();
-      if (val) {
-        results.push(val);
-      }
-    });
-
-    return promise.then(function() {
-      return results;
+  return this._getTerminalNames().then(function(terminalNames) {
+    // TODO(mattr): We should try all the names instead of just the first.
+    // Perhpas the library should allow me to pass a list of names.
+    return this.client.bind(terminalNames[0]).then(function(mtService) {
+      var promise = mtService.glob(expr);
+      var stream = promise.stream;
+      
+      stream.on('readable', function() {
+        var val = stream.read();
+        if (val) {
+          results.push(val);
+        }
+      });
+      
+      return promise.then(function() {
+        return results;
+      });
     });
   });
 };
@@ -96,4 +124,4 @@
 
 exports.MountPoint = MountPoint;
 exports.isMounttable = isMounttable;
-})(window);
\ No newline at end of file
+})(window);
diff --git a/examples/veyron_browser/browser/config.js b/examples/veyron_browser/browser/config.js
index 6147fda..041055e 100644
--- a/examples/veyron_browser/browser/config.js
+++ b/examples/veyron_browser/browser/config.js
@@ -16,4 +16,4 @@
 
   // Daemon that handles JavaScript communication with the rest of Veyron
   'proxy': 'http://localhost:5165'
-};
\ No newline at end of file
+};