1+ <!doctype html>
2+ < html lang ="en ">
3+ < head >
4+ < meta charset ="utf-8 ">
5+ < meta name ="viewport " content ="width=device-width, initial-scale=1, minimum-scale=1 ">
6+ < title > Search</ title >
7+ < link rel ="stylesheet " href ="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css " integrity ="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA== " crossorigin >
8+ < link rel ="stylesheet " href ="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/typography.min.css " integrity ="sha512-Y1DYSb995BAfxobCkKepB1BqJJTPrOp3zPL74AWFugHHmmdcvO+C48WLrUOlhGMc0QG7AE3f7gmvvcrmX2fDoA== " crossorigin >
9+ < style >
10+ body {margin : 0 1em ;}
11+ footer ,
12+ # search-status {
13+ font : 14px normal;
14+ color : grey;
15+ }
16+
17+ footer {text-align : right;}
18+
19+ a {
20+ color : # 058 ;
21+ text-decoration : none;
22+ transition : color .3s ease-in-out;
23+ }
24+ a : hover {color : # e82 ;}
25+
26+ li {padding-top : 10px ;}
27+ </ style >
28+ < base target ="_parent ">
29+ < script async src ='https://www.googletagmanager.com/gtag/js?id=G-QJH7PLMB12 '> </ script > < script > window . dataLayer = window . dataLayer || [ ] ; function gtag ( ) { dataLayer . push ( arguments ) ; } gtag ( 'js' , new Date ( ) ) ; gtag ( 'config' , 'G-QJH7PLMB12' ) ; </ script > < script async src ="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-2900001379782823 " crossorigin > </ script > </ head >
30+ < body >
31+ < noscript >
32+ JavaScript is not supported/enabled in your browser. The search feature won't work.
33+ </ noscript >
34+ < main >
35+ < h3 id ="search-status "> </ h3 >
36+ < ul id ="search-results "> </ ul >
37+ </ main >
38+ < footer >
39+ < p > Search results provided by < a href ="https://lunrjs.com "> Lunr.js</ a > </ p >
40+ </ footer >
41+
42+ < script src ="index.js "> </ script >
43+ < script src ="https://cdnjs.cloudflare.com/ajax/libs/lunr.js/2.3.9/lunr.min.js " integrity ="sha512-4xUl/d6D6THrAnXAwGajXkoWaeMNwEKK4iNfq5DotEbLPAfk6FSxSP3ydNxqDgCw1c/0Z1Jg6L8h2j+++9BZmg== " crossorigin > </ script >
44+ < script >
45+ 'use strict' ;
46+
47+ const lunr_index = build_index ( ) ;
48+ search ( decodeURIComponent ( new URL ( window . location ) . hash . substring ( 1 ) ) ) ;
49+
50+ function set_status ( message ) {
51+ document . getElementById ( 'search-status' ) . textContent = message ;
52+ }
53+
54+ async function build_index ( ) {
55+ try {
56+ return lunr . Index . load ( _expand ( INDEX ) ) ; // Prebuilt index
57+ } catch {
58+ window . DOCS = INDEX ;
59+ return lunr ( function ( ) {
60+ this . ref ( 'i' ) ;
61+ this . field ( 'name' , { boost : 10 } ) ;
62+ this . field ( 'ref' , { boost : 5 } ) ;
63+ this . field ( 'doc' ) ;
64+ this . metadataWhitelist = [ 'position' ] ;
65+
66+ INDEX . forEach ( ( doc , i ) => {
67+ const parts = doc . ref . split ( '.' ) ;
68+ doc [ 'name' ] = parts [ parts . length - 1 ] ;
69+ doc [ 'i' ] = i ;
70+
71+ this . add ( doc ) ;
72+ } , this ) ;
73+ } ) ;
74+ }
75+ }
76+
77+ function _expand ( compact ) {
78+ // https://john-millikin.com/compacting-lunr-search-indices
79+ const fields = compact [ "fields" ] ;
80+ const fieldVectors = compact [ "fieldVectors" ] . map ( ( item ) => {
81+ const id = item [ 0 ] ;
82+ const vectors = item [ 1 ] ;
83+ let prev = null ;
84+ const expanded = vectors . map ( ( v , ii ) => {
85+ if ( ii % 2 === 0 ) {
86+ if ( v === null ) {
87+ v = prev + 1 ;
88+ }
89+ prev = v ;
90+ }
91+ return v ;
92+ } ) ;
93+ return [ id , expanded ] ;
94+ } ) ;
95+ const invertedIndex = compact [ "invertedIndex" ] . map ( ( item , itemIdx ) => {
96+ const token = item [ 0 ] ;
97+ const fieldMap = { "_index" : itemIdx } ;
98+ fields . forEach ( ( field , fieldIdx ) => {
99+ const matches = { } ;
100+ let docRef = null ;
101+ item [ fieldIdx + 1 ] . forEach ( ( v , ii ) => {
102+ if ( ii % 2 === 0 ) {
103+ docRef = fieldVectors [ v ] [ 0 ] . slice ( ( field + '/' ) . length ) ;
104+ } else {
105+ matches [ docRef ] = v ;
106+ }
107+ } ) ;
108+ fieldMap [ field ] = matches ;
109+ } )
110+ return [ token , fieldMap ] ;
111+ } ) ;
112+ invertedIndex . sort ( ( a , b ) => {
113+ if ( a [ 0 ] < b [ 0 ] ) {
114+ return - 1 ;
115+ }
116+ if ( a [ 0 ] > b [ 0 ] ) {
117+ return 1 ;
118+ }
119+ return 0 ;
120+ } ) ;
121+ return {
122+ "version" : compact [ "version" ] ,
123+ "fields" : fields ,
124+ "fieldVectors" : fieldVectors ,
125+ "invertedIndex" : invertedIndex ,
126+ "pipeline" : compact [ "pipeline" ] ,
127+ } ;
128+ }
129+
130+ function search ( query ) {
131+ _search ( query ) . catch ( err => {
132+ set_status ( "Something went wrong. See development console for details." ) ;
133+ throw err ;
134+ } ) ;
135+ }
136+
137+ async function _search ( query ) {
138+ if ( ! query ) {
139+ set_status ( 'No query provided, so there is nothing to search.' ) ;
140+ return ;
141+ }
142+
143+ const fuzziness = 1 ;
144+ if ( fuzziness ) {
145+ query = query . split ( / \s + / )
146+ . map ( str => str . includes ( '~' ) ? str : str + '~' + fuzziness ) . join ( ' ' ) ;
147+ }
148+
149+ const results = ( await lunr_index ) . search ( query ) ;
150+ if ( ! results . length ) {
151+ set_status ( 'No results match your query.' ) ;
152+ return ;
153+ }
154+
155+ set_status (
156+ 'Search for "' + encodeURIComponent ( query ) . replace ( '%20' , ' ' ) + '" yielded ' + results . length + ' ' +
157+ ( results . length === 1 ? 'result' : 'results' ) + ':' ) ;
158+
159+ results . forEach ( function ( result ) {
160+ const dobj = DOCS [ parseInt ( result . ref ) ] ;
161+ const docstring = dobj . doc ;
162+ const url = URLS [ dobj . url ] + '#' + dobj . ref ;
163+ const pretty_name = dobj . ref + ( dobj . func ? '()' : '' ) ;
164+ let text = '' ;
165+ if ( docstring ) {
166+ text = Object . values ( result . matchData . metadata )
167+ . filter ( ( { doc} ) => doc !== undefined )
168+ . map ( ( { doc : { position} } ) => {
169+ return position . map ( ( [ start , length ] ) => {
170+ const PAD_CHARS = 30 ;
171+ const end = start + length ;
172+ return [
173+ start ,
174+ ( start - PAD_CHARS > 0 ? '…' : '' ) +
175+ docstring . substring ( start - PAD_CHARS , start ) +
176+ '<mark>' + docstring . slice ( start , end ) + '</mark>' +
177+ docstring . substring ( end , end + PAD_CHARS ) +
178+ ( end + PAD_CHARS < docstring . length ? '…' : '' )
179+ ] ;
180+ } ) ;
181+ } )
182+ . flat ( )
183+ . sort ( ( [ pos1 , ] , [ pos2 , ] ) => pos1 - pos2 )
184+ . map ( ( [ , text ] ) => text )
185+ . join ( '' )
186+ . replace ( / … … / g, '…' ) ;
187+ }
188+
189+ if ( text )
190+ text = '<div>' + text + '</div>' ;
191+ text = '<a href="' + url + '"><code>' + pretty_name + '</code></a>' + text ;
192+
193+ const li = document . createElement ( 'li' ) ;
194+ li . innerHTML = text ;
195+ document . getElementById ( 'search-results' ) . appendChild ( li ) ;
196+ } ) ;
197+ }
198+ </ script >
199+ </ body >
0 commit comments