Several folks have recently asked me how my “Search site” box works, so here’s a quick writeup on the implementation as it stands at the time of this writing:
The current site is entirely static and is built by calling pandoc from a
top-level non-recursive site Makefile, written in GNU
Make.
The main inputs to this Makefile are “pages”, which are
represented as directories each containing a file named index.txt containing the pandoc
Markdown source for the page.
When run, my Makefile uses find
to locate page directories and then uses pandoc to convert
the index.txt page sources into index.html
outputs. Additionally, a tiny script named jsonify.py, reproduced below:
#!/usr/bin/env python
from __future__ import with_statement
import sys
import cjson
data = {}
for fn in sys.argv[1:]:
with open(fn) as f:
data[fn.replace('index.txt', '')] = f.read()
print cjson.encode(data)is used to build a target called site.json containing a JSON-encoded representation of all the source
texts for all the pages in the site indexed by the name of the “page”
directory that contains them.
Next, my Makefile uses pandoc with a custom HTML template to produce HTML from my page
sources that contains a text input field named #searchbox
(initially hidden by CSS) and that runs the following JavaScript program
during page rendering:
(function($){
$(document).ready(function(){
var site;
$.getJSON('/site.json', function(data){
site = data;
$("#searchbar").css("display", "inline-block");
});
var doSearch;
doSearch = function(){
var search;
search = $("#searchbox").val();
if (search.length > 0) {
var results, pat;
pat = RegExp(search);
results = [];
$.each(site, function(k, v){
if (v.match(pat)) {
results.push('<li><a href="/' + k + '">' + v.split(/\n/)[0].replace(/^% /, '') + '</a></li>');
}
});
if (results.length > 0) {
$("#searchresults").html('<br/><h2>Matching Posts</h2><ul>' + results.join('') + '</ul>');
} else {
$("#searchresults").html('<br/><h2>Matching Posts</h2><p>None</p>');
}
}
else {
$("#searchresults").empty();
}
};
$("#searchbox").keyup(doSearch);
});})(jQuery);When run, this script uses the jQuery library to
asynchronously fetch site.json, to unhide the searchbox on
success, and to wait for jQuery keyup events.
keyup events are then handled by selecting keys from the
previously fetched site JavaScript object whose values are
matched by the pat JavaScript
regexp (which was, in turn, built from the value of the
#searchbox text field).
Finally, the matching object keys are transformed into links and
added to the #searchresults element’s
innerHTML.