diff --git a/_layouts/default.html b/_layouts/default.html index 8047d27..a6a42fc 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -1,7 +1,6 @@ - + - + @@ -19,13 +18,18 @@ {% for css in page.css %} <link rel="stylesheet" href="{{ css }}" type="text/css" /> {% endfor %} - </head> <body> <a href="http://github.com/jqr"><img style="position: absolute; top: 0; left: 0; border: 0;" src="http://s3.amazonaws.com/github/ribbons/forkme_left_orange_ff7600.png" alt="Fork me on GitHub" /></a> <div id="site"> <a id="subscribe" href="http://feeds2.feedburner.com/jqr"><img src="/images/rss.png" alt="Subscribe to RSS Feed" /></a> + + <div id="search_area" style="display: none"> + <label for="search" style="display: none">Search</label> + <img src="http://www.famfamfam.com/lab/icons/silk/icons/cancel.png" alt="X"/> + <input id="search" /> + </div> <h1><a href="/">Elijah Miller</a></h1> diff --git a/index.html b/index.html index 541a85e..b5fe132 100644 --- a/index.html +++ b/index.html @@ -1,10 +1,20 @@ --- layout: default +javascript: + - /javascripts/json_search.0.9.0.js + - /javascripts/home.js --- + +<div id="search_results_area"> + <h2>Posts <span id="result_count"></span></h2> + <ul id="search_results" class="posts"> + </ul> +</div> + <div id="home"> - <h2>Posts <span id="post_count"></span></h2> - <ul id="posts"> + <h2>Posts</h2> + <ul class="posts"> {% for post in site.posts %} <li> <a href="{{ post.url }}">{{ post.title }}</a> @@ -12,12 +22,27 @@ <h2>Posts <span id="post_count"></span></h2> </li> {% endfor %} </ul> - + <h2>Tweets</h2> <div id="tweets"> Loading... </ul> + <script type="text/javascript" charset="utf-8"> + var posts = [ + {% for post in site.posts %} + { + title: unescape('{{ post.title | strip_html | cgi_escape }}').gsub(/\+/, ' '), + summary: unescape('{{ post.summary | strip_html | cgi_escape }}').gsub(/\+/, ' '), + content: unescape('{{ post.content | strip_html | cgi_escape }}').gsub(/\+/, ' '), + url: unescape('{{ post.url | strip_html | cgi_escape }}').gsub(/\+/, ' '), + }, + {% endfor %} + 0]; + + posts.pop(); + </script> + <script type="text/javascript" src="/javascripts/twitter-1.11.2.js"></script> <script type="text/javascript"> diff --git a/javascripts/json_search.0.9.0.js b/javascripts/json_search.0.9.0.js new file mode 100644 index 0000000..bd2180e --- /dev/null +++ b/javascripts/json_search.0.9.0.js @@ -0,0 +1,182 @@ +var JSONSearch = function(options) { + + this.default_options = { + fields: {}, + ranks: {}, + limit: null, + offset: 0, + case_sensitive: false + }; + + this.patterns = { + infix: '.*$token.*', + prefix: '^$token.*', + exact: '^$token$', + word: '\\b$token\\b', + word_prefix: '\\b$token.*' + }; + + this.query_string = ''; + this.query_function = "1&&function(object) { var hits = 0, ranks_array = []; #{query_string} if (hits > 0) { return [(ranks_array.sort()[ranks_array.length - 1] || 0), hits, object]; } }"; + + this.initialize(options); +}; + +JSONSearch.InstanceMethods = { + initialize: function(options) { + this.options = {}; + for(var property in this.default_options) { + this.options[property] = this.default_options[property] + }; + for(var property in options) { + this.options[property] = options[property]; + } + this.setAttributes(options); + this.buildMatcherTemplates(); + this.buildQueryString(); + }, + + setAttributes: function() { + var args = ['fields', 'limit', 'offset', 'case_sensitive']; + for(var i = 0; i < args.length; i++) { + this[args[i]] = this.options[args[i]]; + }; + this.ranks = this.getRanks(); + this.fields_ordered_by_rank = this.fieldsOrderedByRank(); + }, + + buildMatcherTemplates: function() { + for(var property in this.patterns) { + this[property + '_matcher'] = 'if(/' + this.patterns[property] + '/#{regex_options}.test(object["#{name}"])){hits++;ranks_array.push(ranks["#{name}"]);}'; + }; + }, + + getRanks: function(object) { + var ranks = {}; + for(var property in this.fields) { + ranks[property] = (this.options.ranks && this.options.ranks[property] || 0); + }; + return ranks; + }, + + // TODO if ranks are all 0 might just use the format order if supplied + fieldsOrderedByRank: function() { + var fields_ordered_by_rank = []; + for(var property in this.ranks){ + fields_ordered_by_rank.push([this.ranks['property'], property]); + }; + fields_ordered_by_rank = fields_ordered_by_rank.sort().reverse(); + for(var i = 0; i < fields_ordered_by_rank.length; i++) { + fields_ordered_by_rank[i] = fields_ordered_by_rank[i][1]; + } + return fields_ordered_by_rank; + }, + + buildQueryString: function() { + var query_string_array = [], field; + for(var i = 0; i < this.fields_ordered_by_rank.length; i++) { + field = this.fields_ordered_by_rank[i]; + query_string_array.push(this.buildMatcher(field, this.fields[field])); + } + this.query_string = query_string_array.join(''); + }, + + buildMatcher: function(name, pattern) { + return this.subMatcher(this[pattern + '_matcher'], { name: name, regex_options: this.getRegexOptions() }); + }, + + subMatcher: function(matcher, object) { + var subbed_matcher = matcher; + for(var property in object) { + subbed_matcher = subbed_matcher.replace('#{' + property + '}', object[property], 'g') + } + return subbed_matcher; + }, + + subQueryString: function(token) { + return this.query_string.replace(/\$token/g, this.regexEscape(token)); + }, + + subQueryFunction: function(object) { + var subbed_query_function; + for(var property in object) { + subbed_query_function = this.query_function.replace('#{' + property + '}', object[property], 'g') + } + return subbed_query_function; + }, + + //TODO add an options object to pass limit/offset. + getResults: function(token, object) { + object = this.evalJSON(object); + if (!(object instanceof Array)) { + object = [object]; + } + var results, subbed_query_string = this.subQueryString(token); + results = this.getFilteredResults(subbed_query_string, object); + results = this.sortResults(results); + results = this.limitResults(results); + return this.cleanResults(results); + }, + + getFilteredResults: function(query_string, array) { + var results = [], ranks = this.ranks, result, len = (array.length), query_string = (query_string || ''); + var query_function = eval(this.subQueryFunction({ query_string: query_string })); + + for(var i = 0; i < len; ++i) { + if(result = query_function(array[i])) { + results.push(result); + } + } + return results; + }, + + sortResults: function(results) { + return results.sort().reverse(); + }, + + limitResults: function(results) { + if (this.limit) { + return results.slice(this.offset, (this.limit + this.offset)); + } else if (this.offset > 0) { + return results.slice(this.offset, (results.length)); + } else { + return results; + } + }, + + cleanResults: function(results) { + var clean_results = []; len = (results.length); + for(var i = 0; i < len; ++i) { + clean_results.push(results[i][2]); + } + return clean_results; + }, + + evalJSON: function(json) { + if (typeof json == 'string') { + try { + json = eval('(' + json + ')'); + } catch(e) { + throw new SyntaxError('Badly formed JSON string'); + } + } + return json; + }, + + getRegex: function(token, pattern) { + return new RegExp(this.patterns[pattern].replace(/\$token/, this.regexEscape(token)), this.getRegexOptions()); + }, + + getRegexOptions: function() { + return (this.case_sensitive && '' || 'i'); + }, + + regexEscape: function(string) { + return String(string).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); + } +} + +for(var property in JSONSearch.InstanceMethods) { + JSONSearch.prototype[property] = JSONSearch.InstanceMethods[property] +} +delete JSONSearch.InstanceMethods; diff --git a/stylesheets/default.css b/stylesheets/default.css index f4e58d6..5be73d1 100644 --- a/stylesheets/default.css +++ b/stylesheets/default.css @@ -106,10 +106,61 @@ h3 { margin-bottom: 30px; } -#posts p { +.posts p { margin: 5px 0 25px 0; } .rss_only { display: none; +} + +#search_area { + width: 80px; + float: right; + margin: 19px 50px; + position: relative; +} +#search { + font-size: 17px; + font-family: inherit; + border: 1px solid #ccc; + color: #666; + -moz-border-radius: 30px; + -webkit-border-radius: 13px; + padding: 3px 27px 3px 27px; + background: url(http://www.famfamfam.com/lab/icons/silk/icons/magnifier.png) 7px 6px no-repeat ; + width: 100%; + margin: 0; +} +#search:focus { + outline: none; +} + +#search_area label { + font-size: 17px; + position: absolute; + top: 4px; + left: 29px; + color: #ccc; + cursor: text; + padding: 0; +} + +#search_area img { + display: none; +} +.searching #search_area img { + position: absolute; + left: 113px; + top: 7px; + display: block; +} +#search_results_area { + display: none; +} +.searching #search_results_area { + display: block; +} +.searching #home { + display: none; } \ No newline at end of file