Skip to content

Commit fa88440

Browse files
committed
Search: Fix scroll-to-top on escape (unmark). Rearrange javascript and add comments
1 parent 66ae6b6 commit fa88440

File tree

1 file changed

+98
-89
lines changed

1 file changed

+98
-89
lines changed

src/theme/book.js

Lines changed: 98 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ $( document ).ready(function() {
22

33
// Search functionality
44
//
5-
// Usage: call init() on startup. You can use hasFocus() to disable prevent keyhandling
6-
// while the user is typing his search.
5+
// Usage: call init() on startup. You can use !hasFocus() to prevent keyhandling in your key
6+
// event handlers while the user is typing his search.
77
var search = {
88
searchbar : $('#searchbar'),
99
searchbar_outer : $('#searchbar-outer'),
@@ -25,27 +25,19 @@ $( document ).ready(function() {
2525
breadcrumbs: {boost: 0}
2626
}
2727
},
28-
mark_exclude : [], // ['.hljs']
28+
mark_exclude : [],
2929
current_searchterm : "",
30-
SEARCH_PARAM : 'search',
31-
MARK_PARAM : 'highlight',
30+
URL_SEARCH_PARAM : 'search',
31+
URL_MARK_PARAM : 'highlight',
3232

3333
SEARCH_HOTKEY_KEYCODE: 83,
3434
ESCAPE_KEYCODE: 27,
3535
DOWN_KEYCODE: 40,
3636
UP_KEYCODE: 38,
3737
SELECT_KEYCODE: 13,
3838

39-
formatSearchMetric : function(count, searchterm) {
40-
if (count == 1) {
41-
return count + " search result for '" + searchterm + "':";
42-
} else if (count == 0) {
43-
return "No search results for '" + searchterm + "'.";
44-
} else {
45-
return count + " search results for '" + searchterm + "':";
46-
}
47-
}
48-
,
39+
40+
// Helper to parse a url into its building blocks.
4941
parseURL : function (url) {
5042
var a = document.createElement('a');
5143
a.href = url;
@@ -71,6 +63,7 @@ $( document ).ready(function() {
7163
};
7264
}
7365
,
66+
// Helper to recreate a url string from its building blocks.
7467
renderURL : function (urlobject) {
7568
var url = urlobject.protocol + "://" + urlobject.host;
7669
if (urlobject.port != "") {
@@ -90,6 +83,7 @@ $( document ).ready(function() {
9083
return url;
9184
}
9285
,
86+
// Helper to escape html special chars for displaying the teasers
9387
escapeHTML: (function() {
9488
var MAP = {
9589
'&': '&',
@@ -104,18 +98,27 @@ $( document ).ready(function() {
10498
};
10599
})()
106100
,
101+
formatSearchMetric : function(count, searchterm) {
102+
if (count == 1) {
103+
return count + " search result for '" + searchterm + "':";
104+
} else if (count == 0) {
105+
return "No search results for '" + searchterm + "'.";
106+
} else {
107+
return count + " search results for '" + searchterm + "':";
108+
}
109+
}
110+
,
107111
formatSearchResult : function (result, searchterms) {
108-
// Show text around first occurrence of first search term.
109112
var teaser = this.makeTeaser(this.escapeHTML(result.doc.body), searchterms);
110113

111-
// The ?MARK_PARAM= parameter belongs inbetween the page and the #heading-anchor
114+
// The ?URL_MARK_PARAM= parameter belongs inbetween the page and the #heading-anchor
112115
var url = result.ref.split("#");
113-
if (url.length == 1) {
116+
if (url.length == 1) { // no anchor found
114117
url.push("");
115118
}
116119

117120
return $('<li><a href="'
118-
+ url[0] + '?' + this.MARK_PARAM + '=' + searchterms + '#' + url[1]
121+
+ url[0] + '?' + this.URL_MARK_PARAM + '=' + searchterms + '#' + url[1]
119122
+ '">' + result.doc.breadcrumbs + '</a>' // doc.title
120123
+ '<span class="breadcrumbs">' + '</span>'
121124
+ '<span class="teaser">' + teaser + '</span>'
@@ -216,55 +219,6 @@ $( document ).ready(function() {
216219
return teaser_split.join('');
217220
}
218221
,
219-
doSearch : function (searchterm) {
220-
221-
// Don't search the same twice
222-
if (this.current_searchterm == searchterm) { return; }
223-
else { this.current_searchterm = searchterm; }
224-
225-
if (this.searchindex == null) { return; }
226-
227-
// Do the actual search
228-
var results = this.searchindex.search(searchterm, this.searchoptions);
229-
var resultcount = Math.min(results.length, this.searchoptions.limit_results);
230-
231-
// Display search metrics
232-
this.searchresults_header.text(this.formatSearchMetric(resultcount, searchterm));
233-
234-
// Clear and insert results
235-
var searchterms = searchterm.split(' ');
236-
this.searchresults.empty();
237-
for(var i = 0; i < resultcount ; i++){
238-
this.searchresults.append(this.formatSearchResult(results[i], searchterms));
239-
}
240-
241-
// Display and scroll to results
242-
this.searchresults_outer.slideDown();
243-
// this.searchicon.scrollTop(0);
244-
}
245-
,
246-
doSearchOrMarkFromUrl : function () {
247-
// Check current URL for search request
248-
var url = this.parseURL(window.location.href);
249-
if (url.params.hasOwnProperty(this.SEARCH_PARAM)
250-
&& url.params[this.SEARCH_PARAM] != "") {
251-
this.searchbar_outer.slideDown();
252-
this.searchbar[0].value = decodeURIComponent(
253-
(url.params[this.SEARCH_PARAM]+'').replace(/\+/g, '%20'));
254-
this.searchbarKeyUpHandler();
255-
} else {
256-
this.searchbar_outer.slideUp();
257-
}
258-
259-
if (url.params.hasOwnProperty(this.MARK_PARAM)) {
260-
var words = url.params[this.MARK_PARAM].split(' ');
261-
var header = $('#' + url.hash);
262-
this.content.mark(words, {
263-
exclude : this.mark_exclude
264-
});
265-
}
266-
}
267-
,
268222
init : function () {
269223
var this_ = this;
270224

@@ -296,6 +250,38 @@ $( document ).ready(function() {
296250
return this.searchbar.is(':focus');
297251
}
298252
,
253+
unfocusSearchbar : function () {
254+
// hacky, but just focusing a div only works once
255+
var tmp = $('<input style="position: absolute; opacity: 0;">');
256+
tmp.insertAfter(this.searchicon);
257+
tmp.focus();
258+
tmp.remove();
259+
}
260+
,
261+
// On reload or browser history backwards/forwards events, parse the url and do search or mark
262+
doSearchOrMarkFromUrl : function () {
263+
// Check current URL for search request
264+
var url = this.parseURL(window.location.href);
265+
if (url.params.hasOwnProperty(this.URL_SEARCH_PARAM)
266+
&& url.params[this.URL_SEARCH_PARAM] != "") {
267+
this.searchbar_outer.slideDown();
268+
this.searchbar[0].value = decodeURIComponent(
269+
(url.params[this.URL_SEARCH_PARAM]+'').replace(/\+/g, '%20'));
270+
this.searchbarKeyUpHandler(); // -> doSearch()
271+
} else {
272+
this.searchbar_outer.slideUp();
273+
}
274+
275+
if (url.params.hasOwnProperty(this.URL_MARK_PARAM)) {
276+
var words = url.params[this.URL_MARK_PARAM].split(' ');
277+
var header = $('#' + url.hash);
278+
this.content.mark(words, {
279+
exclude : this.mark_exclude
280+
});
281+
}
282+
}
283+
,
284+
// Eventhandler for keyevents on `document`
299285
globalKeyHandler : function (e) {
300286
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
301287

@@ -304,8 +290,10 @@ $( document ).ready(function() {
304290
this.searchbar.removeClass("active");
305291
// this.searchbar[0].value = "";
306292
this.setSearchUrlParameters("",
307-
(this.searchbar[0].value.trim() != 0) ? "push" : "replace");
308-
this.unfocusSearchbar();
293+
(this.searchbar[0].value.trim() != "") ? "push" : "replace");
294+
if (this.hasFocus()) {
295+
this.unfocusSearchbar();
296+
}
309297
this.searchbar_outer.slideUp();
310298
this.content.unmark();
311299
return;
@@ -349,21 +337,13 @@ $( document ).ready(function() {
349337
}
350338
}
351339
,
352-
unfocusSearchbar : function () {
353-
// hacky, but just focusing a div only works once
354-
var tmp = $('<input style="position: absolute; opacity: 0;">');
355-
tmp.insertAfter(this.searchicon);
356-
tmp.focus();
357-
tmp.remove();
358-
}
359-
,
340+
// Eventhandler for search icon
360341
searchIconClickHandler : function () {
361342
this.searchbar_outer.slideToggle();
362343
this.searchbar.focus();
363-
// TODO:
364-
// If invisible, clear URL search parameter
365344
}
366345
,
346+
// Eventhandler for keyevents while the searchbar is focused
367347
searchbarKeyUpHandler : function () {
368348
var searchterm = this.searchbar[0].value.trim();
369349
if (searchterm != "") {
@@ -375,32 +355,61 @@ $( document ).ready(function() {
375355
this.searchresults.empty();
376356
}
377357

378-
this.setSearchUrlParameters(searchterm, "if_begin_search");
358+
this.setSearchUrlParameters(searchterm, "push_if_new_search_else_replace");
379359

380360
// Remove marks
381361
this.content.unmark();
382362
}
383363
,
364+
// Update current url with ?URL_SEARCH_PARAM= parameter, remove ?URL_MARK_PARAM and #heading-anchor .
365+
// `action` can be one of "push", "replace", "push_if_new_search_else_replace"
366+
// and replaces or pushes a new browser history item.
367+
// "push_if_new_search_else_replace" pushes if there is no `?URL_SEARCH_PARAM=abc` yet.
384368
setSearchUrlParameters : function(searchterm, action) {
385-
// Update url with ?SEARCH_PARAM= parameter, remove ?MARK_PARAM and #heading-anchor
386369
var url = this.parseURL(window.location.href);
387-
var first_search = ! url.params.hasOwnProperty(this.SEARCH_PARAM);
388-
if (searchterm != "" || action == "if_begin_search") {
389-
url.params[this.SEARCH_PARAM] = searchterm;
390-
delete url.params[this.MARK_PARAM];
370+
var first_search = ! url.params.hasOwnProperty(this.URL_SEARCH_PARAM);
371+
if (searchterm != "" || action == "push_if_new_search_else_replace") {
372+
url.params[this.URL_SEARCH_PARAM] = searchterm;
373+
delete url.params[this.URL_MARK_PARAM];
391374
url.hash = "";
392375
} else {
393-
delete url.params[this.SEARCH_PARAM];
376+
delete url.params[this.URL_SEARCH_PARAM];
394377
}
395378
// A new search will also add a new history item, so the user can go back
396379
// to the page prior to searching. A updated search term will only replace
397380
// the url.
398-
if (action == "push" || (action == "if_begin_search" && first_search) ) {
381+
if (action == "push" || (action == "push_if_new_search_else_replace" && first_search) ) {
399382
history.pushState({}, document.title, this.renderURL(url));
400-
} else if (action == "replace" || (action == "if_begin_search" && !first_search) ) {
383+
} else if (action == "replace" || (action == "push_if_new_search_else_replace" && !first_search) ) {
401384
history.replaceState({}, document.title, this.renderURL(url));
402385
}
386+
}
387+
,
388+
doSearch : function (searchterm) {
403389

390+
// Don't search the same twice
391+
if (this.current_searchterm == searchterm) { return; }
392+
else { this.current_searchterm = searchterm; }
393+
394+
if (this.searchindex == null) { return; }
395+
396+
// Do the actual search
397+
var results = this.searchindex.search(searchterm, this.searchoptions);
398+
var resultcount = Math.min(results.length, this.searchoptions.limit_results);
399+
400+
// Display search metrics
401+
this.searchresults_header.text(this.formatSearchMetric(resultcount, searchterm));
402+
403+
// Clear and insert results
404+
var searchterms = searchterm.split(' ');
405+
this.searchresults.empty();
406+
for(var i = 0; i < resultcount ; i++){
407+
this.searchresults.append(this.formatSearchResult(results[i], searchterms));
408+
}
409+
410+
// Display and scroll to results
411+
this.searchresults_outer.slideDown();
412+
// this.searchicon.scrollTop(0);
404413
}
405414
};
406415

0 commit comments

Comments
 (0)