Browse Source

Merge remote-tracking branch 'origin/develop' into develop

Conflicts:
	bitshift/crawler/crawl.py
	bitshift/languages.py
tags/v1.0^2
Ben Kurtovic 10 years ago
parent
commit
3c786c5b47
19 changed files with 700 additions and 224 deletions
  1. +1
    -1
      .gitignore
  2. +1
    -1
      app.py
  3. +1
    -0
      bitshift/crawler/crawl.py
  4. +17
    -37
      bitshift/crawler/indexer.py
  5. +1
    -1
      bitshift/database/__init__.py
  6. +23
    -1
      bitshift/database/migration.py
  7. +18
    -4
      bitshift/database/schema.sql
  8. +2
    -1
      bitshift/languages.py
  9. +4
    -4
      parsers/java/src/main/java/com/bitshift/parsing/parsers/JavaParser.java
  10. +65
    -0
      static/css/lib/github.css
  11. +64
    -0
      static/css/lib/highlight.css
  12. +13
    -8
      static/js/index.advanced-search-form.js
  13. +266
    -53
      static/js/index.js
  14. +0
    -7
      static/js/lib/typeahead.bundle.min.js
  15. +5
    -0
      static/sass/_mixins.sass
  16. +157
    -101
      static/sass/index.sass
  17. +14
    -3
      templates/index.html
  18. +8
    -2
      templates/layout.html
  19. +40
    -0
      test/resources/app.py

+ 1
- 1
.gitignore View File

@@ -1,5 +1,5 @@
static/css/*
!static/css/lib/*
!lib

*.swp
.sass-cache


+ 1
- 1
app.py View File

@@ -22,7 +22,7 @@ database = Database()

@app.route("/")
def index():
return render_template("index.html", typeahead_languages=LANGS)
return render_template("index.html", autocomplete_languages=LANGS)

@app.route("/search.json")
def search():


+ 1
- 0
bitshift/crawler/crawl.py View File

@@ -39,6 +39,7 @@ def crawl():
indexer.GitIndexer(repo_clone_queue, run_event)]

parse_servers = start_parse_servers()
time.sleep(5)
for thread in threads:
thread.start()



+ 17
- 37
bitshift/crawler/indexer.py View File

@@ -33,6 +33,7 @@ class GitRepository(object):
repository belongs to (eg, GitHub, BitBucket).
:ivar rank: (float) The rank of the repository, as assigned by
:class:`crawler.GitHubCrawler`.
:ivar dirname: (str) The repository's on-disk directory name.
"""

def __init__(self, url, name, framework_name, rank):
@@ -54,6 +55,7 @@ class GitRepository(object):
self.name = name
self.framework_name = framework_name
self.rank = rank
self.dirname = name.replace("-", "--").replace("/", "-")

class GitIndexer(threading.Thread):
"""
@@ -125,19 +127,14 @@ class GitIndexer(threading.Thread):
:type repo_url: :class:`GitRepository`
"""

with _ChangeDir("%s/%s" % (GIT_CLONE_DIR, repo.name)):
with _ChangeDir("%s/%s" % (GIT_CLONE_DIR, repo.dirname)):
try:
self._insert_repository_codelets(repo)
except Exception:
self._logger.exception("Exception raised while indexing:")
finally:
if os.path.isdir("%s/%s" % (GIT_CLONE_DIR, repo.name)):
if len([obj for obj in os.listdir('.') if
os.path.isdir(obj)]) <= 1:
shutil.rmtree("%s/%s" % (
GIT_CLONE_DIR, repo.name.split("/")[0]))
else:
shutil.rmtree("%s/%s" % (GIT_CLONE_DIR, repo.name))
if os.path.isdir("%s/%s" % (GIT_CLONE_DIR, repo.dirname)):
shutil.rmtree("%s/%s" % (GIT_CLONE_DIR, repo.dirname))

def _insert_repository_codelets(self, repo):
"""
@@ -167,9 +164,9 @@ class GitIndexer(threading.Thread):

authors = [(self._decode(author), None) for author in
commits_meta[filename]["authors"]]
codelet = Codelet("%s:%s" % (repo.name, filename), source, filename,
None, authors, self._generate_file_url(filename,
repo.url, repo.framework_name),
url = self._generate_file_url(filename, repo.url, repo.framework_name)
codelet = Codelet("%s: %s" % (repo.name, filename), source,
filename, None, authors, url,
commits_meta[filename]["time_created"],
commits_meta[filename]["time_last_modified"],
repo.rank)
@@ -437,37 +434,20 @@ class _GitCloner(threading.Thread):
"""

GIT_CLONE_TIMEOUT = 500

queue_percent_full = (float(self.index_queue.qsize()) /
self.index_queue.maxsize) * 100

exit_code = None
command = ("perl -e 'alarm shift @ARGV; exec @ARGV' %d git clone"
" --single-branch %s %s/%s || pkill -f git")

command_attempt = 0
while exit_code is None:
try:
exit_code = subprocess.call(command % (GIT_CLONE_TIMEOUT,
repo.url, GIT_CLONE_DIR, repo.name), shell=True)
except Exception:
time.sleep(1)
command_attempt += 1
if command_attempt == 20:
break
else:
continue
else:
break

if exit_code != 0:
if os.path.isdir("%s/%s" % (GIT_CLONE_DIR, repo.name)):
shutil.rmtree("%s/%s" % (GIT_CLONE_DIR, repo.name))
self.index_queue.maxsize) * 100

command = ["perl", "-e", "alarm shift @ARGV; exec @ARGV",
str(GIT_CLONE_TIMEOUT), "git", "clone", "--single-branch",
repo.url, GIT_CLONE_DIR + "/" + repo.dirname]
if subprocess.call(command) != 0:
subprocess.call(["pkill", "-f", "git"]) # This makes Ben K upset
if os.path.isdir("%s/%s" % (GIT_CLONE_DIR, repo.dirname)):
shutil.rmtree("%s/%s" % (GIT_CLONE_DIR, repo.dirname))
return

while self.index_queue.full():
time.sleep(THREAD_QUEUE_SLEEP)

self.index_queue.put(repo)

class _ChangeDir(object):


+ 1
- 1
bitshift/database/__init__.py View File

@@ -131,7 +131,7 @@ class Database(object):

def _decompose_url(self, cursor, url):
"""Break up a URL into an origin (with a URL base) and a suffix."""
query = """SELECT origin_id, SUBSTR(?, LENGTH(origin_url_base))
query = """SELECT origin_id, SUBSTR(?, LENGTH(origin_url_base) + 1)
FROM origins
WHERE origin_url_base IS NOT NULL
AND ? LIKE CONCAT(origin_url_base, "%")"""


+ 23
- 1
bitshift/database/migration.py View File

@@ -3,7 +3,7 @@ Contains information about database schema versions, and SQL queries to update
between them.
"""

VERSION = 8
VERSION = 10

MIGRATIONS = [
# 1 -> 2
@@ -100,6 +100,28 @@ MIGRATIONS = [
[
"""ALTER TABLE `origins`
DROP COLUMN `origin_image`"""
],
# 8 -> 9
[
"""DELIMITER //
CREATE PROCEDURE `empty_database`()
BEGIN
DELETE FROM `codelets`;
DELETE FROM `code`;
DELETE FROM `cache`;
ALTER TABLE `codelets` AUTO_INCREMENT = 1;
ALTER TABLE `authors` AUTO_INCREMENT = 1;
ALTER TABLE `symbols` AUTO_INCREMENT = 1;
ALTER TABLE `symbol_locations` AUTO_INCREMENT = 1;
END//
DELIMITER ;"""
],
# 9 -> 10
[
"""ALTER TABLE `symbol_locations`
MODIFY COLUMN `sloc_col` INT UNSIGNED DEFAULT NULL,
MODIFY COLUMN `sloc_end_row` INT UNSIGNED DEFAULT NULL,
MODIFY COLUMN `sloc_end_col` INT UNSIGNED DEFAULT NULL"""
]
]



+ 18
- 4
bitshift/database/schema.sql View File

@@ -1,4 +1,4 @@
-- Schema version 10

CREATE DATABASE `bitshift` DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
USE `bitshift`;
@@ -6,7 +6,7 @@ USE `bitshift`;
CREATE TABLE `version` (
`version` INT UNSIGNED NOT NULL
) ENGINE=InnoDB;
INSERT INTO `version` VALUES (8);
INSERT INTO `version` VALUES (10);

CREATE TABLE `origins` (
`origin_id` TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
@@ -77,9 +77,9 @@ CREATE TABLE `symbol_locations` (
`sloc_symbol` BIGINT UNSIGNED NOT NULL,
`sloc_type` TINYINT UNSIGNED NOT NULL,
`sloc_row` INT UNSIGNED NOT NULL,
`sloc_col` INT UNSIGNED NOT NULL,
`sloc_end_row` INT UNSIGNED NOT NULL,
`sloc_end_col` INT UNSIGNED NOT NULL,
`sloc_col` INT UNSIGNED DEFAULT NULL,
`sloc_end_row` INT UNSIGNED DEFAULT NULL,
`sloc_end_col` INT UNSIGNED DEFAULT NULL,
PRIMARY KEY (`sloc_id`),
FOREIGN KEY (`sloc_symbol`)
REFERENCES `symbols` (`symbol_id`)
@@ -107,6 +107,19 @@ CREATE TABLE `cache_data` (
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;

DELIMITER //
CREATE PROCEDURE `empty_database`()
BEGIN
DELETE FROM `codelets`;
DELETE FROM `code`;
DELETE FROM `cache`;
ALTER TABLE `codelets` AUTO_INCREMENT = 1;
ALTER TABLE `authors` AUTO_INCREMENT = 1;
ALTER TABLE `symbols` AUTO_INCREMENT = 1;
ALTER TABLE `symbol_locations` AUTO_INCREMENT = 1;
END//
DELIMITER ;

CREATE EVENT `flush_cache`
ON SCHEDULE EVERY 1 HOUR
DO


+ 2
- 1
bitshift/languages.py View File

@@ -8,7 +8,8 @@ def _load_langs():
filename = path.join(path.dirname(__file__), "languages.yml")
with open(filename) as fp:
data = yaml.load(fp)["languages"]
langs = [it.keys()[0] if isinstance(it, dict) else it for it in data]
langs = [(it.keys()[0] if isinstance(it, dict) else it).encode("utf8")
for it in data]
all_langs = {}
for i, lang in enumerate(data):
if isinstance(lang, dict):


+ 4
- 4
parsers/java/src/main/java/com/bitshift/parsing/parsers/JavaParser.java View File

@@ -80,7 +80,7 @@ public class JavaParser extends Parser {

int sl = this.root.getLineNumber(node.getStartPosition());
int sc = this.root.getColumnNumber(node.getStartPosition());
Integer el = -1;
Integer el = sl;
Integer ec = -1;

if (statements.size() > 0) {
@@ -110,7 +110,7 @@ public class JavaParser extends Parser {
int sl = this.root.getLineNumber(node.getStartPosition());
int sc = this.root.getColumnNumber(node.getStartPosition());

data.put("coord", Symbols.createCoord(sl, sc, -1, -1));
data.put("coord", Symbols.createCoord(sl, sc, sl, -1));
data.put("name", name);
this._cache.push(data);
return true;
@@ -140,7 +140,7 @@ public class JavaParser extends Parser {
int sl = this.root.getLineNumber(node.getStartPosition());
int sc = this.root.getColumnNumber(node.getStartPosition());

data.put("coord", Symbols.createCoord(sl, sc, -1, -1));
data.put("coord", Symbols.createCoord(sl, sc, sl, -1));
this._cache.push(data);
return true;
}
@@ -161,7 +161,7 @@ public class JavaParser extends Parser {
int sl = this.root.getLineNumber(node.getStartPosition());
int sc = this.root.getColumnNumber(node.getStartPosition());

data.put("coord", Symbols.createCoord(sl, sc, -1, -1));
data.put("coord", Symbols.createCoord(sl, sc, sl, -1));
this._cache.push(data);
return true;
}


+ 65
- 0
static/css/lib/github.css View File

@@ -0,0 +1,65 @@
td.linenos { background: rgba(65,131,196,0.05); padding-right: 10px; border-right: 1px solid #bbb; }
span.lineno { background: rgba(65,131,196,0.05); padding: 0 5px 0 5px; }
pre { line-height: 125% }
.highlighttable { background-color: #fff; padding-left: 10px; width: inherit; height: inherit; }
.hll { display: block }
.c { color: #999988; font-style: italic } /* Comment */
.err { color: #a61717; background-color: #e3d2d2 } /* Error */
.k { color: #000000; font-weight: bold } /* Keyword */
.o { color: #000000; font-weight: bold } /* Operator */
.cm { color: #999988; font-style: italic } /* Comment.Multiline */
.cp { color: #999999; font-weight: bold; font-style: italic } /* Comment.Preproc */
.c1 { color: #999988; font-style: italic } /* Comment.Single */
.cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */
.gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.ge { color: #000000; font-style: italic } /* Generic.Emph */
.gr { color: #aa0000 } /* Generic.Error */
.gh { color: #999999 } /* Generic.Heading */
.gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.go { color: #888888 } /* Generic.Output */
.gp { color: #555555 } /* Generic.Prompt */
.gs { font-weight: bold } /* Generic.Strong */
.gu { color: #aaaaaa } /* Generic.Subheading */
.gt { color: #aa0000 } /* Generic.Traceback */
.kc { color: #000000; font-weight: bold } /* Keyword.Constant */
.kd { color: #000000; font-weight: bold } /* Keyword.Declaration */
.kn { color: #000000; font-weight: bold } /* Keyword.Namespace */
.kp { color: #000000; font-weight: bold } /* Keyword.Pseudo */
.kr { color: #000000; font-weight: bold } /* Keyword.Reserved */
.kt { color: #445588; font-weight: bold } /* Keyword.Type */
.m { color: #009999 } /* Literal.Number */
.s { color: #d01040 } /* Literal.String */
.na { color: #008080 } /* Name.Attribute */
.nb { color: #0086B3 } /* Name.Builtin */
.nc { color: #445588; font-weight: bold } /* Name.Class */
.no { color: #008080 } /* Name.Constant */
.nd { color: #3c5d5d; font-weight: bold } /* Name.Decorator */
.ni { color: #800080 } /* Name.Entity */
.ne { color: #990000; font-weight: bold } /* Name.Exception */
.nf { color: #990000; font-weight: bold } /* Name.Function */
.nl { color: #990000; font-weight: bold } /* Name.Label */
.nn { color: #555555 } /* Name.Namespace */
.nt { color: #000080 } /* Name.Tag */
.nv { color: #008080 } /* Name.Variable */
.ow { color: #000000; font-weight: bold } /* Operator.Word */
.w { color: #bbbbbb } /* Text.Whitespace */
.mf { color: #009999 } /* Literal.Number.Float */
.mh { color: #009999 } /* Literal.Number.Hex */
.mi { color: #009999 } /* Literal.Number.Integer */
.mo { color: #009999 } /* Literal.Number.Oct */
.sb { color: #d01040 } /* Literal.String.Backtick */
.sc { color: #d01040 } /* Literal.String.Char */
.sd { color: #d01040 } /* Literal.String.Doc */
.s2 { color: #d01040 } /* Literal.String.Double */
.se { color: #d01040 } /* Literal.String.Escape */
.sh { color: #d01040 } /* Literal.String.Heredoc */
.si { color: #d01040 } /* Literal.String.Interpol */
.sx { color: #d01040 } /* Literal.String.Other */
.sr { color: #009926 } /* Literal.String.Regex */
.s1 { color: #d01040 } /* Literal.String.Single */
.ss { color: #990073 } /* Literal.String.Symbol */
.bp { color: #999999 } /* Name.Builtin.Pseudo */
.vc { color: #008080 } /* Name.Variable.Class */
.vg { color: #008080 } /* Name.Variable.Global */
.vi { color: #008080 } /* Name.Variable.Instance */
.il { color: #009999 } /* Literal.Number.Integer.Long */

+ 64
- 0
static/css/lib/highlight.css View File

@@ -0,0 +1,64 @@
td.linenos { background-color: #f0f0f0; padding-right: 10px; }
span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
pre { line-height: 125% }
.highlighttable { background-color: #49483e; width: inherit; height: inherit; }
.hll { display: block }
{ background: #272822; color: #f8f8f2 }
.c { color: #75715e } /* Comment */
.err { color: #960050; background-color: #1e0010 } /* Error */
.k { color: #66d9ef } /* Keyword */
.l { color: #ae81ff } /* Literal */
.n { color: #f8f8f2 } /* Name */
.o { color: #f92672 } /* Operator */
.p { color: #f8f8f2 } /* Punctuation */
.cm { color: #75715e } /* Comment.Multiline */
.cp { color: #75715e } /* Comment.Preproc */
.c1 { color: #75715e } /* Comment.Single */
.cs { color: #75715e } /* Comment.Special */
.ge { font-style: italic } /* Generic.Emph */
.gs { font-weight: bold } /* Generic.Strong */
.kc { color: #66d9ef } /* Keyword.Constant */
.kd { color: #66d9ef } /* Keyword.Declaration */
.kn { color: #f92672 } /* Keyword.Namespace */
.kp { color: #66d9ef } /* Keyword.Pseudo */
.kr { color: #66d9ef } /* Keyword.Reserved */
.kt { color: #66d9ef } /* Keyword.Type */
.ld { color: #e6db74 } /* Literal.Date */
.m { color: #ae81ff } /* Literal.Number */
.s { color: #e6db74 } /* Literal.String */
.na { color: #a6e22e } /* Name.Attribute */
.nb { color: #f8f8f2 } /* Name.Builtin */
.nc { color: #a6e22e } /* Name.Class */
.no { color: #66d9ef } /* Name.Constant */
.nd { color: #a6e22e } /* Name.Decorator */
.ni { color: #f8f8f2 } /* Name.Entity */
.ne { color: #a6e22e } /* Name.Exception */
.nf { color: #a6e22e } /* Name.Function */
.nl { color: #f8f8f2 } /* Name.Label */
.nn { color: #f8f8f2 } /* Name.Namespace */
.nx { color: #a6e22e } /* Name.Other */
.py { color: #f8f8f2 } /* Name.Property */
.nt { color: #f92672 } /* Name.Tag */
.nv { color: #f8f8f2 } /* Name.Variable */
.ow { color: #f92672 } /* Operator.Word */
.w { color: #f8f8f2 } /* Text.Whitespace */
.mf { color: #ae81ff } /* Literal.Number.Float */
.mh { color: #ae81ff } /* Literal.Number.Hex */
.mi { color: #ae81ff } /* Literal.Number.Integer */
.mo { color: #ae81ff } /* Literal.Number.Oct */
.sb { color: #e6db74 } /* Literal.String.Backtick */
.sc { color: #e6db74 } /* Literal.String.Char */
.sd { color: #e6db74 } /* Literal.String.Doc */
.s2 { color: #e6db74 } /* Literal.String.Double */
.se { color: #ae81ff } /* Literal.String.Escape */
.sh { color: #e6db74 } /* Literal.String.Heredoc */
.si { color: #e6db74 } /* Literal.String.Interpol */
.sx { color: #e6db74 } /* Literal.String.Other */
.sr { color: #e6db74 } /* Literal.String.Regex */
.s1 { color: #e6db74 } /* Literal.String.Single */
.ss { color: #e6db74 } /* Literal.String.Symbol */
.bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.vc { color: #f8f8f2 } /* Name.Variable.Class */
.vg { color: #f8f8f2 } /* Name.Variable.Global */
.vi { color: #f8f8f2 } /* Name.Variable.Instance */
.il { color: #ae81ff } /* Literal.Number.Integer.Long */

+ 13
- 8
static/js/index.advanced-search-form.js View File

@@ -37,6 +37,8 @@ var searchGroups = $("div#search-groups");
searchGroups.append(
searchGroup.append(createSearchGroupInput("language")));
$("div#sidebar input[type=checkbox]#language").prop("checked", true);

searchGroups[0].scrollTop = searchGroups[0].scrollHeight;
});

// Remove the currently selected group if it's not the only one, and mark
@@ -67,7 +69,7 @@ var searchGroups = $("div#search-groups");
})
});

// Add an input field to the currently selected search group.
// Toggle the presence of an input field.
$("div#sidebar input[type=checkbox]").click(function(){
var fieldId = $(this).prop("id");
if($(this).is(":checked")){
@@ -76,8 +78,13 @@ var searchGroups = $("div#search-groups");
if(fieldId.slice(0, 4) == "date")
$(".search-group#selected ." + fieldId).datepicker();
}
else
$("div.search-group#selected #" + fieldId).remove()
else {
if($(".search-group#selected").children("div").length > 1)
$(".search-group#selected #" + fieldId).remove()
else
$(this).prop("checked", true);
}
searchGroups[0].scrollTop = searchGroups[0].scrollHeight;
});

var previousAdvancedQuery = "";
@@ -125,7 +132,8 @@ function assembleQuery(){
groupQuery.push(genFieldQueryString(
inputFields[field], regexCheckbox[field].checked));

groupQueries.push(groupQuery.join(" AND "));
if(groupQuery.length > 0)
groupQueries.push(groupQuery.join(" AND "));
}

return groupQueries.join(" OR ");
@@ -140,8 +148,5 @@ function assembleQuery(){
function genFieldQueryString(field, hasRegex){
var terms = field.value.replace(/\\/g, "\\\\").replace(/\"/g, "\\\"");
var query = field.getAttribute("name") + ":" + (hasRegex?"re:":"") + terms;
if(field.value.indexOf('"') >= 0){
return '"' + query + '"';
}
return query;
return '"' + query + '"';
}

+ 266
- 53
static/js/index.js View File

@@ -5,34 +5,145 @@

var advancedSearchDiv = $("div#advanced-search");
var advancedSearchButton = $("button#advanced-search");
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");
FINISH_TYPING_INTERVAL = 650;
var searchBar = $("form#search-bar input[type='text']")[0];
var resultsDiv = $("div#results")[0];

var typingTimer, scrollTimer, lastValue;
var searchResultsPage = 1;

/*
* Set all page callbacks.
*/
(function setHomePageCallbabacks(){
var results = $('#results').get(0);

// Enable infinite scrolling down the results page.
$(window).scroll(function(){
if($(window).scrollTop() + $(window).height() == $(document).height() &&
resultsDiv.querySelectorAll(".result").length > 0)
loadMoreResults();

clearTimeout(scrollTimer);
if (!results.classList.contains('disable-hover'))
results.classList.add('disable-hover')

scrollTimer = setTimeout(function(){
if (results.classList.contains('disable-hover'))
results.classList.remove('disable-hover');
}, 200);
});

// 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;
}());

/*
* Set keyboard shortcut mappings.
*/
(function resultsHotkeys(){
/*
* If the currently viewed result is not the first, scroll to the previous
* result.
*/
var previousResult = function(){
var currResult = $(".display-all");
if(currResult.length) {
currResult.removeClass("display-all");
currResult = currResult.closest(".result").prev(".result");
} else {
currResult = $(document.querySelectorAll(".result")[0]);
}

currResult.addClass("display-all");
currResult.each(function(){
$('html,body').stop().animate({
scrollTop: $(this).offset().top - (
$(window).height() - $(this).outerHeight(true)) / 2
}, 140);
});
};

/*
* If the currently viewed result is not the last, scroll to the next
* result.
*/
var nextResult = function(){
var currResult = $(".display-all");
if(currResult.length) {
currResult.removeClass("display-all");
currResult = currResult.closest(".result").next(".result");
} else {
currResult = $(document.querySelectorAll(".result")[0]);
}

currResult.addClass('display-all');
currResult.each(function(){
$('html,body').stop().animate({
scrollTop: $(this).offset().top - (
$(window).height() - $(this).outerHeight(true)) / 2
}, 140);
});
};

var displayHotkeyHelp = function(){
var help = $("div#hotkey-help");
console.log("H");
if(help.hasClass("hidden"))
help.fadeIn(420);
else
help.fadeOut(420);

$("div#body").toggleClass("faded");
help.toggleClass("hidden");
}
});

FINISH_TYPING_INTERVAL = 650;
searchBar = $("form#search-bar input[type='text']")[0];
resultsDiv = $("div#results")[0];
var hotkeyActions = {
"k" : previousResult,
"j" : nextResult,
"h" : previousSymbolMatch,
"l" : nextSymbolMatch,
"?" : displayHotkeyHelp
};

$(window).keypress(function(key){
for(var hotkey in hotkeyActions){
var keyChar = String.fromCharCode(key.keyCode);
if(keyChar == hotkey &&
!($(key.target).is("textarea") || $(key.target).is("input")))
hotkeyActions[keyChar]();
}
});
}());

var typingTimer, lastValue;
//Obtained by parsing python file with pygments
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>'
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>'
searchBar.onkeyup = typingTimer;

var testCodelet = {
'url': 'https://github.com/earwig/bitshift/blob/develop/app.py',
'filename': 'app.py',
'language': 'Python',
'language': 'python',
'date_created': 'May 10, 2014',
'date_modified': '2 days ago',
'origin': ['GitHub', 'https://github.com', ''],
@@ -43,17 +154,12 @@ var testCodelet = {
// 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')){
if($(window).scrollTop() + $(window).height() == $(document).height() &&
searchField.hasClass('partly-visible')){
loadMoreResults();
}
});

// Enable capturing the `enter` key.
$("form#search-bar").submit(function(event){
event.preventDefault();
return false;
});

/*
* Clear the existing timer and set a new one the the user types text into the
* search bar.
@@ -109,6 +215,7 @@ function clearResults(){
* with its response.
*/
function populateResults(){
searchResultsPage = 1;
var results = queryServer();

for(var result = 0; result < results.length; result++){
@@ -135,74 +242,108 @@ function createResult(codelet) {
row = document.createElement("tr");
//Level 2
var displayInfo = document.createElement("div"),
sidebar = document.createElement("td"),
codeElt = document.createElement("td"),
displayButton = document.createElement("td"),
hiddenInfoContainer = document.createElement("td"),
hiddenInfo = document.createElement("div");
hiddenInfo = document.createElement("div"),
cycle = document.createElement("div");
//Level 3
var title = document.createElement("span"),
site = document.createElement("span"),
dateModified = document.createElement("span"),
language = document.createElement("span"),
dateCreated = 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';
sidebar.id = 'sidebar';
codeElt.id = 'code';
displayButton.id = 'display-button';
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 = 'File <a href="' + codelet.url + '">'
title.innerHTML = ' &raquo; <a href="' + codelet.url + '">'
+ codelet.filename + '</a>';
site.innerHTML = 'on <a href="' + codelet.origin[1] + '">' + codelet.origin[0] +'</a>';
dateModified.innerHTML = 'Last modified ' + codelet.date_modified;
site.innerHTML = '<a href="' + codelet.origin[1] + '">' + codelet.origin[0] +'</a>';
nextMatch.innerHTML = 'next match';
prevMatch.innerHTML = 'prev match';
language.innerHTML = 'Language: <span>' + codelet.language + '</span>';
dateModified.innerHTML = 'Last modified: <span>' + codelet.date_modified + '</span>';
// Needs to be changed from int to string on the server
language.innerHTML = codelet.language;
dateCreated.innerHTML = 'Created ' + codelet.date_created;
authors.innerHTML = 'Authors: ';
$.each(codelet.authors, function(i, a) {
authors.innerHTML += '<a href=#>' + a + ' </a>';
dateCreated.innerHTML = 'Created: <span>' + codelet.date_created + '</span>';

var authorsHtml = 'Authors: <span>';
codelet.authors.forEach(function(a, i) {
if (i == codelet.authors.length - 1)
authorsHtml += '<a href=#>' + a + ' </a>';
else
authorsHtml += '<a href=#>' + a + ' </a>, ';
});
authors.innerHTML = authorsHtml;

sidebar.innerHTML = '';
// Needs to be processed on the server
codeElt.innerHTML = '<div id=tablecontainer>' + codelet.html_code + '</div>';

//Event binding
$(displayButton).click(function(e) {
$(hiddenInfo).toggleClass('visible');
$(this).toggleClass('active');
$(newDiv).on('mousemove', function(e) {
var holdCondition = $('.disable-hover');

if(holdCondition.length == 0) {
$(this).siblings().removeClass('display-all');
$(this).addClass('display-all');
}
});

$(newDiv).on('mouseleave', function(e) {
var holdCondition = $('.disable-hover');

if(holdCondition.length == 0)
$(this).removeClass('display-all');
});

$(nextMatch).click(function(e) {
e.stopPropagation();
e.preventDefault();
nextSymbolMatch();
});

$(prevMatch).click(function(e) {
e.stopPropagation();
e.preventDefault();
previousSymbolMatch();
});

//Finish and append elements to parent elements
hiddenInfo.appendChild(dateCreated);
hiddenInfo.appendChild(dateModified);
hiddenInfo.appendChild(authors);
hiddenInfo.appendChild(language);
hiddenInfo.appendChild(authors);

hiddenInfoContainer.appendChild(hiddenInfo);

row.appendChild(sidebar);
row.appendChild(codeElt);
row.appendChild(hiddenInfoContainer);
row.appendChild(displayButton);
table.appendChild(row);

displayInfo.appendChild(title);
displayInfo.appendChild(site);
displayInfo.appendChild(title);

cycle.appendChild(prevMatch);
cycle.appendChild(nextMatch);

newDiv.appendChild(displayInfo);
newDiv.appendChild(table);
@@ -210,6 +351,52 @@ function createResult(codelet) {
return newDiv;
}

function previousSymbolMatch() {
var currResult = $(".display-all"),
currMatch = currResult.find(".hll.current"),
matches = currResult.find(".hll"),
scrollDiv = currResult.find("#tablecontainer");

if (currMatch.length == 0)
currMatch = matches[0];
else
currMatch.removeClass('current');

var index = matches.index(currMatch.get(0)) - 1;
index = index <= 0 ? matches.length - 1 : index;
var newMatch = $(matches[index]);

scrollDiv.scrollTop(scrollDiv.scrollTop()
- scrollDiv.height() / 2
+ newMatch.position().top + newMatch.height() / 2);

newMatch.effect("highlight", {color: '#FFF'}, 750)
newMatch.addClass('current');
};

function nextSymbolMatch() {
var currResult = $(".display-all"),
currMatch = currResult.find(".hll.current"),
matches = currResult.find(".hll"),
scrollDiv = currResult.find("#tablecontainer");

if (currMatch.length == 0)
currMatch = $(matches[0]);
else
currMatch.removeClass("current");

var index = matches.index(currMatch.get(0)) + 1;
index = index >= matches.length ? 0 : index;
var newMatch = $(matches[index]);

scrollDiv.scrollTop(scrollDiv.scrollTop()
- scrollDiv.height() / 2
+ newMatch.position().top + newMatch.height() / 2);

newMatch.effect("highlight", {color: "#FFF"}, 750)
newMatch.addClass("current");
};

/*
* AJAX the current query string to the server, and return its response.
*
@@ -217,10 +404,23 @@ function createResult(codelet) {
* elements, to fill `div#results`.
*/
function queryServer(){
var resultDivs = []
var queryUrl = document.URL + "search.json?" + $.param({
"q" : searchBar.value,
"p" : searchResultsPage++
});

var resultDivs = [];
$.getJSON(queryUrl, function(result){
if("error" in result)
insertErrorMessage(result["error"]);
else
for(var codelet = 0; codelet < result["results"].length; codelet++)
resultDivs.push(result["results"][codelet]);
});

for(var result = 0; result < 20; result++){
var newDiv = createResult(testCodelet);
resultDivs.push(newDiv);
resultDivs.push(newDiv)
}

return resultDivs;
@@ -230,7 +430,7 @@ function queryServer(){
* Adds more results to `div#results`.
*/
function loadMoreResults(){
results = queryServer();
var results = queryServer();
for(var result = 0; result < results.length; result++){
var newDiv = results[result];
resultsDiv.appendChild(newDiv);
@@ -243,3 +443,16 @@ function loadMoreResults(){
result * 20);
}
}

/*
* Displays a warning message in the UI.
*
* @param msg (str) The message string.
*/
function insertErrorMessage(msg){
var error = $("<div id='error'><span>Error: </span></div>");
error.append(msg);
resultsDiv.appendChild(error[0]);
}

// loadMoreResults();

+ 0
- 7
static/js/lib/typeahead.bundle.min.js
File diff suppressed because it is too large
View File


+ 5
- 0
static/sass/_mixins.sass View File

@@ -10,6 +10,11 @@
-o-#{$property}: $value
#{$property}: $value

// Add portable opacity style.
@mixin opaque($opacity)
@include vendor(opacity, $opacity)
filter: alpha(opacity=$opacity)

.t1
@include vendor(transition, all 0.1s ease-out)



+ 157
- 101
static/sass/index.sass View File

@@ -6,9 +6,8 @@
@import variables

$minSearchFieldsWidth: 490px
$resultWidth: 830px
$sidebarWidth: 30px
$codeWidth: 500px
$resultWidth: 1000px
$codeWidth: 650px
$hiddenInfoWidth: 250px

.ui-datepicker
@@ -23,6 +22,54 @@ $hiddenInfoWidth: 250px
>li.ui-menu-item a.ui-state-focus
@include vendor(transition, background-color 0.3s ease-out)

div#body
@extend .t3

&.faded
@include opaque(0.8)

div#hotkey-help
$width: 40%

background-color: white
border: 1px solid $baseColor3
left: 50% - $width / 2
min-width: 400px
padding: 35px
position: fixed
top: 30%
width: $width
z-index: 200

&.hidden
display: none

div
border-bottom: 1px solid $baseColor2
color: $baseColor1
font-size: 130%
padding-bottom: 8px
text-align: center

ul
list-style: none
margin-left: auto
margin-right: auto
position: relative
width: 300px

li
margin-bottom: 4px

span.hotkey
color: $baseColor1
font-family: monospace
font-size: 130%
font-weight: bold

span.seperator
color: $baseColor2

div#search-field
@extend .t2

@@ -34,6 +81,7 @@ div#search-field
max-height: 100px
right: 0
position: absolute
z-index: 2
top: 0
width: 40%

@@ -103,7 +151,6 @@ div#search-field

&.partly-visible
margin-top: 0%
padding-bottom: 3%
position: absolute
width: 100%

@@ -121,6 +168,11 @@ div#search-field
margin-right: auto
width: 60%

input:hover
@extend .t3

border: 1px solid $baseColor1

div#advanced-search
background-color: white
border: 1px solid $baseColor3
@@ -237,7 +289,7 @@ div#advanced-search

#search-groups
margin-top: 1%
max-height: 93%
max-height: 87%
overflow-y: auto
width: 75%

@@ -286,112 +338,116 @@ div#results
margin-right: auto
width: 80%

/* TODO:
1) Sidebar
- Add way to cycle through hits in the code.
2) Hidden info
- Add links for authors.
- Remove language field.
3) Header
- Add an icon for the website.
- Add language tag.
4) Code body
- Add highlighting.*/
div.result
width: $resultWidth
height: 200px
margin-top: 2%
margin-bottom: 6%

#display-info
font-size: 1.2em

a
text-decoration: none

&:hover
color: orange

#title
margin-right: 10px

#site
text-transform: capitalize
a
text-decoration: none

&:hover
color: orange

div#error
font-size: 170%
margin-top: 22%
text-align: center

span
color: $baseColor1
font-size: 150%

&.disable-hover
pointer-events: none

div.result
@extend .t3

width: $resultWidth
height: 200px
margin-bottom: 100%
pointer-events: auto

table
border-collapse: collapse
height: inherit

tr
@extend .t3
@include opaque(0.8)

table
border-collapse: collapse
border: 1px solid $baseColor3
height: inherit

tr
height: inherit

#sidebar
width: $sidebarWidth
background-color: #eee
border-right: 1px solid $baseColor3
height: inherit

#code
width: $codeWidth
height: inherit
border-right: 1px solid $baseColor3

#tablecontainer
overflow: scroll
width: 100%
height: inherit
background-color: #49483e

table
table-layout:fixed
border-collapse: collapse
border: none
font-family: monospace

#display-button
width: 25px
background: url(http://icons.iconarchive.com/icons/visualpharm/icons8-metro-style/512/Image-Edition-Tools-Arrow-icon.png)
background-size: 25px 25px
background-repeat: no-repeat
background-position: center
&.cascade
@extend .t1
margin-bottom: 15%

&:hover
background-color: #eee
&.display-all table tr
@include opaque(1.0)

&.active
@include vendor(transform, rotateY(180deg))
div#display-info
font-size: 1.3em
padding: 5px 0px 5px 5px
width: 100%

#hidden-info
width: $hiddenInfoWidth
margin-left: -$hiddenInfoWidth
height: 100%
padding-top: 50px
text-align: center
font-size: 1em
line-height: 1.5em
@include vendor(transition, margin-left 0.2s ease-in-out)
#title
margin-right: 10px

&.visible
margin-left: 0px
#site
text-transform: capitalize

#date-created
display: inline-block
td#code
@include vendor(transition, width 0.2s ease-in-out)

#date-modified
display: block
width: $codeWidth
height: inherit
padding: 0px
border: 1px solid #bbb

#language
display: inline-block
font-weight: bold
color: orange
#tablecontainer
overflow: hidden
width: 100%
height: inherit
background-color: #49483e
position: relative
z-index: 1

#authors
a
text-decoration: none
table
border-collapse: collapse
font-family: monospace

&:hover
color: orange
.linenos
padding-left: 1%

&.cascade
@extend .t3
pre
margin-top: 5px

.code pre
margin-top: 5px

.hll
background: #5B5A51

div#hidden-info
width: $hiddenInfoWidth
margin-left: -$hiddenInfoWidth - 10px
height: 100%
padding-top: 40px
font-size: 1.2em
line-height: 1.5em
position: relative
z-index: 0

@include vendor(transition, margin-left 0.2s ease-in-out)

.display-all &
margin-left: 0px
padding-left: 20px

span
font-family: monospace
color: $baseColor1
float: right

div
display: block

#authors
a
font-family: monospace

+ 14
- 3
templates/index.html View File

@@ -1,14 +1,13 @@
= extends "layout.html"

= block title
Home
home
= endblock

= block head
{{ assets.tag("lib/jqueryui.custom.min.css") }}
{{ assets.tag("lib/jquery.min.js") }}
{{ assets.tag("lib/jquery-ui.min.js") }}
{{ assets.tag("lib/typeahead.bundle.min.js") }}
{{ assets.tag("lib/highlight.css") }}

{{ assets.tag("index.css") }}
@@ -30,7 +29,7 @@

<form id="search-bar">
<input type="text" name="query"
><button id="advanced-search" title="advanced search">
><button id="advanced-search" title="advanced search" type="button">
<img src="static/img/search_bar_magnifying_glass.png">
</button>

@@ -100,3 +99,15 @@
{{ assets.tag("index.js") }}
{{ assets.tag("index.advanced-search-form.js") }}
= endblock

= block after_body
<div id="hotkey-help" class="hidden">
<div>Hotkeys</div>
<ul>
<li><span class="hotkey">k</span> <span class="seperator">:</span> move window up to the previous result</li>
<li><span class="hotkey">j</span> <span class="seperator">:</span> move window down to the next result</li>
<li><span class="hotkey">h</span> <span class="seperator">:</span> move to the previous symbol match</li>
<li><span class="hotkey">l</span> <span class="seperator">:</span> move to the next symbol match</li>
</ul>
</div>
= endblock

+ 8
- 2
templates/layout.html View File

@@ -4,8 +4,11 @@
<html>
<head>
<title>
= block title
= endblock
bitshift &laquo;
= filter lower
= block title
= endblock
= endfilter
</title>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
@@ -30,6 +33,9 @@
</div>
</div>

= block after_body
= endblock

<div id="footer">
<a href="/">home</a>
<a href="/about">about</a>


+ 40
- 0
test/resources/app.py View File

@@ -0,0 +1,40 @@
"""
Module to contain all the project's Flask server plumbing.
"""

from flask import Flask
from flask import render_template, session

from bitshift import assets
# from bitshift.database import Database
# from bitshift.query import parse_query

app = Flask(__name__)
app.config.from_object("bitshift.config")

app_env = app.jinja_env
app_env.line_statement_prefix = "="
app_env.globals.update(assets=assets)

# database = Database()

@app.route("/")
def index():
return render_template("index.html")

@app.route("/search/<query>")
def search(query):
# tree = parse_query(query)
# database.search(tree)
pass

@app.route("/about")
def about():
return render_template("about.html")

@app.route("/developers")
def developers():
return render_template("developers.html")

if __name__ == "__main__":
app.run(debug=True)

Loading…
Cancel
Save