A semantic search engine for source code https://bitshift.benkurtovic.com/
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

246 行
11 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. advancedSearchButton.click(function(){
  8. var searchField = $("div#search-field");
  9. if(!advancedSearchDiv.hasClass("visible")){
  10. searchField.addClass("partly-visible");
  11. advancedSearchDiv.fadeIn(500).addClass("visible");
  12. advancedSearchButton.addClass("clicked");
  13. }
  14. else {
  15. advancedSearchDiv.fadeOut(300).removeClass("visible");
  16. advancedSearchButton.removeClass("clicked");
  17. if($("div#results .result").length == 0)
  18. searchField.removeClass("partly-visible");
  19. }
  20. });
  21. FINISH_TYPING_INTERVAL = 650;
  22. searchBar = $("form#search-bar input[type='text']")[0];
  23. resultsDiv = $("div#results")[0];
  24. var typingTimer, lastValue;
  25. //Obtained by parsing python file with pygments
  26. 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="hll"><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="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 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\n<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 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 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\n<span class="c"># database = Database()</span>\n\n<span class="nd">@app.route</span><span class="p">(</span><span class="s">&quot;/&quot;</span><span class="p">)</span>\n<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="nd">@app.route</span><span class="p">(</span><span class="s">&quot;/search/&lt;query&gt;&quot;</span><span class="p">)</span>\n<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="nd">@app.route</span><span class="p">(</span><span class="s">&quot;/about&quot;</span><span class="p">)</span>\n<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="nd">@app.route</span><span class="p">(</span><span class="s">&quot;/developers&quot;</span><span class="p">)</span>\n<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="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</pre></div>\n</td></tr></table>'
  27. searchBar.onkeyup = typingTimer;
  28. var testCodelet = {
  29. 'code_url': 'https://github.com/earwig/bitshift/blob/develop/app.py',
  30. 'filename': 'app.py',
  31. 'language': 'Python',
  32. 'date_created': 'May 10, 2014',
  33. 'date_modified': '2 days ago',
  34. 'origin': ['GitHub', 'https://github.com', ''],
  35. 'authors': ['sevko', 'earwig'],
  36. 'html_code': codeExample
  37. };
  38. // Enable infinite scrolling down the results page.
  39. $(window).scroll(function() {
  40. var searchField = $("div#search-field");
  41. if($(window).scrollTop() + $(window).height() == $(document).height() && searchField.hasClass('partly-visible')){
  42. loadMoreResults();
  43. }
  44. });
  45. // Enable capturing the `enter` key.
  46. $("form#search-bar").submit(function(event){
  47. event.preventDefault();
  48. return false;
  49. });
  50. /*
  51. * Clear the existing timer and set a new one the the user types text into the
  52. * search bar.
  53. */
  54. function typingTimer(event){
  55. clearTimeout(typingTimer);
  56. var enterKeyCode = 13;
  57. if(event.keyCode != enterKeyCode){
  58. if(lastValue != searchBar.value)
  59. typingTimer = setTimeout(finishedTyping, FINISH_TYPING_INTERVAL);
  60. }
  61. else {
  62. event.preventDefault();
  63. finishedTyping();
  64. return false;
  65. }
  66. };
  67. /*
  68. * Callback which queries the server whenver the user stops typing.
  69. *
  70. * Whenever the user doesn't type for a `FINISH_TYPING_INTERVAL` after having
  71. * entered new text in the search bar, send the current query request to the
  72. * server.
  73. */
  74. function finishedTyping(){
  75. lastValue = searchBar.value;
  76. var searchField = $("div#search-field");
  77. clearResults();
  78. if(searchBar.value){
  79. searchField.addClass("partly-visible");
  80. populateResults();
  81. }
  82. else {
  83. searchField.removeClass("partly-visible");
  84. $("div#advanced-search").fadeOut(50);
  85. advancedSearchButton.removeClass("clicked");
  86. }
  87. }
  88. /*
  89. * Removes any child elements of `div#results`.
  90. */
  91. function clearResults(){
  92. while(resultsDiv.firstChild)
  93. resultsDiv.removeChild(resultsDiv.firstChild);
  94. }
  95. /*
  96. * Query the server with the current search string, and populate `div#results`
  97. * with its response.
  98. */
  99. function populateResults(){
  100. var results = queryServer();
  101. for(var result = 0; result < results.length; result++){
  102. var newDiv = results[result];
  103. resultsDiv.appendChild(newDiv);
  104. setTimeout(
  105. (function(divReference){
  106. return function(){
  107. divReference.classList.add("cascade");
  108. };
  109. }(newDiv)), result * 20);
  110. }
  111. }
  112. /*
  113. * Create a result element based upon a codelet instance.
  114. *
  115. * @return {Element} The result element.
  116. */
  117. function createResult(codelet) {
  118. //Level 1
  119. var newDiv = document.createElement("div"),
  120. table = document.createElement("table"),
  121. row = document.createElement("tr");
  122. //Level 2
  123. var displayInfo = document.createElement("div"),
  124. sidebar = document.createElement("td"),
  125. codeElt = document.createElement("td"),
  126. displayButton = document.createElement("td"),
  127. hiddenInfoContainer = document.createElement("td"),
  128. hiddenInfo = document.createElement("div");
  129. //Level 3
  130. var title = document.createElement("span"),
  131. site = document.createElement("span"),
  132. dateModified = document.createElement("span"),
  133. language = document.createElement("span"),
  134. dateCreated = document.createElement("span"),
  135. authors = document.createElement("div");
  136. //Classes and ID's
  137. newDiv.classList.add('result');
  138. displayInfo.id = 'display-info';
  139. sidebar.id = 'sidebar';
  140. codeElt.id = 'code';
  141. displayButton.id = 'display-button';
  142. hiddenInfo.id = 'hidden-info';
  143. title.id = 'title';
  144. site.id = 'site';
  145. dateModified.id = 'date-modified';
  146. language.id = 'language';
  147. dateCreated.id = 'date-created';
  148. authors.id = 'authors';
  149. //Add the bulk of the html
  150. title.innerHTML = 'File <a href="' + codelet.code_url + '">'
  151. + codelet.filename + '</a>';
  152. site.innerHTML = 'on <a href="' + codelet.origin[1] + '">' + codelet.origin[0] +'</a>';
  153. dateModified.innerHTML = 'Last modified ' + codelet.date_modified;
  154. // Needs to be changed from int to string on the server
  155. language.innerHTML = codelet.language;
  156. dateCreated.innerHTML = 'Created ' + codelet.date_created;
  157. authors.innerHTML = 'Authors: ';
  158. $.each(codelet.authors, function(i, a) {
  159. authors.innerHTML += '<a href=#>' + a + ' </a>';
  160. });
  161. sidebar.innerHTML = '';
  162. // Needs to be processed on the server
  163. codeElt.innerHTML = '<div id=tablecontainer>' + codelet.html_code + '</div>';
  164. //Event binding
  165. $(displayButton).click(function(e) {
  166. $(hiddenInfo).toggleClass('visible');
  167. $(this).toggleClass('active');
  168. });
  169. //Finish and append elements to parent elements
  170. hiddenInfo.appendChild(dateCreated);
  171. hiddenInfo.appendChild(dateModified);
  172. hiddenInfo.appendChild(authors);
  173. hiddenInfo.appendChild(language);
  174. hiddenInfoContainer.appendChild(hiddenInfo);
  175. row.appendChild(sidebar);
  176. row.appendChild(codeElt);
  177. row.appendChild(hiddenInfoContainer);
  178. row.appendChild(displayButton);
  179. table.appendChild(row);
  180. displayInfo.appendChild(title);
  181. displayInfo.appendChild(site);
  182. newDiv.appendChild(displayInfo);
  183. newDiv.appendChild(table);
  184. return newDiv;
  185. }
  186. /*
  187. * AJAX the current query string to the server, and return its response.
  188. *
  189. * @return {Array} The server's response in the form of `div.result` DOM
  190. * elements, to fill `div#results`.
  191. */
  192. function queryServer(){
  193. var resultDivs = []
  194. for(var result = 0; result < 20; result++){
  195. var newDiv = createResult(testCodelet);
  196. resultDivs.push(newDiv);
  197. }
  198. return resultDivs;
  199. }
  200. /*
  201. * Adds more results to `div#results`.
  202. */
  203. function loadMoreResults(){
  204. results = queryServer();
  205. for(var result = 0; result < results.length; result++){
  206. var newDiv = results[result];
  207. resultsDiv.appendChild(newDiv);
  208. setTimeout(
  209. (function(divReference){
  210. return function(){
  211. divReference.classList.add("cascade");
  212. };
  213. }(newDiv)),
  214. result * 20);
  215. }
  216. }