Initial work on remaking the interface

The interface is being upgraded to use bootstrap.
Additionally, files have been separated out into their
correct pieces. (JS and CSS don't belong in the html anymore)

Also modified the repo to permit cross linux and windows access
without weird effects from line endings. Ndn-js is also now
a submodule.

Change-Id: I2682b2c599745bef5e71170e51e4abc5cf7cdf48
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..ec64f59
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+* 	text=auto
diff --git a/.gitignore b/.gitignore
index 0a24ecd..7679b3c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,7 @@
-.waf-1*
-.waf3-*
-.lock*
+.*
+!.git*
 **/*.pyc
 build/
+waf-*
+
+
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..909cb9a
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "client/query/ndn-js"]
+	path = client/query/ndn-js
+	url = http://gerrit.named-data.net/ndn-js
diff --git a/client/query/ndn-js b/client/query/ndn-js
new file mode 160000
index 0000000..03d3f74
--- /dev/null
+++ b/client/query/ndn-js
@@ -0,0 +1 @@
+Subproject commit 03d3f743a741d8808b5c60fad89ad5f3bb21cb57
diff --git a/client/query/query.html b/client/query/query.html
index 08fe6f5..de4da56 100644
--- a/client/query/query.html
+++ b/client/query/query.html
@@ -2,440 +2,70 @@
 <html lang="en-US">
 
 <head>
-    <title>Atmospheric Query and Retrieval Tool</title>
-    <meta charset="UTF-8" />
+<title>Atmospheric Query and Retrieval Tool</title>
 
+<!-- Styles -->
+<link rel="stylesheet"
+  href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.4/yeti/bootstrap.min.css">
+<link rel="stylesheet" href="query2.css">
 
-    <script type="text/javascript" src="../jquery/jquery-latest.min.js"></script>
-    <script type="text/javascript" src="../jquery/ui/1.10.1/jquery-ui.js"></script>
-    <script type="text/javascript" src="../ndn-js/build/ndn.js"></script>
-
-
-    <link rel="stylesheet" href="query.css">
-    <link rel="stylesheet" href="../jquery/ui/1.11.4/themes/smoothness/jquery-ui.css">
-
-    <script>
-        // {@ @todo: this need to be configured before the document load
-        var catalog = "/catalog/myUniqueName";
-        var face = new Face({
-            host: "localhost",
-            port: 9696
-        });
-
-        // @}
-
-        var searchMenuOptions = {}
-        var results = [];
-        var resultCount = 0;
-        var page = 1;
-        var totalPages = 1;
-        var selectedSearch = {};
-        var dropdown = [];
-
-        $(document).ready(function () {
-            var searchMenu = $(".cssmenu");
-            var currentPage = $(".page");
-            var resultTable = $(".resultTable");
-            var data = $.getJSON("search_catagories.json", function () {}).done(function (data) {
-                $.each(data, function (pageSection, contents) {
-                    if (pageSection == "SearchCatagories") {
-                        $.each(contents, function (search, searchOptions) {
-                            search = search.replace(/\_/g, " ");
-
-                            searchMenu.append('<li id="' + search + '" onclick="getDropDown(this.id)"><a href="#">' + search + '</a></li>');
-                            searchMenuOptions[String(search)] = searchOptions;
-                        });
-                    }
-                });
-            });
-        });
-
-        function onData(data) {
-            var payloadStr = data.content.toString().split("\n")[0];
-
-            var queryResults = JSON.parse(payloadStr);
-
-            var resultTable = $(".resultTable");
-            $.each(queryResults, function (queryResult, field) {
-
-                if (queryResult == "next") {
-                    populateAutocomplete(field);
-                }
-
-                $.each(field, function (entryCount, name) {
-                    results.push(name);
-                });
-            });
-
-            // Calculating the current page and the view
-            totalPages = Math.ceil(resultCount / 20);
-            populateResults(0);
-        }
-
-        var state = {};
-
-        function query(prefix, parameters, callback, pipeline) {
-            results = [];
-            dropdown = [];
-            var resultTable = $(".resultTable");
-            resultTable.empty();
-            resultTable.append('<tr><td>Results</td></tr>');
-
-            var queryPrefix = new Name(prefix);
-            queryPrefix.append("query");
-
-            var jsonString = JSON.stringify(parameters);
-            queryPrefix.append(JSON.stringify(parameters));
-
-            state = {
-                prefix: new Name(prefix),
-                userOnData: callback,
-                outstanding: {},
-                nextSegment: 0,
-            };
-
-            /*if (state.hasOwnProperty("version")) {
-                console.log("state already has version");
-            }*/
-
-            var queryInterest = new Interest(queryPrefix);
-            queryInterest.setInterestLifetimeMilliseconds(10000);
-
-            face.expressInterest(queryInterest,
-                onQueryData,
-                onQueryTimeout);
-
-            state["outstanding"][queryInterest.getName().toUri()] = 0;
-        }
-
-        function expressNextInterest() {
-            // @todo pipelines
-            var nextName = new Name(state["results"]);
-            nextName.appendSegment(state["nextSegment"]);
-
-            var nextInterest = new Interest(nextName);
-            nextInterest.setInterestLifetimeMilliseconds(10000);
-
-            face.expressInterest(nextInterest,
-                onQueryResultsData,
-                onQueryResultsTimeout);
-
-            state["nextSegment"] ++;
-            state["outstanding"][nextName.toUri()] = 0;
-        }
-
-        function onQueryData(interest, data) {
-            var name = data.getName();
-
-            delete state["outstanding"][interest.getName().toUri()];
-
-            state["version"] = name.get(state["prefix"].size() + 2).toVersion();
-
-            state["results"] = new Name(state["prefix"]).append("query-results").appendVersion(state["version"]);
-
-            expressNextInterest();
-        }
-
-        function onQueryResultsData(interest, data) {
-            var name = data.getName();
-            delete state["outstanding"][interest.getName().toUri()];
-            if (!name.get(-1).equals(data.getMetaInfo().getFinalBlockId())) {
-                expressNextInterest();
-            } else {
-                //alert("found final block");
-            }
-
-            state["userOnData"](data);
-        }
-
-        function onQueryTimeout(interest) {
-            var uri = interest.getName().toUri();
-            if (state["outstanding"][uri] < 1) {
-                state["outstanding"][uri] ++;
-                face.expressInterest(interest,
-                    onQueryData,
-                    onQueryTimeout);
-            } else {
-                delete state["outstanding"][uri];
-
-                // We modify the autocomplete box here because we need to know
-                // we have all of the entries first. Fairly hacky.
-                var autocompleteFullName = autocompleteText.value;
-                for (var i = 0; i < dropdown.length; ++i) {
-                    if (dropdown[i].substr(0, dropdown[i].length - 1).toUpperCase === autocompleteText.value.toUpperCase || dropdown.length == 1) {
-                        autocompleteText.value = dropdown[i];
-                    }
-                }
-            }
-        }
-
-        function onQueryResultsTimeout(interest) {
-            var uri = interest.getName().toUri();
-            if (state["outstanding"][uri] < 1) {
-                state["outstanding"][uri] ++;
-                face.expressInterest(interest,
-                    onQueryResultsData,
-                    onQueryResultsTimeout);
-            } else {
-                delete state["outstanding"][uri];
-                // We modify the autocomplete box here because we need to know
-                // we have all of the entries first. Fairly hacky.
-                var autocompleteFullName = autocompleteText.value;
-                for (var i = 0; i < dropdown.length; ++i) {
-                    if (dropdown[i].substr(0, dropdown[i].length - 1).toUpperCase === autocompleteText.value.toUpperCase || dropdown.length == 1) {
-                        autocompleteText.value = dropdown[i];
-                    }
-                }
-            }
-        }
-
-
-        var currentViewIndex = 0;
-
-        function populateResults(startIndex) {
-            var resultTable = $(".resultTable");
-            resultTable.empty();
-            resultTable.append('<tr><td>Results</td></tr>');
-
-
-            for (var i = startIndex; i < startIndex + 20 && i < results.length; ++i) {
-                resultTable.append('<tr><td>' + results[i] + '</td></tr>');
-            }
-
-            if (results.length <= 20) {
-                page = 1;
-            } else {
-                page = startIndex / 20 + 1;
-            }
-
-            totalPages = Math.ceil(results.length / 20);
-
-            var currentPage = $(".page");
-            currentPage.empty();
-            if (page != 1) {
-                currentPage.append('<a href="#" onclick="getPage(this.id);" id="<"><</a>');
-            }
-            // This section of code creates the paging for the results.
-            // To prevent it from having a 1000+ pages, it will only show the 5 pages before/after
-            // the current page and the total pages (expect users not to really jump around a lot).
-            for (var i = 1; i <= totalPages; ++i) {
-                if (i == 1 || i == totalPages     // Min or max
-                    || (i <= page && i + 5 >= page)    // in our current page range
-                    || (i >= page && i - 5 <= page)) { // in our current page range
-                       if (i != page) {
-                         currentPage.append(' <a href="#" onclick="getPage(' + i + ');">' + i + '</a>')
-                         if (i == 1 && page > i + 5) {
-                             currentPage.append(' ... ');
-                         }
-                       } else {
-                           currentPage.append(' ' + i);
-                       }
-                } else { // Need to skip ahead
-                    if (i == page + 6) {
-                        currentPage.append(' ... ');
-
-                        currentPage.append(' <a href="#" onclick="getPage(this.id);" id=">">></a>')
-                        i = totalPages - 1;
-                    }
-                }
-            }
-            currentPage.append('  ' + results.length + ' results');
-        }
-
-        var dropState = "";
-
-        function getDropDown(str) {
-            var searchMenu = $(".cssmenu");
-            if (str == dropState) {
-                dropState = "";
-                searchMenu.find("#" + str).find("#options_" + str).empty();
-            } else {
-                dropState = str;
-
-                $.each(searchMenuOptions, function (search, fields) {
-                    if (search === str) {
-                        searchMenu.find("#" + search).append('<ul id="options_' + search + '" class="sub-menu">');
-                        for (var i = 0; i < fields.length; ++i) {
-                            searchMenu.find("#options_" + search).append('<li id="' + fields[i] + '"onclick="submitCatalogSearch(this.id)"><a href="#">' + fields[i] + '</a></li>');
-                        }
-                        searchMenu.append('</ul>');
-                    } else {
-                        var ul = $("options_" + search);
-                        ul.empty();
-                        searchMenu.find("#" + search).find("#options_" + search).empty();
-                    }
-                });
-            }
-        }
-
-        function getPage(clickedPage) {
-            console.log(clickedPage);
-
-            var nextPage = clickedPage;
-            if (clickedPage === "<") {
-                nextPage = page - 5;
-            } else if (clickedPage === ">") {
-                console.log("> enabled");
-
-                nextPage = page + 5;
-            }
-
-            nextPage--; // Need to adjust for starting at 0
-
-            if (nextPage < 0 ) {
-                nextPage = 0;
-                console.log("0 enabled");
-            } else if (nextPage > totalPages - 1) {
-                nextPage = totalPages - 1;
-                console.log("total enabled");
-            }
-
-            populateResults(nextPage * 20);
-            return false;
-        }
-
-        function submitAutoComplete() {
-            if (autocompleteText.value.length > 0) {
-                var selection = autocompleteText.value;
-                $.each(dropdown, function (i, dropdownEntry) {
-                    if (dropdownEntry.substr(0, dropdownEntry.length - 1) == selection) {
-                        selection = dropdownEntry;
-                    }
-                });
-
-                selectedSearch["?"] = selection;
-                query(catalog, selectedSearch, onData, 1);
-                delete selectedSearch["?"];
-            }
-        }
-
-        function submitCatalogSearch(field) {
-            console.log("Sumbit Catalog Search: " + field);
-            // @todo: this logic isn't quite right
-            var remove = false;
-            $.each(selectedSearch, function (search, f) {
-                if (field == f) {
-                    delete selectedSearch[field];
-                    remove = true;
-                }
-            });
-            if (!remove) {
-                $.each(searchMenuOptions, function (search, fields) {
-                    $.each(fields, function (index, f) {
-                        if (f == field) {
-                            selectedSearch[search] = field;
-                        }
-                    });
-                });
-            }
-            query(catalog, selectedSearch, onData, 1);
-            populateCurrentSelections();
-            return false;
-        }
-
-        function populateAutocomplete(fields) {
-            var isAutocompleteFullName = (autocompleteText.value.charAt(autocompleteText.value.length - 1) === "/");
-            var autocompleteFullName = autocompleteText.value;
-            for (var i = 0; i < fields.length; ++i) {
-                var fieldFullName = fields[i];
-                var entry = autocompleteFullName;
-                var skipahead = "";
-
-                if (isAutocompleteFullName) {
-                    skipahead = fieldFullName.substr(autocompleteText.value.length, fieldFullName.length);
-                } else {
-                    if (fieldFullName.charAt(autocompleteText.value.length) === "/") {
-                        entry += "/";
-                        skipahead = fieldFullName.substr(autocompleteText.value.length + 1, fieldFullName.length);
-                    } else {
-                        skipahead = fieldFullName.substr(autocompleteText.value.length, fieldFullName.length);
-                    }
-                }
-                if (skipahead.indexOf("/") != -1) {
-                    entry += skipahead.substr(0, skipahead.indexOf("/") + 1);
-                } else {
-                    entry += skipahead;
-                }
-
-                var added = false;
-                for (var j = 0; j < dropdown.length && !added; ++j) {
-                    if (dropdown[j] === entry) {
-                        added = true;
-                    } else if (dropdown[j] > entry) {
-                        dropdown.splice(j, 0, entry);
-                        added = true;
-                    }
-                }
-                if (!added) {
-                    dropdown.push(entry);
-                }
-
-            }
-            $("#autocompleteText").autocomplete({
-                source: dropdown
-            });
-        }
-
-        function populateCurrentSelections() {
-            var currentSelection = $(".currentSelections");
-            currentSelection.empty();
-
-            currentSelection.append("<p>Filtering on:");
-
-            $.each(selectedSearch, function (searchMenuCatagory, selection) {
-                currentSelection.append('  <a href="#" onclick="removeFilter(this.id);" id="' + searchMenuCatagory + ':' + selection + '">[X] ' + searchMenuCatagory + ":" + selection + '</a>');
-            });
-
-            currentSelection.append("</p>");
-        }
-
-
-        function removeFilter(filter) {
-            console.log("Remove filter" + filter);
-            var searchFilter = filter.split(":");
-
-            var search = "";
-            for (var j = 0; j < searchFilter.length; ++j) {
-                search += searchFilter[j] + " ";
-            }
-            console.log("Split values: '" + search + "'");
-
-            delete selectedSearch[searchFilter[0]];
-            query(catalog, selectedSearch, onData, 1);
-            populateCurrentSelections();
+<!-- Scripts -->
+<script
+  src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
+<script src="ndn-js/build/ndn-js.js"></script>
+<script src="query.js"></script>
 
-            return false;
-        }
-    </script>
 </head>
 
 <body id="body">
-    <header>
-        <h1>Atmospheric Query and Retrieval Tool</h1>
-    </header>
+  <header>
+    <div class="navbar navbar-default container-fluid">
+      <div class="navbar-header">
+        <div class="navbar-brand">Atmospheric Query and Retrieval Tool</div>
+      </div>
+    </div>
+  </header>
 
-    <ul class='cssmenu'>
+  <div class="container-fluid">
 
-    </ul>
+    <div class="row full-width">
 
-    <div class="currentSelections"></div>
+      <div class="col-sm-3 col-md-2 sidebar panel panel-default">
+        <div class="panel-body">
+          <ul id="side-menu" class="nav nav-pills nav-stacked"></ul>
+        </div>
+      </div>
 
+      <div class="col-sm-9 col-md-10">
 
-    <div class="autocomplete">
-        <div class="ui-widget">
-            <label for="tags"> Search: </label>
-            <input id="autocompleteText" placeholder="/cmip" class="textbox" onkeydown="if (event.keyCode == 13) submitAutoComplete(); ">
-            <button id="autoButton" value="Search" onclick="submitAutoComplete()" id="autocompleteButton">Search</button>
+        <div class="panel panel-default">
+          <div class="panel-body">
+            <div class="currentSelections"></div>
+            <div class="autocomplete">
+              <div class="ui-widget">
+                <label for="tags"> Search: </label> <input id="autocompleteText"
+                  placeholder="/cmip" class="textbox"
+                  onkeydown="if (event.keyCode == 13) submitAutoComplete(); ">
+                <button id="autoButton" value="Search"
+                  onclick="submitAutoComplete()" id="autocompleteButton">Search</button>
+              </div>
+            </div>
+          </div>
         </div>
 
-        <div class="page"></div>
+        <div class="panel panel-default">
+          <div class="panel-heading">Results:</div>
+          <div class="panel-body">
+            <div class="page"></div>
+            <table class="resultTable"></table>
+          </div>
+        </div>
 
-        <table class="resultTable">
+      </div>
 
-
-        </table>
     </div>
 
+  </div>
 
 </body>
 
diff --git a/client/query/query.js b/client/query/query.js
new file mode 100644
index 0000000..6412d18
--- /dev/null
+++ b/client/query/query.js
@@ -0,0 +1,392 @@
+// {@ @todo: this need to be configured before the document load
+var catalog = "/catalog/myUniqueName";
+var face = new FaceInstance({
+  host: "localhost",
+  port: 9696
+});
+
+// @}
+
+var searchMenuOptions = {};
+var results = [];
+var resultCount = 0;
+var page = 1;
+var totalPages = 1;
+var selectedSearch = {};
+var dropdown = [];
+
+$(function () {
+  var searchMenu = $("#side-menu");
+  var currentPage = $(".page");
+  var resultTable = $(".resultTable");
+  var data = $.getJSON("search_catagories.json", function (data) { //url, success
+    $.each(data, function (pageSection, contents) {
+      if (pageSection == "SearchCatagories") {
+        $.each(contents, function (search, searchOptions) {
+          search = search.replace(/\_/g, " ");
+
+          searchMenu.append('<li id="' + search + '" onclick="getDropDown(this.id)"><a href="#">' + search + '</a></li>');
+          searchMenuOptions[String(search)] = searchOptions;
+        });
+      }
+    });
+  });
+});
+
+function onData(data) {
+  var payloadStr = data.content.toString().split("\n")[0];
+
+  var queryResults = JSON.parse(payloadStr);
+
+  var resultTable = $(".resultTable");
+  $.each(queryResults, function (queryResult, field) {
+
+    if (queryResult == "next") {
+      populateAutocomplete(field);
+    }
+
+    $.each(field, function (entryCount, name) {
+      results.push(name);
+    });
+  });
+
+  // Calculating the current page and the view
+  totalPages = Math.ceil(resultCount / 20);
+  populateResults(0);
+}
+
+var state = {};
+
+function query(prefix, parameters, callback, pipeline) {
+  results = [];
+  dropdown = [];
+
+  var resultTable = $(".resultTable");
+  resultTable.empty();
+  resultTable.append('<tr><td>Results</td></tr>');
+
+  var queryPrefix = new Name(prefix);
+  queryPrefix.add("query");
+
+  queryPrefix.add(JSON.stringify(parameters));
+
+  state = {
+      prefix: new Name(prefix),
+      userOnData: callback,
+      outstanding: {},
+      nextSegment: 0,
+  };
+
+  /*if (state.hasOwnProperty("version")) {
+                console.log("state already has version");
+            }*/
+
+  var queryInterest = new Interest(queryPrefix);
+  queryInterest.setInterestLifetimeMilliseconds(10000);
+
+  face.expressInterest(queryInterest,
+      onQueryData,
+      onQueryTimeout);
+
+  state["outstanding"][queryInterest.getName().toUri()] = 0;
+}
+
+function expressNextInterest() {
+  // @todo pipelines
+  var nextName = new Name(state["results"]);
+  nextName.appendSegment(state["nextSegment"]);
+
+  var nextInterest = new Interest(nextName);
+  nextInterest.setInterestLifetimeMilliseconds(10000);
+
+  face.expressInterest(nextInterest,
+      onQueryResultsData,
+      onQueryResultsTimeout);
+
+  state["nextSegment"] ++;
+  state["outstanding"][nextName.toUri()] = 0;
+}
+
+function onQueryData(interest, data) {
+  var name = data.getName();
+
+  delete state["outstanding"][interest.getName().toUri()];
+
+  state["version"] = name.get(state["prefix"].size() + 2).toVersion();
+
+  state["results"] = new Name(state["prefix"]).append("query-results").appendVersion(state["version"]);
+
+  expressNextInterest();
+}
+
+function onQueryResultsData(interest, data) {
+  var name = data.getName();
+  delete state["outstanding"][interest.getName().toUri()];
+
+  if (!name.get(-1).equals(new Name.Component("END"))) {
+    expressNextInterest();
+  } else {
+    alert("found final block");
+  }
+
+  state["userOnData"](data);
+}
+
+function onQueryTimeout(interest) {
+  var uri = interest.getName().toUri();
+  if (state["outstanding"][uri] < 1) {
+    state["outstanding"][uri] ++;
+    face.expressInterest(interest,
+        onQueryData,
+        onQueryTimeout);
+  } else {
+    delete state["outstanding"][uri];
+
+    // We modify the autocomplete box here because we need to know
+    // we have all of the entries first. Fairly hacky.
+    var autocompleteFullName = autocompleteText.value;
+    for (var i = 0; i < dropdown.length; ++i) {
+      if (dropdown[i].substr(0, dropdown[i].length - 1).toUpperCase === autocompleteText.value.toUpperCase || dropdown.length == 1) {
+        autocompleteText.value = dropdown[i];
+      }
+    }
+  }
+}
+
+function onQueryResultsTimeout(interest) {
+  var uri = interest.getName().toUri();
+  if (state["outstanding"][uri] < 1) {
+    state["outstanding"][uri] ++;
+    face.expressInterest(interest,
+        onQueryResultsData,
+        onQueryResultsTimeout);
+  } else {
+    delete state["outstanding"][uri];
+    // We modify the autocomplete box here because we need to know
+    // we have all of the entries first. Fairly hacky.
+    var autocompleteFullName = autocompleteText.value;
+    for (var i = 0; i < dropdown.length; ++i) {
+      if (dropdown[i].substr(0, dropdown[i].length - 1).toUpperCase === autocompleteText.value.toUpperCase || dropdown.length == 1) {
+        autocompleteText.value = dropdown[i];
+      }
+    }
+  }
+}
+
+
+var currentViewIndex = 0;
+
+function populateResults(startIndex) {
+  var resultTable = $(".resultTable");
+  resultTable.empty();
+  resultTable.append('<tr><td>Results</td></tr>');
+
+
+  for (var i = startIndex; i < startIndex + 20 && i < results.length; ++i) {
+    resultTable.append('<tr><td>' + results[i] + '</td></tr>');
+  }
+
+  if (results.length <= 20) {
+    page = 1;
+  } else {
+    page = startIndex / 20 + 1;
+  }
+
+  totalPages = Math.ceil(results.length / 20);
+
+  var currentPage = $(".page");
+  currentPage.empty();
+  if (page != 1) {
+    currentPage.append('<a href="#" onclick="getPage(this.id);" id="<"><</a>');
+  }
+  // This section of code creates the paging for the results.
+  // To prevent it from having a 1000+ pages, it will only show the 5 pages before/after
+  // the current page and the total pages (expect users not to really jump around a lot).
+  for (var i = 1; i <= totalPages; ++i) {
+    if (i == 1 || i == totalPages     // Min or max
+        || (i <= page && i + 5 >= page)    // in our current page range
+        || (i >= page && i - 5 <= page)) { // in our current page range
+      if (i != page) {
+        currentPage.append(' <a href="#" onclick="getPage(' + i + ');">' + i + '</a>');
+        if (i == 1 && page > i + 5) {
+          currentPage.append(' ... ');
+        }
+      } else {
+        currentPage.append(' ' + i);
+      }
+    } else { // Need to skip ahead
+      if (i == page + 6) {
+        currentPage.append(' ... ');
+
+        currentPage.append(' <a href="#" onclick="getPage(this.id);" id=">">></a>');
+        i = totalPages - 1;
+      }
+    }
+  }
+  currentPage.append('  ' + results.length + ' results');
+}
+
+var dropState = "";
+
+function getDropDown(str) {
+  var searchMenu = $("#side-menu");
+  if (str == dropState) {
+    dropState = "";
+    searchMenu.find("#" + str).find("#options_" + str).empty();
+  } else {
+    dropState = str;
+
+    $.each(searchMenuOptions, function (search, fields) {
+      if (search === str) {
+        searchMenu.find("#" + search).append('<ul id="options_' + search + '" class="sub-menu">');
+        for (var i = 0; i < fields.length; ++i) {
+          searchMenu.find("#options_" + search).append('<li id="' + fields[i] + '"onclick="submitCatalogSearch(this.id)"><a href="#">' + fields[i] + '</a></li>');
+        }
+        searchMenu.append('</ul>');
+      } else {
+        var ul = $("options_" + search);
+        ul.empty();
+        searchMenu.find("#" + search).find("#options_" + search).empty();
+      }
+    });
+  }
+}
+
+function getPage(clickedPage) {
+  console.log(clickedPage);
+
+  var nextPage = clickedPage;
+  if (clickedPage === "<") {
+    nextPage = page - 5;
+  } else if (clickedPage === ">") {
+    console.log("> enabled");
+
+    nextPage = page + 5;
+  }
+
+  nextPage--; // Need to adjust for starting at 0
+
+  if (nextPage < 0 ) {
+    nextPage = 0;
+    console.log("0 enabled");
+  } else if (nextPage > totalPages - 1) {
+    nextPage = totalPages - 1;
+    console.log("total enabled");
+  }
+
+  populateResults(nextPage * 20);
+  return false;
+}
+
+function submitAutoComplete() {
+  if (autocompleteText.value.length > 0) {
+    var selection = autocompleteText.value;
+    $.each(dropdown, function (i, dropdownEntry) {
+      if (dropdownEntry.substr(0, dropdownEntry.length - 1) == selection) {
+        selection = dropdownEntry;
+      }
+    });
+
+    selectedSearch["?"] = selection;
+    query(catalog, selectedSearch, onData, 1);
+    delete selectedSearch["?"];
+  }
+}
+
+function submitCatalogSearch(field) {
+  console.log("Sumbit Catalog Search: " + field);
+  // @todo: this logic isn't quite right
+  var remove = false;
+  $.each(selectedSearch, function (search, f) {
+    if (field == f) {
+      delete selectedSearch[field];
+      remove = true;
+    }
+  });
+  if (!remove) {
+    $.each(searchMenuOptions, function (search, fields) {
+      $.each(fields, function (index, f) {
+        if (f == field) {
+          selectedSearch[search] = field;
+        }
+      });
+    });
+  }
+  query(catalog, selectedSearch, onData, 1);
+  populateCurrentSelections();
+  return false;
+}
+
+function populateAutocomplete(fields) {
+  var isAutocompleteFullName = (autocompleteText.value.charAt(autocompleteText.value.length - 1) === "/");
+  var autocompleteFullName = autocompleteText.value;
+  for (var i = 0; i < fields.length; ++i) {
+    var fieldFullName = fields[i];
+    var entry = autocompleteFullName;
+    var skipahead = "";
+
+    if (isAutocompleteFullName) {
+      skipahead = fieldFullName.substr(autocompleteText.value.length, fieldFullName.length);
+    } else {
+      if (fieldFullName.charAt(autocompleteText.value.length) === "/") {
+        entry += "/";
+        skipahead = fieldFullName.substr(autocompleteText.value.length + 1, fieldFullName.length);
+      } else {
+        skipahead = fieldFullName.substr(autocompleteText.value.length, fieldFullName.length);
+      }
+    }
+    if (skipahead.indexOf("/") != -1) {
+      entry += skipahead.substr(0, skipahead.indexOf("/") + 1);
+    } else {
+      entry += skipahead;
+    }
+
+    var added = false;
+    for (var j = 0; j < dropdown.length && !added; ++j) {
+      if (dropdown[j] === entry) {
+        added = true;
+      } else if (dropdown[j] > entry) {
+        dropdown.splice(j, 0, entry);
+        added = true;
+      }
+    }
+    if (!added) {
+      dropdown.push(entry);
+    }
+
+  }
+  $("#autocompleteText").autocomplete({
+    source: dropdown
+  });
+}
+
+function populateCurrentSelections() {
+  var currentSelection = $(".currentSelections");
+  currentSelection.empty();
+
+  currentSelection.append("<p>Filtering on:");
+
+  $.each(selectedSearch, function (searchMenuCatagory, selection) {
+    currentSelection.append('  <a href="#" onclick="removeFilter(this.id);" id="' + searchMenuCatagory + ':' + selection + '">[X] ' + searchMenuCatagory + ":" + selection + '</a>');
+  });
+
+  currentSelection.append("</p>");
+}
+
+
+function removeFilter(filter) {
+  console.log("Remove filter" + filter);
+  var searchFilter = filter.split(":");
+
+  var search = "";
+  for (var j = 0; j < searchFilter.length; ++j) {
+    search += searchFilter[j] + " ";
+  }
+  console.log("Split values: '" + search + "'");
+
+  delete selectedSearch[searchFilter[0]];
+  query(catalog, selectedSearch, onData, 1);
+  populateCurrentSelections();
+
+  return false;
+}
diff --git a/client/query/query2.css b/client/query/query2.css
new file mode 100644
index 0000000..3818f8b
--- /dev/null
+++ b/client/query/query2.css
@@ -0,0 +1,15 @@
+html, body {
+  width: 100%;
+  height: 100%;
+  margin: 0;
+}
+
+.full-width {
+  width: 100%;
+}
+
+.sidebar {
+  height: 100%;
+  max-height: 100%;
+  overflow: auto;
+}