Added Tree Explorer Search

Change-Id: I3de5ec2ad84526b6308f37f4a6df7396175d183a
diff --git a/client/catalog/css/style.css b/client/catalog/css/style.css
index e9593b3..1a66da4 100644
--- a/client/catalog/css/style.css
+++ b/client/catalog/css/style.css
@@ -124,3 +124,46 @@
 .disabled {
   cursor: not-allowed;
 }
+
+.panel-heading a[data-toggle="collapse"]::after {
+  font-family: "Glyphicons Halflings";
+  content: "\e114";
+  float: right;
+  color: gray;
+}
+
+.panel-heading a[data-toggle="collapse"].collapsed::after {
+  content: "\e080";
+}
+
+.treeExplorer .treeExplorerNode {
+  padding-left: 25px;
+  display: block;
+  cursor: pointer;
+}
+
+.treeExplorer .treeExplorerNode > .nodeChildren {
+  display: none;
+}
+
+.treeExplorer .treeExplorerNode.open > .nodeChildren {
+  display: block;
+}
+
+.treeExplorer .treeExplorerNode::before {
+  font-family: "Glyphicons Halflings";
+  content: "\e080";
+  color: gray;
+}
+
+.treeExplorer .treeExplorerNode.open::before {
+  content: "\e114";
+}
+
+.treeExplorer .treeExplorerNode.file {
+  cursor: default;
+}
+
+.treeExplorer .treeExplorerNode.file::before {
+  content: "\e022"
+}
diff --git a/client/catalog/index.html b/client/catalog/index.html
index 61e4215..bd2e933 100644
--- a/client/catalog/index.html
+++ b/client/catalog/index.html
@@ -24,6 +24,7 @@
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
 <script src="../ndn-js/build/ndn.min.js"></script>
 <script src="js/autocomplete.js"></script>
+<script src="js/treeExplorer.js"></script>
 <script src="js/catalog.js"></script>
 
 </head>
@@ -82,6 +83,13 @@
           </div>
         </div>
 
+        <div class="panel panel-info">
+          <div class="panel-heading">Tree Based Search<a data-toggle="collapse" class="collapsed" data-target="#treeSearch" href="#treeSearch"></a></div>
+          <div class="panel-body collapse" id="treeSearch">
+            <div></div>
+          </div>
+        </div>
+
         <div id="results" class="panel panel-default hidden">
           <div class="panel-body">
             <nav class="navbar navbar-inverse col-md-12 resultMenu hidden">
diff --git a/client/catalog/js/catalog.js b/client/catalog/js/catalog.js
index 347366e..3f703cb 100644
--- a/client/catalog/js/catalog.js
+++ b/client/catalog/js/catalog.js
@@ -81,11 +81,23 @@
     this.filterSetup();
 
     this.searchInput.autoComplete(function(field, callback){
-      scope.autoComplete(field, callback);
+      scope.autoComplete(field, function(list){
+        callback(list.map(function(element){
+          return field + element + "/";
+        }));
+      });
     });
 
     this.searchBar.submit(function(e){
       e.preventDefault();
+      if (scope.searchInput.val().length === 0){
+        if (!scope.searchBar.hasClass('has-error')){
+          scope.searchBar.addClass('has-error').append('<span class="help-block">Search path is required!</span>');
+        }
+        return;
+      } else {
+        scope.searchBar.removeClass('has-error').find('.help-block').fadeOut(function(){$(this).remove()});
+      }
       scope.pathSearch();
     });
 
@@ -120,6 +132,16 @@
 
     });
 
+    $('#treeSearch div').treeExplorer(function(path, callback){
+      console.log("Tree Explorer request", path);
+      scope.autoComplete(path, function(list){
+        console.log("Autocomplete response", list);
+        callback(list.map(function(element){
+          return (path == "/"?"/":"") + element + "/";
+        }));
+      })
+    });
+
   }
 
   Atmos.prototype.clearResults = function(){
@@ -182,15 +204,6 @@
 
   Atmos.prototype.autoComplete = function(field, callback){
 
-    if (this.searchInput.val().length === 0 && !filters.hasOwnProperty()){
-      if (!this.searchBar.hasClass('has-error')){
-        this.searchBar.addClass('has-error').append('<span class="help-block">A filter or search value is required!</span>');
-      }
-      return;
-    } else {
-      this.searchBar.removeClass('has-error').find('.help-block').fadeOut(function(){$(this).remove()});
-    }
-
     var scope = this;
 
     this.query(this.catalog, {"?": field},
@@ -206,10 +219,9 @@
       function(interest, data){
 
         if (data.getContent().length !== 0){
-          var options = JSON.parse(data.getContent().toString().replace(/[\n\0]/g, "")).next.map(function(element){
-            return field + element + "/";
-          });
-          callback(options);
+          callback(JSON.parse(data.getContent().toString().replace(/[\n\0]/g, "")).next);
+        } else {
+          callback([]);
         }
 
       }, function(interest){
diff --git a/client/catalog/js/treeExplorer.js b/client/catalog/js/treeExplorer.js
new file mode 100644
index 0000000..fc6a7a3
--- /dev/null
+++ b/client/catalog/js/treeExplorer.js
@@ -0,0 +1,73 @@
+
+(function(){
+  "use strict";
+  jQuery.fn.extend({
+    treeExplorer: function(getChildren){
+
+      var cache = {}; //Cache previously requested paths.
+
+      var tree = $('<div class="treeExplorer"></div>');
+      this.append(tree);
+
+      var lookup = function(path, callback){
+        if (cache[path]){
+          callback(path, cache[path]);
+        } else {
+          getChildren(path, function(children){
+            cache[path] = children;
+            callback(path, children);
+          });
+        }
+      }
+
+      var append = function(path, children, node){
+        var c = $('<div class="nodeChildren"></div>');
+        node.append(c);
+        children.forEach(function(current){
+          var el = $('<div class="treeExplorerNode"></div>');
+          if (current.match(/\/$/)){
+            el.attr('id', path + current);
+            el.append(['<a href="#' , path , current , '">' , current , '</a>'].join(""));
+          } else {
+            el.addClass('file');
+            el.text(current);
+          }
+          c.append(el);
+        });
+      }
+
+      tree.on('click', '.treeExplorerNode > a', function(){
+        var node = $(this).parent();
+
+        if (node.hasClass('open')){ //Are we open already?
+          node.removeClass('open');
+          return;
+        } else { //We need to open
+          if (node.find('.treeExplorerNode').length > 0){ //We already have children
+            node.addClass('open');
+          } else { //We need to get the children.
+            var path = node.attr('id');
+            lookup(path, function(path, children){
+              if (children.length === 0){
+                node.addClass('file');
+                var name = node.find('a').text().replace(/\/$/, "");
+                node.empty().text(name);
+              } else {
+                append(path, children, node);
+                node.addClass('open');
+              }
+            });
+          }
+        }
+
+      });
+
+      getChildren("/", function(children){
+        append("", children, tree);
+      });
+
+      return this;
+
+    }
+  })
+})();