Conflicts: bitshift/crawler/crawl.py bitshift/languages.pytags/v1.0^2
| @@ -1,5 +1,5 @@ | |||||
| static/css/* | static/css/* | ||||
| !static/css/lib/* | |||||
| !lib | |||||
| *.swp | *.swp | ||||
| .sass-cache | .sass-cache | ||||
| @@ -22,7 +22,7 @@ database = Database() | |||||
| @app.route("/") | @app.route("/") | ||||
| def index(): | def index(): | ||||
| return render_template("index.html", typeahead_languages=LANGS) | |||||
| return render_template("index.html", autocomplete_languages=LANGS) | |||||
| @app.route("/search.json") | @app.route("/search.json") | ||||
| def search(): | def search(): | ||||
| @@ -39,6 +39,7 @@ def crawl(): | |||||
| indexer.GitIndexer(repo_clone_queue, run_event)] | indexer.GitIndexer(repo_clone_queue, run_event)] | ||||
| parse_servers = start_parse_servers() | parse_servers = start_parse_servers() | ||||
| time.sleep(5) | |||||
| for thread in threads: | for thread in threads: | ||||
| thread.start() | thread.start() | ||||
| @@ -33,6 +33,7 @@ class GitRepository(object): | |||||
| repository belongs to (eg, GitHub, BitBucket). | repository belongs to (eg, GitHub, BitBucket). | ||||
| :ivar rank: (float) The rank of the repository, as assigned by | :ivar rank: (float) The rank of the repository, as assigned by | ||||
| :class:`crawler.GitHubCrawler`. | :class:`crawler.GitHubCrawler`. | ||||
| :ivar dirname: (str) The repository's on-disk directory name. | |||||
| """ | """ | ||||
| def __init__(self, url, name, framework_name, rank): | def __init__(self, url, name, framework_name, rank): | ||||
| @@ -54,6 +55,7 @@ class GitRepository(object): | |||||
| self.name = name | self.name = name | ||||
| self.framework_name = framework_name | self.framework_name = framework_name | ||||
| self.rank = rank | self.rank = rank | ||||
| self.dirname = name.replace("-", "--").replace("/", "-") | |||||
| class GitIndexer(threading.Thread): | class GitIndexer(threading.Thread): | ||||
| """ | """ | ||||
| @@ -125,19 +127,14 @@ class GitIndexer(threading.Thread): | |||||
| :type repo_url: :class:`GitRepository` | :type repo_url: :class:`GitRepository` | ||||
| """ | """ | ||||
| with _ChangeDir("%s/%s" % (GIT_CLONE_DIR, repo.name)): | |||||
| with _ChangeDir("%s/%s" % (GIT_CLONE_DIR, repo.dirname)): | |||||
| try: | try: | ||||
| self._insert_repository_codelets(repo) | self._insert_repository_codelets(repo) | ||||
| except Exception: | except Exception: | ||||
| self._logger.exception("Exception raised while indexing:") | self._logger.exception("Exception raised while indexing:") | ||||
| finally: | 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): | def _insert_repository_codelets(self, repo): | ||||
| """ | """ | ||||
| @@ -167,9 +164,9 @@ class GitIndexer(threading.Thread): | |||||
| authors = [(self._decode(author), None) for author in | authors = [(self._decode(author), None) for author in | ||||
| commits_meta[filename]["authors"]] | 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_created"], | ||||
| commits_meta[filename]["time_last_modified"], | commits_meta[filename]["time_last_modified"], | ||||
| repo.rank) | repo.rank) | ||||
| @@ -437,37 +434,20 @@ class _GitCloner(threading.Thread): | |||||
| """ | """ | ||||
| GIT_CLONE_TIMEOUT = 500 | GIT_CLONE_TIMEOUT = 500 | ||||
| queue_percent_full = (float(self.index_queue.qsize()) / | 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 | return | ||||
| while self.index_queue.full(): | while self.index_queue.full(): | ||||
| time.sleep(THREAD_QUEUE_SLEEP) | time.sleep(THREAD_QUEUE_SLEEP) | ||||
| self.index_queue.put(repo) | self.index_queue.put(repo) | ||||
| class _ChangeDir(object): | class _ChangeDir(object): | ||||
| @@ -131,7 +131,7 @@ class Database(object): | |||||
| def _decompose_url(self, cursor, url): | def _decompose_url(self, cursor, url): | ||||
| """Break up a URL into an origin (with a URL base) and a suffix.""" | """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 | FROM origins | ||||
| WHERE origin_url_base IS NOT NULL | WHERE origin_url_base IS NOT NULL | ||||
| AND ? LIKE CONCAT(origin_url_base, "%")""" | AND ? LIKE CONCAT(origin_url_base, "%")""" | ||||
| @@ -3,7 +3,7 @@ Contains information about database schema versions, and SQL queries to update | |||||
| between them. | between them. | ||||
| """ | """ | ||||
| VERSION = 8 | |||||
| VERSION = 10 | |||||
| MIGRATIONS = [ | MIGRATIONS = [ | ||||
| # 1 -> 2 | # 1 -> 2 | ||||
| @@ -100,6 +100,28 @@ MIGRATIONS = [ | |||||
| [ | [ | ||||
| """ALTER TABLE `origins` | """ALTER TABLE `origins` | ||||
| DROP COLUMN `origin_image`""" | 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""" | |||||
| ] | ] | ||||
| ] | ] | ||||
| @@ -1,4 +1,4 @@ | |||||
| -- Schema version 10 | |||||
| CREATE DATABASE `bitshift` DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci; | CREATE DATABASE `bitshift` DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci; | ||||
| USE `bitshift`; | USE `bitshift`; | ||||
| @@ -6,7 +6,7 @@ USE `bitshift`; | |||||
| CREATE TABLE `version` ( | CREATE TABLE `version` ( | ||||
| `version` INT UNSIGNED NOT NULL | `version` INT UNSIGNED NOT NULL | ||||
| ) ENGINE=InnoDB; | ) ENGINE=InnoDB; | ||||
| INSERT INTO `version` VALUES (8); | |||||
| INSERT INTO `version` VALUES (10); | |||||
| CREATE TABLE `origins` ( | CREATE TABLE `origins` ( | ||||
| `origin_id` TINYINT UNSIGNED NOT NULL AUTO_INCREMENT, | `origin_id` TINYINT UNSIGNED NOT NULL AUTO_INCREMENT, | ||||
| @@ -77,9 +77,9 @@ CREATE TABLE `symbol_locations` ( | |||||
| `sloc_symbol` BIGINT UNSIGNED NOT NULL, | `sloc_symbol` BIGINT UNSIGNED NOT NULL, | ||||
| `sloc_type` TINYINT UNSIGNED NOT NULL, | `sloc_type` TINYINT UNSIGNED NOT NULL, | ||||
| `sloc_row` INT 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`), | PRIMARY KEY (`sloc_id`), | ||||
| FOREIGN KEY (`sloc_symbol`) | FOREIGN KEY (`sloc_symbol`) | ||||
| REFERENCES `symbols` (`symbol_id`) | REFERENCES `symbols` (`symbol_id`) | ||||
| @@ -107,6 +107,19 @@ CREATE TABLE `cache_data` ( | |||||
| ON DELETE CASCADE ON UPDATE CASCADE | ON DELETE CASCADE ON UPDATE CASCADE | ||||
| ) ENGINE=InnoDB; | ) 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` | CREATE EVENT `flush_cache` | ||||
| ON SCHEDULE EVERY 1 HOUR | ON SCHEDULE EVERY 1 HOUR | ||||
| DO | DO | ||||
| @@ -8,7 +8,8 @@ def _load_langs(): | |||||
| filename = path.join(path.dirname(__file__), "languages.yml") | filename = path.join(path.dirname(__file__), "languages.yml") | ||||
| with open(filename) as fp: | with open(filename) as fp: | ||||
| data = yaml.load(fp)["languages"] | 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 = {} | all_langs = {} | ||||
| for i, lang in enumerate(data): | for i, lang in enumerate(data): | ||||
| if isinstance(lang, dict): | if isinstance(lang, dict): | ||||
| @@ -80,7 +80,7 @@ public class JavaParser extends Parser { | |||||
| int sl = this.root.getLineNumber(node.getStartPosition()); | int sl = this.root.getLineNumber(node.getStartPosition()); | ||||
| int sc = this.root.getColumnNumber(node.getStartPosition()); | int sc = this.root.getColumnNumber(node.getStartPosition()); | ||||
| Integer el = -1; | |||||
| Integer el = sl; | |||||
| Integer ec = -1; | Integer ec = -1; | ||||
| if (statements.size() > 0) { | if (statements.size() > 0) { | ||||
| @@ -110,7 +110,7 @@ public class JavaParser extends Parser { | |||||
| int sl = this.root.getLineNumber(node.getStartPosition()); | int sl = this.root.getLineNumber(node.getStartPosition()); | ||||
| int sc = this.root.getColumnNumber(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); | data.put("name", name); | ||||
| this._cache.push(data); | this._cache.push(data); | ||||
| return true; | return true; | ||||
| @@ -140,7 +140,7 @@ public class JavaParser extends Parser { | |||||
| int sl = this.root.getLineNumber(node.getStartPosition()); | int sl = this.root.getLineNumber(node.getStartPosition()); | ||||
| int sc = this.root.getColumnNumber(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); | this._cache.push(data); | ||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -161,7 +161,7 @@ public class JavaParser extends Parser { | |||||
| int sl = this.root.getLineNumber(node.getStartPosition()); | int sl = this.root.getLineNumber(node.getStartPosition()); | ||||
| int sc = this.root.getColumnNumber(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); | this._cache.push(data); | ||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -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 */ | |||||
| @@ -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 */ | |||||
| @@ -37,6 +37,8 @@ var searchGroups = $("div#search-groups"); | |||||
| searchGroups.append( | searchGroups.append( | ||||
| searchGroup.append(createSearchGroupInput("language"))); | searchGroup.append(createSearchGroupInput("language"))); | ||||
| $("div#sidebar input[type=checkbox]#language").prop("checked", true); | $("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 | // 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(){ | $("div#sidebar input[type=checkbox]").click(function(){ | ||||
| var fieldId = $(this).prop("id"); | var fieldId = $(this).prop("id"); | ||||
| if($(this).is(":checked")){ | if($(this).is(":checked")){ | ||||
| @@ -76,8 +78,13 @@ var searchGroups = $("div#search-groups"); | |||||
| if(fieldId.slice(0, 4) == "date") | if(fieldId.slice(0, 4) == "date") | ||||
| $(".search-group#selected ." + fieldId).datepicker(); | $(".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 = ""; | var previousAdvancedQuery = ""; | ||||
| @@ -125,7 +132,8 @@ function assembleQuery(){ | |||||
| groupQuery.push(genFieldQueryString( | groupQuery.push(genFieldQueryString( | ||||
| inputFields[field], regexCheckbox[field].checked)); | inputFields[field], regexCheckbox[field].checked)); | ||||
| groupQueries.push(groupQuery.join(" AND ")); | |||||
| if(groupQuery.length > 0) | |||||
| groupQueries.push(groupQuery.join(" AND ")); | |||||
| } | } | ||||
| return groupQueries.join(" OR "); | return groupQueries.join(" OR "); | ||||
| @@ -140,8 +148,5 @@ function assembleQuery(){ | |||||
| function genFieldQueryString(field, hasRegex){ | function genFieldQueryString(field, hasRegex){ | ||||
| var terms = field.value.replace(/\\/g, "\\\\").replace(/\"/g, "\\\""); | var terms = field.value.replace(/\\/g, "\\\\").replace(/\"/g, "\\\""); | ||||
| var query = field.getAttribute("name") + ":" + (hasRegex?"re:":"") + terms; | var query = field.getAttribute("name") + ":" + (hasRegex?"re:":"") + terms; | ||||
| if(field.value.indexOf('"') >= 0){ | |||||
| return '"' + query + '"'; | |||||
| } | |||||
| return query; | |||||
| return '"' + query + '"'; | |||||
| } | } | ||||
| @@ -5,34 +5,145 @@ | |||||
| var advancedSearchDiv = $("div#advanced-search"); | var advancedSearchDiv = $("div#advanced-search"); | ||||
| var advancedSearchButton = $("button#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 | //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">"""</span>\n<span class="sd">Module to contain all the project's Flask server plumbing.</span>\n<span class="sd">"""</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">"bitshift.config"</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">"="</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">"/"</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">"index.html"</span><span class="p">)</span>\n\n<span class="nd">@app.route</span><span class="p">(</span><span class="s">"/search/<query>"</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">"/about"</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">"about.html"</span><span class="p">)</span>\n\n<span class="nd">@app.route</span><span class="p">(</span><span class="s">"/developers"</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">"developers.html"</span><span class="p">)</span>\n\n<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</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">"""</span>\n<span class="sd">Module to contain all the project's Flask server plumbing.</span>\n<span class="sd">"""</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">"bitshift.config"</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">"="</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">"/"</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">"index.html"</span><span class="p">)</span>\n\n<span class="hll"><span class="nd">@app.route</span><span class="p">(</span><span class="s">"/search/<query>"</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">"/about"</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">"about.html"</span><span class="p">)</span>\n\n<span class="hll"><span class="nd">@app.route</span><span class="p">(</span><span class="s">"/developers"</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">"developers.html"</span><span class="p">)</span>\n\n<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</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; | searchBar.onkeyup = typingTimer; | ||||
| var testCodelet = { | var testCodelet = { | ||||
| 'url': 'https://github.com/earwig/bitshift/blob/develop/app.py', | 'url': 'https://github.com/earwig/bitshift/blob/develop/app.py', | ||||
| 'filename': 'app.py', | 'filename': 'app.py', | ||||
| 'language': 'Python', | |||||
| 'language': 'python', | |||||
| 'date_created': 'May 10, 2014', | 'date_created': 'May 10, 2014', | ||||
| 'date_modified': '2 days ago', | 'date_modified': '2 days ago', | ||||
| 'origin': ['GitHub', 'https://github.com', ''], | 'origin': ['GitHub', 'https://github.com', ''], | ||||
| @@ -43,17 +154,12 @@ var testCodelet = { | |||||
| // Enable infinite scrolling down the results page. | // Enable infinite scrolling down the results page. | ||||
| $(window).scroll(function() { | $(window).scroll(function() { | ||||
| var searchField = $("div#search-field"); | 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(); | 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 | * Clear the existing timer and set a new one the the user types text into the | ||||
| * search bar. | * search bar. | ||||
| @@ -109,6 +215,7 @@ function clearResults(){ | |||||
| * with its response. | * with its response. | ||||
| */ | */ | ||||
| function populateResults(){ | function populateResults(){ | ||||
| searchResultsPage = 1; | |||||
| var results = queryServer(); | var results = queryServer(); | ||||
| for(var result = 0; result < results.length; result++){ | for(var result = 0; result < results.length; result++){ | ||||
| @@ -135,74 +242,108 @@ function createResult(codelet) { | |||||
| row = document.createElement("tr"); | row = document.createElement("tr"); | ||||
| //Level 2 | //Level 2 | ||||
| var displayInfo = document.createElement("div"), | var displayInfo = document.createElement("div"), | ||||
| sidebar = document.createElement("td"), | |||||
| codeElt = document.createElement("td"), | codeElt = document.createElement("td"), | ||||
| displayButton = document.createElement("td"), | |||||
| hiddenInfoContainer = document.createElement("td"), | hiddenInfoContainer = document.createElement("td"), | ||||
| hiddenInfo = document.createElement("div"); | |||||
| hiddenInfo = document.createElement("div"), | |||||
| cycle = document.createElement("div"); | |||||
| //Level 3 | //Level 3 | ||||
| var title = document.createElement("span"), | var title = document.createElement("span"), | ||||
| site = 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"); | authors = document.createElement("div"); | ||||
| //Classes and ID's | //Classes and ID's | ||||
| newDiv.classList.add('result'); | newDiv.classList.add('result'); | ||||
| displayInfo.id = 'display-info'; | displayInfo.id = 'display-info'; | ||||
| sidebar.id = 'sidebar'; | |||||
| codeElt.id = 'code'; | codeElt.id = 'code'; | ||||
| displayButton.id = 'display-button'; | |||||
| hiddenInfo.id = 'hidden-info'; | hiddenInfo.id = 'hidden-info'; | ||||
| cycle.id = 'cycle-matches' | |||||
| title.id = 'title'; | title.id = 'title'; | ||||
| site.id = 'site'; | site.id = 'site'; | ||||
| nextMatch.id = 'next-match'; | |||||
| nextMatch.href = '#'; | |||||
| prevMatch.id = 'prev-match'; | |||||
| prevMatch.href = '#'; | |||||
| dateModified.id = 'date-modified'; | dateModified.id = 'date-modified'; | ||||
| language.id = 'language'; | language.id = 'language'; | ||||
| dateCreated.id = 'date-created'; | dateCreated.id = 'date-created'; | ||||
| authors.id = 'authors'; | authors.id = 'authors'; | ||||
| //Add the bulk of the html | //Add the bulk of the html | ||||
| title.innerHTML = 'File <a href="' + codelet.url + '">' | |||||
| title.innerHTML = ' » <a href="' + codelet.url + '">' | |||||
| + codelet.filename + '</a>'; | + 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 | // 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 | // Needs to be processed on the server | ||||
| codeElt.innerHTML = '<div id=tablecontainer>' + codelet.html_code + '</div>'; | codeElt.innerHTML = '<div id=tablecontainer>' + codelet.html_code + '</div>'; | ||||
| //Event binding | //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 | //Finish and append elements to parent elements | ||||
| hiddenInfo.appendChild(dateCreated); | hiddenInfo.appendChild(dateCreated); | ||||
| hiddenInfo.appendChild(dateModified); | hiddenInfo.appendChild(dateModified); | ||||
| hiddenInfo.appendChild(authors); | |||||
| hiddenInfo.appendChild(language); | hiddenInfo.appendChild(language); | ||||
| hiddenInfo.appendChild(authors); | |||||
| hiddenInfoContainer.appendChild(hiddenInfo); | hiddenInfoContainer.appendChild(hiddenInfo); | ||||
| row.appendChild(sidebar); | |||||
| row.appendChild(codeElt); | row.appendChild(codeElt); | ||||
| row.appendChild(hiddenInfoContainer); | row.appendChild(hiddenInfoContainer); | ||||
| row.appendChild(displayButton); | |||||
| table.appendChild(row); | table.appendChild(row); | ||||
| displayInfo.appendChild(title); | |||||
| displayInfo.appendChild(site); | displayInfo.appendChild(site); | ||||
| displayInfo.appendChild(title); | |||||
| cycle.appendChild(prevMatch); | |||||
| cycle.appendChild(nextMatch); | |||||
| newDiv.appendChild(displayInfo); | newDiv.appendChild(displayInfo); | ||||
| newDiv.appendChild(table); | newDiv.appendChild(table); | ||||
| @@ -210,6 +351,52 @@ function createResult(codelet) { | |||||
| return newDiv; | 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. | * AJAX the current query string to the server, and return its response. | ||||
| * | * | ||||
| @@ -217,10 +404,23 @@ function createResult(codelet) { | |||||
| * elements, to fill `div#results`. | * elements, to fill `div#results`. | ||||
| */ | */ | ||||
| function queryServer(){ | 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++){ | for(var result = 0; result < 20; result++){ | ||||
| var newDiv = createResult(testCodelet); | var newDiv = createResult(testCodelet); | ||||
| resultDivs.push(newDiv); | |||||
| resultDivs.push(newDiv) | |||||
| } | } | ||||
| return resultDivs; | return resultDivs; | ||||
| @@ -230,7 +430,7 @@ function queryServer(){ | |||||
| * Adds more results to `div#results`. | * Adds more results to `div#results`. | ||||
| */ | */ | ||||
| function loadMoreResults(){ | function loadMoreResults(){ | ||||
| results = queryServer(); | |||||
| var results = queryServer(); | |||||
| for(var result = 0; result < results.length; result++){ | for(var result = 0; result < results.length; result++){ | ||||
| var newDiv = results[result]; | var newDiv = results[result]; | ||||
| resultsDiv.appendChild(newDiv); | resultsDiv.appendChild(newDiv); | ||||
| @@ -243,3 +443,16 @@ function loadMoreResults(){ | |||||
| result * 20); | 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(); | |||||
| @@ -10,6 +10,11 @@ | |||||
| -o-#{$property}: $value | -o-#{$property}: $value | ||||
| #{$property}: $value | #{$property}: $value | ||||
| // Add portable opacity style. | |||||
| @mixin opaque($opacity) | |||||
| @include vendor(opacity, $opacity) | |||||
| filter: alpha(opacity=$opacity) | |||||
| .t1 | .t1 | ||||
| @include vendor(transition, all 0.1s ease-out) | @include vendor(transition, all 0.1s ease-out) | ||||
| @@ -6,9 +6,8 @@ | |||||
| @import variables | @import variables | ||||
| $minSearchFieldsWidth: 490px | $minSearchFieldsWidth: 490px | ||||
| $resultWidth: 830px | |||||
| $sidebarWidth: 30px | |||||
| $codeWidth: 500px | |||||
| $resultWidth: 1000px | |||||
| $codeWidth: 650px | |||||
| $hiddenInfoWidth: 250px | $hiddenInfoWidth: 250px | ||||
| .ui-datepicker | .ui-datepicker | ||||
| @@ -23,6 +22,54 @@ $hiddenInfoWidth: 250px | |||||
| >li.ui-menu-item a.ui-state-focus | >li.ui-menu-item a.ui-state-focus | ||||
| @include vendor(transition, background-color 0.3s ease-out) | @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 | div#search-field | ||||
| @extend .t2 | @extend .t2 | ||||
| @@ -34,6 +81,7 @@ div#search-field | |||||
| max-height: 100px | max-height: 100px | ||||
| right: 0 | right: 0 | ||||
| position: absolute | position: absolute | ||||
| z-index: 2 | |||||
| top: 0 | top: 0 | ||||
| width: 40% | width: 40% | ||||
| @@ -103,7 +151,6 @@ div#search-field | |||||
| &.partly-visible | &.partly-visible | ||||
| margin-top: 0% | margin-top: 0% | ||||
| padding-bottom: 3% | |||||
| position: absolute | position: absolute | ||||
| width: 100% | width: 100% | ||||
| @@ -121,6 +168,11 @@ div#search-field | |||||
| margin-right: auto | margin-right: auto | ||||
| width: 60% | width: 60% | ||||
| input:hover | |||||
| @extend .t3 | |||||
| border: 1px solid $baseColor1 | |||||
| div#advanced-search | div#advanced-search | ||||
| background-color: white | background-color: white | ||||
| border: 1px solid $baseColor3 | border: 1px solid $baseColor3 | ||||
| @@ -237,7 +289,7 @@ div#advanced-search | |||||
| #search-groups | #search-groups | ||||
| margin-top: 1% | margin-top: 1% | ||||
| max-height: 93% | |||||
| max-height: 87% | |||||
| overflow-y: auto | overflow-y: auto | ||||
| width: 75% | width: 75% | ||||
| @@ -286,112 +338,116 @@ div#results | |||||
| margin-right: auto | margin-right: auto | ||||
| width: 80% | 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 | 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 | |||||
| @@ -1,14 +1,13 @@ | |||||
| = extends "layout.html" | = extends "layout.html" | ||||
| = block title | = block title | ||||
| Home | |||||
| home | |||||
| = endblock | = endblock | ||||
| = block head | = block head | ||||
| {{ assets.tag("lib/jqueryui.custom.min.css") }} | {{ assets.tag("lib/jqueryui.custom.min.css") }} | ||||
| {{ assets.tag("lib/jquery.min.js") }} | {{ assets.tag("lib/jquery.min.js") }} | ||||
| {{ assets.tag("lib/jquery-ui.min.js") }} | {{ assets.tag("lib/jquery-ui.min.js") }} | ||||
| {{ assets.tag("lib/typeahead.bundle.min.js") }} | |||||
| {{ assets.tag("lib/highlight.css") }} | {{ assets.tag("lib/highlight.css") }} | ||||
| {{ assets.tag("index.css") }} | {{ assets.tag("index.css") }} | ||||
| @@ -30,7 +29,7 @@ | |||||
| <form id="search-bar"> | <form id="search-bar"> | ||||
| <input type="text" name="query" | <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"> | <img src="static/img/search_bar_magnifying_glass.png"> | ||||
| </button> | </button> | ||||
| @@ -100,3 +99,15 @@ | |||||
| {{ assets.tag("index.js") }} | {{ assets.tag("index.js") }} | ||||
| {{ assets.tag("index.advanced-search-form.js") }} | {{ assets.tag("index.advanced-search-form.js") }} | ||||
| = endblock | = 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 | |||||
| @@ -4,8 +4,11 @@ | |||||
| <html> | <html> | ||||
| <head> | <head> | ||||
| <title> | <title> | ||||
| = block title | |||||
| = endblock | |||||
| bitshift « | |||||
| = filter lower | |||||
| = block title | |||||
| = endblock | |||||
| = endfilter | |||||
| </title> | </title> | ||||
| <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/> | <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/> | ||||
| @@ -30,6 +33,9 @@ | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| = block after_body | |||||
| = endblock | |||||
| <div id="footer"> | <div id="footer"> | ||||
| <a href="/">home</a> | <a href="/">home</a> | ||||
| <a href="/about">about</a> | <a href="/about">about</a> | ||||
| @@ -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) | |||||