blob: f6b81ad1ce71080fae27db5b08eea93074c9c6a3 [file] [log] [blame]
Tyler Scottf08ab962015-06-30 16:31:29 -06001//{@ @todo: this need to be configured before the document load
Tyler Scott3c17d5f2015-06-23 17:49:29 -06002var catalog = "/catalog/myUniqueName";
Tyler Scotta1ac69d2015-07-02 17:42:03 -06003var config = {
Tyler Scott575c61b2015-07-13 13:42:16 -06004 host: "ndn-atmos.atmos.colostate.edu",
Tyler Scott3c17d5f2015-06-23 17:49:29 -06005 port: 9696
Tyler Scotta1ac69d2015-07-02 17:42:03 -06006};
Tyler Scott3c17d5f2015-06-23 17:49:29 -06007
8// @}
9
Tyler Scotta1ac69d2015-07-02 17:42:03 -060010var atmos = {}; //Comment this out if you don't want debug access.
11
12//Run when the document loads.
13$(function () {
Tyler Scott7d076e22015-07-06 19:21:50 -060014
15 //remove "atmos =" if you don't want debug access
Tyler Scotta1ac69d2015-07-02 17:42:03 -060016 atmos = new Atmos(catalog, config);
Tyler Scottf65b7102015-06-30 18:40:14 -060017
Tyler Scott3c17d5f2015-06-23 17:49:29 -060018});
19
Tyler Scott7d076e22015-07-06 19:21:50 -060020/*
Tyler Scotte815d3e2015-07-09 16:56:17 -060021
Tyler Scott7d076e22015-07-06 19:21:50 -060022*/
23
Tyler Scotte815d3e2015-07-09 16:56:17 -060024/**
25 * Atmos
26 * @version 2.0
27 *
28 * Configures an Atmos object. This manages the atmos interface.
29 *
30 * @constructor
31 * @param {string} catalog - NDN path
32 * @param {Object} config - Object of configuration options for a Face.
33 */
Tyler Scott7d076e22015-07-06 19:21:50 -060034function Atmos(catalog, config){
35 "use strict";
36 //Internal variables.
37 this.results = []
38 this.resultCount = 0;
39 this.page = 1;
40 this.totalPages = 1;
41 this.selectedSearch = {};
42 this.dropdown = [];
43 this.state = {};
44 this.currentViewIndex = 0;
45
Tyler Scotte815d3e2015-07-09 16:56:17 -060046 this.catalog = catalog;
47
Tyler Scott7d076e22015-07-06 19:21:50 -060048 this.face = new Face(config);
49 this.categories = $('#side-menu');
50 this.resultTable = $('#resultTable');
Tyler Scotte815d3e2015-07-09 16:56:17 -060051 this.filters = $('#filters');
Tyler Scott7d076e22015-07-06 19:21:50 -060052
53 var scope = this;
54
55 this.resultTable.on('click', '.interest-button', function(){
56 var button = $(this);
57
58 var name = button.parent().prev().text();
59 var interest = new Interest(new Name('/retrieve' + name));
60 scope.face.expressInterest(interest, function(){
61 var message = $('<div class="success"><span class="glyphicon glyphicon-ok"></span> Success!</div>');
62 message.insertAfter(button);
63 message.fadeOut(5000);
64 }, function(){
65 var message = $('<div class="fail"><span class="glyphicon glyphicon-remove"></span> Failed!</div>');
66 message.insertAfter(button);
67 message.fadeOut(5000);
68 });
69
70 });
71
72 $.getJSON("search_catagories.json").done(function (data) {
73 $.each(data, function (pageSection, contents) {
74 if (pageSection == "SearchCatagories") {
75 $.each(contents, function (search, searchOptions) {
76 var e = $('<li><a href="#">' + search.replace(/\_/g, " ") + '</a><ul class="subnav nav nav-pills nav-stacked"></ul></li>');
77
78 var sub = e.find('ul.subnav');
79 $.each(searchOptions, function(index, name){
80 var item = $('<li><a href="#">' + name + '</a></li>');
81 sub.append(item);
82 item.click(function(){
Tyler Scott575c61b2015-07-13 13:42:16 -060083 scope.addFilter(name, search);
Tyler Scott7d076e22015-07-06 19:21:50 -060084 });
85 });
86
87 //Toggle the menus.
88 e.click(function(){
89 scope.categories.find('.subnav').slideUp();
90 var t = $(this).find('.subnav');
91 if ( !t.is(':visible')){
92 t.slideDown().triggerHandler('focus'); //Cancel other animations and slide down.
93 }
94 });
95
96 scope.categories.append(e);
97 });
98 }
99 });
100 });
101
102 $('#searchBar').submit(function(e){
103 e.preventDefault();
Tyler Scotte815d3e2015-07-09 16:56:17 -0600104 console.log("Search started!", $('#search'));
Tyler Scott7d076e22015-07-06 19:21:50 -0600105 })
106
107}
108
109Atmos.prototype.onData = function(data) {
110 var payloadStr = data.content.toString().split("\n")[0];
111
112 var queryResults = JSON.parse(payloadStr);
113
114 var scope = this;
115
116 $.each(this.queryResults, function (queryResult, field) {
117
118 if (queryResult == "next") {
119 scope.populateAutocomplete(field);
120 }
121
122 $.each(field, function (entryCount, name) {
123 scope.results.push(name);
124 });
125 });
126
127 // Calculating the current page and the view
128 this.totalPages = Math.ceil(this.resultCount / 20);
129 this.populateResults(0);
130}
131
132Atmos.prototype.query = function(prefix, parameters, callback, pipeline) {
133 this.results = [];
134 this.dropdown = [];
135 this.resultTable.empty();
136 this.resultTable.append('<tr><th colspan="2">Results</th></tr>');
137
138 var queryPrefix = new Name(prefix);
139 queryPrefix.append("query");
140
141 var jsonString = JSON.stringify(parameters);
142 queryPrefix.append(jsonString);
143
144 this.state = {
145 prefix: new Name(prefix),
146 userOnData: callback,
147 outstanding: {},
148 nextSegment: 0,
Tyler Scott575c61b2015-07-13 13:42:16 -0600149 parameters: jsonString
Tyler Scott7d076e22015-07-06 19:21:50 -0600150 };
151
152 /*if (state.hasOwnProperty("version")) {
153 console.log("state already has version");
154 }*/
155
156 var queryInterest = new Interest(queryPrefix);
157 queryInterest.setInterestLifetimeMilliseconds(10000);
158
159 var scope = this;
160
161 this.face.expressInterest(queryInterest,
Tyler Scotte815d3e2015-07-09 16:56:17 -0600162 function(interest, data){
163 scope.onQueryData(interest, data);
164 }, function(interest){
165 scope.onQueryTimeout(interest);
Tyler Scott7d076e22015-07-06 19:21:50 -0600166 }
167 );
168
169 this.state["outstanding"][queryInterest.getName().toUri()] = 0;
170}
171
Tyler Scotte815d3e2015-07-09 16:56:17 -0600172/**
173 * @deprecated
174 * Use applyFilters/addFilters as appropriate.
175 */
Tyler Scott7d076e22015-07-06 19:21:50 -0600176Atmos.prototype.submitCatalogSearch = function(field) {
Tyler Scotte815d3e2015-07-09 16:56:17 -0600177 console.warn("Use of deprecated function submitCatalogSearch! (Use applyFilters/addFilters as appropriate)");
Tyler Scott7d076e22015-07-06 19:21:50 -0600178}
179
180Atmos.prototype.expressNextInterest = function() {
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600181 // @todo pipelines
Tyler Scotte815d3e2015-07-09 16:56:17 -0600182 var nextName = new Name(this.state["results"]);
183 nextName.appendSegment(this.state["nextSegment"]);
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600184
185 var nextInterest = new Interest(nextName);
186 nextInterest.setInterestLifetimeMilliseconds(10000);
187
Tyler Scott7d076e22015-07-06 19:21:50 -0600188 var scope = this;
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600189
Tyler Scott7d076e22015-07-06 19:21:50 -0600190 this.face.expressInterest(nextInterest,
Tyler Scotte815d3e2015-07-09 16:56:17 -0600191 function(interest, data){
192 scope.onQueryResultsData(interest, data);
Tyler Scott7d076e22015-07-06 19:21:50 -0600193 },
Tyler Scotte815d3e2015-07-09 16:56:17 -0600194 function(interest){
195 scope.onQueryResultsTimeout(interest);
Tyler Scott7d076e22015-07-06 19:21:50 -0600196 });
197
198 this.state["nextSegment"] ++;
199 this.state["outstanding"][nextName.toUri()] = 0;
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600200}
201
Tyler Scott7d076e22015-07-06 19:21:50 -0600202Atmos.prototype.onQueryData = function(interest, data) {
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600203 var name = data.getName();
204
Tyler Scott7d076e22015-07-06 19:21:50 -0600205 delete this.state["outstanding"][interest.getName().toUri()];
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600206
Tyler Scott7d076e22015-07-06 19:21:50 -0600207 this.state["version"] = name.get(this.state["prefix"].size() + 2).toVersion();
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600208
Tyler Scott575c61b2015-07-13 13:42:16 -0600209 this.state["results"] = new Name(this.state["prefix"]).append("query-results").append(this.state['parameters']).appendVersion(this.state["version"]);
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600210
Tyler Scott7d076e22015-07-06 19:21:50 -0600211 this.expressNextInterest();
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600212}
213
Tyler Scott7d076e22015-07-06 19:21:50 -0600214Atmos.prototype.onQueryResultsData = function(interest, data) {
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600215 var name = data.getName();
Tyler Scott7d076e22015-07-06 19:21:50 -0600216 delete this.state["outstanding"][interest.getName().toUri()];
Tyler Scottf08ab962015-06-30 16:31:29 -0600217 if (!name.get(-1).equals(data.getMetaInfo().getFinalBlockId())) {
Tyler Scott7d076e22015-07-06 19:21:50 -0600218 this.expressNextInterest();
Tyler Scottf08ab962015-06-30 16:31:29 -0600219 } //else {
220 //alert("found final block");
221 //}
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600222
Tyler Scott7d076e22015-07-06 19:21:50 -0600223 this.state["userOnData"](data);
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600224}
225
Tyler Scott7d076e22015-07-06 19:21:50 -0600226Atmos.prototype.onQueryTimeout = function(interest) {
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600227 var uri = interest.getName().toUri();
Tyler Scott7d076e22015-07-06 19:21:50 -0600228 if (this.state["outstanding"][uri] < 1) {
229 this.state["outstanding"][uri] ++;
230 var scope = this;
231 this.face.expressInterest(interest,
Tyler Scott575c61b2015-07-13 13:42:16 -0600232 function(interest, data){
233 scope.onQueryData(interest, data);
Tyler Scott7d076e22015-07-06 19:21:50 -0600234 },
Tyler Scott575c61b2015-07-13 13:42:16 -0600235 function(interest){
236 scope.onQueryTimeout(interest);
Tyler Scott7d076e22015-07-06 19:21:50 -0600237 });
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600238 } else {
Tyler Scott7d076e22015-07-06 19:21:50 -0600239 delete this.state["outstanding"][uri];
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600240
241 // We modify the autocomplete box here because we need to know
242 // we have all of the entries first. Fairly hacky.
Tyler Scott7d076e22015-07-06 19:21:50 -0600243 /* TODO FIXME
244 var autocompleteFullName = this.autocompleteText.value;
245 for (var i = 0; i < dropdown.length; ++i) {
246 if (this.dropdown[i].substr(0, dropdown[i].length - 1).toUpperCase === this.autocompleteText.value.toUpperCase || dropdown.length == 1) {
247 autocompleteText.value = dropdown[i];
248 }
249 }
250 */
251 }
252}
253
254Atmos.prototype.onQueryResultsTimeout = function(interest) {
255 var uri = interest.getName().toUri();
256 if (this.state["outstanding"][uri] < 1) {
257 this.state["outstanding"][uri] ++;
258 var scope = this;
259 this.face.expressInterest(interest,
260 function(){
261 scope.onQueryResultsData.apply(scope, arguments);
262 },
263 function(){
264 scope.onQueryResultsTimeout.apply(scope, arguments);
265 });
266 } else {
267 delete this.state["outstanding"][uri];
268 // We modify the autocomplete box here because we need to know
269 // we have all of the entries first. Fairly hacky.
270 /* TODO FIXME
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600271 var autocompleteFullName = autocompleteText.value;
272 for (var i = 0; i < dropdown.length; ++i) {
273 if (dropdown[i].substr(0, dropdown[i].length - 1).toUpperCase === autocompleteText.value.toUpperCase || dropdown.length == 1) {
274 autocompleteText.value = dropdown[i];
275 }
276 }
Tyler Scott7d076e22015-07-06 19:21:50 -0600277 */
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600278 }
279}
280
Tyler Scott7d076e22015-07-06 19:21:50 -0600281Atmos.prototype.populateResults = function(startIndex) {
282 this.resultTable.empty();
283 this.resultTable.append('<tr><th colspan="2">Results</th></tr>');
284
285
286 for (var i = startIndex; i < startIndex + 20 && i < this.results.length; ++i) {
287 resultTable.append('<tr><td>' + this.results[i]
288 + '</td><td><button class="interest-button btn btn-default btn-xs">Retrieve</button></td></tr>');
289 }
290
291 if (this.results.length <= 20) {
292 this.page = 1;
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600293 } else {
Tyler Scott7d076e22015-07-06 19:21:50 -0600294 this.page = startIndex / 20 + 1;
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600295 }
296
Tyler Scott7d076e22015-07-06 19:21:50 -0600297 this.totalPages = Math.ceil(this.results.length / 20);
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600298
Tyler Scott7d076e22015-07-06 19:21:50 -0600299 //TODO Fix the page to fit the theme.
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600300 var currentPage = $(".page");
301 currentPage.empty();
Tyler Scott7d076e22015-07-06 19:21:50 -0600302 if (this.page != 1) {
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600303 currentPage.append('<a href="#" onclick="getPage(this.id);" id="<"><</a>');
304 }
305 // This section of code creates the paging for the results.
306 // To prevent it from having a 1000+ pages, it will only show the 5 pages before/after
307 // the current page and the total pages (expect users not to really jump around a lot).
Tyler Scott7d076e22015-07-06 19:21:50 -0600308 for (var i = 1; i <= this.totalPages; ++i) {
309 if (i == 1 || i == this.totalPages // Min or max
310 || (i <= this.page && i + 5 >= this.page) // in our current page range
311 || (i >= this.page && i - 5 <= this.page)) { // in our current page range
312 if (i != this.page) {
Tyler Scottf08ab962015-06-30 16:31:29 -0600313 currentPage.append(' <a href="#" onclick="getPage(' + i + ');">' + i + '</a>')
Tyler Scott7d076e22015-07-06 19:21:50 -0600314 if (i == 1 && this.page > i + 5) {
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600315 currentPage.append(' ... ');
316 }
317 } else {
318 currentPage.append(' ' + i);
319 }
320 } else { // Need to skip ahead
321 if (i == page + 6) {
322 currentPage.append(' ... ');
323
Tyler Scottf08ab962015-06-30 16:31:29 -0600324 currentPage.append(' <a href="#" onclick="getPage(this.id);" id=">">></a>')
Tyler Scott7d076e22015-07-06 19:21:50 -0600325 i = this.totalPages - 1;
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600326 }
327 }
328 }
Tyler Scott7d076e22015-07-06 19:21:50 -0600329 currentPage.append(' ' + this.results.length + ' results');
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600330}
331
Tyler Scott7d076e22015-07-06 19:21:50 -0600332Atmos.prototype.getPage = function(clickedPage) {
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600333 console.log(clickedPage);
334
335 var nextPage = clickedPage;
336 if (clickedPage === "<") {
Tyler Scott7d076e22015-07-06 19:21:50 -0600337 nextPage = this.page - 5;
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600338 } else if (clickedPage === ">") {
339 console.log("> enabled");
340
Tyler Scott7d076e22015-07-06 19:21:50 -0600341 nextPage = this.page + 5;
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600342 }
343
344 nextPage--; // Need to adjust for starting at 0
345
346 if (nextPage < 0 ) {
347 nextPage = 0;
348 console.log("0 enabled");
Tyler Scott7d076e22015-07-06 19:21:50 -0600349 } else if (nextPage > this.totalPages - 1) {
350 nextPage = this.totalPages - 1;
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600351 console.log("total enabled");
352 }
353
Tyler Scott7d076e22015-07-06 19:21:50 -0600354 this.populateResults(nextPage * 20);
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600355 return false;
356}
357
Tyler Scott7d076e22015-07-06 19:21:50 -0600358
359Atmos.prototype.submitAutoComplete = function() {
360 /* FIXME TODO
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600361 if (autocompleteText.value.length > 0) {
362 var selection = autocompleteText.value;
363 $.each(dropdown, function (i, dropdownEntry) {
364 if (dropdownEntry.substr(0, dropdownEntry.length - 1) == selection) {
365 selection = dropdownEntry;
366 }
367 });
368
369 selectedSearch["?"] = selection;
370 query(catalog, selectedSearch, onData, 1);
371 delete selectedSearch["?"];
372 }
Tyler Scott7d076e22015-07-06 19:21:50 -0600373 */
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600374}
375
Tyler Scott7d076e22015-07-06 19:21:50 -0600376Atmos.prototype.populateAutocomplete = function(fields) {
377 /* FIXME TODO
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600378 var isAutocompleteFullName = (autocompleteText.value.charAt(autocompleteText.value.length - 1) === "/");
379 var autocompleteFullName = autocompleteText.value;
380 for (var i = 0; i < fields.length; ++i) {
381 var fieldFullName = fields[i];
382 var entry = autocompleteFullName;
383 var skipahead = "";
384
385 if (isAutocompleteFullName) {
386 skipahead = fieldFullName.substr(autocompleteText.value.length, fieldFullName.length);
387 } else {
388 if (fieldFullName.charAt(autocompleteText.value.length) === "/") {
389 entry += "/";
390 skipahead = fieldFullName.substr(autocompleteText.value.length + 1, fieldFullName.length);
391 } else {
392 skipahead = fieldFullName.substr(autocompleteText.value.length, fieldFullName.length);
393 }
394 }
395 if (skipahead.indexOf("/") != -1) {
396 entry += skipahead.substr(0, skipahead.indexOf("/") + 1);
397 } else {
398 entry += skipahead;
399 }
400
401 var added = false;
402 for (var j = 0; j < dropdown.length && !added; ++j) {
403 if (dropdown[j] === entry) {
404 added = true;
405 } else if (dropdown[j] > entry) {
406 dropdown.splice(j, 0, entry);
407 added = true;
408 }
409 }
410 if (!added) {
411 dropdown.push(entry);
412 }
413
414 }
415 $("#autocompleteText").autocomplete({
416 source: dropdown
417 });
Tyler Scott7d076e22015-07-06 19:21:50 -0600418 */
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600419}
420
Tyler Scotte815d3e2015-07-09 16:56:17 -0600421/**
422 * Adds a filter to the list of filters.
423 * There is no need to remove the filter, it is done via ui clicks.
424 *
425 * @param {string} filter
426 */
Tyler Scott575c61b2015-07-13 13:42:16 -0600427Atmos.prototype.addFilter = function(name, category){
Tyler Scotte815d3e2015-07-09 16:56:17 -0600428 var existing = this.filters.find('span:contains(' + name + ')');
429 if (existing.length){
430 //If the category is clicked twice, then we delete it.
431 existing.remove();
432 this.applyFilters();
433
434 } else {
435
436 var filter = $('<span class="label label-default"></span>');
Tyler Scott575c61b2015-07-13 13:42:16 -0600437 filter.text(category + ':' + name);
Tyler Scotte815d3e2015-07-09 16:56:17 -0600438
439 this.filters.append(filter);
440
441 this.applyFilters();
442
443 var scope = this;
444 filter.click(function(){
445 $(this).remove();
446 scope.applyFilters();
447 });
448
449 }
450
451}
452
453Atmos.prototype.applyFilters = function(){
Tyler Scott575c61b2015-07-13 13:42:16 -0600454 var filters = this.filters.children().toArray().reduce(function(prev, current){
455 var data = $(current).text().split(/:/);
456 prev[data[0]] = data[1];
457 return prev;
458 }, {});
Tyler Scotte815d3e2015-07-09 16:56:17 -0600459 console.log('Collected filters:', filters);
460 this.query(this.catalog, filters, function(data){
461 scope.onData(data);
462 }, 1);
463}
464
465/**
466 * @deprecated
467 * Use addFilter instead.
468 */
469Atmos.prototype.populateCurrentSelections = function() {
470 console.warn("Use of deprecated function populateCurrentSelections! (Use addFilter instead)");
471 /*
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600472 var currentSelection = $(".currentSelections");
473 currentSelection.empty();
474
475 currentSelection.append("<p>Filtering on:");
476
Tyler Scott7d076e22015-07-06 19:21:50 -0600477 var scope = this;
478
479 $.each(this.selectedSearch, function (searchMenuCatagory, selection) {
480 var e = $('<a href="#">[X] ' + searchMenuCatagory + ":" + selection + '</a>');
481 e.onclick(function(){
482 var searchFilter = $(this).text();
483
484 var search = "";
485 for (var j = 0; j < searchFilter.length; ++j) {
486 search += searchFilter[j] + " ";
487 }
488 console.log("Split values: '" + search + "'");
489
490 delete this.selectedSearch[searchFilter[0]];
491 this.query(catalog, selectedSearch, onData, 1);
492 populateCurrentSelections();
493 });
494 currentSelection.append(e);
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600495 });
496
497 currentSelection.append("</p>");
Tyler Scotte815d3e2015-07-09 16:56:17 -0600498 */
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600499}