2020-03-09 03:41:52 +03:00
/* global Fuse, Mark */
2019-11-13 21:03:18 +03:00
function ready ( fn ) {
2020-03-09 03:41:52 +03:00
if ( document . readyState !== 'loading' ) {
fn ( ) ;
} else {
document . addEventListener ( 'DOMContentLoaded' , fn ) ;
}
2019-11-13 21:03:18 +03:00
}
ready ( doSearch ) ;
const summaryInclude = 60 ;
const fuseOptions = {
2020-03-09 03:41:52 +03:00
shouldSort : true ,
includeMatches : true ,
matchAllTokens : true ,
2021-11-22 11:19:01 +03:00
threshold : 0 , // for parsing diacritics
2020-03-09 03:41:52 +03:00
tokenize : true ,
location : 0 ,
distance : 100 ,
maxPatternLength : 32 ,
minMatchCharLength : 1 ,
keys : [ {
name : 'title' ,
weight : 0.8
} ,
{
name : 'contents' ,
weight : 0.5
} ,
{
name : 'tags' ,
weight : 0.3
} ,
{
name : 'categories' ,
weight : 0.3
}
]
2019-11-13 21:03:18 +03:00
} ;
function param ( name ) {
2020-03-09 03:41:52 +03:00
return decodeURIComponent ( ( window . location . search . split ( ` ${ name } = ` ) [ 1 ] || '' ) . split ( '&' ) [ 0 ] ) . replace ( /\+/g , ' ' ) ;
2019-11-13 21:03:18 +03:00
}
2020-03-09 03:41:52 +03:00
const searchQuery = param ( 's' ) ;
2019-11-13 21:03:18 +03:00
function doSearch ( ) {
2020-03-09 03:41:52 +03:00
if ( searchQuery ) {
document . getElementById ( 'search-query' ) . value = searchQuery ;
executeSearch ( searchQuery ) ;
} else {
const para = document . createElement ( 'P' ) ;
2021-11-22 11:19:01 +03:00
para . textContent = 'Please enter a word or phrase above' ;
2023-05-09 05:35:49 +03:00
document . getElementById ( 'search-results' ) . append ( para ) ;
2020-03-09 03:41:52 +03:00
}
2019-11-13 21:03:18 +03:00
}
function getJSON ( url , fn ) {
2020-03-09 03:41:52 +03:00
const request = new XMLHttpRequest ( ) ;
request . open ( 'GET' , url , true ) ;
2021-11-22 11:19:01 +03:00
request . addEventListener ( 'load' , ( ) => {
2020-03-09 03:41:52 +03:00
if ( request . status >= 200 && request . status < 400 ) {
const data = JSON . parse ( request . responseText ) ;
fn ( data ) ;
} else {
console . error ( ` Target reached on ${ url } with error ${ request . status } ` ) ;
}
2021-11-22 11:19:01 +03:00
} ) ;
request . addEventListener ( 'error' , ( ) => {
2020-03-09 03:41:52 +03:00
console . error ( ` Connection error ${ request . status } ` ) ;
2021-11-22 11:19:01 +03:00
} ) ;
2020-03-09 03:41:52 +03:00
request . send ( ) ;
2019-11-13 21:03:18 +03:00
}
function executeSearch ( searchQuery ) {
2020-03-09 03:41:52 +03:00
getJSON ( ` / ${ document . LANG } /index.json ` , ( data ) => {
const pages = data ;
const fuse = new Fuse ( pages , fuseOptions ) ;
const result = fuse . search ( searchQuery ) ;
document . getElementById ( 'search-results' ) . innerHTML = '' ;
if ( result . length > 0 ) {
populateResults ( result ) ;
} else {
const para = document . createElement ( 'P' ) ;
2021-11-22 11:19:01 +03:00
para . textContent = 'No matches found' ;
2023-05-09 05:35:49 +03:00
document . getElementById ( 'search-results' ) . append ( para ) ;
2020-03-09 03:41:52 +03:00
}
} ) ;
2019-11-13 21:03:18 +03:00
}
function populateResults ( result ) {
2021-11-22 11:19:01 +03:00
for ( const [ key , value ] of result . entries ( ) ) {
2020-03-09 03:41:52 +03:00
const content = value . item . contents ;
let snippet = '' ;
const snippetHighlights = [ ] ;
if ( fuseOptions . tokenize ) {
snippetHighlights . push ( searchQuery ) ;
2021-11-22 11:19:01 +03:00
for ( const mvalue of value . matches ) {
2020-03-09 03:41:52 +03:00
if ( mvalue . key === 'tags' || mvalue . key === 'categories' ) {
snippetHighlights . push ( mvalue . value ) ;
} else if ( mvalue . key === 'contents' ) {
const ind = content . toLowerCase ( ) . indexOf ( searchQuery . toLowerCase ( ) ) ;
const start = ind - summaryInclude > 0 ? ind - summaryInclude : 0 ;
const end = ind + searchQuery . length + summaryInclude < content . length ? ind + searchQuery . length + summaryInclude : content . length ;
snippet += content . substring ( start , end ) ;
if ( ind > - 1 ) {
snippetHighlights . push ( content . substring ( ind , ind + searchQuery . length ) ) ;
} else {
snippetHighlights . push ( mvalue . value . substring ( mvalue . indices [ 0 ] [ 0 ] , mvalue . indices [ 0 ] [ 1 ] - mvalue . indices [ 0 ] [ 0 ] + 1 ) ) ;
}
2019-11-13 21:03:18 +03:00
}
2021-11-22 11:19:01 +03:00
}
2020-03-09 03:41:52 +03:00
}
2019-11-13 21:03:18 +03:00
2020-03-09 03:41:52 +03:00
if ( snippet . length < 1 ) {
snippet += content . substring ( 0 , summaryInclude * 2 ) ;
}
// pull template from hugo template definition
const templateDefinition = document . getElementById ( 'search-result-template' ) . innerHTML ;
// replace values
const output = render ( templateDefinition , {
key ,
title : value . item . title ,
link : value . item . permalink ,
tags : value . item . tags ,
categories : value . item . categories ,
snippet
} ) ;
2023-05-09 05:35:49 +03:00
document . getElementById ( 'search-results' ) . append ( htmlToElement ( output ) ) ;
2019-11-13 21:03:18 +03:00
2021-11-22 11:19:01 +03:00
for ( const snipvalue of snippetHighlights ) {
2020-03-09 03:41:52 +03:00
new Mark ( document . getElementById ( ` summary- ${ key } ` ) ) . mark ( snipvalue ) ;
2021-11-22 11:19:01 +03:00
}
}
2019-11-13 21:03:18 +03:00
}
function render ( templateString , data ) {
2020-03-09 03:41:52 +03:00
let conditionalMatches , copy ;
2023-04-27 05:08:16 +03:00
const conditionalPattern = /\$\{\s*isset ([a-zA-Z]*) \s*\}(.*)\$\{\s*end\s*\}/g ;
// since loop below depends on re.lastIndex, we use a copy to capture any manipulations whilst inside the loop
2020-03-09 03:41:52 +03:00
copy = templateString ;
while ( ( conditionalMatches = conditionalPattern . exec ( templateString ) ) !== null ) {
if ( data [ conditionalMatches [ 1 ] ] ) {
// valid key, remove conditionals, leave content.
copy = copy . replace ( conditionalMatches [ 0 ] , conditionalMatches [ 2 ] ) ;
} else {
// not valid, remove entire section
copy = copy . replace ( conditionalMatches [ 0 ] , '' ) ;
2019-11-13 21:03:18 +03:00
}
2020-03-09 03:41:52 +03:00
}
templateString = copy ;
// now any conditionals removed we can do simple substitution
let key , find , re ;
for ( key of Object . keys ( data ) ) {
find = ` \\ $ \\ { \\ s* ${ key } \\ s* \\ } ` ;
re = new RegExp ( find , 'g' ) ;
templateString = templateString . replace ( re , data [ key ] ) ;
}
return templateString ;
2019-11-13 21:03:18 +03:00
}
/ * *
* By Mark Amery : https : //stackoverflow.com/a/35385518
* @ param { String } HTML representing a single element
* @ return { Element }
* /
function htmlToElement ( html ) {
2020-03-09 03:41:52 +03:00
const template = document . createElement ( 'template' ) ;
html = html . trim ( ) ; // Never return a text node of whitespace as the result
template . innerHTML = html ;
return template . content . firstChild ;
2019-11-13 21:03:18 +03:00
}