For XPCOM (the Firefox add-on), connect to a random testbed host.
diff --git a/js/tools/build/ndn-js-uncomp.js b/js/tools/build/ndn-js-uncomp.js
index c2f610c..73d3862 100644
--- a/js/tools/build/ndn-js-uncomp.js
+++ b/js/tools/build/ndn-js-uncomp.js
@@ -75,19 +75,25 @@
 /**
  * settings is an associative array with the following defaults:
  * {
- *   host: 'localhost',
- *   port: 9696,
  *   getTransport: function() { return new WebSocketTransport(); }
+ *   getHostAndPort: transport.defaultGetHostAndPort,
+ *   host: 'localhost', // If null, use getHostAndPort when connecting.
+ *   port: 9696,
  *   onopen: function() { if (LOG > 3) console.log("NDN connection established."); }
  *   onclose: function() { if (LOG > 3) console.log("NDN connection closed."); }
  * }
+ * 
+ * getHostAndPort is a function, on each call it returns a new { host: host, port: port } or
+ *   null if there are no more hosts.
  */
 var NDN = function NDN(settings) {
     settings = (settings || {});
-	this.host = (settings.host || "localhost");
-	this.port = (settings.port || 9696);
     var getTransport = (settings.getTransport || function() { return new WebSocketTransport(); });
     this.transport = getTransport();
+    this.getHostAndPort = (settings.getHostAndPort || this.transport.defaultGetHostAndPort);
+	this.host = (settings.host !== undefined ? settings.host : 'localhost');
+    dump("this.host " + this.host + "\n");
+	this.port = (settings.port || 9696);
     this.readyStatus = NDN.UNOPEN;
     // Event handler
     this.onopen = (settings.onopen || function() { if (LOG > 3) console.log("NDN connection established."); });
@@ -98,13 +104,55 @@
 NDN.OPENED = 1;  // connection to ccnd opened
 NDN.CLOSED = 2;  // connection to ccnd closed
 
-/* Java Socket Bridge and XPCOM transport */
+NDN.ccndIdFetcher = '/%C1.M.S.localhost/%C1.M.SRV/ccnd/KEY';
 
 NDN.prototype.createRoute = function(host,port){
 	this.host=host;
 	this.port=port;
 }
 
+// For fetching data
+NDN.PITTable = new Array();
+
+var PITEntry = function PITEntry(interest, closure) {
+	this.interest = interest;  // Interest
+	this.closure = closure;    // Closure
+};
+
+// Return the longest entry from NDN.PITTable that matches name.
+NDN.getEntryForExpressedInterest = function(/*Name*/ name) {
+    // TODO: handle multiple matches?  Maybe not from registerPrefix because multiple ContentObject
+    //   could be sent for one Interest?
+    var result = null;
+    
+	for (var i = 0; i < NDN.PITTable.length; i++) {
+		if (NDN.PITTable[i].interest.matches_name(name)) {
+            if (result == null || 
+                NDN.PITTable[i].interest.name.components.length > result.interest.name.components.length)
+                result = NDN.PITTable[i];
+        }
+	}
+    
+	return result;
+};
+
+/*
+ * Return a function that selects a host at random from hostList and returns { host: host, port: port }.
+ * If no more hosts remain, return null.
+ */
+NDN.makeShuffledGetHostAndPort = function(hostList, port) {
+    // Make a copy.
+    hostList = hostList.slice(0, hostList.length);
+    DataUtils.shuffle(hostList);
+
+    return function() {
+        if (hostList.length == 0)
+            return null;
+        
+        return { host: hostList.splice(0, 1)[0], port: port };
+    };
+};
+
 /** Encode name as an Interest. If template is not null, use its attributes.
  *  Send the interest to host:port, read the entire response and call
  *  closure.upcall(Closure.UPCALL_CONTENT (or Closure.UPCALL_CONTENT_UNVERIFIED),
@@ -117,11 +165,6 @@
         closure,
         // Interest
         template) {
-	if (this.host == null || this.port == null) {
-		dump('ERROR host OR port NOT SET\n');
-        return;
-    }
-    
 	var interest = new Interest(name);
     if (template != null) {
 		interest.minSuffixComponents = template.minSuffixComponents;
@@ -135,14 +178,83 @@
     }
     else
         interest.interestLifetime = 4.0;   // default interest timeout value in seconds.
-    
-    this.transport.expressInterest(this, interest, closure);
-};
 
+	if (this.host == null || this.port == null) {
+        if (this.getHostAndPort == null)
+            console.log('ERROR: host OR port NOT SET');
+        else
+            this.connectAndExpressInterest(interest, closure);
+    }
+    else
+        this.transport.expressInterest(this, interest, closure);
+};
 
 NDN.prototype.registerPrefix = function(name, closure, flag) {
     return this.transport.registerPrefix(this, name, closure, flag);
 }
+
+/*
+ * Assume this.getHostAndPort is not null.  This is called when this.host is null or its host
+ *   is not alive.  Get a host and port, connect, then express callerInterest with callerClosure.
+ */
+NDN.prototype.connectAndExpressInterest = function(callerInterest, callerClosure) {
+    var hostAndPort = this.getHostAndPort();
+    if (hostAndPort == null) {
+        console.log('ERROR: No more hosts from getHostAndPort');
+        this.host = null;
+        return;
+    }
+
+    if (hostAndPort.host == this.host && hostAndPort.port == this.port) {
+        console.log('ERROR: The host returned by getHostAndPort is not alive: ' + 
+                this.host + ":" + this.port);
+        return;
+    }
+        
+    this.host = hostAndPort.host;
+    this.port = hostAndPort.port;   
+    console.log("Trying host from getHostAndPort: " + this.host);
+    
+    // Fetch the ccndId.
+    var interest = new Interest(new Name(NDN.ccndIdFetcher));
+	interest.interestLifetime = 4.0; // seconds    
+
+    var thisNDN = this;
+	var timerID = setTimeout(function() {
+        console.log("Timeout waiting for host " + thisNDN.host);
+        // Try again.
+        thisNDN.connectAndExpressInterest(callerInterest, callerClosure);
+	}, 3000);
+  
+    this.transport.expressInterest
+        (this, interest, new NDN.ConnectClosure(this, callerInterest, callerClosure, timerID));
+}
+
+NDN.ConnectClosure = function ConnectClosure(ndn, callerInterest, callerClosure, timerID) {
+    // Inherit from Closure.
+    Closure.call(this);
+    
+    this.ndn = ndn;
+    this.callerInterest = callerInterest;
+    this.callerClosure = callerClosure;
+    this.timerID = timerID;
+};
+
+NDN.ConnectClosure.prototype.upcall = function(kind, upcallInfo) {
+    if (!(kind == Closure.UPCALL_CONTENT ||
+          kind == Closure.UPCALL_CONTENT_UNVERIFIED ||
+          kind == Closure.UPCALL_INTEREST))
+        // The upcall is not for us.
+        return Closure.RESULT_ERR;
+        
+    // The host is alive, so cancel the timeout and issue the caller's interest.
+    clearTimeout(this.timerID);
+    console.log(this.ndn.host + ": Host is alive. Fetching callerInterest.");
+    this.ndn.transport.expressInterest(this.ndn, this.callerInterest, this.callerClosure);
+
+    return Closure.RESULT_OK;
+};
+
 /** 
  * @author: Wentao Shang
  * See COPYING for copyright and distribution information.
@@ -155,10 +267,12 @@
 	this.buffer = new Uint8Array(this.maxBufferSize);
 	this.bufferOffset = 0;
 	this.structureDecoder = new BinaryXMLStructureDecoder();
+    this.defaultGetHostAndPort = NDN.makeShuffledGetHostAndPort
+        (["A.ws.ndn.ucla.edu", "B.ws.ndn.ucla.edu", "C.ws.ndn.ucla.edu", "D.ws.ndn.ucla.edu", 
+          "E.ws.ndn.ucla.edu"],
+         9696);
 };
 
-var ccndIdFetcher = '/%C1.M.S.localhost/%C1.M.SRV/ccnd/KEY';
-
 WebSocketTransport.prototype.connectWebSocket = function(ndn) {
 	if (this.ws != null)
 		delete this.ws;
@@ -239,7 +353,7 @@
 				nameStr = co.name.getName();
 				if (LOG > 3) console.log(nameStr);
 				
-				if (self.ccndid == null && nameStr.match(ccndIdFetcher) != null) {
+				if (self.ccndid == null && nameStr.match(NDN.ccndIdFetcher) != null) {
 					// We are in starting phase, record publisherPublicKeyDigest in self.ccndid
 					if(!co.signedInfo || !co.signedInfo.publisher 
 						|| !co.signedInfo.publisher.publisherPublicKeyDigest) {
@@ -260,7 +374,7 @@
 						//console.log("NDN.onopen event fired.");
 					}
 				} else {
-					var pitEntry = getEntryForExpressedInterest(nameStr);
+					var pitEntry = NDN.getEntryForExpressedInterest(co.name);
 					if (pitEntry != null) {
 						//console.log(pitEntry);
 						
@@ -269,9 +383,10 @@
 						//console.log("Clear interest timer");
 						//console.log(pitEntry.closure.timerID);
 						
-						// Remove PIT entry from PITTable
-						var index = PITTable.indexOf(pitEntry);
-						PITTable.splice(index, 1);
+						// Remove PIT entry from NDN.PITTable
+						var index = NDN.PITTable.indexOf(pitEntry);
+						if (index >= 0)
+                            NDN.PITTable.splice(index, 1);
 						
 						// Raise callback
 						pitEntry.closure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(ndn, null, 0, co));
@@ -298,7 +413,7 @@
 		if (LOG > 3) console.log('ws.onopen: ReadyState: ' + this.readyState);
 
 		// Fetch ccndid now
-		var interest = new Interest(new Name(ccndIdFetcher));
+		var interest = new Interest(new Name(NDN.ccndIdFetcher));
 		interest.interestLifetime = 4.0; // seconds
 		var subarray = encodeToBinaryInterest(interest);
 		
@@ -323,25 +438,7 @@
 		ndn.onclose();
 		//console.log("NDN.onclose event fired.");
 	}
-}
-
-
-// For fetching data
-var PITTable = new Array();
-
-var PITEntry = function PITEntry(interest, closure) {
-	this.interest = interest;  // String
-	this.closure = closure;    // Closure
-}
-
-function getEntryForExpressedInterest(name) {
-	for (var i = 0; i < PITTable.length; i++) {
-		if (name.match(PITTable[i].interest) != null)
-			return PITTable[i];
-			// TODO: handle multiple matching prefixes
-	}
-	return null;
-}
+};
 
 WebSocketTransport.prototype.expressInterest = function(ndn, interest, closure) {
 	if (this.ws != null) {
@@ -351,8 +448,8 @@
 		var bytearray = new Uint8Array(binaryInterest.length);
 		bytearray.set(binaryInterest);
 		
-		var pitEntry = new PITEntry(interest.name.getName(), closure);
-		PITTable.push(pitEntry);
+		var pitEntry = new PITEntry(interest, closure);
+		NDN.PITTable.push(pitEntry);
 		
 		this.ws.send(bytearray.buffer);
 		if (LOG > 3) console.log('ws.send() returned.');
@@ -361,11 +458,12 @@
 		closure.timerID = setTimeout(function() {
 			if (LOG > 3) console.log("Interest time out.");
 			
-			// Remove PIT entry from PITTable
-			var index = PITTable.indexOf(pitEntry);
-			//console.log(PITTable);
-			PITTable.splice(index, 1);
-			//console.log(PITTable);
+			// Remove PIT entry from NDN.PITTable
+			var index = NDN.PITTable.indexOf(pitEntry);
+			//console.log(NDN.PITTable);
+			if (index >= 0) 
+                NDN.PITTable.splice(index, 1);
+			//console.log(NDN.PITTable);
 			// Raise closure callback
 			closure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT, new UpcallInfo(ndn, interest, 0, null));
 		}, interest.interestLifetime * 1000);  // convert interestLifetime from seconds to ms.
@@ -382,7 +480,7 @@
 var CSEntry = function CSEntry(name, closure) {
 	this.name = name;        // String
 	this.closure = closure;  // Closure
-}
+};
 
 function getEntryForRegisteredPrefix(name) {
 	for (var i = 0; i < CSTable.length; i++) {
@@ -437,7 +535,7 @@
 		console.log('WebSocket connection is not established.');
 		return -1;
 	}
-}
+};
 
 /**
  * @author: Meki Cheraoui
@@ -900,6 +998,22 @@
 }
 
 /*
+ * Return true if this Name has the same components as name.
+ */
+Name.prototype.equalsName = function(name) {
+    if (this.components.length != name.components.length)
+        return false;
+    
+    // Start from the last component because they are more likely to differ.
+    for (var i = this.components.length - 1; i >= 0; --i) {
+        if (!DataUtils.arraysEqual(this.components[i], name.components[i]))
+            return false;
+    }
+    
+    return true;
+}
+
+/*
  * Find the last component in name that has a ContentDigest and return the digest value as Uint8Array, 
  *   or null if not found.
  * A ContentDigest component is Name.ContentDigestPrefix + 32 bytes + Name.ContentDigestSuffix.
@@ -4086,6 +4200,19 @@
     }
     return result.subarray(size - i, size);
 };
+
+/*
+ * Modify array to randomly shuffle the elements.
+ */
+DataUtils.shuffle = function(array) {
+    for (var i = array.length - 1; i >= 1; --i) {
+        // j is from 0 to i.
+        var j = Math.floor(Math.random() * (i + 1));
+        var temp = array[i];
+        array[i] = array[j];
+        array[j] = temp;
+    }
+}
 /**
  * This file contains utilities to help encode and decode NDN objects.
  * author: Meki Cheraoui