2021-10-01 05:23:35 +00:00
( ( ) => {
2021-09-24 04:08:37 +00:00
let searchField = document . querySelector ( "#search" ) ;
2021-10-01 05:23:35 +00:00
if ( searchField === null )
return ;
2021-09-24 04:08:37 +00:00
let client = new XMLHttpRequest ( ) ;
2021-08-08 01:43:23 +00:00
client . open ( "GET" , "/pages.json" ) ;
2021-09-14 03:55:02 +00:00
client . onreadystatechange = ( ) => {
if ( client . readyState === 4 )
fuzzyInit ( client . responseText ) ;
2021-06-03 01:57:01 +00:00
}
client . send ( ) ;
2021-09-24 04:08:37 +00:00
searchField . focus ( ) ;
2021-10-01 05:23:35 +00:00
} ) ( ) ;
2021-06-03 01:57:01 +00:00
2021-09-14 03:55:02 +00:00
function fuzzyInit ( pagesFileName ) {
if ( pagesFileName == "" )
2021-06-03 01:57:01 +00:00
return ;
2021-09-24 04:08:37 +00:00
let searchField = document . querySelector ( "#search" ) ;
let resultBlock = document . querySelector ( "#results" ) ;
if ( searchField === null || resultBlock === null )
return ;
2021-06-03 01:57:01 +00:00
var pages ;
try {
2021-09-14 03:55:02 +00:00
pages = JSON . parse ( pagesFileName ) ;
2021-06-03 01:57:01 +00:00
} catch ( e ) {
2021-09-14 03:55:02 +00:00
console . error ( e ) ;
document . body . innerHTML = e . message ;
2021-06-03 01:57:01 +00:00
return ;
}
pages . sort ( ) ;
2021-09-24 04:08:37 +00:00
searchField . addEventListener ( "keyup" , ( e ) => {
2021-10-01 05:23:35 +00:00
let searchValue = searchField . value ? searchField . value . toLowerCase ( ) : "" ;
2021-09-24 04:08:37 +00:00
2021-10-01 05:23:35 +00:00
if ( e . code === "Enter" ) {
if ( resultBlock . childNodes === null
|| resultBlock . childNodes [ 0 ] === null
|| resultBlock . childNodes [ 0 ] . href === undefined )
2021-09-24 04:08:37 +00:00
return ;
2021-09-14 03:55:02 +00:00
2021-10-01 05:23:35 +00:00
window . location = resultBlock . childNodes [ 0 ] . href ;
return ;
}
2021-06-03 01:57:01 +00:00
2021-10-01 05:23:35 +00:00
// help
if ( searchValue === "?" || searchValue === "help" ) {
resultBlock . innerHTML = "<h2>Help</h2>Enter a page or directory name.<br>If do not know any, clear the search field to list everything.<br> Using the <code>Enter</code> key would take you to the first page in list, if the list is not empty.<br>Alternatively, use the <code>Up</code> and <code>Down</code> arrow keys to select the page you want and use the <code>Enter</code> key.<br>Use <code>Backspace</code> to go back to the search."
return ;
}
2021-06-03 01:57:01 +00:00
2021-10-01 05:23:35 +00:00
let results = [ ] ;
for ( const [ i , page ] of pages . entries ( ) ) {
ret = fuzzySearch ( page . title , searchValue ) ;
if ( ret === null )
continue ;
results . push ( { formatted : ret . formatted , link : page . link , score : ret . score } ) ;
}
2021-06-03 01:57:01 +00:00
2021-10-01 05:23:35 +00:00
results . sort ( ( x , y ) => { return x . score - y . score } ) ;
2021-06-03 01:57:01 +00:00
2021-10-01 05:23:35 +00:00
resultBlock . innerHTML = "" ;
for ( const res of results ) {
linkBlock = document . createElement ( "a" ) ;
linkBlock . classList . add ( "hyperlink" ) ;
linkBlock . href = res . link ;
linkBlock . innerHTML = ` <div class="name"> ${ res . formatted } </div><div class="link"> ${ res . link } </div> ` ;
resultBlock . appendChild ( linkBlock ) ;
2021-06-03 01:57:01 +00:00
}
2021-10-01 05:23:35 +00:00
if ( results . length <= 0 )
resultBlock . innerHTML = "Unknown command or no matching pages found."
} ) ;
2021-09-24 04:08:37 +00:00
2021-09-24 05:34:34 +00:00
document . body . addEventListener ( "keydown" , ( e ) => {
2021-10-01 05:23:35 +00:00
if ( e . code === "Backspace" ) {
searchField . focus ( ) ;
}
2021-09-24 04:08:37 +00:00
if ( e . code === "ArrowDown" || e . code === "ArrowUp" ) {
if ( resultBlock . childNodes === null )
return ;
resultNodes = resultBlock . childNodes ;
if ( resultNodes . length <= 1 )
return ;
let currNode = document . activeElement ;
if ( searchField === currNode ) {
2021-09-24 05:34:34 +00:00
e . preventDefault ( ) ;
2021-09-24 04:08:37 +00:00
if ( e . code === "ArrowDown" )
resultNodes [ 0 ] . focus ( ) ;
else
resultNodes [ resultNodes . length - 1 ] . focus ( ) ;
return ;
}
if ( Array . from ( resultNodes ) . indexOf ( currNode ) < 0 )
return ;
2021-09-24 05:34:34 +00:00
e . preventDefault ( ) ;
2021-09-24 04:08:37 +00:00
if ( e . code === "ArrowDown" )
if ( currNode . nextElementSibling === null )
searchField . focus ( ) ;
else
currNode . nextElementSibling . focus ( ) ;
else if ( e . code === "ArrowUp" )
if ( currNode . previousElementSibling === null )
searchField . focus ( ) ;
else
currNode . previousElementSibling . focus ( ) ;
return ;
}
} ) ;
2021-06-03 01:57:01 +00:00
}
2021-09-24 04:08:37 +00:00
function fuzzySearch ( findIn , find ) {
let search = find . replace ( /\s/g , "" ) ;
2021-06-03 01:57:01 +00:00
search = search . toLowerCase ( ) ;
2021-09-24 04:08:37 +00:00
let tokens = findIn . split ( '' ) ;
2021-06-03 01:57:01 +00:00
let pc = 0 ;
let score = 0 ;
for ( const [ i , ch ] of tokens . entries ( ) ) {
2021-09-24 04:08:37 +00:00
if ( ch . toLowerCase ( ) === search [ pc ] ) {
2021-06-03 01:57:01 +00:00
score += i - pc ;
tokens [ i ] = ` <span class="highlight"> ${ ch } </span> ` ;
pc ++ ;
if ( search . length < pc )
2021-09-14 03:55:02 +00:00
return null ;
2021-06-03 01:57:01 +00:00
}
}
2021-09-24 04:08:37 +00:00
if ( search . length === pc )
return { formatted : tokens . join ( '' ) , score : ( score / search . length ) } ;
2021-06-03 01:57:01 +00:00
2021-09-24 04:08:37 +00:00
return null ;
2021-06-03 01:57:01 +00:00
}