@@ -32,16 +32,22 @@ var catMembers = "Members";
3232var catSearchTags = "SearchTags" ;
3333var highlight = "<span class=\"result-highlight\">$&</span>" ;
3434var searchPattern = "" ;
35+ var fallbackPattern = "" ;
3536var RANKING_THRESHOLD = 2 ;
3637var NO_MATCH = 0xffff ;
37- var MAX_RESULTS_PER_CATEGORY = 500 ;
38+ var MIN_RESULTS = 3 ;
39+ var MAX_RESULTS = 500 ;
3840var UNNAMED = "<Unnamed>" ;
3941function escapeHtml ( str ) {
4042 return str . replace ( / < / g, "<" ) . replace ( / > / g, ">" ) ;
4143}
42- function getHighlightedText ( item , matcher ) {
44+ function getHighlightedText ( item , matcher , fallbackMatcher ) {
4345 var escapedItem = escapeHtml ( item ) ;
44- return escapedItem . replace ( matcher , highlight ) ;
46+ var highlighted = escapedItem . replace ( matcher , highlight ) ;
47+ if ( highlighted === escapedItem ) {
48+ highlighted = escapedItem . replace ( fallbackMatcher , highlight )
49+ }
50+ return highlighted ;
4551}
4652function getURLPrefix ( ui ) {
4753 var urlPrefix = "" ;
@@ -60,11 +66,10 @@ function getURLPrefix(ui) {
6066 }
6167 } ) ;
6268 }
63- return urlPrefix ;
6469 }
6570 return urlPrefix ;
6671}
67- function makeCamelCaseRegex ( term ) {
72+ function createSearchPattern ( term ) {
6873 var pattern = "" ;
6974 var isWordToken = false ;
7075 term . replace ( / , \s * / g, ", " ) . trim ( ) . split ( / \s + / ) . forEach ( function ( w , index ) {
@@ -93,26 +98,26 @@ function createMatcher(pattern, flags) {
9398}
9499var watermark = 'Search' ;
95100$ ( function ( ) {
96- $ ( "#search" ) . val ( '' ) ;
97- $ ( "#search" ) . prop ( "disabled" , false ) ;
98- $ ( "#reset" ) . prop ( "disabled" , false ) ;
99- $ ( "#search" ) . val ( watermark ) . addClass ( 'watermark' ) ;
100- $ ( "#search" ) . blur ( function ( ) {
101- if ( $ ( this ) . val ( ) . length == 0 ) {
101+ var search = $ ( "#search" ) ;
102+ var reset = $ ( "#reset" ) ;
103+ search . val ( '' ) ;
104+ search . prop ( "disabled" , false ) ;
105+ reset . prop ( "disabled" , false ) ;
106+ search . val ( watermark ) . addClass ( 'watermark' ) ;
107+ search . blur ( function ( ) {
108+ if ( $ ( this ) . val ( ) . length === 0 ) {
102109 $ ( this ) . val ( watermark ) . addClass ( 'watermark' ) ;
103110 }
104111 } ) ;
105- $ ( "# search" ) . on ( 'click keydown paste' , function ( ) {
106- if ( $ ( this ) . val ( ) == watermark ) {
112+ search . on ( 'click keydown paste' , function ( ) {
113+ if ( $ ( this ) . val ( ) === watermark ) {
107114 $ ( this ) . val ( '' ) . removeClass ( 'watermark' ) ;
108115 }
109116 } ) ;
110- $ ( "#reset" ) . click ( function ( ) {
111- $ ( "#search" ) . val ( '' ) ;
112- $ ( "#search" ) . focus ( ) ;
117+ reset . click ( function ( ) {
118+ search . val ( '' ) . focus ( ) ;
113119 } ) ;
114- $ ( "#search" ) . focus ( ) ;
115- $ ( "#search" ) [ 0 ] . setSelectionRange ( 0 , 0 ) ;
120+ search . focus ( ) [ 0 ] . setSelectionRange ( 0 , 0 ) ;
116121} ) ;
117122$ . widget ( "custom.catcomplete" , $ . ui . autocomplete , {
118123 _create : function ( ) {
@@ -142,20 +147,21 @@ $.widget("custom.catcomplete", $.ui.autocomplete, {
142147 _renderItem : function ( ul , item ) {
143148 var label = "" ;
144149 var matcher = createMatcher ( escapeHtml ( searchPattern ) , "g" ) ;
150+ var fallbackMatcher = new RegExp ( fallbackPattern , "gi" )
145151 if ( item . category === catModules ) {
146- label = getHighlightedText ( item . l , matcher ) ;
152+ label = getHighlightedText ( item . l , matcher , fallbackMatcher ) ;
147153 } else if ( item . category === catPackages ) {
148- label = getHighlightedText ( item . l , matcher ) ;
154+ label = getHighlightedText ( item . l , matcher , fallbackMatcher ) ;
149155 } else if ( item . category === catTypes ) {
150156 label = ( item . p && item . p !== UNNAMED )
151- ? getHighlightedText ( item . p + "." + item . l , matcher )
152- : getHighlightedText ( item . l , matcher ) ;
157+ ? getHighlightedText ( item . p + "." + item . l , matcher , fallbackMatcher )
158+ : getHighlightedText ( item . l , matcher , fallbackMatcher ) ;
153159 } else if ( item . category === catMembers ) {
154160 label = ( item . p && item . p !== UNNAMED )
155- ? getHighlightedText ( item . p + "." + item . c + "." + item . l , matcher )
156- : getHighlightedText ( item . c + "." + item . l , matcher ) ;
161+ ? getHighlightedText ( item . p + "." + item . c + "." + item . l , matcher , fallbackMatcher )
162+ : getHighlightedText ( item . c + "." + item . l , matcher , fallbackMatcher ) ;
157163 } else if ( item . category === catSearchTags ) {
158- label = getHighlightedText ( item . l , matcher ) ;
164+ label = getHighlightedText ( item . l , matcher , fallbackMatcher ) ;
159165 } else {
160166 label = item . l ;
161167 }
@@ -186,25 +192,25 @@ function rankMatch(match, category) {
186192 var input = match . input ;
187193 var leftBoundaryMatch = 2 ;
188194 var periferalMatch = 0 ;
189- var delta = 0 ;
190195 // make sure match is anchored on a left word boundary
191- if ( index === 0 || / \W / . test ( input [ index - 1 ] ) || "_" === input [ index - 1 ] || "_" === input [ index ] ) {
196+ if ( index === 0 || / \W / . test ( input [ index - 1 ] ) || "_" === input [ index ] ) {
192197 leftBoundaryMatch = 0 ;
193- } else if ( input [ index ] === input [ index ] . toUpperCase ( ) && ! / ^ [ A - Z 0 - 9 _ $ ] + $ / . test ( input ) ) {
198+ } else if ( "_" === input [ index - 1 ] || ( input [ index ] === input [ index ] . toUpperCase ( ) && ! / ^ [ A - Z 0 - 9 _ $ ] + $ / . test ( input ) ) ) {
194199 leftBoundaryMatch = 1 ;
195200 }
196201 var matchEnd = index + match [ 0 ] . length ;
197202 var leftParen = input . indexOf ( "(" ) ;
203+ var endOfName = leftParen > - 1 ? leftParen : input . length ;
198204 // exclude peripheral matches
199205 if ( category !== catModules && category !== catSearchTags ) {
200- var endOfName = leftParen > - 1 ? leftParen : input . length ;
201206 var delim = category === catPackages ? "/" : "." ;
202207 if ( leftParen > - 1 && leftParen < index ) {
203208 periferalMatch += 2 ;
204209 } else if ( input . lastIndexOf ( delim , endOfName ) >= matchEnd ) {
205210 periferalMatch += 2 ;
206211 }
207212 }
213+ var delta = match [ 0 ] . length === endOfName ? 0 : 1 ; // rank full match higher than partial match
208214 for ( var i = 1 ; i < match . length ; i ++ ) {
209215 // lower ranking if parts of the name are missing
210216 if ( match [ i ] )
@@ -222,88 +228,58 @@ function rankMatch(match, category) {
222228}
223229function doSearch ( request , response ) {
224230 var result = [ ] ;
225- var newResults = [ ] ;
226-
227- searchPattern = makeCamelCaseRegex ( request . term ) ;
231+ searchPattern = createSearchPattern ( request . term ) ;
232+ fallbackPattern = createSearchPattern ( request . term . toLowerCase ( ) ) ;
228233 if ( searchPattern === "" ) {
229234 return this . close ( ) ;
230235 }
231236 var camelCaseMatcher = createMatcher ( searchPattern , "" ) ;
232- var boundaryMatcher = createMatcher ( "\\b" + searchPattern , "" ) ;
237+ var fallbackMatcher = new RegExp ( fallbackPattern , "i " ) ;
233238
234- function concatResults ( a1 , a2 ) {
235- a2 . sort ( function ( e1 , e2 ) {
236- return e1 . ranking - e2 . ranking ;
237- } ) ;
238- a1 = a1 . concat ( a2 . map ( function ( e ) { return e . item ; } ) ) ;
239- a2 . length = 0 ;
240- return a1 ;
241- }
242-
243- if ( moduleSearchIndex ) {
244- $ . each ( moduleSearchIndex , function ( index , item ) {
245- item . category = catModules ;
246- var ranking = rankMatch ( boundaryMatcher . exec ( item . l ) , catModules ) ;
247- if ( ranking < RANKING_THRESHOLD ) {
248- newResults . push ( { ranking : ranking , item : item } ) ;
249- }
250- return newResults . length < MAX_RESULTS_PER_CATEGORY ;
251- } ) ;
252- result = concatResults ( result , newResults ) ;
253- }
254- if ( packageSearchIndex ) {
255- $ . each ( packageSearchIndex , function ( index , item ) {
256- item . category = catPackages ;
257- var name = ( item . m && request . term . indexOf ( "/" ) > - 1 )
258- ? ( item . m + "/" + item . l )
259- : item . l ;
260- var ranking = rankMatch ( boundaryMatcher . exec ( name ) , catPackages ) ;
261- if ( ranking < RANKING_THRESHOLD ) {
262- newResults . push ( { ranking : ranking , item : item } ) ;
263- }
264- return newResults . length < MAX_RESULTS_PER_CATEGORY ;
265- } ) ;
266- result = concatResults ( result , newResults ) ;
267- }
268- if ( typeSearchIndex ) {
269- $ . each ( typeSearchIndex , function ( index , item ) {
270- item . category = catTypes ;
271- var name = request . term . indexOf ( "." ) > - 1
272- ? item . p + "." + item . l
273- : item . l ;
274- var ranking = rankMatch ( camelCaseMatcher . exec ( name ) , catTypes ) ;
275- if ( ranking < RANKING_THRESHOLD ) {
276- newResults . push ( { ranking : ranking , item : item } ) ;
277- }
278- return newResults . length < MAX_RESULTS_PER_CATEGORY ;
279- } ) ;
280- result = concatResults ( result , newResults ) ;
281- }
282- if ( memberSearchIndex ) {
283- $ . each ( memberSearchIndex , function ( index , item ) {
284- item . category = catMembers ;
285- var name = request . term . indexOf ( "." ) > - 1
286- ? item . p + "." + item . c + "." + item . l
287- : item . l ;
288- var ranking = rankMatch ( camelCaseMatcher . exec ( name ) , catMembers ) ;
289- if ( ranking < RANKING_THRESHOLD ) {
290- newResults . push ( { ranking : ranking , item : item } ) ;
291- }
292- return newResults . length < MAX_RESULTS_PER_CATEGORY ;
293- } ) ;
294- result = concatResults ( result , newResults ) ;
239+ function searchIndexWithMatcher ( indexArray , matcher , category , nameFunc ) {
240+ if ( indexArray ) {
241+ var newResults = [ ] ;
242+ $ . each ( indexArray , function ( i , item ) {
243+ item . category = category ;
244+ var ranking = rankMatch ( matcher . exec ( nameFunc ( item ) ) , category ) ;
245+ if ( ranking < RANKING_THRESHOLD ) {
246+ newResults . push ( { ranking : ranking , item : item } ) ;
247+ }
248+ return newResults . length <= MAX_RESULTS ;
249+ } ) ;
250+ return newResults . sort ( function ( e1 , e2 ) {
251+ return e1 . ranking - e2 . ranking ;
252+ } ) . map ( function ( e ) {
253+ return e . item ;
254+ } ) ;
255+ }
256+ return [ ] ;
295257 }
296- if ( tagSearchIndex ) {
297- $ . each ( tagSearchIndex , function ( index , item ) {
298- item . category = catSearchTags ;
299- var ranking = rankMatch ( boundaryMatcher . exec ( item . l ) , catSearchTags ) ;
300- if ( ranking < RANKING_THRESHOLD ) {
301- newResults . push ( { ranking : ranking , item : item } ) ;
302- }
303- return newResults . length < MAX_RESULTS_PER_CATEGORY ;
304- } ) ;
305- result = concatResults ( result , newResults ) ;
258+ function searchIndex ( indexArray , category , nameFunc ) {
259+ var primaryResults = searchIndexWithMatcher ( indexArray , camelCaseMatcher , category , nameFunc ) ;
260+ result = result . concat ( primaryResults ) ;
261+ if ( primaryResults . length <= MIN_RESULTS && camelCaseMatcher . flags . indexOf ( "i" ) === - 1 ) {
262+ var secondaryResults = searchIndexWithMatcher ( indexArray , fallbackMatcher , category , nameFunc ) ;
263+ result = result . concat ( secondaryResults . filter ( function ( item ) {
264+ return primaryResults . indexOf ( item ) === - 1 ;
265+ } ) ) ;
266+ }
306267 }
268+
269+ searchIndex ( moduleSearchIndex , catModules , function ( item ) { return item . l ; } ) ;
270+ searchIndex ( packageSearchIndex , catPackages , function ( item ) {
271+ return ( item . m && request . term . indexOf ( "/" ) > - 1 )
272+ ? ( item . m + "/" + item . l ) : item . l ;
273+ } ) ;
274+ searchIndex ( typeSearchIndex , catTypes , function ( item ) {
275+ return request . term . indexOf ( "." ) > - 1 ? item . p + "." + item . l : item . l ;
276+ } ) ;
277+ searchIndex ( memberSearchIndex , catMembers , function ( item ) {
278+ return request . term . indexOf ( "." ) > - 1
279+ ? item . p + "." + item . c + "." + item . l : item . l ;
280+ } ) ;
281+ searchIndex ( tagSearchIndex , catSearchTags , function ( item ) { return item . l ; } ) ;
282+
307283 if ( ! indexFilesLoaded ( ) ) {
308284 updateSearchResults = function ( ) {
309285 doSearch ( request , response ) ;
0 commit comments