@@ -4,7 +4,7 @@ var searchResultsRenderer = require("./templates/search-results.stache!steal-sta
44var joinURIs = require ( "can-util/js/join-uris/" ) ;
55
66//https://lunrjs.com/guides/getting_started.html
7- var searchEngine = require ( "lunr" ) ;
7+ var lunr = require ( "lunr" ) ;
88
99var Search = Control . extend ( {
1010
@@ -32,7 +32,7 @@ var Search = Control.extend({
3232 //search options
3333 searchTimeout : 400 ,
3434
35- localStorageKeyPrefix : "bit-docs- search" ,
35+ localStorageKeyPrefix : "search" ,
3636
3737 //whether or not to animate in upon initialization
3838 animateInOnStart : true
@@ -66,6 +66,7 @@ var Search = Control.extend({
6666
6767 init : function ( ) {
6868
69+ var options = this . options ;
6970 var self = this ;
7071
7172 //init elements
@@ -76,23 +77,28 @@ var Search = Control.extend({
7677
7778 this . useLocalStorage = this . localStorageIsAvailable ( ) ;
7879
79- this . checkSearchMapHash ( this . options . pathPrefix + this . options . searchMapHashUrl ) . then ( function ( searchMapHashChangedObject ) {
80- self . getSearchMap ( self . options . pathPrefix + self . options . searchMapUrl , searchMapHashChangedObject ) . then ( function ( searchMap ) {
81- self . initSearchEngine ( searchMap ) ;
80+ this . searchEnginePromise = new Promise ( function ( resolve , reject ) {
81+ self . checkSearchMapHash ( options . pathPrefix + options . searchMapHashUrl ) . then ( function ( searchMapHashChangedObject ) {
82+ self . getSearchMap ( options . pathPrefix + options . searchMapUrl , searchMapHashChangedObject ) . then ( function ( searchMap ) {
83+ var searchEngine = self . initSearchEngine ( searchMap ) ;
84+ resolve ( searchEngine ) ;
8285
83- //show the search input when the search engine is ready
84- if ( self . options . animateInOnStart ) {
85- self . $inputWrap . fadeIn ( 400 ) ;
86- } else {
87- self . $inputWrap . show ( ) ;
88- }
86+ //show the search input when the search engine is ready
87+ if ( self . options . animateInOnStart ) {
88+ self . $inputWrap . fadeIn ( 400 ) ;
89+ } else {
90+ self . $inputWrap . show ( ) ;
91+ }
8992
90- self . bindResultsEvents ( ) ;
93+ self . bindResultsEvents ( ) ;
94+ } , function ( error ) {
95+ console . error ( "getSearchMap error" , error ) ;
96+ reject ( error ) ;
97+ } ) ;
9198 } , function ( error ) {
92- console . error ( "getSearchMap error" , error ) ;
99+ console . error ( "checkSearchMapHash error" , error ) ;
100+ reject ( error ) ;
93101 } ) ;
94- } , function ( error ) {
95- console . error ( "checkSearchMapHash error" , error ) ;
96102 } ) ;
97103 } ,
98104 destroy : function ( ) {
@@ -164,7 +170,7 @@ var Search = Control.extend({
164170 // ---- END LOCAL STORAGE ---- //
165171
166172 // ---- END DATA RETRIEVAL ---- //
167- searchMapLocalStorageKey : "searchMap" ,
173+ searchMapLocalStorageKey : 'map' ,
168174 searchMap : null ,
169175
170176 // function getSearchMap
@@ -225,7 +231,7 @@ var Search = Control.extend({
225231 return returnDeferred ;
226232 } ,
227233
228- searchMapHashLocalStorageKey : "searchMapHash" ,
234+ searchMapHashLocalStorageKey : 'map-hash' ,
229235 // function checkSearchMapHash
230236 // retrieves the searchMapHash localStorage (if present)
231237 // and from the specified url
@@ -235,22 +241,18 @@ var Search = Control.extend({
235241 //
236242 // @returns thenable that resolves to true if localStorage was cleared and false otherwise
237243 checkSearchMapHash : function ( dataUrl ) {
238- var self = this ,
239- returnDeferred = $ . Deferred ( ) ,
240- localStorageKey = self . formatLocalStorageKey ( self . searchMapHashLocalStorageKey ) ,
241- searchMapHashLocalStorage = self . getLocalStorageItem ( localStorageKey ) ,
242- lsHash = searchMapHashLocalStorage && searchMapHashLocalStorage . hash ;
244+ var returnDeferred = $ . Deferred ( ) ;
245+ var self = this ;
243246
244247 //no need to do anything if localStorage isn't present
245- if ( ! window . localStorage ) {
248+ if ( ! this . useLocalStorage ) {
246249 returnDeferred . resolve ( false ) ;
247250 return ;
248251 }
249252
250-
251- localStorageKey = self . formatLocalStorageKey ( self . searchMapHashLocalStorageKey ) ;
252- searchMapHashLocalStorage = self . getLocalStorageItem ( localStorageKey ) ;
253- lsHash = searchMapHashLocalStorage && searchMapHashLocalStorage . hash ;
253+ var localStorageKey = self . formatLocalStorageKey ( self . searchMapHashLocalStorageKey ) ;
254+ var searchMapHashLocalStorage = self . getLocalStorageItem ( localStorageKey ) ;
255+ var lsHash = searchMapHashLocalStorage && searchMapHashLocalStorage . hash ;
254256
255257 $ . ajax ( {
256258 url : dataUrl ,
@@ -296,7 +298,8 @@ var Search = Control.extend({
296298
297299 // ---- SEARCHING / PARSING ---- //
298300
299- searchIndexLocalStorageKey : "searchIndex" ,
301+ searchIndexLocalStorageKey : 'index' ,
302+ searchIndexVersionLocalStorageKey : 'index-version' ,
300303 searchEngine : null ,
301304 // function initSearchEngine
302305 // checks localStorage for an index
@@ -305,15 +308,27 @@ var Search = Control.extend({
305308 // else
306309 // generates search engine from searchMap & saves index to local storage
307310 initSearchEngine : function ( searchMap ) {
308- var localStorageKey = this . formatLocalStorageKey ( this . searchIndexLocalStorageKey ) ,
309- index = this . getLocalStorageItem ( localStorageKey ) ;
310- if ( index ) {
311- this . searchEngine = searchEngine . Index . load ( index ) ;
311+ var searchEngine ;
312+ var searchIndexKey = this . formatLocalStorageKey ( this . searchIndexLocalStorageKey ) ;
313+ var searchIndexVersionKey = this . formatLocalStorageKey ( this . searchIndexVersionLocalStorageKey ) ;
314+ var index = this . getLocalStorageItem ( searchIndexKey ) ;
315+ var indexVersion = this . getLocalStorageItem ( searchIndexVersionKey ) ;
316+ var currentIndexVersion = 1 ; // Bump this whenever the index code is changed
317+
318+ if ( index && currentIndexVersion === indexVersion ) {
319+ searchEngine = lunr . Index . load ( index ) ;
312320 } else {
313- this . searchEngine = searchEngine ( function ( ) {
321+ searchEngine = lunr ( function ( ) {
322+ lunr . tokenizer . separator = / [ \s ] + / ;
323+
324+ this . pipeline . remove ( lunr . stemmer ) ;
325+ this . pipeline . remove ( lunr . stopWordFilter ) ;
326+ this . searchPipeline . remove ( lunr . stemmer ) ;
327+
314328 this . ref ( 'name' ) ;
315329 this . field ( 'title' ) ;
316330 this . field ( 'description' ) ;
331+ this . field ( 'name' ) ;
317332 this . field ( 'url' ) ;
318333
319334 for ( var itemKey in searchMap ) {
@@ -322,19 +337,46 @@ var Search = Control.extend({
322337 }
323338 }
324339 } ) ;
325- this . setLocalStorageItem ( localStorageKey , this . searchEngine ) ;
340+ this . setLocalStorageItem ( searchIndexKey , searchEngine ) ;
341+ this . setLocalStorageItem ( searchIndexVersionKey , currentIndexVersion ) ;
326342 }
343+ return searchEngine ;
327344 } ,
328345
329346 // function searchEngineSearch
330347 // takes a value and returns a map of all relevant search items
331348 searchEngineSearch : function ( value ) {
349+ var searchTerm = value . toLowerCase ( ) ;
332350 var self = this ;
333- return this . searchEngine
334- //run the search
335- . search ( this . formatSearchTerm ( value ) )
336- //convert the results into a searchMap subset
337- . map ( function ( result ) { return self . searchMap [ result . ref ] } ) ;
351+ return this . searchEnginePromise . then ( function ( searchEngine ) {
352+ return searchEngine
353+ //run the search
354+ . query ( function ( q ) {
355+
356+
357+ if ( searchTerm . indexOf ( 'can-' ) > - 1 ) { // If the search term includes “can-”
358+
359+ // look for an exact match and apply a large positive boost
360+ q . term ( searchTerm , { usePipeline : true , boost : 120 } ) ;
361+
362+ } else {
363+ // add “can-”, look for an exact match in the title field, and apply a positive boost
364+ q . term ( 'can-' + searchTerm , { usePipeline : false , fields : [ 'title' ] , boost : 12 } ) ;
365+ }
366+
367+ // look for terms that match the beginning or end of this query
368+ q . term ( '*' + searchTerm + '*' , { usePipeline : false } ) ;
369+
370+ // look for matches in any of the fields and apply a medium positive boost
371+ var split = searchTerm . split ( lunr . tokenizer . separator ) ;
372+ split . forEach ( function ( term ) {
373+ q . term ( term , { usePipeline : false , fields : q . allFields , boost : 10 } ) ;
374+ q . term ( term + '*' , { usePipeline : false , fields : q . allFields } ) ;
375+ } ) ;
376+ } )
377+ //convert the results into a searchMap subset
378+ . map ( function ( result ) { return self . searchMap [ result . ref ] } ) ;
379+ } ) ;
338380 } ,
339381
340382 //function formatSearchTerm
@@ -395,7 +437,7 @@ var Search = Control.extend({
395437 this . selectActiveResult ( ) ;
396438 break ;
397439 default :
398-
440+
399441 if ( value !== this . searchTerm ) {
400442 this . searchTerm = value ;
401443 this . search ( value ) ;
@@ -485,36 +527,37 @@ var Search = Control.extend({
485527 clearTimeout ( this . searchDebounceHandle ) ;
486528 var self = this ;
487529 this . searchDebounceHandle = setTimeout ( function ( ) {
488- var resultsMap = self . searchEngineSearch ( value ) ,
489- numResults = Object . keys ( resultsMap ) . length ,
490- resultsFrag = self . options . resultsRenderer ( {
491- results :resultsMap ,
492- numResults :numResults ,
493- searchValue :value ,
494- pathPrefix : ( self . options . pathPrefix === '.' ) ? '' : '/' + self . options . pathPrefix + '/'
495- } , {
496- docUrl : function ( ) {
497- if ( ! self . options . pathPrefix ) {
498- return this . url ;
499- }
500-
501- var root = joinURIs ( window . location . href , self . options . pathPrefix ) ;
502- if ( root . substr ( - 1 ) === "/" ) {
503- root = root . substr ( 0 , root . length - 1 ) ;
504- }
505-
506- return root + "/" + this . url ;
530+ self . searchEngineSearch ( value ) . then ( function ( resultsMap ) {
531+ var numResults = Object . keys ( resultsMap ) . length ;
532+ var resultsFrag = self . options . resultsRenderer ( {
533+ results :resultsMap ,
534+ numResults :numResults ,
535+ searchValue :value ,
536+ pathPrefix : ( self . options . pathPrefix === '.' ) ? '' : '/' + self . options . pathPrefix + '/'
537+ } , {
538+ docUrl : function ( ) {
539+ if ( ! self . options . pathPrefix ) {
540+ return this . url ;
507541 }
508- } ) ;
509542
510- self . $resultsWrap . empty ( ) ;
511- self . $resultsWrap [ 0 ] . appendChild ( resultsFrag ) ;
543+ var root = joinURIs ( window . location . href , self . options . pathPrefix ) ;
544+ if ( root . substr ( - 1 ) === "/" ) {
545+ root = root . substr ( 0 , root . length - 1 ) ;
546+ }
512547
513- //refresh necessary dom
514- self . $resultsList = null ;
515- if ( numResults ) {
516- self . $resultsList = self . $resultsWrap . find ( ".search-results > ul" ) ;
517- }
548+ return root + "/" + this . url ;
549+ }
550+ } ) ;
551+
552+ self . $resultsWrap . empty ( ) ;
553+ self . $resultsWrap [ 0 ] . appendChild ( resultsFrag ) ;
554+
555+ //refresh necessary dom
556+ self . $resultsList = null ;
557+ if ( numResults ) {
558+ self . $resultsList = self . $resultsWrap . find ( ".search-results > ul" ) ;
559+ }
560+ } ) ;
518561 } , this . options . searchTimeout ) ;
519562 } ,
520563
0 commit comments