1 gitbook.require(["gitbook", "lodash", "jQuery"], function(gitbook, _, $) {
4 var _search = {engine: 'lunr', opts: {}};
5 var $searchInput, $searchLabel, $searchForm;
6 var $highlighted = [], hi, hiOpts = { className: 'search-highlight' };
7 var collapse = false, toc_visible = [];
9 function init(config) {
10 // Instantiate search settings
11 _search = gitbook.storage.get("search", {
12 engine: config.search.engine || 'lunr',
13 opts: config.search.options || {},
17 // Save current search settings
18 function saveSearchSettings() {
19 gitbook.storage.set("search", _search);
22 // Use a specific index
23 function loadIndex(data) {
24 // [Yihui] In bookdown, I use a character matrix to store the chapter
25 // content, and the index is dynamically built on the client side.
26 // Gitbook prebuilds the index data instead: https://github.com/GitbookIO/plugin-search
27 // We can certainly do that via R packages V8 and jsonlite, but let's
28 // see how slow it really is before improving it. On the other hand,
29 // lunr cannot handle non-English text very well, e.g. the default
30 // tokenizer cannot deal with Chinese text, so we may want to replace
31 // lunr with a dumb simple text matching approach.
32 if (_search.engine === 'lunr') {
33 index = lunr(function () {
35 this.field('title', { boost: 10 });
38 data.map(function(item) {
47 fuse = new Fuse(data.map((_data => {
58 keys: ["title", "body"]
64 // Fetch the search index
65 function fetchIndex() {
66 return $.getJSON(gitbook.state.basePath+"/search_index.json")
67 .then(loadIndex); // [Yihui] we need to use this object later
70 // Search for a term and return results
73 switch (_search.engine) {
76 results = fuse.search(q).map(function(result) {
77 var parts = result.item.url.split('#');
87 results = _.chain(index.search(q)).map(function(result) {
88 var parts = result.ref.split("#");
97 // [Yihui] Highlight the search keyword on current page
98 $highlighted = $('.page-inner')
99 .unhighlight(hiOpts).highlight(q, hiOpts).find('span.search-highlight');
100 scrollToHighlighted(0);
105 // [Yihui] Scroll the chapter body to the i-th highlighted string
106 function scrollToHighlighted(d) {
107 var n = $highlighted.length;
108 hi = hi === undefined ? 0 : hi + d;
109 // navignate to the previous/next page in the search results if reached the top/bottom
111 if (d !== 0 && (b || hi >= n)) {
112 var path = currentPath(), n2 = toc_visible.length;
113 if (n2 === 0) return;
114 for (var i = b ? 0 : n2; (b && i < n2) || (!b && i >= 0); i += b ? 1 : -1) {
115 if (toc_visible.eq(i).data('path') === path) break;
118 if (i < 0) i = n2 - 1;
120 var lnk = toc_visible.eq(i).find('a[href$=".html"]');
121 if (lnk.length) lnk[0].click();
125 var $p = $highlighted.eq(hi);
126 $p[0].scrollIntoView();
127 $highlighted.css('background-color', '');
128 // an orange background color on the current item and removed later
129 $p.css('background-color', 'orange');
130 setTimeout(function() {
131 $p.css('background-color', '');
135 function currentPath() {
136 var href = window.location.pathname;
137 href = href.substr(href.lastIndexOf('/') + 1);
138 return href === '' ? 'index.html' : href;
141 // Create search form
142 function createForm(value) {
143 if ($searchForm) $searchForm.remove();
144 if ($searchLabel) $searchLabel.remove();
145 if ($searchInput) $searchInput.remove();
147 $searchForm = $('<div>', {
148 'class': 'book-search',
152 $searchLabel = $('<label>', {
154 'aria-hidden': 'false',
158 $searchInput = $('<input>', {
161 'class': 'form-control',
163 'placeholder': 'Type to search (Enter for navigation)',
164 'title': 'Use Enter or the <Down> key to navigate to the next match, or the <Up> key to the previous match'
167 $searchLabel.append("Type to search");
168 $searchLabel.appendTo($searchForm);
169 $searchInput.appendTo($searchForm);
170 $searchForm.prependTo(gitbook.state.$book.find('.book-summary'));
173 // Return true if search is open
174 function isSearchOpen() {
175 return gitbook.state.$book.hasClass("with-search");
179 function toggleSearch(_state) {
180 if (isSearchOpen() === _state) return;
181 if (!$searchInput) return;
183 gitbook.state.$book.toggleClass("with-search", _state);
185 // If search bar is open: focus input
186 if (isSearchOpen()) {
187 gitbook.sidebar.toggle(true);
188 $searchInput.focus();
191 $searchInput.val("");
192 gitbook.storage.remove("keyword");
193 gitbook.sidebar.filter(null);
194 $('.page-inner').unhighlight(hiOpts);
198 function sidebarFilter(results) {
199 gitbook.sidebar.filter(_.pluck(results, "path"));
200 toc_visible = $('ul.summary').find('li:visible');
203 // Recover current search when page changed
204 function recoverSearch() {
205 var keyword = gitbook.storage.get("keyword", "");
209 if (keyword.length > 0) {
210 if(!isSearchOpen()) {
211 toggleSearch(true); // [Yihui] open the search box
213 sidebarFilter(search(keyword));
218 gitbook.events.bind("start", function(e, config) {
219 // [Yihui] disable search
220 if (config.search === false) return;
222 collapse = !config.toc || config.toc.collapse === 'section' ||
223 config.toc.collapse === 'subsection';
225 // Pre-fetch search index and create the form
227 // [Yihui] recover search after the page is loaded
228 .then(recoverSearch);
231 // Type in search bar
232 $(document).on("keyup", ".book-search input", function(e) {
233 var key = (e.keyCode ? e.keyCode : e.which);
234 // [Yihui] Escape -> close search box; Up/Down/Enter: previous/next highlighted
238 } else if (key == 38) {
239 scrollToHighlighted(-1);
240 } else if (key == 40 || key == 13) {
241 scrollToHighlighted(1);
243 }).on("input", ".book-search input", function(e) {
244 var q = $(this).val().trim();
245 if (q.length === 0) {
246 gitbook.sidebar.filter(null);
247 gitbook.storage.remove("keyword");
248 $('.page-inner').unhighlight(hiOpts);
250 var results = search(q);
251 sidebarFilter(results);
252 gitbook.storage.set("keyword", q);
256 // Create the toggle search button
257 gitbook.toolbar.createButton({
258 icon: 'fa fa-search',
261 onClick: toggleSearch
264 // Bind keyboard to toggle search
265 gitbook.keyboard.bind(['f'], toggleSearch);
268 // [Yihui] do not try to recover search; always start fresh
269 // gitbook.events.bind("page.change", recoverSearch);