Steps for merging WebSocket and Xpcom code: Moved the processing of Interest and ContentObject from WebSocketTransport onmessages to NDN.onReceivedData.
Added method WebSocketTransport.send and use in expressInterest, etc.
Moved CSEntry definition and CSTable to NDN.js.
Moved registerPrefix from WebSocketTransport to NDN.
diff --git a/js/NDN.js b/js/NDN.js
index 0410425..41de1d7 100644
--- a/js/NDN.js
+++ b/js/NDN.js
@@ -31,6 +31,7 @@
     // Event handler
     this.onopen = (settings.onopen || function() { if (LOG > 3) console.log("NDN connection established."); });
     this.onclose = (settings.onclose || function() { if (LOG > 3) console.log("NDN connection closed."); });
+	this.ccndid = null;
 };
 
 NDN.UNOPEN = 0;  // created but not opened yet
@@ -101,6 +102,22 @@
 	return result;
 };
 
+// For publishing data
+NDN.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 < NDN.CSTable.length; i++) {
+		if (NDN.CSTable[i].name.match(name) != null)
+			return NDN.CSTable[i];
+	}
+	return null;
+}
+
 /*
  * Return a function that selects a host at random from hostList and returns { host: host, port: port }.
  * If no more hosts remain, return null.
@@ -155,7 +172,219 @@
 };
 
 NDN.prototype.registerPrefix = function(name, closure, flag) {
-    return this.transport.registerPrefix(this, name, closure, flag);
+    if (this.readyStatus != NDN.OPENED) {
+		console.log('Connection is not established.');
+        return -1;
+    }
+
+	if (this.ccndid == null) {
+		console.log('ccnd node ID unkonwn. Cannot register prefix.');
+		return -1;
+	}
+		
+	var fe = new ForwardingEntry('selfreg', 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 nodename = 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 nodename = this.ccndid;
+	var interestName = new Name(['ccnx', nodename, 'selfreg', coBinary]);
+
+	var interest = new Interest(interestName);
+	interest.scope = 1;
+	if (LOG > 3) console.log('Send Interest registration packet.');
+    	
+    var csEntry = new CSEntry(name.getName(), closure);
+	NDN.CSTable.push(csEntry);
+    
+    this.transport.send(encodeToBinaryInterest(interest));
+		
+	return 0;
+};
+
+/*
+ * This is called when an entire binary XML element is received, such as a ContentObject or Interest.
+ * Look up in the PITTable and call the closure callback.
+ */
+NDN.prototype.onReceivedElement = function(element) {
+	var decoder = new BinaryXMLDecoder(element);
+	// Dispatch according to packet type
+	if (decoder.peekStartElement(CCNProtocolDTags.Interest)) {  // Interest packet
+		if (LOG > 3) 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());
+		if (LOG > 3) console.log(nameStr);
+				
+		var entry = getEntryForRegisteredPrefix(nameStr);
+		if (entry != null) {
+			//console.log(entry);
+			var info = new UpcallInfo(this, interest, 0, null);
+			var ret = entry.closure.upcall(Closure.UPCALL_INTEREST, info);
+			if (ret == Closure.RESULT_INTEREST_CONSUMED && info.contentObject != null) 
+				this.transport.send(encodeToBinaryContentObject(info.contentObject));
+		}				
+	} else if (decoder.peekStartElement(CCNProtocolDTags.ContentObject)) {  // Content packet
+		if (LOG > 3) console.log('ContentObject packet received.');
+				
+		var co = new ContentObject();
+		co.from_ccnb(decoder);
+				
+		if (this.ccndid == null && NDN.ccndIdFetcher.match(co.name)) {
+			// We are in starting phase, record publisherPublicKeyDigest in ccndid
+			if(!co.signedInfo || !co.signedInfo.publisher 
+				|| !co.signedInfo.publisher.publisherPublicKeyDigest) {
+				console.log("Cannot contact router, close NDN now.");
+						
+				// Close NDN if we fail to connect to a ccn router
+				this.readyStatus = NDN.CLOSED;
+				this.onclose();
+				//console.log("NDN.onclose event fired.");
+			} else {
+				//console.log('Connected to ccnd.');
+				this.ccndid = co.signedInfo.publisher.publisherPublicKeyDigest;
+				if (LOG>3) console.log(ndn.ccndid);
+						
+				// Call NDN.onopen after success
+				this.readyStatus = NDN.OPENED;
+				this.onopen();
+				//console.log("NDN.onopen event fired.");
+			}
+		} else {
+			var pitEntry = NDN.getEntryForExpressedInterest(co.name);
+			if (pitEntry != null) {
+				//console.log(pitEntry);
+				// Remove PIT entry from NDN.PITTable
+				var index = NDN.PITTable.indexOf(pitEntry);
+				if (index >= 0)
+					NDN.PITTable.splice(index, 1);
+						
+				var currentClosure = pitEntry.closure;
+						
+				// Cancel interest timer
+				clearTimeout(pitEntry.timerID);
+				//console.log("Clear interest timer");
+				//console.log(currentClosure.timerID);
+						
+				// Key verification
+						
+				// Recursive key fetching & verification closure
+				var KeyFetchClosure = function KeyFetchClosure(content, closure, key, sig, wit) {
+					this.contentObject = content;  // unverified content object
+					this.closure = closure;  // closure corresponding to the contentObject
+					this.keyName = key;  // name of current key to be fetched
+					this.sigHex = sig;  // hex signature string to be verified
+					this.witness = wit;
+						
+					Closure.call(this);
+				};
+						
+                var thisNdn = this;
+				KeyFetchClosure.prototype.upcall = function(kind, upcallInfo) {
+					if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
+						console.log("In KeyFetchClosure.upcall: interest time out.");
+						console.log(this.keyName.contentName.getName());
+					} else if (kind == Closure.UPCALL_CONTENT) {
+						//console.log("In KeyFetchClosure.upcall: signature verification passed");
+								
+						var rsakey = decodeSubjectPublicKeyInfo(upcallInfo.contentObject.content);
+						var verified = rsakey.verifyByteArray(this.contentObject.rawSignatureData, this.witness, this.sigHex);
+								
+						var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
+						//console.log("raise encapsulated closure");
+						this.closure.upcall(flag, new UpcallInfo(thisNdn, null, 0, this.contentObject));
+								
+						// Store key in cache
+						var keyEntry = new KeyStoreEntry(keylocator.keyName, rsakey, new Date().getTime());
+						NDN.addKeyEntry(keyEntry);
+						//console.log(NDN.KeyStore);
+					} else if (kind == Closure.UPCALL_CONTENT_BAD) {
+						console.log("In KeyFetchClosure.upcall: signature verification failed");
+					}
+				};
+						
+				if (co.signedInfo && co.signedInfo.locator && co.signature) {
+					if (LOG > 3) console.log("Key verification...");
+					var sigHex = DataUtils.toHex(co.signature.signature).toLowerCase();
+							
+					var wit = null;
+					if (co.signature.Witness != null) {
+						wit = new Witness();
+						wit.decode(co.signature.Witness);
+					}
+							
+					var keylocator = co.signedInfo.locator;
+					if (keylocator.type == KeyLocatorType.KEYNAME) {
+						if (LOG > 3) console.log("KeyLocator contains KEYNAME");
+						//var keyname = keylocator.keyName.contentName.getName();
+						//console.log(nameStr);
+						//console.log(keyname);
+								
+						if (keylocator.keyName.contentName.match(co.name)) {
+							if (LOG > 3) console.log("Content is key itself");
+									
+							var rsakey = decodeSubjectPublicKeyInfo(co.content);
+							var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
+							var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
+									
+							currentClosure.upcall(flag, new UpcallInfo(this, null, 0, co));
+									
+							// SWT: We don't need to store key here since the same key will be
+							//      stored again in the closure.
+							//var keyEntry = new KeyStoreEntry(keylocator.keyName, rsakey, new Date().getTime());
+							//NDN.addKeyEntry(keyEntry);
+							//console.log(NDN.KeyStore);
+						} else {
+							// Check local key store
+							var keyEntry = NDN.getKeyByName(keylocator.keyName);
+							if (keyEntry) {
+								// Key found, verify now
+								if (LOG > 3) console.log("Local key cache hit");
+								var rsakey = keyEntry.rsaKey;
+								var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
+								var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
+										
+								// Raise callback
+								currentClosure.upcall(flag, new UpcallInfo(this, null, 0, co));
+							} else {
+								// Not found, fetch now
+								if (LOG > 3) console.log("Fetch key according to keylocator");
+								var nextClosure = new KeyFetchClosure(co, currentClosure, keylocator.keyName, sigHex, wit);
+								this.expressInterest(keylocator.keyName.contentName.getPrefix(4), nextClosure);
+							}
+						}
+					} else if (keylocator.type == KeyLocatorType.KEY) {
+						if (LOG > 3) console.log("Keylocator contains KEY");
+								
+						var rsakey = decodeSubjectPublicKeyInfo(co.signedInfo.locator.publicKey);
+						var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
+								
+						var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
+						// Raise callback
+						currentClosure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(this, null, 0, co));
+								
+						// Since KeyLocator does not contain key name for this key,
+						// we have no way to store it as a key entry in KeyStore.
+					} else {
+						var cert = keylocator.certificate;
+						console.log("KeyLocator contains CERT");
+						console.log(cert);
+								
+						// TODO: verify certificate
+					}
+				}
+			}
+		}
+	} else
+		console.log('Incoming packet is not Interest or ContentObject. Discard now.');
 };
 
 /*
@@ -180,8 +409,8 @@
     this.port = hostAndPort.port;   
     console.log("Trying host from getHostAndPort: " + this.host);
     
-    // Fetch the ccndId.
-    var interest = new Interest(NDN.ccndIdFetcher);
+    // Fetch any content.
+    var interest = new Interest(new Name("/"));
 	interest.interestLifetime = 4000; // milliseconds    
 
     var thisNDN = this;
@@ -207,8 +436,7 @@
 
 NDN.ConnectClosure.prototype.upcall = function(kind, upcallInfo) {
     if (!(kind == Closure.UPCALL_CONTENT ||
-          kind == Closure.UPCALL_CONTENT_UNVERIFIED ||
-          kind == Closure.UPCALL_INTEREST))
+          kind == Closure.UPCALL_CONTENT_UNVERIFIED))
         // The upcall is not for us.
         return Closure.RESULT_ERR;
         
@@ -220,3 +448,44 @@
     return Closure.RESULT_OK;
 };
 
+/*
+ * A BinaryXmlElementReader lets you call onReceivedData multiple times which uses a
+ *   BinaryXMLStructureDecoder to detect the end of a binary XML element and calls
+ *   elementListener.onReceivedElement(element) with the element. 
+ * This handles the case where a single call to onReceivedData may contain multiple elements.
+ */
+var BinaryXmlElementReader = function BinaryXmlElementReader(elementListener) {
+    this.elementListener = elementListener;
+	this.dataParts = [];
+    this.structureDecoder = new BinaryXMLStructureDecoder();
+};
+
+BinaryXmlElementReader.prototype.onReceivedData = function(/* Uint8Array */ rawData) {
+    dump("got " + rawData.length + " bytes\n");
+    // Process multiple objects in the data.
+    while(true) {
+        // Scan the input to check if a whole ccnb object has been read.
+        this.structureDecoder.seek(0);
+        if (this.structureDecoder.findElementEnd(rawData)) {
+            // Got the remainder of an object.  Report to the caller.
+            this.dataParts.push(rawData.subarray(0, this.structureDecoder.offset));
+            dump("calling onReceivedElement\n");
+            this.elementListener.onReceivedElement(DataUtils.concatArrays(this.dataParts));
+        
+            // Need to read a new object.
+            rawData = rawData.subarray(this.structureDecoder.offset, rawData.length);
+            this.dataParts = [];
+            this.structureDecoder = new BinaryXMLStructureDecoder();
+            if (rawData.length == 0)
+                // No more data in the packet.
+                return;
+            
+            // else loop back to decode.
+        }
+        else {
+            // Save for a later call to concatArrays so that we only copy data once.
+            this.dataParts.push(rawData);
+            return;
+        }
+    }    
+}
\ No newline at end of file
diff --git a/js/WebSocketTransport.js b/js/WebSocketTransport.js
index 594aff4..dbda8b7 100644
--- a/js/WebSocketTransport.js
+++ b/js/WebSocketTransport.js
@@ -5,7 +5,6 @@
 
 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.bufferOffset = 0;
@@ -70,199 +69,8 @@
 				return;
 			}
 			
-			var decoder = new BinaryXMLDecoder(self.buffer);
-			// Dispatch according to packet type
-			if (decoder.peekStartElement(CCNProtocolDTags.Interest)) {  // Interest packet
-				if (LOG > 3) 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());
-				if (LOG > 3) console.log(nameStr);
-				
-				var entry = getEntryForRegisteredPrefix(nameStr);
-				if (entry != null) {
-					//console.log(entry);
-					var info = new UpcallInfo(ndn, interest, 0, null);
-					var ret = entry.closure.upcall(Closure.UPCALL_INTEREST, info);
-					if (ret == Closure.RESULT_INTEREST_CONSUMED && info.contentObject != null) { 
-						var coBinary = encodeToBinaryContentObject(info.contentObject);
-						// If we directly use coBinary.buffer to feed ws.send(), WebSocket 
-						// will end up sending a packet with 10000 bytes of data. That 
-						// is, WebSocket will flush the entire buffer in BinaryXMLEncoder
-						// regardless of the offset of the Uint8Array. So we have to
-						// create a new Uint8Array buffer with just the right size and
-						// copy the content from coBinary to the new buffer.
-						//    ---Wentao
-						var bytearray = new Uint8Array(coBinary.length);
-						bytearray.set(coBinary);
-						
-						self.ws.send(bytearray.buffer);
-					}
-				}
-				
-			} else if (decoder.peekStartElement(CCNProtocolDTags.ContentObject)) {  // Content packet
-				if (LOG > 3) console.log('ContentObject packet received.');
-				
-				var co = new ContentObject();
-				co.from_ccnb(decoder);
-				//console.log(co);
-				//var nameStr = co.name.getName();
-				//console.log(nameStr);
-				
-				if (self.ccndid == null && NDN.ccndIdFetcher.match(co.name)) {
-					// 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, close NDN now.");
-						
-						// Close NDN if we fail to connect to a ccn router
-						ndn.readyStatus = NDN.CLOSED;
-						ndn.onclose();
-						//console.log("NDN.onclose event fired.");
-					} else {
-						//console.log('Connected to ccnd.');
-						self.ccndid = co.signedInfo.publisher.publisherPublicKeyDigest;
-						if (LOG>3) console.log(self.ccndid);
-						
-						// Call NDN.onopen after success
-						ndn.readyStatus = NDN.OPENED;
-						ndn.onopen();
-						//console.log("NDN.onopen event fired.");
-					}
-				} else {
-					var pitEntry = NDN.getEntryForExpressedInterest(co.name);
-					if (pitEntry != null) {
-						//console.log(pitEntry);
-						// Remove PIT entry from NDN.PITTable
-						var index = NDN.PITTable.indexOf(pitEntry);
-						if (index >= 0)
-							NDN.PITTable.splice(index, 1);
-						
-						var currentClosure = pitEntry.closure;
-						
-						// Cancel interest timer
-						clearTimeout(pitEntry.timerID);
-						//console.log("Clear interest timer");
-						//console.log(currentClosure.timerID);
-						
-						// Key verification
-						
-						// Recursive key fetching & verification closure
-						var KeyFetchClosure = function KeyFetchClosure(content, closure, key, sig, wit) {
-							this.contentObject = content;  // unverified content object
-							this.closure = closure;  // closure corresponding to the contentObject
-							this.keyName = key;  // name of current key to be fetched
-							this.sigHex = sig;  // hex signature string to be verified
-							this.witness = wit;
-							
-							Closure.call(this);
-						};
-						
-						KeyFetchClosure.prototype.upcall = function(kind, upcallInfo) {
-							if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
-								console.log("In KeyFetchClosure.upcall: interest time out.");
-								console.log(this.keyName.contentName.getName());
-							} else if (kind == Closure.UPCALL_CONTENT) {
-								//console.log("In KeyFetchClosure.upcall: signature verification passed");
-								
-								var rsakey = decodeSubjectPublicKeyInfo(upcallInfo.contentObject.content);
-								var verified = rsakey.verifyByteArray(this.contentObject.rawSignatureData, this.witness, this.sigHex);
-								
-								var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
-								//console.log("raise encapsulated closure");
-								this.closure.upcall(flag, new UpcallInfo(ndn, null, 0, this.contentObject));
-								
-								// Store key in cache
-								var keyEntry = new KeyStoreEntry(keylocator.keyName, rsakey, new Date().getTime());
-								NDN.addKeyEntry(keyEntry);
-								//console.log(NDN.KeyStore);
-							} else if (kind == Closure.UPCALL_CONTENT_BAD) {
-								console.log("In KeyFetchClosure.upcall: signature verification failed");
-							}
-						};
-						
-						if (co.signedInfo && co.signedInfo.locator && co.signature) {
-							if (LOG > 3) console.log("Key verification...");
-							var sigHex = DataUtils.toHex(co.signature.signature).toLowerCase();
-							
-							var wit = null;
-							if (co.signature.Witness != null) {
-								wit = new Witness();
-								wit.decode(co.signature.Witness);
-							}
-							
-							var keylocator = co.signedInfo.locator;
-							if (keylocator.type == KeyLocatorType.KEYNAME) {
-								if (LOG > 3) console.log("KeyLocator contains KEYNAME");
-								//var keyname = keylocator.keyName.contentName.getName();
-								//console.log(nameStr);
-								//console.log(keyname);
-								
-								if (keylocator.keyName.contentName.match(co.name)) {
-									if (LOG > 3) console.log("Content is key itself");
-									
-									var rsakey = decodeSubjectPublicKeyInfo(co.content);
-									var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
-									var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
-									
-									currentClosure.upcall(flag, new UpcallInfo(ndn, null, 0, co));
-									
-									// SWT: We don't need to store key here since the same key will be
-									//      stored again in the closure.
-									//var keyEntry = new KeyStoreEntry(keylocator.keyName, rsakey, new Date().getTime());
-									//NDN.addKeyEntry(keyEntry);
-									//console.log(NDN.KeyStore);
-								} else {
-									// Check local key store
-									var keyEntry = NDN.getKeyByName(keylocator.keyName);
-									if (keyEntry) {
-										// Key found, verify now
-										if (LOG > 3) console.log("Local key cache hit");
-										var rsakey = keyEntry.rsaKey;
-										var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
-										var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
-										
-										// Raise callback
-										currentClosure.upcall(flag, new UpcallInfo(ndn, null, 0, co));
-									} else {
-										// Not found, fetch now
-										if (LOG > 3) console.log("Fetch key according to keylocator");
-										var nextClosure = new KeyFetchClosure(co, currentClosure, keylocator.keyName, sigHex, wit);
-										var interest = new Interest(keylocator.keyName.contentName.getPrefix(4));
-										interest.interestLifetime = 4000;  // milliseconds
-										self.expressInterest(ndn, interest, nextClosure);
-									}
-								}
-							} else if (keylocator.type == KeyLocatorType.KEY) {
-								if (LOG > 3) console.log("Keylocator contains KEY");
-								
-								var rsakey = decodeSubjectPublicKeyInfo(co.signedInfo.locator.publicKey);
-								var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
-								
-								var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
-								// Raise callback
-								currentClosure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(ndn, null, 0, co));
-								
-								// Since KeyLocator does not contain key name for this key,
-								// we have no way to store it as a key entry in KeyStore.
-							} else {
-								var cert = keylocator.certificate;
-								console.log("KeyLocator contains CERT");
-								console.log(cert);
-								
-								// TODO: verify certificate
-							}
-						}
-					}
-				}
-			} else {
-				console.log('Incoming packet is not Interest or ContentObject. Discard now.');
-			}
-			
-			delete decoder;
-			
+            ndn.onReceivedElement(self.buffer);
+
 			// Renew StrcutureDecoder and buffer after we process a full packet
 			delete self.structureDecoder;
 			delete self.buffer;
@@ -280,12 +88,7 @@
 		// Fetch ccndid now
 		var interest = new Interest(NDN.ccndIdFetcher);
 		interest.interestLifetime = 4000; // milliseconds
-		var subarray = encodeToBinaryInterest(interest);
-		
-		var bytes = new Uint8Array(subarray.length);
-		bytes.set(subarray);
-		
-		self.ws.send(bytes.buffer);
+        this.send(encodeToBinaryInterest(interest));
 	}
 	
 	this.ws.onerror = function(ev) {
@@ -305,107 +108,61 @@
 	}
 };
 
-WebSocketTransport.prototype.expressInterest = function(ndn, interest, closure) {
+/*
+ * Send the Uint8Array data.
+ */
+WebSocketTransport.prototype.send = function(data) {
 	if (this.ws != null) {
-		//TODO: check local content store first
-
-        var binaryInterest = encodeToBinaryInterest(interest);
-		var bytearray = new Uint8Array(binaryInterest.length);
-		bytearray.set(binaryInterest);
-		
-		if (closure != null) {
-			var pitEntry = new PITEntry(interest, closure);
-			NDN.PITTable.push(pitEntry);
-			closure.pitEntry = pitEntry;
-		}
-		
-		this.ws.send(bytearray.buffer);
+        // If we directly use data.buffer to feed ws.send(), 
+        // WebSocket may end up sending a packet with 10000 bytes of data.
+        // That is, WebSocket will flush the entire buffer
+        // regardless of the offset of the Uint8Array. So we have to create
+        // a new Uint8Array buffer with just the right size and copy the 
+        // content from binaryInterest to the new buffer.
+        //    ---Wentao
+        var bytearray = new Uint8Array(data.length);
+        bytearray.set(data);
+        this.ws.send(bytearray.buffer);
 		if (LOG > 3) console.log('ws.send() returned.');
-		
-		// Set interest timer
-		if (closure != null) {
-			pitEntry.timerID = setTimeout(function() {
-				if (LOG > 3) console.log("Interest time out.");
-				
-				// 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);
-				//console.log(pitEntry.interest.name.getName());
-				
-				// Raise closure callback
-				closure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT, new UpcallInfo(ndn, interest, 0, null));
-			}, interest.interestLifetime);  // interestLifetime is in milliseconds.
-			//console.log(closure.timerID);
-		}
 	}
 	else
 		console.log('WebSocket connection is not established.');
-};
-
-
-// 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 -1;
-		}
-		
-		var fe = new ForwardingEntry('selfreg', 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 nodename = 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 nodename = this.ccndid;
-		var interestName = new Name(['ccnx', nodename, 'selfreg', coBinary]);
-
-		var interest = new Interest(interestName);
-		interest.scope = 1;
-		var binaryInterest = encodeToBinaryInterest(interest);
-		// If we directly use binaryInterest.buffer to feed ws.send(), 
-		// WebSocket will end up sending a packet with 10000 bytes of data.
-		// That is, WebSocket will flush the entire buffer in BinaryXMLEncoder
-		// regardless of the offset of the Uint8Array. So we have to create
-		// a new Uint8Array buffer with just the right size and copy the 
-		// content from binaryInterest to the new buffer.
-		//    ---Wentao
-    	var bytearray = new Uint8Array(binaryInterest.length);
-		bytearray.set(binaryInterest);
-		if (LOG > 3) console.log('Send Interest registration packet.');
-    	
-    	var csEntry = new CSEntry(name.getName(), closure);
-		CSTable.push(csEntry);
-    	
-    	this.ws.send(bytearray.buffer);
-		
-		return 0;
-	} else {
-		console.log('WebSocket connection is not established.');
-		return -1;
+WebSocketTransport.prototype.expressInterest = function(ndn, interest, closure) {
+    if (ndn.readyStatus != NDN.OPENED) {
+		console.log('Connection is not established.');
+        return;
+    }
+    
+	//TODO: check local content store first
+	if (closure != null) {
+		var pitEntry = new PITEntry(interest, closure);
+        // TODO: This needs to be a single thread-safe transaction on a global object.
+		NDN.PITTable.push(pitEntry);
+		closure.pitEntry = pitEntry;
 	}
+
+	// Set interest timer
+	if (closure != null) {
+		pitEntry.timerID = setTimeout(function() {
+			if (LOG > 3) console.log("Interest time out.");
+				
+			// Remove PIT entry from NDN.PITTable.
+            // TODO: Make this a thread-safe operation on the global PITTable.
+			var index = NDN.PITTable.indexOf(pitEntry);
+			//console.log(NDN.PITTable);
+			if (index >= 0) 
+	            NDN.PITTable.splice(index, 1);
+			//console.log(NDN.PITTable);
+			//console.log(pitEntry.interest.name.getName());
+				
+			// Raise closure callback
+			closure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT, new UpcallInfo(ndn, interest, 0, null));
+		}, interest.interestLifetime);  // interestLifetime is in milliseconds.
+		//console.log(closure.timerID);
+	}
+
+	this.send(encodeToBinaryInterest(interest));
 };