1gitbook.require(["gitbook", "lodash", "jQuery"], function(gitbook, _, $) { 2 var index = null; 3 var $searchInput, $searchForm; 4 var $highlighted, hi = 0, hiOpts = { className: 'search-highlight' }; 5 var collapse = false; 6 7 // Use a specific index 8 function loadIndex(data) { 9 // [Yihui] In bookdown, I use a character matrix to store the chapter 10 // content, and the index is dynamically built on the client side. 11 // Gitbook prebuilds the index data instead: https://github.com/GitbookIO/plugin-search 12 // We can certainly do that via R packages V8 and jsonlite, but let's 13 // see how slow it really is before improving it. On the other hand, 14 // lunr cannot handle non-English text very well, e.g. the default 15 // tokenizer cannot deal with Chinese text, so we may want to replace 16 // lunr with a dumb simple text matching approach. 17 index = lunr(function () { 18 this.ref('url'); 19 this.field('title', { boost: 10 }); 20 this.field('body'); 21 }); 22 data.map(function(item) { 23 index.add({ 24 url: item[0], 25 title: item[1], 26 body: item[2] 27 }); 28 }); 29 } 30 31 // Fetch the search index 32 function fetchIndex() { 33 return $.getJSON(gitbook.state.basePath+"/search_index.json") 34 .then(loadIndex); // [Yihui] we need to use this object later 35 } 36 37 // Search for a term and return results 38 function search(q) { 39 if (!index) return; 40 41 var results = _.chain(index.search(q)) 42 .map(function(result) { 43 var parts = result.ref.split("#"); 44 return { 45 path: parts[0], 46 hash: parts[1] 47 }; 48 }) 49 .value(); 50 51 // [Yihui] Highlight the search keyword on current page 52 hi = 0; 53 $highlighted = results.length === 0 ? undefined : $('.page-inner') 54 .unhighlight(hiOpts).highlight(q, hiOpts).find('span.search-highlight'); 55 scrollToHighlighted(); 56 toggleTOC(results.length > 0); 57 58 return results; 59 } 60 61 // [Yihui] Scroll the chapter body to the i-th highlighted string 62 function scrollToHighlighted() { 63 if (!$highlighted) return; 64 var n = $highlighted.length; 65 if (n === 0) return; 66 var $p = $highlighted.eq(hi), p = $p[0], rect = p.getBoundingClientRect(); 67 if (rect.top < 0 || rect.bottom > $(window).height()) { 68 ($(window).width() >= 1240 ? $('.body-inner') : $('.book-body')) 69 .scrollTop(p.offsetTop - 100); 70 } 71 $highlighted.css('background-color', ''); 72 // an orange background color on the current item and removed later 73 $p.css('background-color', 'orange'); 74 setTimeout(function() { 75 $p.css('background-color', ''); 76 }, 2000); 77 } 78 79 // [Yihui] Expand/collapse TOC 80 function toggleTOC(show) { 81 if (!collapse) return; 82 var toc_sub = $('ul.summary').children('li[data-level]').children('ul'); 83 if (show) return toc_sub.show(); 84 var href = window.location.pathname; 85 href = href.substr(href.lastIndexOf('/') + 1); 86 if (href === '') href = 'index.html'; 87 var li = $('a[href^="' + href + location.hash + '"]').parent('li.chapter').first(); 88 toc_sub.hide().parent().has(li).children('ul').show(); 89 li.children('ul').show(); 90 } 91 92 // Create search form 93 function createForm(value) { 94 if ($searchForm) $searchForm.remove(); 95 if ($searchInput) $searchInput.remove(); 96 97 $searchForm = $('<div>', { 98 'class': 'book-search', 99 'role': 'search' 100 }); 101 102 $searchInput = $('<input>', { 103 'type': 'search', 104 'class': 'form-control', 105 'val': value, 106 'placeholder': 'Type to search' 107 }); 108 109 $searchInput.appendTo($searchForm); 110 $searchForm.prependTo(gitbook.state.$book.find('.book-summary')); 111 } 112 113 // Return true if search is open 114 function isSearchOpen() { 115 return gitbook.state.$book.hasClass("with-search"); 116 } 117 118 // Toggle the search 119 function toggleSearch(_state) { 120 if (isSearchOpen() === _state) return; 121 if (!$searchInput) return; 122 123 gitbook.state.$book.toggleClass("with-search", _state); 124 125 // If search bar is open: focus input 126 if (isSearchOpen()) { 127 gitbook.sidebar.toggle(true); 128 $searchInput.focus(); 129 } else { 130 $searchInput.blur(); 131 $searchInput.val(""); 132 gitbook.storage.remove("keyword"); 133 gitbook.sidebar.filter(null); 134 $('.page-inner').unhighlight(hiOpts); 135 toggleTOC(false); 136 } 137 } 138 139 // Recover current search when page changed 140 function recoverSearch() { 141 var keyword = gitbook.storage.get("keyword", ""); 142 143 createForm(keyword); 144 145 if (keyword.length > 0) { 146 if(!isSearchOpen()) { 147 toggleSearch(true); // [Yihui] open the search box 148 } 149 gitbook.sidebar.filter(_.pluck(search(keyword), "path")); 150 } 151 } 152 153
154 gitbook.events.bind("start", function(e, config) { 155 // [Yihui] disable search 156 if (config.search === false) return; 157 collapse = !config.toc || config.toc.collapse === 'section' || 158 config.toc.collapse === 'subsection'; 159 160 // Pre-fetch search index and create the form 161 fetchIndex() 162 // [Yihui] recover search after the page is loaded 163 .then(recoverSearch); 164 165 166 // Type in search bar 167 $(document).on("keyup", ".book-search input", function(e) { 168 var key = (e.keyCode ? e.keyCode : e.which); 169 // [Yihui] Escape -> close search box; Up/Down: previous/next highlighted 170 if (key == 27) { 171 e.preventDefault(); 172 toggleSearch(false); 173 } else if (key == 38) { 174 if (hi <= 0 && $highlighted) hi = $highlighted.length; 175 hi--; 176 scrollToHighlighted(); 177 } else if (key == 40) { 178 hi++; 179 if ($highlighted && hi >= $highlighted.length) hi = 0; 180 scrollToHighlighted(); 181 } 182 }).on("input", ".book-search input", function(e) { 183 var q = $(this).val().trim(); 184 if (q.length === 0) { 185 gitbook.sidebar.filter(null); 186 gitbook.storage.remove("keyword"); 187 $('.page-inner').unhighlight(hiOpts); 188 toggleTOC(false); 189 } else { 190 var results = search(q); 191 gitbook.sidebar.filter( 192 _.pluck(results, "path") 193 ); 194 gitbook.storage.set("keyword", q); 195 } 196 }); 197 198 // Create the toggle search button 199 gitbook.toolbar.createButton({ 200 icon: 'fa fa-search', 201 label: 'Search', 202 position: 'left', 203 onClick: toggleSearch 204 }); 205 206 // Bind keyboard to toggle search 207 gitbook.keyboard.bind(['f'], toggleSearch); 208 }); 209 210 // [Yihui] do not try to recover search; always start fresh 211 // gitbook.events.bind("page.change", recoverSearch); 212});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.