Skip to content
This repository has been archived by the owner on Dec 8, 2017. It is now read-only.

Commit

Permalink
Merge pull request #80 from 18F/lunr-search
Browse files Browse the repository at this point in the history
add client-side search
  • Loading branch information
mbland committed Feb 5, 2015
2 parents 4714f7e + 6daf8f4 commit ae2a72b
Show file tree
Hide file tree
Showing 13 changed files with 216 additions and 19 deletions.
3 changes: 2 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ gem 'team_hub'
gem 'weekly_snippets'

group :jekyll_plugins do
gem 'jekyll_pages_api', git: 'https://github.com/18F/jekyll_pages_api.git'
gem 'jekyll-assets'
gem 'jekyll_pages_api'
end

group :test do
Expand Down
41 changes: 30 additions & 11 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
GIT
remote: https://github.com/18F/jekyll_pages_api.git
revision: ebe34affd93282a1f8fe7e36c1730d59932298f2
specs:
jekyll_pages_api (0.1.0)
htmlentities (~> 4.3)
jekyll (~> 2.0)

GEM
remote: https://rubygems.org/
specs:
RedCloth (4.2.9)
addressable (2.3.7)
blankslate (2.1.2.4)
bourbon (4.1.1)
sass (~> 3.3)
Expand All @@ -26,11 +19,14 @@ GEM
coffee-script-source (1.9.0)
colorator (0.1)
docile (1.1.5)
execjs (2.2.2)
execjs (2.3.0)
fast-stemmer (1.0.2)
fastimage (1.6.6)
addressable (~> 2.3, >= 2.3.5)
ffi (1.9.6)
hash-joiner (0.0.3)
safe_yaml
hike (1.2.3)
hitimes (1.2.2)
htmlentities (4.3.3)
jekyll (2.5.3)
Expand All @@ -48,15 +44,25 @@ GEM
redcarpet (~> 3.1)
safe_yaml (~> 1.0)
toml (~> 0.1.0)
jekyll-assets (0.13.0)
fastimage (~> 1.6)
jekyll (~> 2.0)
sass (~> 3.2)
sprockets (~> 2.10)
sprockets-helpers
sprockets-sass
jekyll-coffeescript (1.0.1)
coffee-script (~> 2.2)
jekyll-gist (1.1.0)
jekyll-paginate (1.1.0)
jekyll-sass-converter (1.3.0)
sass (~> 3.2)
jekyll-sitemap (0.7.0)
jekyll-sitemap (0.8.0)
jekyll-watch (1.2.1)
listen (~> 2.7)
jekyll_pages_api (0.1.0)
htmlentities (~> 4.3)
jekyll (~> 2.0)
kramdown (1.5.0)
liquid (2.6.2)
listen (2.8.5)
Expand All @@ -72,6 +78,7 @@ GEM
pygments.rb (0.6.2)
posix-spawn (~> 0.3.6)
yajl-ruby (~> 1.2.0)
rack (1.6.0)
rake (10.4.2)
rb-fsevent (0.9.4)
rb-inotify (0.9.5)
Expand All @@ -84,13 +91,24 @@ GEM
multi_json (~> 1.0)
simplecov-html (~> 0.8.0)
simplecov-html (0.8.0)
sprockets (2.12.3)
hike (~> 1.2)
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
sprockets-helpers (1.1.0)
sprockets (~> 2.0)
sprockets-sass (1.3.1)
sprockets (~> 2.0)
tilt (~> 1.1)
team_hub (0.0.2)
hash-joiner
jekyll
weekly_snippets
test_temp_file_helper (0.0.2)
rake (~> 10.0)
thor (0.19.1)
tilt (1.4.1)
timers (4.0.1)
hitimes
toml (0.1.2)
Expand All @@ -107,8 +125,9 @@ DEPENDENCIES
codeclimate-test-reporter
hash-joiner
jekyll
jekyll-assets
jekyll-sitemap
jekyll_pages_api!
jekyll_pages_api
minitest
rake
team_hub
Expand Down
116 changes: 116 additions & 0 deletions _assets/javascripts/search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//= require vendor/angular/angular
//= require vendor/angular-livesearch/liveSearch
//= require vendor/lunr.js/lunr

var ngHub = angular.module('hubSearch', ['LiveSearch']);

ngHub.factory('pagesPromise', function($http, $q) {
return $http.get(SITE_BASEURL + '/api/v1/pages.json').then(function(response) {
return response.data.entries;
});
});

ngHub.factory('pagesByUrl', function(pagesPromise) {
var result = {};

// populate asynchronously
pagesPromise.then(function(docs) {
angular.forEach(docs, function(doc) {
result[doc.url] = doc;
});
});

return result;
});

ngHub.factory('pageIndex', function(pagesPromise) {
var index = lunr(function() {
this.ref('url');

this.field('title', {boost: 10});
this.field('url', {boost: 5});
this.field('body');
});

// populate asynchronously
pagesPromise.then(function(docs) {
angular.forEach(docs, function(page) {
index.add(page);
});
});

return index;
});

ngHub.factory('pagesSearch', function(pagesByUrl, pageIndex) {
return function(term) {
var results = pageIndex.search(term);
angular.forEach(results, function(result) {
var page = pagesByUrl[result.ref];
result.page = page;
// make top-level attribute available for LiveSearch
result.displayTitle = page.title || page.url;
});
return results;
};
});

// based on https://github.com/angular/angular.js/blob/54ddca537/docs/app/src/search.js#L198-L206
ngHub.factory('searchUi', function($document) {
var isForwardSlash = function(keyCode) {
return keyCode === 191;
};

var isInput = function(el) {
var tagName = el.tagName.toLowerCase();
return tagName === 'input';
};

var giveSearchFocus = function() {
var input = angular.element('#search1')[0];
input.focus();
};

var onKeyDown = function(event) {
if (isForwardSlash(event.keyCode) && !isInput(document.activeElement)) {
event.stopPropagation();
event.preventDefault();
giveSearchFocus();
}
};

return {
enableGlobalShortcut: function() {
angular.element($document[0].body).on('keydown', onKeyDown);
},

getSelectedResult: function() {
// TODO find a less hacky way to retrieve this
var selectionScope = angular.element('.searchresultspopup').scope();
var resultIndex = selectionScope.selectedIndex;
return selectionScope.results[resultIndex];
}
};
});

ngHub.controller('SearchController', function($scope, $q, searchUi, pagesSearch) {
searchUi.enableGlobalShortcut();

var isEnter = function(keyCode) {
return keyCode === 13;
}

$scope.searchKeyDown = function($event) {
if (isEnter($event.keyCode)) {
var result = searchUi.getSelectedResult();
window.location = result.page.url;
}
};

$scope.searchCallback = function(params) {
var defer = $q.defer();
var results = pagesSearch(params.query);
defer.resolve(results);
return defer.promise;
};
});
2 changes: 2 additions & 0 deletions _config_public.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ generated_page_title_format: '%s — 18F Public Hub'
public: true
destination: _site_public/hub
baseurl: /hub
assets:
baseurl: /hub/assets
4 changes: 4 additions & 0 deletions _includes/head.html
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@
================================================== -->
<script src="{{ site.baseurl }}/assets/js/jquery-2.1.1.min.js"></script>
<script src="{{ site.baseurl }}/assets/js/responsiveslides.min.js"></script>
<script>
SITE_BASEURL = '{{site.baseurl}}';
</script>
{% javascript search %}

<!-- IE
================================================== -->
Expand Down
27 changes: 21 additions & 6 deletions _layouts/bare.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,27 @@

<section class="container bare">

<div class="bare-content" role="main" itemscope itemprop="mainContentOfPage">

{{ content }}

{% include edit_link.html %}

<div ng-app="hubSearch" ng-controller="SearchController" class="bare-content" role="main" itemscope itemprop="mainContentOfPage">
{% raw %}
<div>
<live-search id="search1" type="text"
live-search-callback="searchCallback"
live-search-item-template="<a href='{{result.page.url}}'>{{result.page.title || result.page.url}}</a>"
live-search-select="displayTitle"
live-search-max-result-size="20"
ng-model="searchText"
ng-keydown="searchKeyDown($event)"
placeholder="Search – click or press '/'"
></live-search>
</div>
<div ng-hide="searchText">
{% endraw %}
{{ content }}

{% include edit_link.html %}
{% raw %}
</div>
{% endraw %}
</div>

</section>
Expand Down
4 changes: 4 additions & 0 deletions assets/_sass/_hub.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// hub.18F.gsa.gov CUSTOM SASS
//********************************************************

@import "_search";

// Site-wide media queries
//********************************************************
.news {
Expand Down Expand Up @@ -207,6 +209,8 @@ ul li {
//********************************************************

.bare-content {
padding-top: 2em;

h1 {
margin-top: 0.5em;
margin-bottom: 0.5em;
Expand Down
36 changes: 36 additions & 0 deletions assets/_sass/_search.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// https://github.com/mauriciogentile/angular-livesearch/blob/1f4e357ab3b50701eeedcfb45c12cd901d612087/example/styles.css#L31-L65
ul.searchresultspopup {
border: #a4bed4 1px solid;
margin-top: 0px;
padding: 0px;
z-index: 99999!important;
position: fixed;
background-color: white;
/*max-height:200px;*/
border-collapse: separate;
overflow-y: hidden;

li {
cursor: pointer;
padding-left: 1px;
text-align: left;
list-style: none;
line-height: 20px;
list-style-image: none;
list-style-position: outside;
list-style-type: none;
}

li.selected,
li:hover {
background-color: #ededed;
}

b {
color: blue;
}

strong {
color: green;
}
}
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"requirejs": "~2.1.15"
},
"install": {
"path": "assets/js/vendor",
"path": "_assets/javascripts/vendor",
"sources": {
"angular-livesearch": "bower_components/angular-livesearch/liveSearch.js",
"requirejs": "bower_components/requirejs/require.js"
Expand Down

0 comments on commit ae2a72b

Please sign in to comment.