179 lines
5.4 KiB
JavaScript
179 lines
5.4 KiB
JavaScript
|
/*************************************************
|
||
|
* academia
|
||
|
* https://github.com/gcushen/hugo-academia
|
||
|
*
|
||
|
* In-built Fuse based search algorithm.
|
||
|
**************************************************/
|
||
|
|
||
|
/* ---------------------------------------------------------------------------
|
||
|
* Configuration.
|
||
|
* --------------------------------------------------------------------------- */
|
||
|
|
||
|
// Configure Fuse.
|
||
|
let fuseOptions = {
|
||
|
shouldSort: true,
|
||
|
includeMatches: true,
|
||
|
tokenize: true,
|
||
|
threshold: 0.0,
|
||
|
location: 0,
|
||
|
distance: 100,
|
||
|
maxPatternLength: 32,
|
||
|
minMatchCharLength: 2,
|
||
|
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.pushState) {
|
||
|
window.history.pushState({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();
|
||
|
}
|
||
|
|
||
|
// 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();
|
||
|
searchacademia(query, fuse);
|
||
|
let newURL = window.location.protocol + "//" + window.location.host + window.location.pathname + '?q=' + encodeURIComponent(query) + window.location.hash;
|
||
|
updateURL(newURL);
|
||
|
}
|
||
|
|
||
|
// Perform search.
|
||
|
function searchacademia(query, fuse) {
|
||
|
let results = fuse.search(query);
|
||
|
// console.log({"results": results});
|
||
|
|
||
|
if (results.length > 0) {
|
||
|
$('#search-hits').append('<h3 class="mt-0">' + results.length + ' ' + i18n.results + '</h3>');
|
||
|
parseResults(query, results);
|
||
|
} else {
|
||
|
$('#search-hits').append('<div class="search-no-results">' + i18n.no_results + '</div>');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Parse search results.
|
||
|
function parseResults(query, results) {
|
||
|
$.each( results, function(key, value) {
|
||
|
let content = value.item.content;
|
||
|
let snippet = "";
|
||
|
let snippetHighlights = [];
|
||
|
|
||
|
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 += content.substring(0, summaryLength*2);
|
||
|
}
|
||
|
|
||
|
// Load template.
|
||
|
var template = $('#search-hit-fuse-template').html();
|
||
|
|
||
|
// Localize content types.
|
||
|
let content_key = value.item.section;
|
||
|
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 academia's in-built search is enabled and Fuse loaded, then initialize it.
|
||
|
if (typeof Fuse === 'function') {
|
||
|
// Wait for Fuse to initialize.
|
||
|
$.getJSON(search_index_filename, function (search_index) {
|
||
|
let fuse = new Fuse(search_index, fuseOptions);
|
||
|
|
||
|
// On page load, check for search query in URL.
|
||
|
if (query = getSearchQuery('q')) {
|
||
|
$("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));
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
}
|