/* * @file Manages all library initialization, jQuery callbacks, query entry * callbacks, server querying, and results diplay for `index.html`. */ var advancedSearchDiv = $("div#advanced-search"); var advancedSearchButton = $("button#advanced-search"); FINISH_TYPING_INTERVAL = 650; var searchBar = $("form#search-bar input[type='text']")[0]; var resultsDiv = $("div#results")[0]; var typingTimer, lastValue; var searchResultsPage = 1; /* * Set all page callbacks. */ (function setHomePageCallbabacks(){ // Enable infinite scrolling down the results page. $(window).scroll(function(){ if($(window).scrollTop() + $(window).height() == $(document).height() && resultsDiv.querySelectorAll(".result").length > 0) loadMoreResults(); }); // Toggle the advanced-search form's visibility. advancedSearchButton.click(function(){ var searchField = $("div#search-field"); if(!advancedSearchDiv.hasClass("visible")){ searchField.addClass("partly-visible"); advancedSearchDiv.fadeIn(500).addClass("visible"); advancedSearchButton.addClass("clicked"); } else { advancedSearchDiv.fadeOut(300).removeClass("visible"); advancedSearchButton.removeClass("clicked"); if($("div#results .result").length == 0) searchField.removeClass("partly-visible"); } }); // Enable capturing the `enter` key. $("form#search-bar").submit(function(event){ event.preventDefault(); return false; }); searchBar.onkeyup = typingTimer; }()); //Obtained by parsing python file with pygments var codeExample = '
 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
"""\nModule to contain all the project's Flask server plumbing.\n"""\n\nfrom flask import Flask\nfrom flask import render_template, session\n\nfrom bitshift import assets\n# from bitshift.database import Database\n# from bitshift.query import parse_query\n\napp = Flask(__name__)\napp.config.from_object("bitshift.config")\n\napp_env = app.jinja_env\napp_env.line_statement_prefix = "="\napp_env.globals.update(assets=assets)\n\n# database = Database()\n\n@app.route("/")\ndef index():\n    return render_template("index.html")\n\n@app.route("/search/<query>")\ndef search(query):\n    # tree = parse_query(query)\n    # database.search(tree)\n    pass\n\n@app.route("/about")\ndef about():\n    return render_template("about.html")\n\n@app.route("/developers")\ndef developers():\n    return render_template("developers.html")\n\nif __name__ == "__main__":\n    app.run(debug=True)\n
\n
' searchBar.onkeyup = typingTimer; var testCodelet = { 'url': 'https://github.com/earwig/bitshift/blob/develop/app.py', 'filename': 'app.py', 'language': 'python', 'date_created': 'May 10, 2014', 'date_modified': '2 days ago', 'origin': ['GitHub', 'https://github.com', ''], 'authors': ['sevko', 'earwig'], 'html_code': codeExample }; // Enable infinite scrolling down the results page. $(window).scroll(function() { var searchField = $("div#search-field"); if($(window).scrollTop() + $(window).height() == $(document).height() && searchField.hasClass('partly-visible')){ loadMoreResults(); } }); /* * Clear the existing timer and set a new one the the user types text into the * search bar. */ function typingTimer(event){ clearTimeout(typingTimer); var enterKeyCode = 13; if(event.keyCode != enterKeyCode){ if(lastValue != searchBar.value) typingTimer = setTimeout(finishedTyping, FINISH_TYPING_INTERVAL); } else { event.preventDefault(); finishedTyping(); return false; } }; /* * Callback which queries the server whenver the user stops typing. * * Whenever the user doesn't type for a `FINISH_TYPING_INTERVAL` after having * entered new text in the search bar, send the current query request to the * server. */ function finishedTyping(){ lastValue = searchBar.value; var searchField = $("div#search-field"); clearResults(); if(searchBar.value){ searchField.addClass("partly-visible"); populateResults(); } else { searchField.removeClass("partly-visible"); $("div#advanced-search").fadeOut(50); advancedSearchButton.removeClass("clicked"); } } /* * Removes any child elements of `div#results`. */ function clearResults(){ while(resultsDiv.firstChild) resultsDiv.removeChild(resultsDiv.firstChild); } /* * Query the server with the current search string, and populate `div#results` * with its response. */ function populateResults(){ searchResultsPage = 1; var results = queryServer(); for(var result = 0; result < results.length; result++){ var newDiv = results[result]; resultsDiv.appendChild(newDiv); setTimeout( (function(divReference){ return function(){ divReference.classList.add("cascade"); }; }(newDiv)), result * 20); } } /* * Create a result element based upon a codelet instance. * * @return {Element} The result element. */ function createResult(codelet) { //Level 1 var newDiv = document.createElement("div"), table = document.createElement("table"), row = document.createElement("tr"); //Level 2 var displayInfo = document.createElement("div"), codeElt = document.createElement("td"), hiddenInfoContainer = document.createElement("td"), hiddenInfo = document.createElement("div"), cycle = document.createElement("div"); //Level 3 var title = document.createElement("span"), site = document.createElement("span"), nextMatch = document.createElement("a"), prevMatch = document.createElement("a"), dateModified = document.createElement("div"), language = document.createElement("div"), dateCreated = document.createElement("div"), authors = document.createElement("div"); //Classes and ID's newDiv.classList.add('result'); displayInfo.id = 'display-info'; codeElt.id = 'code'; hiddenInfo.id = 'hidden-info'; cycle.id = 'cycle-matches' title.id = 'title'; site.id = 'site'; nextMatch.id = 'next-match'; nextMatch.href = '#'; prevMatch.id = 'prev-match'; prevMatch.href = '#'; dateModified.id = 'date-modified'; language.id = 'language'; dateCreated.id = 'date-created'; authors.id = 'authors'; //Add the bulk of the html title.innerHTML = ' » ' + codelet.filename + ''; site.innerHTML = '' + codelet.origin[0] +''; nextMatch.innerHTML = 'next match'; prevMatch.innerHTML = 'prev match'; language.innerHTML = 'Language: ' + codelet.language + ''; dateModified.innerHTML = 'Last modified: ' + codelet.date_modified + ''; // Needs to be changed from int to string on the server dateCreated.innerHTML = 'Created: ' + codelet.date_created + ''; var authorsHtml = 'Authors: '; codelet.authors.forEach(function(a, i) { if (i == codelet.authors.length - 1) authorsHtml += '' + a + ' '; else authorsHtml += '' + a + ' , '; }); authors.innerHTML = authorsHtml; // Needs to be processed on the server codeElt.innerHTML = '
' + codelet.html_code + '
'; var matches = codeElt.querySelectorAll('.hll'); $.each(matches, function(i, a) { a.id = 'match_' + i; }); //Event binding $(codeElt).hover(function(e) { $(row).addClass('display-all'); }); $(newDiv).on('transitionend', function(e) { $(codeElt).one('mouseleave', function(e) { $(row).removeClass('display-all'); }); }); var cur_match = -1; var newMatch = function(e) { var $code = $(newDiv).find('#tablecontainer'), $match = $code.find('#match_' + cur_match); $code.scrollTop($code.scrollTop() - $code.height() / 2 + $match.position().top + $match.height() / 2); $match.effect("highlight", {}, 750) } $(nextMatch).click(function(e) { e.stopPropagation() e.preventDefault() cur_match = cur_match >= matches.length - 1 ? 0 : cur_match + 1; newMatch(); }); $(prevMatch).click(function(e) { e.stopPropagation() cur_match = cur_match <= 0 ? matches.length - 1 : cur_match - 1; newMatch(); }); //Finish and append elements to parent elements hiddenInfo.appendChild(dateCreated); hiddenInfo.appendChild(dateModified); hiddenInfo.appendChild(language); hiddenInfo.appendChild(authors); hiddenInfoContainer.appendChild(hiddenInfo); row.appendChild(codeElt); row.appendChild(hiddenInfoContainer); table.appendChild(row); displayInfo.appendChild(site); displayInfo.appendChild(title); cycle.appendChild(prevMatch); cycle.appendChild(nextMatch); newDiv.appendChild(displayInfo); newDiv.appendChild(table); newDiv.appendChild(cycle); return newDiv; } /* * AJAX the current query string to the server, and return its response. * * @return {Array} The server's response in the form of `div.result` DOM * elements, to fill `div#results`. */ function queryServer(){ var queryUrl = document.URL + "search.json?" + $.param({ "q" : searchBar.value, "p" : searchResultsPage++ }); console.log(queryUrl); var result = $.getJSON(queryUrl, function(result){ $.each(result, function(key, value){ if(key == "error") errorMessage(value); else console.log("Success."); }); }); // return []; var resultDivs = []; for(var result = 0; result < 20; result++){ var newDiv = createResult(testCodelet); resultDivs.push(newDiv) } return resultDivs; } /* * Adds more results to `div#results`. */ function loadMoreResults(){ var results = queryServer(); for(var result = 0; result < results.length; result++){ var newDiv = results[result]; resultsDiv.appendChild(newDiv); setTimeout( (function(divReference){ return function(){ divReference.classList.add("cascade"); }; }(newDiv)), result * 20); } } /* * Displays a warning message in the UI. * * @param msg (str) The message string. */ function errorMessage(msg){ alert(msg); } loadMoreResults();