Skip to content

Commit

Permalink
Adding in site wide search, crappy but it works!
Browse files Browse the repository at this point in the history
  • Loading branch information
jqr committed Aug 6, 2009
1 parent 1190ee3 commit b6d7a4f
Show file tree
Hide file tree
Showing 4 changed files with 270 additions and 8 deletions.
12 changes: 8 additions & 4 deletions _layouts/default.html
@@ -1,7 +1,6 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en-us">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>
Expand All @@ -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>

Expand Down
31 changes: 28 additions & 3 deletions index.html
@@ -1,23 +1,48 @@
---
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>
<p>{{ post.summary }}</p>
</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">
Expand Down
182 changes: 182 additions & 0 deletions 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;
53 changes: 52 additions & 1 deletion stylesheets/default.css
Expand Up @@ -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;
}

0 comments on commit b6d7a4f

Please sign in to comment.