A semantic search engine for source code https://bitshift.benkurtovic.com/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

263 lines
12 KiB

  1. /*
  2. * @file Manages all library initialization, jQuery callbacks, query entry
  3. * callbacks, server querying, and results diplay for `index.html`.
  4. */
  5. var advancedSearchDiv = $("div#advanced-search");
  6. var advancedSearchButton = $("button#advanced-search");
  7. FINISH_TYPING_INTERVAL = 650;
  8. var searchBar = $("form#search-bar input[type='text']")[0];
  9. var resultsDiv = $("div#results")[0];
  10. var typingTimer, lastValue;
  11. /*
  12. * Set all page callbacks.
  13. */
  14. (function setHomePageCallbabacks(){
  15. // Enable infinite scrolling down the results page.
  16. $(window).scroll(function(){
  17. if($(window).scrollTop() + $(window).height() == $(document).height() &&
  18. resultsDiv.querySelectorAll(".result").length > 0)
  19. loadMoreResults();
  20. });
  21. // Toggle the advanced-search form's visibility.
  22. advancedSearchButton.click(function(){
  23. var searchField = $("div#search-field");
  24. if(!advancedSearchDiv.hasClass("visible")){
  25. searchField.addClass("partly-visible");
  26. advancedSearchDiv.fadeIn(500).addClass("visible");
  27. advancedSearchButton.addClass("clicked");
  28. }
  29. else {
  30. advancedSearchDiv.fadeOut(300).removeClass("visible");
  31. advancedSearchButton.removeClass("clicked");
  32. if($("div#results .result").length == 0)
  33. searchField.removeClass("partly-visible");
  34. }
  35. });
  36. // Enable capturing the `enter` key.
  37. $("form#search-bar").submit(function(event){
  38. event.preventDefault();
  39. return false;
  40. });
  41. searchBar.onkeyup = typingTimer;
  42. }());
  43. //Obtained by parsing python file with pygments
  44. var codeExample = '<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1\n 2\n 3\n 4\n 5\n 6\n 7\n 8\n 9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n30\n31\n32\n33\n34\n35\n36\n37\n38\n39\n40</pre></div></td><td class="code"><div class="highlight"><pre><span class="sd">&quot;&quot;&quot;</span>\n<span class="sd">Module to contain all the project&#39;s Flask server plumbing.</span>\n<span class="sd">&quot;&quot;&quot;</span>\n\n<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span>\n<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">render_template</span><span class="p">,</span> <span class="n">session</span>\n\n<span class="kn">from</span> <span class="nn">bitshift</span> <span class="kn">import</span> <span class="n">assets</span>\n<span class="c"># from bitshift.database import Database</span>\n<span class="c"># from bitshift.query import parse_query</span>\n\n<span class="hll"><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>\n</span><span class="hll"><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_object</span><span class="p">(</span><span class="s">&quot;bitshift.config&quot;</span><span class="p">)</span>\n</span>\n<span class="hll"><span class="n">app_env</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">jinja_env</span>\n</span><span class="hll"><span class="n">app_env</span><span class="o">.</span><span class="n">line_statement_prefix</span> <span class="o">=</span> <span class="s">&quot;=&quot;</span>\n</span><span class="hll"><span class="n">app_env</span><span class="o">.</span><span class="n">globals</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">assets</span><span class="o">=</span><span class="n">assets</span><span class="p">)</span>\n</span>\n<span class="c"># database = Database()</span>\n\n<span class="hll"><span class="nd">@app.route</span><span class="p">(</span><span class="s">&quot;/&quot;</span><span class="p">)</span>\n</span><span class="k">def</span> <span class="nf">index</span><span class="p">():</span>\n <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">&quot;index.html&quot;</span><span class="p">)</span>\n\n<span class="hll"><span class="nd">@app.route</span><span class="p">(</span><span class="s">&quot;/search/&lt;query&gt;&quot;</span><span class="p">)</span>\n</span><span class="k">def</span> <span class="nf">search</span><span class="p">(</span><span class="n">query</span><span class="p">):</span>\n <span class="c"># tree = parse_query(query)</span>\n <span class="c"># database.search(tree)</span>\n <span class="k">pass</span>\n\n<span class="hll"><span class="nd">@app.route</span><span class="p">(</span><span class="s">&quot;/about&quot;</span><span class="p">)</span>\n</span><span class="k">def</span> <span class="nf">about</span><span class="p">():</span>\n <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">&quot;about.html&quot;</span><span class="p">)</span>\n\n<span class="hll"><span class="nd">@app.route</span><span class="p">(</span><span class="s">&quot;/developers&quot;</span><span class="p">)</span>\n</span><span class="k">def</span> <span class="nf">developers</span><span class="p">():</span>\n <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">&quot;developers.html&quot;</span><span class="p">)</span>\n\n<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&quot;__main__&quot;</span><span class="p">:</span>\n<span class="hll"> <span class="n">app</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">debug</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>\n</span></pre></div>\n</td></tr></table>'
  45. searchBar.onkeyup = typingTimer;
  46. var testCodelet = {
  47. 'code_url': 'https://github.com/earwig/bitshift/blob/develop/app.py',
  48. 'filename': 'app.py',
  49. 'language': 'python',
  50. 'date_created': 'May 10, 2014',
  51. 'date_modified': '2 days ago',
  52. 'origin': ['GitHub', 'https://github.com', ''],
  53. 'authors': ['sevko', 'earwig'],
  54. 'html_code': codeExample
  55. };
  56. // Enable infinite scrolling down the results page.
  57. $(window).scroll(function() {
  58. var searchField = $("div#search-field");
  59. if($(window).scrollTop() + $(window).height() == $(document).height() && searchField.hasClass('partly-visible')){
  60. loadMoreResults();
  61. }
  62. });
  63. /*
  64. * Clear the existing timer and set a new one the the user types text into the
  65. * search bar.
  66. */
  67. function typingTimer(event){
  68. clearTimeout(typingTimer);
  69. var enterKeyCode = 13;
  70. if(event.keyCode != enterKeyCode){
  71. if(lastValue != searchBar.value)
  72. typingTimer = setTimeout(finishedTyping, FINISH_TYPING_INTERVAL);
  73. }
  74. else {
  75. event.preventDefault();
  76. finishedTyping();
  77. return false;
  78. }
  79. };
  80. /*
  81. * Callback which queries the server whenver the user stops typing.
  82. *
  83. * Whenever the user doesn't type for a `FINISH_TYPING_INTERVAL` after having
  84. * entered new text in the search bar, send the current query request to the
  85. * server.
  86. */
  87. function finishedTyping(){
  88. lastValue = searchBar.value;
  89. var searchField = $("div#search-field");
  90. clearResults();
  91. if(searchBar.value){
  92. searchField.addClass("partly-visible");
  93. populateResults();
  94. }
  95. else {
  96. searchField.removeClass("partly-visible");
  97. $("div#advanced-search").fadeOut(50);
  98. advancedSearchButton.removeClass("clicked");
  99. }
  100. }
  101. /*
  102. * Removes any child elements of `div#results`.
  103. */
  104. function clearResults(){
  105. while(resultsDiv.firstChild)
  106. resultsDiv.removeChild(resultsDiv.firstChild);
  107. }
  108. /*
  109. * Query the server with the current search string, and populate `div#results`
  110. * with its response.
  111. */
  112. function populateResults(){
  113. var results = queryServer();
  114. for(var result = 0; result < results.length; result++){
  115. var newDiv = results[result];
  116. resultsDiv.appendChild(newDiv);
  117. setTimeout(
  118. (function(divReference){
  119. return function(){
  120. divReference.classList.add("cascade");
  121. };
  122. }(newDiv)), result * 20);
  123. }
  124. }
  125. /*
  126. * Create a result element based upon a codelet instance.
  127. *
  128. * @return {Element} The result element.
  129. */
  130. function createResult(codelet) {
  131. //Level 1
  132. var newDiv = document.createElement("div"),
  133. table = document.createElement("table"),
  134. row = document.createElement("tr");
  135. //Level 2
  136. var displayInfo = document.createElement("div"),
  137. codeElt = document.createElement("td"),
  138. hiddenInfoContainer = document.createElement("td"),
  139. hiddenInfo = document.createElement("div");
  140. //Level 3
  141. var title = document.createElement("span"),
  142. site = document.createElement("span"),
  143. dateModified = document.createElement("div"),
  144. language = document.createElement("span"),
  145. dateCreated = document.createElement("div"),
  146. authors = document.createElement("div");
  147. //Classes and ID's
  148. newDiv.classList.add('result');
  149. displayInfo.id = 'display-info';
  150. codeElt.id = 'code';
  151. hiddenInfo.id = 'hidden-info';
  152. title.id = 'title';
  153. site.id = 'site';
  154. dateModified.id = 'date-modified';
  155. language.id = 'language';
  156. dateCreated.id = 'date-created';
  157. authors.id = 'authors';
  158. //Add the bulk of the html
  159. title.innerHTML = 'File <a href="' + codelet.code_url + '">'
  160. + codelet.filename + '</a>';
  161. site.innerHTML = 'on <a href="' + codelet.origin[1] + '">' + codelet.origin[0] +'</a>';
  162. language.innerHTML = codelet.language;
  163. dateModified.innerHTML = 'Last modified: <span>' + codelet.date_modified + '</span>';
  164. // Needs to be changed from int to string on the server
  165. dateCreated.innerHTML = 'Created: <span>' + codelet.date_created + '</span>';
  166. var authorsHtml = 'Authors: <span>';
  167. codelet.authors.forEach(function(a, i) {
  168. if (i == codelet.authors.length - 1)
  169. authorsHtml += '<a href=#>' + a + ' </a>';
  170. else
  171. authorsHtml += '<a href=#>' + a + ' </a>, ';
  172. });
  173. authors.innerHTML = authorsHtml;
  174. // Needs to be processed on the server
  175. codeElt.innerHTML = '<div id=tablecontainer>' + codelet.html_code + '</div>';
  176. //Event binding
  177. $(newDiv).hover(function(e) {
  178. $(row).addClass('display-all');
  179. });
  180. $(newDiv).on('transitionend', function(e) {
  181. $(newDiv).one('mouseleave', function(e) {
  182. $(row).removeClass('display-all');
  183. });
  184. });
  185. //Finish and append elements to parent elements
  186. hiddenInfo.appendChild(dateCreated);
  187. hiddenInfo.appendChild(dateModified);
  188. hiddenInfo.appendChild(authors);
  189. hiddenInfoContainer.appendChild(hiddenInfo);
  190. row.appendChild(codeElt);
  191. row.appendChild(hiddenInfoContainer);
  192. table.appendChild(row);
  193. displayInfo.appendChild(title);
  194. displayInfo.appendChild(site);
  195. displayInfo.appendChild(language);
  196. newDiv.appendChild(displayInfo);
  197. newDiv.appendChild(table);
  198. return newDiv;
  199. }
  200. /*
  201. * AJAX the current query string to the server, and return its response.
  202. *
  203. * @return {Array} The server's response in the form of `div.result` DOM
  204. * elements, to fill `div#results`.
  205. */
  206. function queryServer(){
  207. var resultDivs = []
  208. for(var result = 0; result < 20; result++){
  209. var newDiv = createResult(testCodelet);
  210. resultDivs.push(newDiv);
  211. }
  212. return resultDivs;
  213. }
  214. /*
  215. * Adds more results to `div#results`.
  216. */
  217. function loadMoreResults(){
  218. results = queryServer();
  219. for(var result = 0; result < results.length; result++){
  220. var newDiv = results[result];
  221. resultsDiv.appendChild(newDiv);
  222. setTimeout(
  223. (function(divReference){
  224. return function(){
  225. divReference.classList.add("cascade");
  226. };
  227. }(newDiv)),
  228. result * 20);
  229. }
  230. }