Changed NDN to take getTransport and added WebSocketTransport.js which defines the default transport.  Make NDN.expressInterest take the Name and call the specified transport's expressInterest.
diff --git a/js/Helper.js b/js/Helper.js
index a77aa12..52faac9 100644
--- a/js/Helper.js
+++ b/js/Helper.js
@@ -3,6 +3,8 @@
 
 document.write('<script type="text/javascript" src="../NDN.js"></script>');
 
+document.write('<script type="text/javascript" src="../WebSocketTransport.js"></script>');
+
 document.write('<script type="text/javascript" src="../util/CCNProtocolDTags.js"></script>');
 	
 document.write('<script type="text/javascript" src="../util/CCNTime.js"></script>');
diff --git a/js/NDN.js b/js/NDN.js
index 958a267..8dde5c3 100644
--- a/js/NDN.js
+++ b/js/NDN.js
@@ -1,18 +1,30 @@
 /*
- * @author: ucla-cs
+ * @author: Meki Cherkaoui, Jeff Thompson, Wentao Shang
  * See COPYING for copyright and distribution information.
  * This class represents the top-level object for communicating with an NDN host.
  */
 
+var LOG = 3;
+
 /**
- * host is default '127.0.0.1'.
- * port is default 9695.
+ * settings is an associative array with the following defaults:
+ * {
+ *   host: 'localhost',
+ *   port: 9696,
+ *   getTransport: function() { return new WebSocketTransport(); }
+ * }
  */
-var NDN = function NDN(host, port){
-	this.host = (host || '127.0.0.1');
-	this.port = (port || 9695);
+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();    
 };
 
+
+/* Java Socket Bridge and XPCOM transport */
+
 NDN.prototype.createRoute = function(host,port){
 	this.host=host;
 	this.port=port;
@@ -26,32 +38,32 @@
 			console.log('INVALID INPUT TO GET');
 			return null;
 		}
-		
-		
+
+
 		//var array = Name.createNameArray(message);
 
 		int = new Interest(new Name(message));
 
 		int.InterestLifetime = 4200;
-		
+
 		var hex = encodeToHexInterest(int);
-		
+
 		//var result = get_java_socket_bridge().connectAndStart(ndnurl,ndnport,hex);
-		
+
 		var result = get(this.host,this.port, hex);
 
 
 		if(LOG>0)console.log('BINARY RESPONSE IS ' +result);
-		
+
 		if(result==null || result==undefined || result =="" ){
 			/*if(result[0] != '0'||result[1]!='4') {
 				if(LOG>2)console.log('INVALID ANSWER');
 			}*/
 			return null;
 		}
-		
+
 		else{
-			
+
 			co = decodeHexContentObject(result);
 
 			if(LOG>2) {
@@ -68,76 +80,76 @@
 		return null;
 
 	}
-	
+
 
 }
 
 NDN.prototype.put = function(name,content){
 	if(this.host!=null && this.port!=null){
-		
+
 		var co = this.get("/%C1.M.S.localhost/%C1.M.SRV/ccnd");
-		
+
 		if(!co || !co.signedInfo || !co.signedInfo.publisher || !co.signedInfo.publisher.publisherPublicKeyDigest){
 			alert("Cannot contact router");
-			
+
 			return null;
 		}
-		
+
 		var ccnxnodename = co.signedInfo.publisher.publisherPublicKeyDigest;
-		
+
 		name = name.trim();
-		
+
 		var fe = new ForwardingEntry('selfreg',new Name(name),null, null, 3,2147483647);
-		
+
 		var bytes = encodeForwardingEntry(fe);
-		
-		
+
+
 		var si = new SignedInfo();
 		si.setFields();
-		
+
 		var co = new ContentObject(new Name(),si,bytes,new Signature()); 
 		co.sign();
-		
+
 		var coBinary = encodeToBinaryContentObject(co);
-		
+
 		//var ccnxnodename = unescape('%E0%A0%1E%099h%F9t%0C%E7%F46%1B%AB%F5%BB%05%A4%E5Z%AC%A5%E5%8Fs%ED%DE%B8%E0%13%AA%8F');
-		
+
 		var interestName = new Name(['ccnx',ccnxnodename,'selfreg',coBinary]);
 
 		int = new Interest(interestName);
 		int.scope = 1;
-		
+
 		var hex = encodeToHexInterest(int);
 
 		console.log('GOING TO PUT INTEREST OBJECT');
-		
+
 		console.log(hex);
-		
+
 		//var result = put(this.host,this.port, hex,name);
 
-		
+
 	//if(LOG>3)console.log('received interest'); //from host'+ host +':'+port+' with name '+name);
-	
+
 	//if(LOG>3)console.log('DATA ');
-	
+
 	//if(LOG>3)console.log(result);
-	
+
 	//interest = decodeHexInterest(result);
-	
+
 	//console.log('SUCCESSFULLY PARSED INTEREST');
-	
+
 	console.log('CREATING ANSWER');
 	var si = new SignedInfo();
 	si.setFields();
-	
+
 	var answer = DataUtils.toNumbersFromString(content);
 
 	var co = new ContentObject(new Name(name),si,answer,new Signature()); 
 	co.sign();
-	
-	
+
+
 	var outputHex = encodeToHexContentObject(co);
-	
+
 	//console.log('SENDING ANSWER');
 
 	//return get_java_socket_bridge().putAnswer(outputHex,name);
@@ -171,7 +183,7 @@
         return;
     }
     
-	interest = new Interest(name);
+	var interest = new Interest(name);
     if (template != null) {
 		interest.minSuffixComponents = template.minSuffixComponents;
 		interest.maxSuffixComponents = template.maxSuffixComponents;
@@ -185,51 +197,10 @@
     else
         interest.interestLifetime = 4200;
     
-    var encoder = new BinaryXMLEncoder();
-	interest.to_ccnb(encoder);	
-	var outputData = encoder.getReducedOstream();
-    encoder = null;
-		
-    // Make a local variable so it is not masked by an inner this.
-    var ndn = this;
-	var dataListener = {
-		onReceivedData : function(data) {
-			if (data == null || data == undefined || data.length == 0)
-				dump("NDN.expressInterest: received empty data from socket.\n");
-			else {
-                var decoder = new BinaryXMLDecoder(data);	
-                var co = new ContentObject();
-                co.from_ccnb(decoder);
-                   					
-				if(LOG>2) {
-					dump("DECODED CONTENT OBJECT\n");
-					dump(co);
-					dump("\n");
-				}
-					
-                // TODO: verify the content object and set kind to UPCALL_CONTENT.
-				var result = closure.upcall(Closure.UPCALL_CONTENT_UNVERIFIED,
-                               new UpcallInfo(ndn, interest, 0, co));
-                if (result == Closure.RESULT_OK) {
-                    // success
-                }
-                else if (result == Closure.RESULT_ERR)
-                    dump("NDN.expressInterest: upcall returned RESULT_ERR.\n");
-                else if (result == Closure.RESULT_REEXPRESS)
-                    readAllFromSocket(ndn.host, ndn.port, outputData, dataListener);
-                else if (result == Closure.RESULT_VERIFY) {
-                    // TODO: force verification of content.
-                }
-                else if (result == Closure.RESULT_FETCHKEY) {
-                    // TODO: get the key in the key locator and re-call the interest
-                    //   with the key available in the local storage.
-                }
-			}
-		}
-	}
-        
-    // The application includes a source file that defines readAllFromSocket
-    //   according to the application's communication method.
-	readAllFromSocket(this.host, this.port, outputData, dataListener);
+    this.transport.expressInterest(this, interest, closure);
 };
 
+
+NDN.prototype.registerPrefix = function(name, closure, flag) {
+    return this.transport.registerPrefix(this, name, closure, flag);
+}
diff --git a/js/WebSocketTransport.js b/js/WebSocketTransport.js
new file mode 100644
index 0000000..0b9543e
--- /dev/null
+++ b/js/WebSocketTransport.js
@@ -0,0 +1,257 @@
+/* 
+ * @author: Wentao Shang
+ * See COPYING for copyright and distribution information.
+ * Implement getAsync and putAsync used by NDN using nsISocketTransportService.
+ * This is used inside Firefox XPCOM modules.
+ */
+
+var WebSocketTransport = function WebSocketTransport() {    
+	this.ws = null;
+	this.ccndid = null;
+	this.maxBufferSize = 10000;  // Currently support 10000 bytes data input, consistent with BinaryXMLEncoder
+	this.buffer = new Uint8Array(this.maxBufferSize);
+	this.structureDecoder = new BinaryXMLStructureDecoder();
+};
+
+WebSocketTransport.prototype.expressInterest = function(ndn, interest, closure) {
+	if (this.ws != null) {
+		//TODO: check local content store first
+
+        var binaryInterest = encodeToBinaryInterest(interest);
+		var bytearray = new Uint8Array(binaryInterest.length);
+		bytearray.set(binaryInterest);
+		
+		var pitEntry = new PITEntry(interest.name.getName(), closure);
+		PITTable.push(pitEntry);
+		
+		this.ws.send(bytearray.buffer);
+		console.log('ws.send() returned.');
+	}
+	else{
+		console.log('WebSocket connection is not established.');
+		return null;
+	}
+};
+
+
+var ccndIdFetcher = '/%C1.M.S.localhost/%C1.M.SRV/ccnd/KEY';
+
+WebSocketTransport.prototype.connectWebSocket = function(ndn) {
+	if (this.ws != null)
+		delete this.ws;
+	
+	this.ws = new WebSocket('ws://' + ndn.host + ':' + ndn.port);
+	console.log('ws connection created.');
+	
+	this.ws.binaryType = "arraybuffer";
+	
+	var self = this;
+	this.ws.onmessage = function(ev) {
+		var result = ev.data;
+		//console.log('RecvHandle called.');
+			
+		if(result == null || result == undefined || result == "" ) {
+			console.log('INVALID ANSWER');
+		} else if (result instanceof ArrayBuffer) {
+	        var bytearray = new Uint8Array(result);
+	        
+			if (LOG>3) console.log('BINARY RESPONSE IS ' + bytearray);
+			
+			try {
+				if (bytearray.length + self.buffer.byteOffset >= self.buffer.byteLength) {
+					console.log("NDN.ws.onmessage: buffer overflow. Accumulate received length: " + self.buffer.byteOffset 
+						+ ". Current packet length: " + bytearray.length + ".");
+					// Purge and quit.
+					delete self.structureDecoder;
+					delete self.buffer;
+					self.structureDecoder = new BinaryXMLStructureDecoder();
+					self.buffer = new Uint8Array(self.maxBufferSize);
+					return;
+				}
+				
+				/*for (var i = 0; i < bytearray.length; i++) {
+					self.buffer.push(bytearray[i]);
+				}*/
+				self.buffer.set(bytearray, self.buffer.byteOffset);
+				
+				if (!self.structureDecoder.findElementEnd(self.buffer)) {
+					// Need more data to decode
+					console.log('Incomplete packet received. Length ' + bytearray.length + '. Wait for more input.');
+					console.log('self.buffer length: ' + self.buffer.length);
+					return;
+				}
+			} catch (ex) {
+				console.log("NDN.ws.onmessage exception: " + ex);
+				return;
+			}
+			
+			var decoder = new BinaryXMLDecoder(self.buffer);
+			// Dispatch according to packet type
+			if (decoder.peekStartElement(CCNProtocolDTags.Interest)) {  // Interest packet
+				console.log('Interest packet received.');
+				
+				var interest = new Interest();
+				interest.from_ccnb(decoder);
+				if (LOG>3) console.log(interest);
+				var nameStr = escape(interest.name.getName());
+				console.log(nameStr);
+				
+				var entry = getEntryForRegisteredPrefix(nameStr);
+				if (entry != null) {
+					//console.log(entry);
+					entry.closure.upcall(Closure.UPCALL_INTEREST, new UpcallInfo(ndn, interest, 0, null));
+				}
+				
+			} else if (decoder.peekStartElement(CCNProtocolDTags.ContentObject)) {  // Content packet
+				console.log('ContentObject packet received.');
+				
+				var co = new ContentObject();
+				co.from_ccnb(decoder);
+				if (LOG>3) console.log(co);
+				nameStr = co.name.getName();
+				console.log(nameStr);
+				
+				if (self.ccndid == null && nameStr.match(ccndIdFetcher) != null) {
+					// We are in starting phase, record publisherPublicKeyDigest in self.ccndid
+					if(!co.signedInfo || !co.signedInfo.publisher 
+						|| !co.signedInfo.publisher.publisherPublicKeyDigest) {
+						console.log("Cannot contact router");
+					} else {
+						console.log('Connected to ccnd.');
+						self.ccndid = co.signedInfo.publisher.publisherPublicKeyDigest;
+						if (LOG>3) console.log(self.ccndid);
+					}
+				} else {
+					var pitEntry = getEntryForExpressedInterest(nameStr);
+					if (pitEntry != null) {
+						//console.log(pitEntry);
+						pitEntry.closure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(ndn, null, 0, co));
+					}
+				}
+			} else {
+				console.log('Incoming packet is not Interest or ContentObject. Discard now.');
+			}
+			
+			delete decoder;
+			
+			// Renew StrcutureDecoder and buffer after we process a full packet
+			delete self.structureDecoder;
+			delete self.buffer;
+			self.structureDecoder = new BinaryXMLStructureDecoder();
+			self.buffer = new Uint8Array(self.maxBufferSize);
+		}
+	}
+	
+	this.ws.onopen = function(ev) {
+		console.log(ev);
+		console.log('ws.onopen: WebSocket connection opened.');
+		console.log('ws.onopen: ReadyState: ' + this.readyState);
+
+		// Fetch ccndid now
+		interest = new Interest(new Name(ccndIdFetcher));
+		interest.InterestLifetime = 4200;
+		//var hex = encodeToHexInterest(interest);
+		var hex = encodeToBinaryInterest(interest);
+		
+		/*var bytes = new Uint8Array(hex.length / 2);
+		for (var i = 0; i < hex.length; i = i + 2) {
+	    	bytes[i / 2] = '0x' + hex.substr(i, 2);
+		}*/
+		var bytes = new Uint8Array(hex.length);
+		bytes.set(hex);
+		
+		self.ws.send(bytes.buffer);
+		console.log('ws.onopen: ws.send() returned.');
+	}
+	
+	this.ws.onerror = function(ev) {
+		console.log('ws.onerror: ReadyState: ' + this.readyState);
+		console.log(ev);
+		console.log('ws.onerror: WebSocket error: ' + ev.data);
+	}
+	
+	this.ws.onclose = function(ev) {
+		console.log('ws.onclose: WebSocket connection closed.');
+		self.ws = null;
+	}
+}
+
+
+// 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;
+}
+
+
+// For publishing data
+var CSTable = new Array();
+
+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++) {
+		if (CSTable[i].name.match(name) != null)
+			return CSTable[i];
+	}
+	return null;
+}
+
+WebSocketTransport.prototype.registerPrefix = function(ndn, name, closure, flag) {
+	if (this.ws != null) {
+		if (this.ccndid == null) {
+			console.log('ccnd node ID unkonwn. Cannot register prefix.');
+			return;
+		}
+		
+		name = name.trim();
+		
+		var fe = new ForwardingEntry('selfreg', new Name(name), null, null, 3, 2147483647);
+		var bytes = encodeForwardingEntry(fe);
+		
+		var si = new SignedInfo();
+		si.setFields();
+		
+		var co = new ContentObject(new Name(), si, bytes, new Signature()); 
+		co.sign();
+		var coBinary = encodeToBinaryContentObject(co);
+		
+		//var ccnxnodename = unescape('%00%88%E2%F4%9C%91%16%16%D6%21%8E%A0c%95%A5%A6r%11%E0%A0%82%89%A6%A9%85%AB%D6%E2%065%DB%AF');
+		var ccnxnodename = this.ccndid;
+		var interestName = new Name(['ccnx', ccnxnodename, 'selfreg', coBinary]);
+
+		var interest = new Interest(interestName);
+		interest.scope = 1;
+		//var hex = encodeToHexInterest(int);
+		var binaryInterest = encodeToBinaryInterest(interest);
+    	var bytearray = new Uint8Array(binaryInterest.length);
+		bytearray.set(binaryInterest);
+		console.log('Send Interest registration packet.');
+    	
+    	var csEntry = new CSEntry(name, closure);
+		CSTable.push(csEntry);
+    	
+    	this.ws.send(bytearray.buffer);
+		console.log('ws.send() returned.');
+		
+		return 0;
+	} else {
+		console.log('WebSocket connection is not established.');
+		return -1;
+	}
+}
+
diff --git a/js/testing/test-get-async.html b/js/testing/test-get-async.html
index fc7d84a..f93db6c 100644
--- a/js/testing/test-get-async.html
+++ b/js/testing/test-get-async.html
@@ -11,7 +11,7 @@
 

 	<script type="text/javascript">

 		var ndn = new NDN();

-        ndn.connectWebSocket();

+        ndn.transport.connectWebSocket(ndn);

         

         var AsyncGetClosure = function AsyncGetClosure() {

         	// Inherit from Closure.

@@ -35,7 +35,7 @@
 		

 		

 		function run() {

-			ndn.expressInterestWS(document.getElementById('interest').value, new AsyncGetClosure(), null);

+			ndn.expressInterest(new Name(document.getElementById('interest').value), new AsyncGetClosure());

 		}

 		

 	</script>

diff --git a/js/testing/test-put-async.html b/js/testing/test-put-async.html
index f94d2b6..448d8bf 100644
--- a/js/testing/test-put-async.html
+++ b/js/testing/test-put-async.html
@@ -12,7 +12,7 @@
 	

 	<script type="text/javascript">

 		var ndn = new NDN();

-        ndn.connectWebSocket();

+        ndn.transport.connectWebSocket(ndn);

         

         var AsyncPutClosure = function AsyncPutClosure() {

         	// Inherit from Closure.

@@ -31,7 +31,7 @@
 				console.log('AsyncPutClosure.upcall() called.');

 				var content = document.getElementById('content').value;

 				var interest = upcallInfo.interest;

-				nameStr = escape(interest.name.getName());

+				var nameStr = escape(interest.name.getName());

 				

 				var si = new SignedInfo();

 				si.setFields();

@@ -47,7 +47,7 @@
 	        		bytearray[i / 2] = '0x' + hex.substr(i, 2);

 	    		}

 	    		

-	    		upcallInfo.ndn.ws.send(bytearray.buffer);

+	    		upcallInfo.ndn.transport.ws.send(bytearray.buffer);

 	    		console.log('ws.send() finised.');

 	    		

 	    		return Closure.RESULT_INTEREST_CONSUMED;

@@ -59,7 +59,7 @@
 		function run() {

 			var contentName = document.getElementById('contentname').value;		

 			

-			result = ndn.registerPrefixWS(contentName, new AsyncPutClosure(), null);

+			var result = ndn.registerPrefix(contentName, new AsyncPutClosure());

 			

 			document.getElementById('result').innerHTML = 'Content name \'' + contentName 

 					+'\' published. Result: ' + result;