1
- let summaryInclude = 60 ;
2
- let fuseOptions = {
3
- shouldSort : true ,
4
- includeMatches : true ,
5
- threshold : 0.1 ,
6
- tokenize : true ,
7
- location : 0 ,
8
- distance : 100 ,
9
- maxPatternLength : 32 ,
10
- minMatchCharLength : 1 ,
11
- keys : [
12
- { name : 'title' , weight : 0.8 } ,
13
- { name : 'description' , weight : 0.5 } ,
14
- { name : 'tags' , weight : 0.3 } ,
15
- { name : 'categories' , weight : 0.3 } ,
16
- ] ,
1
+ const miniSearchOptions = {
2
+ fields : [
3
+ 'title' ,
4
+ 'description' ,
5
+ '_key' ,
6
+ 'tags' ,
7
+ 'package.name' ,
8
+ 'license' ,
9
+ 'language' ,
10
+ 'registryType' ,
11
+ ] , // fields to index for full-text search
12
+ storeFields : [ 'title' , '_key' ] , // fields to return with search results
13
+ extractField : ( document , fieldName ) => {
14
+ if ( Array . isArray ( document [ fieldName ] ) ) {
15
+ return document [ fieldName ] . join ( ' ' ) ;
16
+ }
17
+ return fieldName . split ( '.' ) . reduce ( ( doc , key ) => doc && doc [ key ] , document ) ;
18
+ } ,
19
+ searchOptions : {
20
+ prefix : true ,
21
+ boost : {
22
+ title : 4 ,
23
+ tags : 3 ,
24
+ description : 2 ,
25
+ } ,
26
+ fuzzy : 0.2 ,
27
+ } ,
17
28
} ;
18
29
30
+ const originalDocumentTitle = document . title ;
31
+
32
+ let fetched = false ;
33
+ const miniSearch = new MiniSearch ( miniSearchOptions ) ;
34
+
19
35
// Get searchQuery for queryParams
20
36
let pathName = window . location . pathname ;
21
37
let searchQuery = '' ;
@@ -27,14 +43,9 @@ parseUrlParams();
27
43
if ( pathName . includes ( 'registry' ) ) {
28
44
// Run search or display default body
29
45
if ( searchQuery ) {
30
- document . querySelector ( '#search-query' ) . value = searchQuery ;
31
- document . querySelector ( '#default-body' ) . style . display = 'none' ;
32
46
executeSearch ( searchQuery ) ;
33
47
} else {
34
- let defaultBody = document . querySelector ( '#default-body' ) ;
35
- if ( defaultBody . style . display === 'none' ) {
36
- defaultBody . style . display = 'block' ;
37
- }
48
+ showBody ( ) ;
38
49
}
39
50
40
51
if ( selectedLanguage !== 'all' || selectedComponent !== 'all' ) {
@@ -52,116 +63,22 @@ if (pathName.includes('registry')) {
52
63
}
53
64
updateFilters ( ) ;
54
65
}
55
- }
56
66
57
- // Runs search through Fuse for fuzzy search
58
- function executeSearch ( searchQuery ) {
59
- fetch ( '/ecosystem/registry/index.json' )
60
- . then ( ( res ) => res . json ( ) )
61
- . then ( ( json ) => {
62
- let fuse = new Fuse ( json , fuseOptions ) ;
63
- let results = fuse . search ( searchQuery ) ;
64
-
65
- if ( results . length > 0 ) {
66
- populateResults ( results ) ;
67
- } else {
68
- document . querySelector ( '#search-results' ) . innerHTML +=
69
- '<p>No matches found</p>' ;
70
- }
67
+ document . addEventListener ( 'DOMContentLoaded' , ( event ) => {
68
+ let searchForm = document . getElementById ( 'searchForm' ) ;
69
+ searchForm . addEventListener ( 'submit' , function ( evt ) {
70
+ evt . preventDefault ( ) ;
71
+ let val = document . getElementById ( 'input-s' ) . value ;
72
+ setInput ( 's' , val ) ;
73
+ parseUrlParams ( ) ;
74
+ executeSearch ( searchQuery ) ;
71
75
} ) ;
72
- }
73
76
74
- // Populate the search results and render to the page
75
- function populateResults ( results ) {
76
- results . forEach ( ( result , key ) => {
77
- let contents = result . item . description ;
78
- let snippet = '' ;
79
- let snippetHighlights = [ ] ;
80
-
81
- if ( fuseOptions . tokenize ) {
82
- snippetHighlights . push ( searchQuery ) ;
83
- } else {
84
- result . matches . forEach ( ( match ) => {
85
- if ( match . key === 'tags' || match . key === 'categories' ) {
86
- snippetHighlights . push ( match . value ) ;
87
- } else if ( match . key === 'description' ) {
88
- start =
89
- match . indices [ 0 ] [ 0 ] - summaryInclude > 0
90
- ? match . indices [ 0 ] [ 0 ] - summaryInclude
91
- : 0 ;
92
- end =
93
- match . indices [ 0 ] [ 1 ] + summaryInclude < contents . length
94
- ? match . indices [ 0 ] [ 1 ] + summaryInclude
95
- : contents . length ;
96
- snippet += contents . substring ( start , end ) ;
97
- snippetHighlights . push (
98
- match . value . substring (
99
- match . indices [ 0 ] [ 0 ] ,
100
- match . indices [ 0 ] [ 1 ] - mvalue . indices [ 0 ] [ 0 ] + 1 ,
101
- ) ,
102
- ) ;
103
- }
104
- } ) ;
105
- }
106
-
107
- if ( snippet . length < 1 && contents . length > 0 ) {
108
- snippet += contents . substring ( 0 , summaryInclude * 2 ) ;
109
- }
110
-
111
- // Pull template from hugo template definition
112
- let templateDefinition = document . querySelector (
113
- '#search-result-template' ,
114
- ) . innerHTML ;
115
-
116
- // Replace values from template with search results
117
- let output = render ( templateDefinition , {
118
- key : key ,
119
- title : result . item . title ,
120
- link : result . item . permalink ,
121
- tags : result . item . tags ,
122
- categories : result . item . categories ,
123
- description : result . item . description ,
124
- repo : result . item . repo ,
125
- registryType : result . item . registryType ,
126
- language : result . item . language ,
127
- snippet : snippet ,
128
- otVersion : result . item . otVersion ,
77
+ let searchInput = document . getElementById ( 'input-s' ) ;
78
+ searchInput . addEventListener ( 'keyup' , function ( evt ) {
79
+ autoSuggest ( evt . target . value ) ;
129
80
} ) ;
130
- document . querySelector ( '#search-results' ) . innerHTML += output ;
131
- } ) ;
132
- }
133
81
134
- // Helper function to generate HTML for a search result
135
- function render ( templateString , data ) {
136
- let conditionalMatches , conditionalPattern , copy ;
137
- conditionalPattern = / \$ \{ \s * i s s e t ( [ a - z A - Z ] * ) \s * \} ( .* ) \$ \{ \s * e n d \s * } / g;
138
- //since loop below depends on re.lastInxdex, we use a copy to capture any manipulations whilst inside the loop
139
- copy = templateString ;
140
- while (
141
- ( conditionalMatches = conditionalPattern . exec ( templateString ) ) !== null
142
- ) {
143
- if ( data [ conditionalMatches [ 1 ] ] ) {
144
- //valid key, remove conditionals, leave contents.
145
- copy = copy . replace ( conditionalMatches [ 0 ] , conditionalMatches [ 2 ] ) ;
146
- } else {
147
- //not valid, remove entire section
148
- copy = copy . replace ( conditionalMatches [ 0 ] , '' ) ;
149
- }
150
- }
151
- templateString = copy ;
152
-
153
- //now any conditionals removed we can do simple substitution
154
- let key , find , re ;
155
- for ( key in data ) {
156
- find = '\\$\\{\\s*' + key + '\\s*\\}' ;
157
- re = new RegExp ( find , 'g' ) ;
158
- templateString = templateString . replace ( re , data [ key ] ) ;
159
- }
160
- return templateString ;
161
- }
162
-
163
- if ( pathName . includes ( 'registry' ) ) {
164
- document . addEventListener ( 'DOMContentLoaded' , ( event ) => {
165
82
let languageList = document
166
83
. getElementById ( 'languageFilter' )
167
84
. querySelectorAll ( '.dropdown-item' ) ;
@@ -191,6 +108,98 @@ if (pathName.includes('registry')) {
191
108
} ) ;
192
109
}
193
110
111
+ function showBody ( ) {
112
+ document . title = originalDocumentTitle ;
113
+ document . querySelector ( '#search-results' ) . innerHTML = '' ;
114
+ let defaultBody = document . querySelector ( '#default-body' ) ;
115
+ if ( defaultBody . style . display === 'none' ) {
116
+ defaultBody . style . display = 'block' ;
117
+ }
118
+ }
119
+
120
+ // Runs search through Fuse for fuzzy search
121
+ function executeSearch ( searchQuery ) {
122
+ if ( searchQuery === '' ) {
123
+ showBody ( ) ;
124
+ return ;
125
+ }
126
+
127
+ document . title = searchQuery + ' at ' + originalDocumentTitle ;
128
+ document . querySelector ( '#input-s' ) . value = searchQuery ;
129
+ document . querySelector ( '#default-body' ) . style . display = 'none' ;
130
+ document . querySelector ( '#search-results' ) . innerHTML = '' ;
131
+ document . getElementById ( 'search-loading' ) . style . display = 'block' ;
132
+
133
+ const run = function ( searchQuery ) {
134
+ // The 0-timeout is here if search is blocking, such that the "search loading" is rendered properly
135
+ setTimeout ( ( ) => {
136
+ let results = miniSearch . search ( searchQuery ) ;
137
+ document . getElementById ( 'search-loading' ) . style . display = 'none' ;
138
+
139
+ if ( results . length > 0 ) {
140
+ populateResults ( results ) ;
141
+ } else {
142
+ document . querySelector ( '#search-results' ) . innerHTML +=
143
+ '<p>No matches found</p>' ;
144
+ }
145
+ } , 0 ) ;
146
+ } ;
147
+
148
+ if ( fetched ) {
149
+ run ( searchQuery ) ;
150
+ } else {
151
+ fetch ( '/ecosystem/registry/index.json' )
152
+ . then ( ( res ) => res . json ( ) )
153
+ . then ( ( json ) => {
154
+ fetched = true ;
155
+ miniSearch . addAll ( json ) ;
156
+ run ( searchQuery ) ;
157
+ } ) ;
158
+ }
159
+ }
160
+
161
+ function autoSuggest ( value ) {
162
+ if ( value === '' ) {
163
+ return ;
164
+ }
165
+
166
+ const run = function ( value ) {
167
+ const suggestions = miniSearch . autoSuggest ( value , {
168
+ // we only use title, otherwise we get strange suggestions, especially with description
169
+ fields : [ 'title' ] ,
170
+ } ) ;
171
+ const list = document . getElementById ( 'search-suggestions' ) ;
172
+ list . innerHTML = suggestions
173
+ . map ( ( { suggestion } ) => `<option>${ suggestion } </option>` )
174
+ . join ( '' ) ;
175
+ } ;
176
+
177
+ if ( fetched ) {
178
+ run ( value ) ;
179
+ } else {
180
+ fetch ( '/ecosystem/registry/index.json' )
181
+ . then ( ( res ) => res . json ( ) )
182
+ . then ( ( json ) => {
183
+ fetched = true ;
184
+ miniSearch . addAll ( json ) ;
185
+ run ( value ) ;
186
+ } ) ;
187
+ }
188
+ }
189
+
190
+ // Populate the search results and render to the page
191
+ function populateResults ( results ) {
192
+ document . querySelector ( '#search-results' ) . innerHTML += results . reduce (
193
+ ( acc , result ) => {
194
+ return (
195
+ acc +
196
+ document . querySelector ( `[data-registry-id="${ result . _key } "]` ) . outerHTML
197
+ ) ;
198
+ } ,
199
+ '' ,
200
+ ) ;
201
+ }
202
+
194
203
function setInput ( key , value ) {
195
204
document . getElementById ( `input-${ key } ` ) . value = value ;
196
205
var queryParams = new URLSearchParams ( window . location . search ) ;
0 commit comments