PageSourceSearch

https://www.machlis.com/R4Journalists/assets/gitbook-2.6.7/js/plugin-search.js

js machlis.com collected 2026-09-25 21:02:34 UTC 7,271 bytes, 212 lines download raw bytes

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.