@@ -2,8 +2,8 @@ $( document ).ready(function() {
2
2
3
3
// Search functionality
4
4
//
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.
7
7
var search = {
8
8
searchbar : $ ( '#searchbar' ) ,
9
9
searchbar_outer : $ ( '#searchbar-outer' ) ,
@@ -25,27 +25,19 @@ $( document ).ready(function() {
25
25
breadcrumbs : { boost : 0 }
26
26
}
27
27
} ,
28
- mark_exclude : [ ] , // ['.hljs']
28
+ mark_exclude : [ ] ,
29
29
current_searchterm : "" ,
30
- SEARCH_PARAM : 'search' ,
31
- MARK_PARAM : 'highlight' ,
30
+ URL_SEARCH_PARAM : 'search' ,
31
+ URL_MARK_PARAM : 'highlight' ,
32
32
33
33
SEARCH_HOTKEY_KEYCODE : 83 ,
34
34
ESCAPE_KEYCODE : 27 ,
35
35
DOWN_KEYCODE : 40 ,
36
36
UP_KEYCODE : 38 ,
37
37
SELECT_KEYCODE : 13 ,
38
38
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.
49
41
parseURL : function ( url ) {
50
42
var a = document . createElement ( 'a' ) ;
51
43
a . href = url ;
@@ -71,6 +63,7 @@ $( document ).ready(function() {
71
63
} ;
72
64
}
73
65
,
66
+ // Helper to recreate a url string from its building blocks.
74
67
renderURL : function ( urlobject ) {
75
68
var url = urlobject . protocol + "://" + urlobject . host ;
76
69
if ( urlobject . port != "" ) {
@@ -90,6 +83,7 @@ $( document ).ready(function() {
90
83
return url ;
91
84
}
92
85
,
86
+ // Helper to escape html special chars for displaying the teasers
93
87
escapeHTML : ( function ( ) {
94
88
var MAP = {
95
89
'&' : '&' ,
@@ -104,18 +98,27 @@ $( document ).ready(function() {
104
98
} ;
105
99
} ) ( )
106
100
,
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
+ ,
107
111
formatSearchResult : function ( result , searchterms ) {
108
- // Show text around first occurrence of first search term.
109
112
var teaser = this . makeTeaser ( this . escapeHTML ( result . doc . body ) , searchterms ) ;
110
113
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
112
115
var url = result . ref . split ( "#" ) ;
113
- if ( url . length == 1 ) {
116
+ if ( url . length == 1 ) { // no anchor found
114
117
url . push ( "" ) ;
115
118
}
116
119
117
120
return $ ( '<li><a href="'
118
- + url [ 0 ] + '?' + this . MARK_PARAM + '=' + searchterms + '#' + url [ 1 ]
121
+ + url [ 0 ] + '?' + this . URL_MARK_PARAM + '=' + searchterms + '#' + url [ 1 ]
119
122
+ '">' + result . doc . breadcrumbs + '</a>' // doc.title
120
123
+ '<span class="breadcrumbs">' + '</span>'
121
124
+ '<span class="teaser">' + teaser + '</span>'
@@ -216,55 +219,6 @@ $( document ).ready(function() {
216
219
return teaser_split . join ( '' ) ;
217
220
}
218
221
,
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
- ,
268
222
init : function ( ) {
269
223
var this_ = this ;
270
224
@@ -296,6 +250,38 @@ $( document ).ready(function() {
296
250
return this . searchbar . is ( ':focus' ) ;
297
251
}
298
252
,
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`
299
285
globalKeyHandler : function ( e ) {
300
286
if ( e . altKey || e . ctrlKey || e . metaKey || e . shiftKey ) { return ; }
301
287
@@ -304,8 +290,10 @@ $( document ).ready(function() {
304
290
this . searchbar . removeClass ( "active" ) ;
305
291
// this.searchbar[0].value = "";
306
292
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
+ }
309
297
this . searchbar_outer . slideUp ( ) ;
310
298
this . content . unmark ( ) ;
311
299
return ;
@@ -349,21 +337,13 @@ $( document ).ready(function() {
349
337
}
350
338
}
351
339
,
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
360
341
searchIconClickHandler : function ( ) {
361
342
this . searchbar_outer . slideToggle ( ) ;
362
343
this . searchbar . focus ( ) ;
363
- // TODO:
364
- // If invisible, clear URL search parameter
365
344
}
366
345
,
346
+ // Eventhandler for keyevents while the searchbar is focused
367
347
searchbarKeyUpHandler : function ( ) {
368
348
var searchterm = this . searchbar [ 0 ] . value . trim ( ) ;
369
349
if ( searchterm != "" ) {
@@ -375,32 +355,61 @@ $( document ).ready(function() {
375
355
this . searchresults . empty ( ) ;
376
356
}
377
357
378
- this . setSearchUrlParameters ( searchterm , "if_begin_search " ) ;
358
+ this . setSearchUrlParameters ( searchterm , "push_if_new_search_else_replace " ) ;
379
359
380
360
// Remove marks
381
361
this . content . unmark ( ) ;
382
362
}
383
363
,
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.
384
368
setSearchUrlParameters : function ( searchterm , action ) {
385
- // Update url with ?SEARCH_PARAM= parameter, remove ?MARK_PARAM and #heading-anchor
386
369
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 ] ;
391
374
url . hash = "" ;
392
375
} else {
393
- delete url . params [ this . SEARCH_PARAM ] ;
376
+ delete url . params [ this . URL_SEARCH_PARAM ] ;
394
377
}
395
378
// A new search will also add a new history item, so the user can go back
396
379
// to the page prior to searching. A updated search term will only replace
397
380
// 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 ) ) {
399
382
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 ) ) {
401
384
history . replaceState ( { } , document . title , this . renderURL ( url ) ) ;
402
385
}
386
+ }
387
+ ,
388
+ doSearch : function ( searchterm ) {
403
389
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);
404
413
}
405
414
} ;
406
415
0 commit comments