/*************************************************
* Wowchemy
* https://github.com/wowchemy/wowchemy-hugo-themes
*
* In-built Fuse based search algorithm.
**************************************************/
import {search_config, i18n, content_type} from '@params';
/* ---------------------------------------------------------------------------
* Configuration.
* --------------------------------------------------------------------------- */
// Configure Fuse.
let fuseOptions = {
shouldSort: true,
includeMatches: true,
tokenize: true,
threshold: search_config.threshold, // Set to ~0.3 for parsing diacritics and CJK languages.
location: 0,
distance: 100,
maxPatternLength: 32,
minMatchCharLength: search_config.minLength, // Set to 1 for parsing CJK languages.
keys: [
{name: 'title', weight: 0.99} /* 1.0 doesn't work o_O */,
{name: 'summary', weight: 0.6},
{name: 'authors', weight: 0.5},
{name: 'content', weight: 0.2},
{name: 'tags', weight: 0.5},
{name: 'categories', weight: 0.5},
],
};
// Configure summary.
let summaryLength = 60;
/* ---------------------------------------------------------------------------
* Functions.
* --------------------------------------------------------------------------- */
// Get query from URI.
function getSearchQuery(name) {
return decodeURIComponent((location.search.split(name + '=')[1] || '').split('&')[0]).replace(/\+/g, ' ');
}
// Set query in URI without reloading the page.
function updateURL(url) {
if (history.replaceState) {
window.history.replaceState({path: url}, '', url);
}
}
// Pre-process new search query.
function initSearch(force, fuse) {
let query = $('#search-query').val();
// If query deleted, clear results.
if (query.length < 1) {
$('#search-hits').empty();
$('#search-common-queries').show();
}
// Check for timer event (enter key not pressed) and query less than minimum length required.
if (!force && query.length < fuseOptions.minMatchCharLength) return;
// Do search.
$('#search-hits').empty();
$('#search-common-queries').hide();
searchAcademic(query, fuse);
let newURL =
window.location.protocol +
'//' +
window.location.host +
window.location.pathname +
'?q=' +
encodeURIComponent(query) +
window.location.hash;
updateURL(newURL);
}
// Perform search.
function searchAcademic(query, fuse) {
let results = fuse.search(query);
// console.log({"results": results});
if (results.length > 0) {
$('#search-hits').append('
' + results.length + ' ' + i18n.results + '
');
parseResults(query, results);
} else {
$('#search-hits').append('' + i18n.no_results + '
');
}
}
// Parse search results.
function parseResults(query, results) {
$.each(results, function (key, value) {
let content_key = value.item.section;
let content = '';
let snippet = '';
let snippetHighlights = [];
// Show abstract in results for content types where the abstract is often the primary content.
if (['publication', 'event'].includes(content_key)) {
content = value.item.summary;
} else {
content = value.item.content;
}
if (fuseOptions.tokenize) {
snippetHighlights.push(query);
} else {
$.each(value.matches, function (matchKey, matchValue) {
if (matchValue.key == 'content') {
let start = matchValue.indices[0][0] - summaryLength > 0 ? matchValue.indices[0][0] - summaryLength : 0;
let end =
matchValue.indices[0][1] + summaryLength < content.length
? matchValue.indices[0][1] + summaryLength
: content.length;
snippet += content.substring(start, end);
snippetHighlights.push(
matchValue.value.substring(
matchValue.indices[0][0],
matchValue.indices[0][1] - matchValue.indices[0][0] + 1,
),
);
}
});
}
if (snippet.length < 1) {
snippet += value.item.summary; // Alternative fallback: `content.substring(0, summaryLength*2);`
}
// Load template.
let template = $('#search-hit-fuse-template').html();
// Localize content types.
if (content_key in content_type) {
content_key = content_type[content_key];
}
// Parse template.
let templateData = {
key: key,
title: value.item.title,
type: content_key,
relpermalink: value.item.relpermalink,
snippet: snippet,
};
let output = render(template, templateData);
$('#search-hits').append(output);
// Highlight search terms in result.
$.each(snippetHighlights, function (hlKey, hlValue) {
$('#summary-' + key).mark(hlValue);
});
});
}
function render(template, data) {
// Replace placeholders with their values.
let key, find, re;
for (key in data) {
find = '\\{\\{\\s*' + key + '\\s*\\}\\}'; // Expect placeholder in the form `{{x}}`.
re = new RegExp(find, 'g');
template = template.replace(re, data[key]);
}
return template;
}
/* ---------------------------------------------------------------------------
* Initialize.
* --------------------------------------------------------------------------- */
// If Academic's in-built search is enabled and Fuse loaded, then initialize it.
if (typeof Fuse === 'function') {
// Wait for Fuse to initialize.
$.getJSON(search_config.indexURI, function (search_index) {
let fuse = new Fuse(search_index, fuseOptions);
// On page load, check for search query in URL.
let query = getSearchQuery('q');
if (query) {
$('body').addClass('searching');
$('.search-results').css({opacity: 0, visibility: 'visible'}).animate({opacity: 1}, 200);
$('#search-query').val(query);
$('#search-query').focus();
initSearch(true, fuse);
}
// On search box key up, process query.
$('#search-query').keyup(function (e) {
clearTimeout($.data(this, 'searchTimer')); // Ensure only one timer runs!
if (e.keyCode == 13) {
initSearch(true, fuse);
} else {
$(this).data(
'searchTimer',
setTimeout(function () {
initSearch(false, fuse);
}, 250),
);
}
});
});
}