Merge with 'remap/master'
diff --git a/js/Name.js b/js/Name.js
index 1c404fd..fd97d37 100644
--- a/js/Name.js
+++ b/js/Name.js
@@ -3,24 +3,29 @@
  * See COPYING for copyright and distribution information.
  * This class represents a Name as an array of components where each is a byte array.
  */
-
+ 
 /*
  * Create a new Name from _components.
- * If _components is a string, parse it as a URI.  Otherwise it is an array of components
- * where each is a string, byte array, ArrayBuffer or Uint8Array.
+ * If _components is a string, parse it as a URI.
+ * If _components is a Name, add a deep copy of its components.
+ * Otherwise it is an array of components where each is a string, byte array, ArrayBuffer, Uint8Array
+ *   or Name. 
  * Convert and store as an array of Uint8Array.
  * If a component is a string, encode as utf8.
  */
 var Name = function Name(_components){
-	if( typeof _components == 'string') {
+	if( typeof _components == 'string') {		
 		if(LOG>3)console.log('Content Name String '+_components);
 		this.components = Name.createNameArray(_components);
 	}
-	else if(typeof _components === 'object'){
-		if(LOG>4)console.log('Content Name Array '+_components);
+	else if(typeof _components === 'object'){		
 		this.components = [];
-        for (var i = 0; i < _components.length; ++i)
-            this.add(_components[i]);
+        if (_components instanceof Name)
+            this.add(_components);
+        else {
+            for (var i = 0; i < _components.length; ++i)
+                this.add(_components[i]);
+        }
 	}
 	else if(_components==null)
 		this.components =[];
@@ -48,7 +53,7 @@
             // Omit the leading protocol such as ndn:
             name = name.substr(iColon + 1, name.length - iColon - 1).trim();
     }
-
+    
   	if (name[0] == '/') {
         if (name.length >= 2 && name[1] == '/') {
             // Strip the authority following "//".
@@ -64,15 +69,15 @@
     }
 
 	var array = name.split('/');
-
+    
     // Unescape the components.
     for (var i = 0; i < array.length; ++i) {
         var component = Name.fromEscapedString(array[i]);
-
+        
         if (component == null) {
             // Ignore the illegal componenent.  This also gets rid of a trailing '/'.
             array.splice(i, 1);
-            --i;
+            --i;  
             continue;
         }
         else
@@ -86,19 +91,19 @@
 Name.prototype.from_ccnb = function(/*XMLDecoder*/ decoder)  {
 		decoder.readStartElement(this.getElementLabel());
 
-
+		
 		this.components = new Array(); //new ArrayList<byte []>();
 
 		while (decoder.peekStartElement(CCNProtocolDTags.Component)) {
 			this.add(decoder.readBinaryElement(CCNProtocolDTags.Component));
 		}
-
+		
 		decoder.readEndElement();
 };
 
 Name.prototype.to_ccnb = function(/*XMLEncoder*/ encoder)  {
-
-		if( this.components ==null )
+		
+		if( this.components ==null ) 
 			throw new Error("CANNOT ENCODE EMPTY CONTENT NAME");
 
 		encoder.writeStartElement(this.getElementLabel());
@@ -114,48 +119,44 @@
 };
 
 /*
- * component is a string, byte array, ArrayBuffer or Uint8Array.
+ * component is a string, byte array, ArrayBuffer, Uint8Array or Name.
  * Convert to Uint8Array and add to this Name.
  * If a component is a string, encode as utf8.
- * Return the converted value.
+ * Return this Name object to allow chaining calls to add.
  */
 Name.prototype.add = function(component){
     var result;
-    if(typeof component == 'string') {
+    if(typeof component == 'string')
         result = DataUtils.stringToUtf8Array(component);
-        this.components.push (result);
-    }
-    else if(typeof component == 'object' && component instanceof Uint8Array) {
+	else if(typeof component == 'object' && component instanceof Uint8Array)
         result = new Uint8Array(component);
-        this.components.push (result);
-    }
-    else if(typeof component == 'object' && component instanceof ArrayBuffer) {
+	else if(typeof component == 'object' && component instanceof ArrayBuffer) {
         // Make a copy.  Don't use ArrayBuffer.slice since it isn't always supported.
         result = new Uint8Array(new ArrayBuffer(component.byteLength));
         result.set(new Uint8Array(component));
-        this.components.push(result);
     }
-    else if(typeof component == 'object' && component instanceof Name) {
-        components = component;
-        if (this == component) {
-            components = new Name (component.components); // special case, when we need to create a copy
-        }
-	for(var i = 0; i < components.components.length; ++i) {
-            result = new Uint8Array (components.components[i]);
-            this.components.push (result);
-        }
+    else if (typeof component == 'object' && component instanceof Name) {
+        var components;
+        if (component == this)
+            // special case, when we need to create a copy
+            components = this.components.slice(0, this.components.length);
+        else
+            components = component.components;
+        
+        for (var i = 0; i < components.length; ++i)
+            this.components.push(new Uint8Array(components[i]));
+        return this;
     }
-    else if(typeof component == 'object') {
+	else if(typeof component == 'object')
         // Assume component is a byte array.  We can't check instanceof Array because
         //   this doesn't work in JavaScript if the array comes from a different module.
         result = new Uint8Array(component);
-        this.components.push(result);
-    }
-    else
-	throw new Error("Cannot add Name element at index " + this.components.length +
-                        ": Invalid type");
-
-    return this;
+	else 
+		throw new Error("Cannot add Name element at index " + this.components.length + 
+            ": Invalid type");
+    
+    this.components.push(result);
+	return this;
 };
 
 /**
@@ -192,16 +193,36 @@
 }
 
 // Return the escaped name string according to "CCNx URI Scheme".
-Name.prototype.to_uri = function() {
+Name.prototype.to_uri = function() {	
     if (this.components.length == 0)
         return "/";
-
+    
 	var result = "";
-
+	
 	for(var i = 0; i < this.components.length; ++i)
 		result += "/"+ Name.toEscapedString(this.components[i]);
+	
+	return result;	
+};
 
-	return result;
+/**
+* @brief Add component that represents a segment number
+*
+* @param number Segment number (integer is expected)
+*
+* This component has a special format handling:
+* - if number is zero, then %00 is added
+* - if number is between 1 and 255, %00%01 .. %00%FF is added
+* - ...
+*/
+Name.prototype.addSegment = function(number) {
+    var segmentNumberBigEndian = DataUtils.nonNegativeIntToBigEndian(number);
+    // Put a 0 byte in front.
+    var segmentNumberComponent = new Uint8Array(segmentNumberBigEndian.length + 1);
+    segmentNumberComponent.set(segmentNumberBigEndian, 1);
+
+    this.components.push(segmentNumberComponent);
+    return this;
 };
 
 /*
@@ -230,14 +251,14 @@
         var component = this.components[i];
         if (component.length <= 0)
             continue;
-
-        if (component[0] == 0 || component[0] == 0xC0 || component[0] == 0xC1 ||
+        
+        if (component[0] == 0 || component[0] == 0xC0 || component[0] == 0xC1 || 
             (component[0] >= 0xF5 && component[0] <= 0xFF))
             continue;
-
+        
         return i;
     }
-
+    
     return -1;
 }
 
@@ -247,18 +268,18 @@
 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,
+ * Find the last component in name that has a ContentDigest and return the digest value as Uint8Array, 
  *   or null if not found.  See Name.getComponentContentDigestValue.
  */
 Name.prototype.getContentDigestValue = function() {
@@ -267,7 +288,7 @@
         if (digestValue != null)
            return digestValue;
     }
-
+    
     return null;
 }
 
@@ -277,10 +298,10 @@
  * A ContentDigest component is Name.ContentDigestPrefix + 32 bytes + Name.ContentDigestSuffix.
  */
 Name.getComponentContentDigestValue = function(component) {
-    var digestComponentLength = Name.ContentDigestPrefix.length + 32 + Name.ContentDigestSuffix.length;
+    var digestComponentLength = Name.ContentDigestPrefix.length + 32 + Name.ContentDigestSuffix.length; 
     // Check for the correct length and equal ContentDigestPrefix and ContentDigestSuffix.
     if (component.length == digestComponentLength &&
-        DataUtils.arraysEqual(component.subarray(0, Name.ContentDigestPrefix.length),
+        DataUtils.arraysEqual(component.subarray(0, Name.ContentDigestPrefix.length), 
                               Name.ContentDigestPrefix) &&
         DataUtils.arraysEqual(component.subarray
            (component.length - Name.ContentDigestSuffix.length, component.length),
@@ -290,7 +311,7 @@
        return null;
 }
 
-// Meta GUID "%C1.M.G%C1" + ContentDigest with a 32 byte BLOB.
+// Meta GUID "%C1.M.G%C1" + ContentDigest with a 32 byte BLOB. 
 Name.ContentDigestPrefix = new Uint8Array([0xc1, 0x2e, 0x4d, 0x2e, 0x47, 0xc1, 0x01, 0xaa, 0x02, 0x85]);
 Name.ContentDigestSuffix = new Uint8Array([0x00]);
 
@@ -318,7 +339,7 @@
             var value = component[i];
             // Check for 0-9, A-Z, a-z, (+), (-), (.), (_)
             if (value >= 0x30 && value <= 0x39 || value >= 0x41 && value <= 0x5a ||
-                value >= 0x61 && value <= 0x7a || value == 0x2b || value == 0x2d ||
+                value >= 0x61 && value <= 0x7a || value == 0x2b || value == 0x2d || 
                 value == 0x2e || value == 0x5f)
                 result += String.fromCharCode(value);
             else
@@ -334,9 +355,9 @@
  */
 Name.fromEscapedString = function(escapedString) {
     var component = unescape(escapedString.trim());
-
+        
     if (component.match(/[^.]/) == null) {
-        // Special case for component of only periods.
+        // Special case for component of only periods.  
         if (component.length <= 2)
             // Zero, one or two periods is illegal.  Ignore this componenent to be
             //   consistent with the C implementation.
diff --git a/js/ndnProtocol.xpi b/js/ndnProtocol.xpi
index 720ed20..ee9d716 100644
--- a/js/ndnProtocol.xpi
+++ b/js/ndnProtocol.xpi
Binary files differ
diff --git a/js/ndnProtocol/components/ndnProtocolService.js b/js/ndnProtocol/components/ndnProtocolService.js
index 012e8e4..975a6b5 100644
--- a/js/ndnProtocol/components/ndnProtocolService.js
+++ b/js/ndnProtocol/components/ndnProtocolService.js
@@ -85,12 +85,9 @@
     

             var requestContent = function(contentListener) {                

                 var name = new Name(uriParts.name);

-                // TODO: Strip off an ending implicit digest before checking the last component?

-                var uriEndsWithSegmentNumber = endsWithSegmentNumber(name);

-                

                 // Use the same NDN object each time.

                 thisNdnProtocol.ndn.expressInterest(name, new ExponentialReExpressClosure 

-                    (new ContentClosure(thisNdnProtocol.ndn, contentListener, uriEndsWithSegmentNumber, 

+                    (new ContentClosure(thisNdnProtocol.ndn, contentListener, name, 

                             aURI, searchWithoutNdn + uriParts.hash, segmentTemplate)),

                     template);

             };

@@ -115,8 +112,8 @@
 /*

  * Create a closure for calling expressInterest.

  * contentListener is from the call to requestContent.

- * uriEndsWithSegmentNumber is true if the URI passed to newChannel has a segment number

- *    (used to determine whether to request only that segment number and for updating the URL bar).

+ * uriName is the name in the URI passed to newChannel (used in part to determine whether to request 

+ *   only that segment number and for updating the URL bar).

  * aURI is the URI passed to newChannel.

  * uriSearchAndHash is the search and hash part of the URI passed to newChannel, including the '?'

  *    and/or '#' but without the interest selector fields.

@@ -124,14 +121,13 @@
  * The uses ExponentialReExpressClosure in expressInterest to re-express if fetching a segment times out.

  */                                                

 var ContentClosure = function ContentClosure

-        (ndn, contentListener, uriEndsWithSegmentNumber, aURI, uriSearchAndHash, 

-         segmentTemplate) {

+      (ndn, contentListener, uriName, aURI, uriSearchAndHash, segmentTemplate) {

     // Inherit from Closure.

     Closure.call(this);

     

     this.ndn = ndn;

     this.contentListener = contentListener;

-    this.uriEndsWithSegmentNumber = uriEndsWithSegmentNumber;

+    this.uriName = uriName;

     this.aURI = aURI;

     this.uriSearchAndHash = uriSearchAndHash;

     this.segmentTemplate = segmentTemplate;

@@ -141,6 +137,7 @@
     this.didRequestFinalSegment = false;

     this.finalSegmentNumber = null;

     this.didOnStart = false;

+    this.uriEndsWithSegmentNumber = endsWithSegmentNumber(uriName);

 };

 

 ContentClosure.prototype.upcall = function(kind, upcallInfo) {

@@ -187,7 +184,22 @@
     }

     

     if ((segmentNumber == null || segmentNumber == 0) && !this.didOnStart) {

-        // This is the first or only segment, so start.

+        // This is the first or only segment.

+        /* TODO: Finish implementing check for META.

+        var iMetaComponent = getIndexOfMetaComponent(contentObject.name);

+        if (!this.uriEndsWithSegmentNumber && iMetaComponent >= 0 &&

+            getIndexOfMetaComponent(this.uriName) < 0) {

+            // The matched content name has a META component that wasn't requiested in the original

+            //   URI.  Try to exclude the META component to get the "real" content.

+            var nameWithoutMeta = new Name(contentObject.name.components.slice(0, iMetaComponent));

+            var excludeMetaTemplate = this.segmentTemplate.clone();

+            excludeMetaTemplate.exclude = new Exclude([MetaComponentPrefix, Exclude.ANY]);

+            

+            this.ndn.expressInterest

+                (nameWithoutMeta, new ExponentialReExpressClosure(this), excludeMetaTemplate);

+        }

+        */

+        

         this.didOnStart = true;

         

         // Get the URI from the ContentObject including the version.

@@ -255,11 +267,11 @@
         var components = contentObject.name.components.slice

             (0, contentObject.name.components.length - 1);

             

-        // Temporarily set the childSelector in the segmentTemplate.

-        this.segmentTemplate.childSelector = 1;

+        // Clone the template to set the childSelector.

+        var childSelectorTemplate = this.segmentTemplate.clone();

+        childSelectorTemplate.childSelector = 1;

         this.ndn.expressInterest

-            (new Name(components), new ExponentialReExpressClosure(this), this.segmentTemplate);

-        this.segmentTemplate.childSelector = null;

+            (new Name(components), new ExponentialReExpressClosure(this), childSelectorTemplate);

     }

 

     // Request new segments.

@@ -268,17 +280,10 @@
         if (this.finalSegmentNumber != null && toRequest[i] > this.finalSegmentNumber)

             continue;

         

-        // Make a name for the segment and get it.

-        var segmentNumberBigEndian = DataUtils.nonNegativeIntToBigEndian(toRequest[i]);

-        // Put a 0 byte in front.

-        var segmentNumberComponent = new Uint8Array(segmentNumberBigEndian.length + 1);

-        segmentNumberComponent.set(segmentNumberBigEndian, 1);

-        

-        var components = contentObject.name.components.slice

-            (0, contentObject.name.components.length - 1);

-        components.push(segmentNumberComponent);

         this.ndn.expressInterest

-            (new Name(components), new ExponentialReExpressClosure(this), this.segmentTemplate);

+            (new Name(contentObject.name.components.slice

+                      (0, contentObject.name.components.length - 1)).addSegment(toRequest[i]), 

+             new ExponentialReExpressClosure(this), this.segmentTemplate);

     }

         

     return Closure.RESULT_OK;

@@ -490,10 +495,27 @@
     for (var i = 0; i < splitValue.length; ++i) {

         var element = splitValue[i].trim();

         if (element == "*")

-            excludeValues.push("*")

+            excludeValues.push(Exclude.ANY)

         else

             excludeValues.push(Name.fromEscapedString(element));

     }

 

     return new Exclude(excludeValues);

 }

+

+/*

+ * Return the index of the first compoment that starts with %C1.META, or -1 if not found.

+ */

+function getIndexOfMetaComponent(name) {

+    for (var i = 0; i < name.components.length; ++i) {

+        var component = name.components[i];

+        if (component.length >= MetaComponentPrefix.length &&

+            DataUtils.arraysEqual(component.subarray(0, MetaComponentPrefix.length), 

+                                  MetaComponentPrefix))

+            return i;

+    }

+    

+    return -1;

+}

+

+var MetaComponentPrefix = new Uint8Array([0xc1, 0x2e, 0x4d, 0x45, 0x54, 0x41]);

diff --git a/js/ndnProtocol/modules/ndn-js.jsm b/js/ndnProtocol/modules/ndn-js.jsm
index 0a15cf4..0719199 100644
--- a/js/ndnProtocol/modules/ndn-js.jsm
+++ b/js/ndnProtocol/modules/ndn-js.jsm
@@ -36,7 +36,7 @@
 }
 var Closure=function(){this.ndn_data=null;this.ndn_data_dirty=!1};Closure.RESULT_ERR=-1;Closure.RESULT_OK=0;Closure.RESULT_REEXPRESS=1;Closure.RESULT_INTEREST_CONSUMED=2;Closure.RESULT_VERIFY=3;Closure.RESULT_FETCHKEY=4;Closure.UPCALL_FINAL=0;Closure.UPCALL_INTEREST=1;Closure.UPCALL_CONSUMED_INTEREST=2;Closure.UPCALL_CONTENT=3;Closure.UPCALL_INTEREST_TIMED_OUT=4;Closure.UPCALL_CONTENT_UNVERIFIED=5;Closure.UPCALL_CONTENT_BAD=6;Closure.prototype.upcall=function(){return Closure.RESULT_OK};
 var UpcallInfo=function(a,b,c,d){this.ndn=a;this.interest=b;this.matchedComps=c;this.contentObject=d};UpcallInfo.prototype.toString=function(){var a="ndn = "+this.ndn,a=a+("\nInterest = "+this.interest),a=a+("\nmatchedComps = "+this.matchedComps);return a+="\nContentObject: "+this.contentObject};
-var WebSocketTransport=function(){this.elementReader=this.connectedPort=this.connectedHost=this.ws=null;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 WebSocketTransport=function(){if(!WebSocket)throw Error("WebSocket support is not available on this platform.");this.elementReader=this.connectedPort=this.connectedHost=this.ws=null;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)};
 WebSocketTransport.prototype.connect=function(a,b){null!=this.ws&&delete this.ws;this.ws=new WebSocket("ws://"+a.host+":"+a.port);0<LOG&&console.log("ws connection created.");this.connectedHost=a.host;this.connectedPort=a.port;this.ws.binaryType="arraybuffer";this.elementReader=new BinaryXmlElementReader(a);var c=this;this.ws.onmessage=function(a){a=a.data;if(null==a||void 0==a||""==a)console.log("INVALID ANSWER");else if(a instanceof ArrayBuffer){a=new Uint8Array(a);3<LOG&&console.log("BINARY RESPONSE IS "+
 DataUtils.toHex(a));try{c.elementReader.onReceivedData(a)}catch(b){console.log("NDN.ws.onmessage exception: "+b)}}};this.ws.onopen=function(a){3<LOG&&console.log(a);3<LOG&&console.log("ws.onopen: WebSocket connection opened.");3<LOG&&console.log("ws.onopen: ReadyState: "+this.readyState);b()};this.ws.onerror=function(a){console.log("ws.onerror: ReadyState: "+this.readyState);console.log(a);console.log("ws.onerror: WebSocket error: "+a.data)};this.ws.onclose=function(){console.log("ws.onclose: WebSocket connection closed.");
 c.ws=null;a.readyStatus=NDN.CLOSED;a.onclose()}};WebSocketTransport.prototype.send=function(a){if(null!=this.ws){var b=new Uint8Array(a.length);b.set(a);this.ws.send(b.buffer);3<LOG&&console.log("ws.send() returned.")}else console.log("WebSocket connection is not established.")};
@@ -54,9 +54,9 @@
 Name.prototype.from_ccnb=function(a){a.readStartElement(this.getElementLabel());for(this.components=[];a.peekStartElement(CCNProtocolDTags.Component);)this.add(a.readBinaryElement(CCNProtocolDTags.Component));a.readEndElement()};Name.prototype.to_ccnb=function(a){if(null==this.components)throw Error("CANNOT ENCODE EMPTY CONTENT NAME");a.writeStartElement(this.getElementLabel());for(var b=this.components.length,c=0;c<b;c++)a.writeElement(CCNProtocolDTags.Component,this.components[c]);a.writeEndElement()};
 Name.prototype.getElementLabel=function(){return CCNProtocolDTags.Name};
 Name.prototype.add=function(a){var b;if("string"==typeof a)b=DataUtils.stringToUtf8Array(a);else if("object"==typeof a&&a instanceof Uint8Array)b=new Uint8Array(a);else if("object"==typeof a&&a instanceof ArrayBuffer)b=new Uint8Array(new ArrayBuffer(a.byteLength)),b.set(new Uint8Array(a));else if("object"==typeof a)b=new Uint8Array(a);else throw Error("Cannot add Name element at index "+this.components.length+": Invalid type");return this.components.push(b)};
-Name.prototype.to_uri=function(){if(0==this.components.length)return"/";for(var a="",b=0;b<this.components.length;++b)a+="/"+Name.toEscapedString(this.components[b]);return a};Name.prototype.getPrefix=function(a){return new Name(this.components.slice(0,a))};Name.prototype.getComponent=function(a){var b=new ArrayBuffer(this.components[a].length);(new Uint8Array(b)).set(this.components[a]);return b};
-Name.prototype.indexOfFileName=function(){for(var a=this.components.length-1;0<=a;--a){var b=this.components[a];if(!(0>=b.length)&&!(0==b[0]||192==b[0]||193==b[0]||245<=b[0]&&255>=b[0]))return a}return-1};Name.prototype.equalsName=function(a){if(this.components.length!=a.components.length)return!1;for(var b=this.components.length-1;0<=b;--b)if(!DataUtils.arraysEqual(this.components[b],a.components[b]))return!1;return!0};
-Name.prototype.getContentDigestValue=function(){for(var a=this.components.length-1;0<=a;--a){var b=Name.getComponentContentDigestValue(this.components[a]);if(null!=b)return b}return null};
+Name.prototype.to_uri=function(){if(0==this.components.length)return"/";for(var a="",b=0;b<this.components.length;++b)a+="/"+Name.toEscapedString(this.components[b]);return a};Name.prototype.addSegment=function(a){var a=DataUtils.nonNegativeIntToBigEndian(a),b=new Uint8Array(a.length+1);b.set(a,1);this.components.push(b);return this};Name.prototype.getPrefix=function(a){return new Name(this.components.slice(0,a))};
+Name.prototype.getComponent=function(a){var b=new ArrayBuffer(this.components[a].length);(new Uint8Array(b)).set(this.components[a]);return b};Name.prototype.indexOfFileName=function(){for(var a=this.components.length-1;0<=a;--a){var b=this.components[a];if(!(0>=b.length)&&!(0==b[0]||192==b[0]||193==b[0]||245<=b[0]&&255>=b[0]))return a}return-1};
+Name.prototype.equalsName=function(a){if(this.components.length!=a.components.length)return!1;for(var b=this.components.length-1;0<=b;--b)if(!DataUtils.arraysEqual(this.components[b],a.components[b]))return!1;return!0};Name.prototype.getContentDigestValue=function(){for(var a=this.components.length-1;0<=a;--a){var b=Name.getComponentContentDigestValue(this.components[a]);if(null!=b)return b}return null};
 Name.getComponentContentDigestValue=function(a){return a.length==Name.ContentDigestPrefix.length+32+Name.ContentDigestSuffix.length&&DataUtils.arraysEqual(a.subarray(0,Name.ContentDigestPrefix.length),Name.ContentDigestPrefix)&&DataUtils.arraysEqual(a.subarray(a.length-Name.ContentDigestSuffix.length,a.length),Name.ContentDigestSuffix)?a.subarray(Name.ContentDigestPrefix.length,Name.ContentDigestPrefix.length+32):null};Name.ContentDigestPrefix=new Uint8Array([193,46,77,46,71,193,1,170,2,133]);
 Name.ContentDigestSuffix=new Uint8Array([0]);Name.toEscapedString=function(a){for(var b="",c=!1,d=0;d<a.length;++d)if(46!=a[d]){c=!0;break}if(c)for(d=0;d<a.length;++d)c=a[d],b=48<=c&&57>=c||65<=c&&90>=c||97<=c&&122>=c||43==c||45==c||46==c||95==c?b+String.fromCharCode(c):b+("%"+(16>c?"0":"")+c.toString(16).toUpperCase());else{b="...";for(d=0;d<a.length;++d)b+="."}return b};
 Name.fromEscapedString=function(a){a=unescape(a.trim());return null==a.match(/[^.]/)?2>=a.length?null:DataUtils.toNumbersFromString(a.substr(3,a.length-3)):DataUtils.toNumbersFromString(a)};Name.prototype.match=function(a){var b=this.components,a=a.components;if(b.length>a.length)return!1;for(var c=0;c<b.length;++c)if(!DataUtils.arraysEqual(b[c],a[c]))return!1;return!0};
@@ -290,11 +290,11 @@
 BigInteger.prototype.millerRabin=bnpMillerRabin;BigInteger.prototype.clone=bnClone;BigInteger.prototype.intValue=bnIntValue;BigInteger.prototype.byteValue=bnByteValue;BigInteger.prototype.shortValue=bnShortValue;BigInteger.prototype.signum=bnSigNum;BigInteger.prototype.toByteArray=bnToByteArray;BigInteger.prototype.equals=bnEquals;BigInteger.prototype.min=bnMin;BigInteger.prototype.max=bnMax;BigInteger.prototype.and=bnAnd;BigInteger.prototype.or=bnOr;BigInteger.prototype.xor=bnXor;
 BigInteger.prototype.andNot=bnAndNot;BigInteger.prototype.not=bnNot;BigInteger.prototype.shiftLeft=bnShiftLeft;BigInteger.prototype.shiftRight=bnShiftRight;BigInteger.prototype.getLowestSetBit=bnGetLowestSetBit;BigInteger.prototype.bitCount=bnBitCount;BigInteger.prototype.testBit=bnTestBit;BigInteger.prototype.setBit=bnSetBit;BigInteger.prototype.clearBit=bnClearBit;BigInteger.prototype.flipBit=bnFlipBit;BigInteger.prototype.add=bnAdd;BigInteger.prototype.subtract=bnSubtract;
 BigInteger.prototype.multiply=bnMultiply;BigInteger.prototype.divide=bnDivide;BigInteger.prototype.remainder=bnRemainder;BigInteger.prototype.divideAndRemainder=bnDivideAndRemainder;BigInteger.prototype.modPow=bnModPow;BigInteger.prototype.modInverse=bnModInverse;BigInteger.prototype.pow=bnPow;BigInteger.prototype.gcd=bnGCD;BigInteger.prototype.isProbablePrime=bnIsProbablePrime;
-var LOG=0,NDN=function NDN(b){b=b||{};this.transport=(b.getTransport||function(){return new WebSocketTransport})();this.getHostAndPort=b.getHostAndPort||this.transport.defaultGetHostAndPort;this.host=void 0!==b.host?b.host:null;this.port=b.port||9696;this.readyStatus=NDN.UNOPEN;this.verify=void 0!==b.verify?b.verify:!0;this.onopen=b.onopen||function(){3<LOG&&console.log("NDN connection established.")};this.onclose=b.onclose||function(){3<LOG&&console.log("NDN connection closed.")};this.ccndid=null};
-NDN.UNOPEN=0;NDN.OPENED=1;NDN.CLOSED=2;NDN.ccndIdFetcher=new Name("/%C1.M.S.localhost/%C1.M.SRV/ccnd/KEY");NDN.prototype.createRoute=function(a,b){this.host=a;this.port=b};NDN.KeyStore=[];var KeyStoreEntry=function(a,b,c){this.keyName=a;this.rsaKey=b;this.timeStamp=c};NDN.addKeyEntry=function(a){null==NDN.getKeyByName(a.keyName)&&NDN.KeyStore.push(a)};
-NDN.getKeyByName=function(a){for(var b=null,c=0;c<NDN.KeyStore.length;c++)if(NDN.KeyStore[c].keyName.contentName.match(a.contentName)&&(null==b||NDN.KeyStore[c].keyName.contentName.components.length>b.keyName.contentName.components.length))b=NDN.KeyStore[c];return b};NDN.PITTable=[];var PITEntry=function(a,b){this.interest=a;this.closure=b;this.timerID=-1};
-NDN.getEntryForExpressedInterest=function(a){for(var b=null,c=0;c<NDN.PITTable.length;c++)if(NDN.PITTable[c].interest.matches_name(a)&&(null==b||NDN.PITTable[c].interest.name.components.length>b.interest.name.components.length))b=NDN.PITTable[c];return b};NDN.CSTable=[];var CSEntry=function(a,b){this.name=a;this.closure=b};function getEntryForRegisteredPrefix(a){for(var b=0;b<NDN.CSTable.length;b++)if(null!=NDN.CSTable[b].name.match(a))return NDN.CSTable[b];return null}
-NDN.makeShuffledGetHostAndPort=function(a,b){a=a.slice(0,a.length);DataUtils.shuffle(a);return function(){return 0==a.length?null:{host:a.splice(0,1)[0],port:b}}};
+var LOG=0,NDN=function NDN(b){if(!NDN.supported)throw Error("The necessary JavaScript support is not available on this platform.");b=b||{};this.transport=(b.getTransport||function(){return new WebSocketTransport})();this.getHostAndPort=b.getHostAndPort||this.transport.defaultGetHostAndPort;this.host=void 0!==b.host?b.host:null;this.port=b.port||9696;this.readyStatus=NDN.UNOPEN;this.verify=void 0!==b.verify?b.verify:!0;this.onopen=b.onopen||function(){3<LOG&&console.log("NDN connection established.")};
+this.onclose=b.onclose||function(){3<LOG&&console.log("NDN connection closed.")};this.ccndid=null};NDN.UNOPEN=0;NDN.OPENED=1;NDN.CLOSED=2;NDN.getSupported=function(){try{(new Uint8Array(1)).subarray(0,1)}catch(a){return console.log("NDN not available: Uint8Array not supported. "+a),!1}return!0};NDN.supported=NDN.getSupported();NDN.ccndIdFetcher=new Name("/%C1.M.S.localhost/%C1.M.SRV/ccnd/KEY");NDN.prototype.createRoute=function(a,b){this.host=a;this.port=b};NDN.KeyStore=[];
+var KeyStoreEntry=function(a,b,c){this.keyName=a;this.rsaKey=b;this.timeStamp=c};NDN.addKeyEntry=function(a){null==NDN.getKeyByName(a.keyName)&&NDN.KeyStore.push(a)};NDN.getKeyByName=function(a){for(var b=null,c=0;c<NDN.KeyStore.length;c++)if(NDN.KeyStore[c].keyName.contentName.match(a.contentName)&&(null==b||NDN.KeyStore[c].keyName.contentName.components.length>b.keyName.contentName.components.length))b=NDN.KeyStore[c];return b};NDN.PITTable=[];
+var PITEntry=function(a,b){this.interest=a;this.closure=b;this.timerID=-1};NDN.getEntryForExpressedInterest=function(a){for(var b=null,c=0;c<NDN.PITTable.length;c++)if(NDN.PITTable[c].interest.matches_name(a)&&(null==b||NDN.PITTable[c].interest.name.components.length>b.interest.name.components.length))b=NDN.PITTable[c];return b};NDN.CSTable=[];var CSEntry=function(a,b){this.name=a;this.closure=b};
+function getEntryForRegisteredPrefix(a){for(var b=0;b<NDN.CSTable.length;b++)if(null!=NDN.CSTable[b].name.match(a))return NDN.CSTable[b];return null}NDN.makeShuffledGetHostAndPort=function(a,b){a=a.slice(0,a.length);DataUtils.shuffle(a);return function(){return 0==a.length?null:{host:a.splice(0,1)[0],port:b}}};
 NDN.prototype.expressInterest=function(a,b,c){var d=new Interest(a);null!=c?(d.minSuffixComponents=c.minSuffixComponents,d.maxSuffixComponents=c.maxSuffixComponents,d.publisherPublicKeyDigest=c.publisherPublicKeyDigest,d.exclude=c.exclude,d.childSelector=c.childSelector,d.answerOriginKind=c.answerOriginKind,d.scope=c.scope,d.interestLifetime=c.interestLifetime):d.interestLifetime=4E3;if(null==this.host||null==this.port)if(null==this.getHostAndPort)console.log("ERROR: host OR port NOT SET");else{var e=
 this;this.connectAndExecute(function(){e.reconnectAndExpressInterest(d,b)})}else this.reconnectAndExpressInterest(d,b)};NDN.prototype.reconnectAndExpressInterest=function(a,b){if(this.transport.connectedHost!=this.host||this.transport.connectedPort!=this.port){var c=this;this.transport.connect(c,function(){c.expressInterestHelper(a,b)})}else this.expressInterestHelper(a,b)};
 NDN.prototype.expressInterestHelper=function(a,b){var c=encodeToBinaryInterest(a),d=this;if(null!=b){var e=new PITEntry(a,b);NDN.PITTable.push(e);b.pitEntry=e;var f=a.interestLifetime||4E3,g=function(){3<LOG&&console.log("Interest time out: "+a.name.to_uri());var h=NDN.PITTable.indexOf(e);0<=h&&NDN.PITTable.splice(h,1);b.upcall(Closure.UPCALL_INTEREST_TIMED_OUT,new UpcallInfo(d,a,0,null))==Closure.RESULT_REEXPRESS&&(3<LOG&&console.log("Re-express interest: "+a.name.to_uri()),e.timerID=setTimeout(g,
diff --git a/js/testing/test-name.html b/js/testing/test-name.html
index aca50d9..318511d 100644
--- a/js/testing/test-name.html
+++ b/js/testing/test-name.html
@@ -32,73 +32,81 @@
     var name1 = new Name([entree, comp1, ab2]);

     var name1Uri = name1.to_uri();

     var name1UriExpected = "/entr%C3%A9e/..../%00%01%02%03";

-    result.innerHTML += "Name from '" + entree + "', Uint8Array of '.' and ArrayBuffer of 0,1,2,3:<br>";

+    result.innerHTML += "Name from '" + entree + "', Uint8Array of '.' and ArrayBuffer of 0,1,2,3:<br/>";

     if (name1Uri == name1UriExpected)

-        result.innerHTML += "SUCCESS: " + name1Uri + ".<br>";

+        result.innerHTML += "SUCCESS: " + name1Uri + ".<br/>";

     else

-        result.innerHTML += "ERROR: got " + name1Uri + ", expected " + name1UriExpected + " .<br>";

+        result.innerHTML += "ERROR: got " + name1Uri + ", expected " + name1UriExpected + " .<br/>";

 

-    result.innerHTML += "Compare with same Name from '" + entree + "', '.' and ArrayBuffer:<br>";

+    result.innerHTML += "Compare with same Name from '" + entree + "', '.' and ArrayBuffer:<br/>";

     // Equivalent with name1

     var name2 = new Name([entree, ".", comp2]);

     if (name2.components.length != name1.components.length)

         result.innerHTML += "ERROR: Got name with " + name2.components.length + 

-            " components, expected " + name1.components.length + ".<br>";

+            " components, expected " + name1.components.length + ".<br/>";

     else {

         var allEqual = true;

         for (var i = 0; i < name1.components.length; ++i) {

             if (!DataUtils.arraysEqual(name1.components[i], name2.components[i])) {

                 allEqual = false;

-                result.innerHTML += "ERROR: Names differ at component at index " + i + ".<br>";

+                result.innerHTML += "ERROR: Names differ at component at index " + i + ".<br/>";

             }

         }

         if (allEqual)

-            result.innerHTML += "SUCCESS: Names are equal.<br>";

+            result.innerHTML += "SUCCESS: Names are equal.<br/>";

     }

     

-    result.innerHTML += "Compare with same Name from URI:<br>";

+    result.innerHTML += "Compare with same Name from URI:<br/>";

     // Equivalent with name1; when Name constructor is passed a string, it is treated as a URI

     var name3 = new Name("/entr%C3%A9e/..../%00%01%02%03");

     if (name3.components.length != name1.components.length)

         result.innerHTML += "ERROR: Got name with " + name3.components.length + 

-            " components, expected " + name1.components.length + ".<br>";

+            " components, expected " + name1.components.length + ".<br/>";

     else {

         var allEqual = true;

         for (var i = 0; i < name1.components.length; ++i) {

             if (!DataUtils.arraysEqual(name1.components[i], name3.components[i])) {

                 allEqual = false;

-                result.innerHTML += "ERROR: Names differ at component at index " + i + ".<br>";

+                result.innerHTML += "ERROR: Names differ at component at index " + i + ".<br/>";

             }

         }

         if (allEqual)

-            result.innerHTML += "SUCCESS: Names are equal.<br>";

+            result.innerHTML += "SUCCESS: Names are equal.<br/>";

     }    

     

-    result.innerHTML += "Compare with Name prefix of first 2 components:<br>";

+    result.innerHTML += "Compare with Name prefix of first 2 components:<br/>";

     // Returns new Name([entree, "."])

     var name4 = name2.getPrefix(2);

     if (name4.components.length != 2)

         result.innerHTML += "ERROR: Got name with " + name4.components.length + 

-            " components, expected 2.<br>";

+            " components, expected 2.<br/>";

     else {

         var allEqual = true;

         for (var i = 0; i < name4.components.length; ++i) {

             if (!DataUtils.arraysEqual(name1.components[i], name4.components[i])) {

                 allEqual = false;

-                result.innerHTML += "ERROR: Names differ at component at index " + i + ".<br>";

+                result.innerHTML += "ERROR: Names differ at component at index " + i + ".<br/>";

             }

         }

         if (allEqual)

-            result.innerHTML += "SUCCESS: First 2 components are equal: " + name4.to_uri() + " .<br>";

+            result.innerHTML += "SUCCESS: First 2 components are equal: " + name4.to_uri() + " .<br/>";

     }    

     

-    result.innerHTML += "Check with Name component at index 2:<br>";

+    result.innerHTML += "Check with Name component at index 2:<br/>";

     // Returns ArrayBuffer of "%00%01%02%03"

     var component2Out = new Uint8Array(name1.getComponent(2));

     if (DataUtils.arraysEqual(component2Out, comp2))

-        result.innerHTML += "SUCCESS: Components at index 2 are equal.<br>";

+        result.innerHTML += "SUCCESS: Components at index 2 are equal.<br/>";

     else

-        result.innerHTML += "ERROR: Components at index 2 are different.<br>";

+        result.innerHTML += "ERROR: Components at index 2 are different.<br/>";

+    

+    var name5 = new Name("/localhost/user/folders/files/%00%0F");

+    result.innerHTML += "Check chaining calls to add() to create Name: " + name5.to_uri() + "<br/>";

+    if (name5.equalsName(new Name(new Name("/localhost")).add(new Name("/user/folders")).add

+          ("files").addSegment(15)))

+        result.innerHTML += "SUCCESS: Names are equal.<br/>";

+    else

+        result.innerHTML += "ERROR: Names are different.<br/>";

 }

 

 	</script>

diff --git a/js/tools/build/ndn-js-uncomp.js b/js/tools/build/ndn-js-uncomp.js
index b947a5c..274879c 100644
--- a/js/tools/build/ndn-js-uncomp.js
+++ b/js/tools/build/ndn-js-uncomp.js
@@ -633,6 +633,26 @@
 	return result;	
 };
 
+/**
+* @brief Add component that represents a segment number
+*
+* @param number Segment number (integer is expected)
+*
+* This component has a special format handling:
+* - if number is zero, then %00 is added
+* - if number is between 1 and 255, %00%01 .. %00%FF is added
+* - ...
+*/
+Name.prototype.addSegment = function(number) {
+    var segmentNumberBigEndian = DataUtils.nonNegativeIntToBigEndian(number);
+    // Put a 0 byte in front.
+    var segmentNumberComponent = new Uint8Array(segmentNumberBigEndian.length + 1);
+    segmentNumberComponent.set(segmentNumberBigEndian, 1);
+
+    this.components.push(segmentNumberComponent);
+    return this;
+};
+
 /*
  * Return a new Name with the first nComponents components of this Name.
  */
diff --git a/js/tools/build/ndn-js.js b/js/tools/build/ndn-js.js
index fb403f1..fb07e3a 100644
--- a/js/tools/build/ndn-js.js
+++ b/js/tools/build/ndn-js.js
@@ -18,9 +18,9 @@
 Name.prototype.from_ccnb=function(a){a.readStartElement(this.getElementLabel());for(this.components=[];a.peekStartElement(CCNProtocolDTags.Component);)this.add(a.readBinaryElement(CCNProtocolDTags.Component));a.readEndElement()};Name.prototype.to_ccnb=function(a){if(null==this.components)throw Error("CANNOT ENCODE EMPTY CONTENT NAME");a.writeStartElement(this.getElementLabel());for(var b=this.components.length,c=0;c<b;c++)a.writeElement(CCNProtocolDTags.Component,this.components[c]);a.writeEndElement()};
 Name.prototype.getElementLabel=function(){return CCNProtocolDTags.Name};
 Name.prototype.add=function(a){var b;if("string"==typeof a)b=DataUtils.stringToUtf8Array(a);else if("object"==typeof a&&a instanceof Uint8Array)b=new Uint8Array(a);else if("object"==typeof a&&a instanceof ArrayBuffer)b=new Uint8Array(new ArrayBuffer(a.byteLength)),b.set(new Uint8Array(a));else if("object"==typeof a)b=new Uint8Array(a);else throw Error("Cannot add Name element at index "+this.components.length+": Invalid type");return this.components.push(b)};
-Name.prototype.to_uri=function(){if(0==this.components.length)return"/";for(var a="",b=0;b<this.components.length;++b)a+="/"+Name.toEscapedString(this.components[b]);return a};Name.prototype.getPrefix=function(a){return new Name(this.components.slice(0,a))};Name.prototype.getComponent=function(a){var b=new ArrayBuffer(this.components[a].length);(new Uint8Array(b)).set(this.components[a]);return b};
-Name.prototype.indexOfFileName=function(){for(var a=this.components.length-1;0<=a;--a){var b=this.components[a];if(!(0>=b.length)&&!(0==b[0]||192==b[0]||193==b[0]||245<=b[0]&&255>=b[0]))return a}return-1};Name.prototype.equalsName=function(a){if(this.components.length!=a.components.length)return!1;for(var b=this.components.length-1;0<=b;--b)if(!DataUtils.arraysEqual(this.components[b],a.components[b]))return!1;return!0};
-Name.prototype.getContentDigestValue=function(){for(var a=this.components.length-1;0<=a;--a){var b=Name.getComponentContentDigestValue(this.components[a]);if(null!=b)return b}return null};
+Name.prototype.to_uri=function(){if(0==this.components.length)return"/";for(var a="",b=0;b<this.components.length;++b)a+="/"+Name.toEscapedString(this.components[b]);return a};Name.prototype.addSegment=function(a){var a=DataUtils.nonNegativeIntToBigEndian(a),b=new Uint8Array(a.length+1);b.set(a,1);this.components.push(b);return this};Name.prototype.getPrefix=function(a){return new Name(this.components.slice(0,a))};
+Name.prototype.getComponent=function(a){var b=new ArrayBuffer(this.components[a].length);(new Uint8Array(b)).set(this.components[a]);return b};Name.prototype.indexOfFileName=function(){for(var a=this.components.length-1;0<=a;--a){var b=this.components[a];if(!(0>=b.length)&&!(0==b[0]||192==b[0]||193==b[0]||245<=b[0]&&255>=b[0]))return a}return-1};
+Name.prototype.equalsName=function(a){if(this.components.length!=a.components.length)return!1;for(var b=this.components.length-1;0<=b;--b)if(!DataUtils.arraysEqual(this.components[b],a.components[b]))return!1;return!0};Name.prototype.getContentDigestValue=function(){for(var a=this.components.length-1;0<=a;--a){var b=Name.getComponentContentDigestValue(this.components[a]);if(null!=b)return b}return null};
 Name.getComponentContentDigestValue=function(a){return a.length==Name.ContentDigestPrefix.length+32+Name.ContentDigestSuffix.length&&DataUtils.arraysEqual(a.subarray(0,Name.ContentDigestPrefix.length),Name.ContentDigestPrefix)&&DataUtils.arraysEqual(a.subarray(a.length-Name.ContentDigestSuffix.length,a.length),Name.ContentDigestSuffix)?a.subarray(Name.ContentDigestPrefix.length,Name.ContentDigestPrefix.length+32):null};Name.ContentDigestPrefix=new Uint8Array([193,46,77,46,71,193,1,170,2,133]);
 Name.ContentDigestSuffix=new Uint8Array([0]);Name.toEscapedString=function(a){for(var b="",c=!1,d=0;d<a.length;++d)if(46!=a[d]){c=!0;break}if(c)for(d=0;d<a.length;++d)c=a[d],b=48<=c&&57>=c||65<=c&&90>=c||97<=c&&122>=c||43==c||45==c||46==c||95==c?b+String.fromCharCode(c):b+("%"+(16>c?"0":"")+c.toString(16).toUpperCase());else{b="...";for(d=0;d<a.length;++d)b+="."}return b};
 Name.fromEscapedString=function(a){a=unescape(a.trim());return null==a.match(/[^.]/)?2>=a.length?null:DataUtils.toNumbersFromString(a.substr(3,a.length-3)):DataUtils.toNumbersFromString(a)};Name.prototype.match=function(a){var b=this.components,a=a.components;if(b.length>a.length)return!1;for(var c=0;c<b.length;++c)if(!DataUtils.arraysEqual(b[c],a[c]))return!1;return!0};