blob: e20f8f6d4ac84a40b077316c5870c4243c319913 [file] [log] [blame]
Tyler Scott3c17d5f2015-06-23 17:49:29 -06001var catalog = "/catalog/myUniqueName";
Tyler Scotta1ac69d2015-07-02 17:42:03 -06002var config = {
Tyler Scott087aef72015-07-14 14:11:59 -06003 host: "atmos-csu.research-lan.colostate.edu",
Tyler Scott3c17d5f2015-06-23 17:49:29 -06004 port: 9696
Tyler Scotta1ac69d2015-07-02 17:42:03 -06005};
Tyler Scott3c17d5f2015-06-23 17:49:29 -06006
Tyler Scotta1ac69d2015-07-02 17:42:03 -06007//Run when the document loads.
8$(function () {
Tyler Scottbb013562015-07-16 15:52:40 -06009 new Atmos(catalog, config);
Tyler Scott3c17d5f2015-06-23 17:49:29 -060010});
11
Tyler Scott93cae872015-07-21 14:58:23 -060012var Atmos = (function(){
Tyler Scott7d076e22015-07-06 19:21:50 -060013 "use strict";
Tyler Scott93cae872015-07-21 14:58:23 -060014 /**
15 * Atmos
16 * @version 2.0
17 *
18 * Configures an Atmos object. This manages the atmos interface.
19 *
20 * @constructor
21 * @param {string} catalog - NDN path
22 * @param {Object} config - Object of configuration options for a Face.
23 */
24 function Atmos(catalog, config){
Tyler Scott7d076e22015-07-06 19:21:50 -060025
Tyler Scott93cae872015-07-21 14:58:23 -060026 //Internal variables.
27 this.results = [];
28 this.resultCount = 0;
29 this.page = 1;
30 this.totalPages = 1;
31 this.selectedSearch = {};
32 this.dropdown = [];
33 this.state = {};
34 this.currentViewIndex = 0;
Tyler Scotte815d3e2015-07-09 16:56:17 -060035
Tyler Scott93cae872015-07-21 14:58:23 -060036 this.catalog = catalog;
Tyler Scott7d076e22015-07-06 19:21:50 -060037
Tyler Scott93cae872015-07-21 14:58:23 -060038 this.face = new Face(config);
39 this.categories = $('#side-menu');
40 this.resultTable = $('#resultTable');
41 this.filters = $('#filters');
Tyler Scott7d076e22015-07-06 19:21:50 -060042
Tyler Scott93cae872015-07-21 14:58:23 -060043 var scope = this;
Tyler Scott7d076e22015-07-06 19:21:50 -060044
Tyler Scott93cae872015-07-21 14:58:23 -060045 this.resultTable.on('click', '.interest-button', function(){
46 var button = $(this);
Tyler Scott087aef72015-07-14 14:11:59 -060047
Tyler Scott93cae872015-07-21 14:58:23 -060048 if (button.is(':disabled')){
49 console.warn("Attempt to request again!");
Tyler Scott7d076e22015-07-06 19:21:50 -060050 }
Tyler Scott93cae872015-07-21 14:58:23 -060051
52 var name = button.parent().prev().text();
53 var interest = new Interest(new Name('/retrieve' + name));
54 scope.face.expressInterest(interest, function(){}, function(){});
55
56 button.text("Requested!")
57 .removeClass('btn-primary')
58 .addClass('btn-default')
59 .addClass('disabled')
60 .prop('disabled', true);
61
Tyler Scott7d076e22015-07-06 19:21:50 -060062 });
Tyler Scott7d076e22015-07-06 19:21:50 -060063
Tyler Scott93cae872015-07-21 14:58:23 -060064 $.getJSON("search_catagories.json").done(function (data) {
65 $.each(data, function (pageSection, contents) {
66 if (pageSection == "SearchCatagories") {
67 $.each(contents, function (search, searchOptions) {
68 var e = $('<li><a href="#">' + search.replace(/\_/g, " ") + '</a><ul class="subnav nav nav-pills nav-stacked"></ul></li>');
Tyler Scott7d076e22015-07-06 19:21:50 -060069
Tyler Scott93cae872015-07-21 14:58:23 -060070 var sub = e.find('ul.subnav');
71 $.each(searchOptions, function(index, name){
72 var item = $('<li><a href="#">' + name + '</a></li>');
73 sub.append(item);
74 item.click(function(){
75 scope.addFilter(name, search);
76 });
77 });
Tyler Scott7d076e22015-07-06 19:21:50 -060078
Tyler Scott93cae872015-07-21 14:58:23 -060079 //Toggle the menus. (Only respond when the immediate tab is clicked.)
80 e.find('> a').click(function(){
81 scope.categories.find('.subnav').slideUp();
82 var t = $(this).siblings('.subnav');
83 if ( !t.is(':visible')){
84 t.slideDown().triggerHandler('focus'); //Cancel other animations and slide down.
85 }
86 });
Tyler Scott7d076e22015-07-06 19:21:50 -060087
Tyler Scott93cae872015-07-21 14:58:23 -060088 scope.categories.append(e);
89 });
90 }
91 });
92 });
93
94 $('#searchBar').submit(function(e){
95 e.preventDefault();
96 console.log("Search started!", $('#search'));
97 })
98
Tyler Scott424ee102015-07-14 16:50:41 -060099 }
100
Tyler Scott93cae872015-07-21 14:58:23 -0600101 Atmos.prototype.onData = function(data) {
102 console.log("Recieved data", data);
Tyler Scott7d076e22015-07-06 19:21:50 -0600103
Tyler Scott93cae872015-07-21 14:58:23 -0600104 var payloadStr = data.content.toString().split("\n")[0];
Tyler Scott7d076e22015-07-06 19:21:50 -0600105
Tyler Scott93cae872015-07-21 14:58:23 -0600106 if (!payloadStr || payloadStr.length === 0){
107 this.populateResults();
108 return; //No results were returned.
Tyler Scottbb013562015-07-16 15:52:40 -0600109 }
Tyler Scott7d076e22015-07-06 19:21:50 -0600110
Tyler Scott93cae872015-07-21 14:58:23 -0600111 var queryResults = JSON.parse(payloadStr);
Tyler Scott7d076e22015-07-06 19:21:50 -0600112
Tyler Scott93cae872015-07-21 14:58:23 -0600113 var scope = this;
114
115 $.each(queryResults, function (queryResult, field) {
116
117 if (queryResult == "next") {
118 scope.populateAutocomplete(field);
119 }
120
121 if (queryResult == "results" && field == null){
122 return; //Sometimes the results are null. (We should skip this.)
123 }
124
125 $.each(field, function (entryCount, name) {
126 scope.results.push(name);
127 });
Tyler Scott7d076e22015-07-06 19:21:50 -0600128 });
Tyler Scott7d076e22015-07-06 19:21:50 -0600129
Tyler Scott93cae872015-07-21 14:58:23 -0600130 // Calculating the current page and the view
131 this.populateResults();
Tyler Scott424ee102015-07-14 16:50:41 -0600132
Tyler Scott93cae872015-07-21 14:58:23 -0600133 }
Tyler Scott7d076e22015-07-06 19:21:50 -0600134
Tyler Scott93cae872015-07-21 14:58:23 -0600135 Atmos.prototype.query = function(prefix, parameters, callback, pipeline) {
136 this.results = [];
137 this.dropdown = [];
138 this.resultTable.empty();
Tyler Scott7d076e22015-07-06 19:21:50 -0600139
Tyler Scott93cae872015-07-21 14:58:23 -0600140 var queryPrefix = new Name(prefix);
141 queryPrefix.append("query");
Tyler Scott7d076e22015-07-06 19:21:50 -0600142
Tyler Scott93cae872015-07-21 14:58:23 -0600143 var jsonString = JSON.stringify(parameters);
144 queryPrefix.append(jsonString);
Tyler Scott7d076e22015-07-06 19:21:50 -0600145
Tyler Scott93cae872015-07-21 14:58:23 -0600146 this.state = {
Tyler Scott7d076e22015-07-06 19:21:50 -0600147 prefix: new Name(prefix),
148 userOnData: callback,
149 outstanding: {},
150 nextSegment: 0,
Tyler Scott575c61b2015-07-13 13:42:16 -0600151 parameters: jsonString
Tyler Scott93cae872015-07-21 14:58:23 -0600152 };
Tyler Scott7d076e22015-07-06 19:21:50 -0600153
Tyler Scott93cae872015-07-21 14:58:23 -0600154 /*if (state.hasOwnProperty("version")) {
155 console.log("state already has version");
156 }*/
Tyler Scott7d076e22015-07-06 19:21:50 -0600157
Tyler Scott93cae872015-07-21 14:58:23 -0600158 var queryInterest = new Interest(queryPrefix);
159 queryInterest.setInterestLifetimeMilliseconds(10000);
Tyler Scott7d076e22015-07-06 19:21:50 -0600160
Tyler Scott7d076e22015-07-06 19:21:50 -0600161 var scope = this;
Tyler Scott93cae872015-07-21 14:58:23 -0600162
163 this.face.expressInterest(queryInterest,
Tyler Scott575c61b2015-07-13 13:42:16 -0600164 function(interest, data){
165 scope.onQueryData(interest, data);
Tyler Scott93cae872015-07-21 14:58:23 -0600166 }, function(interest){
167 scope.onQueryTimeout(interest);
168 }
169 );
170
171 this.state["outstanding"][queryInterest.getName().toUri()] = 0;
172 }
173
174 /**
175 * @deprecated
176 * Use applyFilters/addFilters as appropriate.
177 */
178 Atmos.prototype.submitCatalogSearch = function(field) {
179 console.warn("Use of deprecated function submitCatalogSearch! (Use applyFilters/addFilters as appropriate)");
180 }
181
182 Atmos.prototype.expressNextInterest = function() {
183 // @todo pipelines
184 var nextName = new Name(this.state["results"]);
185 nextName.appendSegment(this.state["nextSegment"]);
186
187 var nextInterest = new Interest(nextName);
188 nextInterest.setInterestLifetimeMilliseconds(10000);
189
190 var scope = this;
191
192 this.face.expressInterest(nextInterest,
193 function(interest, data){
194 scope.onQueryResultsData(interest, data);
Tyler Scott7d076e22015-07-06 19:21:50 -0600195 },
Tyler Scott575c61b2015-07-13 13:42:16 -0600196 function(interest){
Tyler Scott93cae872015-07-21 14:58:23 -0600197 scope.onQueryResultsTimeout(interest);
Tyler Scott7d076e22015-07-06 19:21:50 -0600198 });
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600199
Tyler Scott93cae872015-07-21 14:58:23 -0600200 this.state["nextSegment"] ++;
201 this.state["outstanding"][nextName.toUri()] = 0;
202 }
203
204 Atmos.prototype.onQueryData = function(interest, data) {
205 var name = data.getName();
206
207 delete this.state["outstanding"][interest.getName().toUri()];
208
209 this.state["version"] = name.get(this.state["prefix"].size() + 2).toVersion();
210
211 this.state["results"] = new Name(this.state["prefix"]).append("query-results").append(this.state['parameters'])
212 .appendVersion(this.state["version"]).append(name.getComponent(name.getComponentCount() - 2));
213
214 console.log("Requested URI", this.state.results.toUri());
215
216 this.expressNextInterest();
217 }
218
219 Atmos.prototype.onQueryResultsData = function(interest, data) {
220 var name = data.getName();
221 delete this.state["outstanding"][interest.getName().toUri()];
222 if (!name.get(-1).equals(data.getMetaInfo().getFinalBlockId())) {
223 this.expressNextInterest();
224 } //else {
225 //alert("found final block");
226 //}
227
228 this.state["userOnData"](data);
229 }
230
231 Atmos.prototype.onQueryTimeout = function(interest) {
232 var uri = interest.getName().toUri();
233 if (this.state["outstanding"][uri] < 1) {
234 this.state["outstanding"][uri] ++;
235 var scope = this;
236 this.face.expressInterest(interest,
237 function(interest, data){
238 scope.onQueryData(interest, data);
239 },
240 function(interest){
241 scope.onQueryTimeout(interest);
242 });
243 } else {
244 delete this.state["outstanding"][uri];
245
246 // We modify the autocomplete box here because we need to know
247 // we have all of the entries first. Fairly hacky.
248 /* TODO FIXME
249 var autocompleteFullName = this.autocompleteText.value;
250 for (var i = 0; i < dropdown.length; ++i) {
251 if (this.dropdown[i].substr(0, dropdown[i].length - 1).toUpperCase === this.autocompleteText.value.toUpperCase || dropdown.length == 1) {
252 autocompleteText.value = dropdown[i];
253 }
254 }
255 */
Tyler Scott7d076e22015-07-06 19:21:50 -0600256 }
Tyler Scott7d076e22015-07-06 19:21:50 -0600257 }
Tyler Scott7d076e22015-07-06 19:21:50 -0600258
Tyler Scott93cae872015-07-21 14:58:23 -0600259 Atmos.prototype.onQueryResultsTimeout = function(interest) {
260 var uri = interest.getName().toUri();
261 if (this.state["outstanding"][uri] < 1) {
262 this.state["outstanding"][uri] ++;
263 var scope = this;
264 this.face.expressInterest(interest,
265 function(){
266 scope.onQueryResultsData.apply(scope, arguments);
267 },
268 function(){
269 scope.onQueryResultsTimeout.apply(scope, arguments);
270 });
271 } else {
272 delete this.state["outstanding"][uri];
273 // We modify the autocomplete box here because we need to know
274 // we have all of the entries first. Fairly hacky.
275 /* TODO FIXME
276 var autocompleteFullName = autocompleteText.value;
277 for (var i = 0; i < dropdown.length; ++i) {
278 if (dropdown[i].substr(0, dropdown[i].length - 1).toUpperCase === autocompleteText.value.toUpperCase || dropdown.length == 1) {
279 autocompleteText.value = dropdown[i];
280 }
281 }
282 */
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600283 }
Tyler Scott7d076e22015-07-06 19:21:50 -0600284 }
285
Tyler Scott93cae872015-07-21 14:58:23 -0600286 Atmos.prototype.populateResults = function() {
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600287
Tyler Scott93cae872015-07-21 14:58:23 -0600288 //TODO Check only for page changes and result length
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600289
Tyler Scott93cae872015-07-21 14:58:23 -0600290 this.resultTable.empty();
291
292 for (var i = startIndex; i < startIndex + 20 && i < this.results.length; ++i) {
293 this.resultTable.append('<tr><td>' + this.results[i]
294 + '</td><td><button class="interest-button btn btn-primary btn-xs">Retrieve</button></td></tr>');
295 }
296
297 if (this.results.length <= 20) {
298 this.page = 1;
299 } else {
300 this.page = startIndex / 20 + 1;
301 }
302
303 this.totalPages = Math.ceil(this.results.length / 20);
304
305 //TODO Fix the page to fit the theme.
306 var currentPage = $(".page");
307 currentPage.empty();
308 if (this.page != 1) {
309 currentPage.append('<a href="#" onclick="getPage(this.id);" id="<"><</a>');
310 }
311 // This section of code creates the paging for the results.
312 // To prevent it from having a 1000+ pages, it will only show the 5 pages before/after
313 // the current page and the total pages (expect users not to really jump around a lot).
314 for (var i = 1; i <= this.totalPages; ++i) {
315 if (i == 1 || i == this.totalPages // Min or max
316 || (i <= this.page && i + 5 >= this.page) // in our current page range
317 || (i >= this.page && i - 5 <= this.page)) { // in our current page range
318 if (i != this.page) {
319 currentPage.append(' <a href="#" onclick="getPage(' + i + ');">' + i + '</a>')
320 if (i == 1 && this.page > i + 5) {
321 currentPage.append(' ... ');
322 }
323 } else {
324 currentPage.append(' ' + i);
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600325 }
Tyler Scott93cae872015-07-21 14:58:23 -0600326 } else { // Need to skip ahead
327 if (i == this.page + 6) {
328 currentPage.append(' ... ');
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600329
Tyler Scott93cae872015-07-21 14:58:23 -0600330 currentPage.append(' <a href="#" onclick="getPage(this.id);" id=">">></a>')
331 i = this.totalPages - 1;
332 }
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600333 }
334 }
Tyler Scott93cae872015-07-21 14:58:23 -0600335 currentPage.append(' ' + this.results.length + ' results');
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600336 }
337
Tyler Scott93cae872015-07-21 14:58:23 -0600338 Atmos.prototype.getPage = function(clickedPage) {
339 console.log(clickedPage);
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600340
Tyler Scott93cae872015-07-21 14:58:23 -0600341 var nextPage = clickedPage;
342 if (clickedPage === "<") {
343 nextPage = this.page - 5;
344 } else if (clickedPage === ">") {
345 console.log("> enabled");
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600346
Tyler Scott93cae872015-07-21 14:58:23 -0600347 nextPage = this.page + 5;
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600348 }
349
Tyler Scott93cae872015-07-21 14:58:23 -0600350 nextPage--; // Need to adjust for starting at 0
351
352 if (nextPage < 0 ) {
353 nextPage = 0;
354 console.log("0 enabled");
355 } else if (nextPage > this.totalPages - 1) {
356 nextPage = this.totalPages - 1;
357 console.log("total enabled");
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600358 }
359
Tyler Scott93cae872015-07-21 14:58:23 -0600360 this.populateResults(nextPage * 20);
361 return false;
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600362 }
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600363
Tyler Scotte815d3e2015-07-09 16:56:17 -0600364
Tyler Scott93cae872015-07-21 14:58:23 -0600365 Atmos.prototype.submitAutoComplete = function() {
366 console.log("Submitted autoComplete");
367 /* FIXME TODO
368 if (autocompleteText.value.length > 0) {
369 var selection = autocompleteText.value;
370 $.each(dropdown, function (i, dropdownEntry) {
371 if (dropdownEntry.substr(0, dropdownEntry.length - 1) == selection) {
372 selection = dropdownEntry;
373 }
374 });
Tyler Scotte815d3e2015-07-09 16:56:17 -0600375
Tyler Scott93cae872015-07-21 14:58:23 -0600376 selectedSearch["?"] = selection;
377 query(catalog, selectedSearch, onData, 1);
378 delete selectedSearch["?"];
379 }
380 */
381 }
Tyler Scotte815d3e2015-07-09 16:56:17 -0600382
Tyler Scott93cae872015-07-21 14:58:23 -0600383 Atmos.prototype.populateAutocomplete = function(fields) {
384 console.log(fields);
385 /* FIXME TODO
386 var isAutocompleteFullName = (autocompleteText.value.charAt(autocompleteText.value.length - 1) === "/");
387 var autocompleteFullName = autocompleteText.value;
388 for (var i = 0; i < fields.length; ++i) {
389 var fieldFullName = fields[i];
390 var entry = autocompleteFullName;
391 var skipahead = "";
Tyler Scotte815d3e2015-07-09 16:56:17 -0600392
Tyler Scott93cae872015-07-21 14:58:23 -0600393 if (isAutocompleteFullName) {
394 skipahead = fieldFullName.substr(autocompleteText.value.length, fieldFullName.length);
395 } else {
396 if (fieldFullName.charAt(autocompleteText.value.length) === "/") {
397 entry += "/";
398 skipahead = fieldFullName.substr(autocompleteText.value.length + 1, fieldFullName.length);
399 } else {
400 skipahead = fieldFullName.substr(autocompleteText.value.length, fieldFullName.length);
401 }
402 }
403 if (skipahead.indexOf("/") != -1) {
404 entry += skipahead.substr(0, skipahead.indexOf("/") + 1);
405 } else {
406 entry += skipahead;
407 }
Tyler Scotte815d3e2015-07-09 16:56:17 -0600408
Tyler Scott93cae872015-07-21 14:58:23 -0600409 var added = false;
410 for (var j = 0; j < dropdown.length && !added; ++j) {
411 if (dropdown[j] === entry) {
412 added = true;
413 } else if (dropdown[j] > entry) {
414 dropdown.splice(j, 0, entry);
415 added = true;
416 }
417 }
418 if (!added) {
419 dropdown.push(entry);
420 }
421
422 }
423 $("#autocompleteText").autocomplete({
424 source: dropdown
425 });
426 */
427 }
428
429 /**
430 * Adds a filter to the list of filters.
431 * If a filter is already added, it will be removed instead.
432 *
433 * @param {string} filter
434 */
435 Atmos.prototype.addFilter = function(name, category){
436 var existing = this.filters.find('span:contains(' + name + ')');
437 if (existing.length){
438 //If the category is clicked twice, then we delete it.
439 existing.remove();
440 this.applyFilters();
441
442 } else {
443
444 var filter = $('<span class="label label-default"></span>');
445 filter.text(category + ':' + name);
446
447 this.filters.append(filter);
448
449 this.applyFilters();
450
451 var scope = this;
452 filter.click(function(){
453 $(this).remove();
454 scope.applyFilters();
455 });
456
457 }
Tyler Scotte815d3e2015-07-09 16:56:17 -0600458
459 }
Tyler Scotte815d3e2015-07-09 16:56:17 -0600460
Tyler Scott93cae872015-07-21 14:58:23 -0600461 Atmos.prototype.applyFilters = function(){
462 var filters = this.filters.children().toArray().reduce(function(prev, current){
Tyler Scott575c61b2015-07-13 13:42:16 -0600463 var data = $(current).text().split(/:/);
464 prev[data[0]] = data[1];
465 return prev;
Tyler Scott93cae872015-07-21 14:58:23 -0600466 }, {});
467 console.log('Collected filters:', filters);
468 var scope = this;
469 this.query(this.catalog, filters, function(data){
470 scope.onData(data);
471 }, 1);
472 }
Tyler Scotte815d3e2015-07-09 16:56:17 -0600473
Tyler Scott93cae872015-07-21 14:58:23 -0600474 /**
475 * @deprecated
476 * Use addFilter instead.
477 */
478 Atmos.prototype.populateCurrentSelections = function() {
479 console.warn("Use of deprecated function populateCurrentSelections! (Use addFilter instead)");
480 /*
481 var currentSelection = $(".currentSelections");
482 currentSelection.empty();
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600483
Tyler Scott93cae872015-07-21 14:58:23 -0600484 currentSelection.append("<p>Filtering on:");
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600485
Tyler Scott93cae872015-07-21 14:58:23 -0600486 var scope = this;
Tyler Scott7d076e22015-07-06 19:21:50 -0600487
Tyler Scott93cae872015-07-21 14:58:23 -0600488 $.each(this.selectedSearch, function (searchMenuCatagory, selection) {
489 var e = $('<a href="#">[X] ' + searchMenuCatagory + ":" + selection + '</a>');
490 e.onclick(function(){
491 var searchFilter = $(this).text();
Tyler Scott7d076e22015-07-06 19:21:50 -0600492
Tyler Scott93cae872015-07-21 14:58:23 -0600493 var search = "";
494 for (var j = 0; j < searchFilter.length; ++j) {
495 search += searchFilter[j] + " ";
496 }
497 console.log("Split values: '" + search + "'");
Tyler Scott7d076e22015-07-06 19:21:50 -0600498
Tyler Scott93cae872015-07-21 14:58:23 -0600499 delete this.selectedSearch[searchFilter[0]];
500 this.query(catalog, selectedSearch, onData, 1);
501 populateCurrentSelections();
502 });
503 currentSelection.append(e);
504 });
Tyler Scott3c17d5f2015-06-23 17:49:29 -0600505
Tyler Scott93cae872015-07-21 14:58:23 -0600506 currentSelection.append("</p>");
507 */
508 }
509
510 return Atmos;
511
512})();
513
514