@@ -5262,11 +5262,15 @@ | |||
* https://gomakethings.com/how-to-create-a-search-page-for-a-static-website-with-vanilla-js/ | |||
* https://gomakethings.com/how-to-update-the-url-of-a-page-without-causing-a-reload-using-vanilla-javascript/ | |||
*/ | |||
(function () { | |||
;(function () { | |||
// Retrieving the search index and stopwords from JSON. | |||
// See https://v8.dev/blog/cost-of-javascript-2019#json | |||
let searchIndex = JSON.parse(document.getElementById('search-index').textContent) | |||
let stopWords = JSON.parse(document.getElementById('search-stop-words').textContent) | |||
let searchIndex = JSON.parse( | |||
document.getElementById('search-index').textContent | |||
) | |||
let stopWords = JSON.parse( | |||
document.getElementById('search-stop-words').textContent | |||
) | |||
// Get the DOM elements | |||
let form = document.querySelector('#form-search') | |||
@@ -5276,7 +5280,16 @@ | |||
let searchResultTemplate = document.querySelector('#search-result') | |||
// Make sure required content exists | |||
if (!form || !input || !resultList || !searchStatus || !searchIndex || !stopWords || !searchResultTemplate) return | |||
if ( | |||
!form || | |||
!input || | |||
!resultList || | |||
!searchStatus || | |||
!searchIndex || | |||
!stopWords || | |||
!searchResultTemplate | |||
) | |||
return | |||
// Create a submit handler | |||
form.addEventListener('submit', function (event) { | |||
@@ -5290,7 +5303,7 @@ | |||
}) | |||
// Create a reset handler | |||
form.addEventListener('reset', function() { | |||
form.addEventListener('reset', function () { | |||
search('') | |||
searchStatus.innerHTML = '' | |||
}) | |||
@@ -5301,7 +5314,7 @@ | |||
/** | |||
* If there's a query string search term, search it on page load | |||
*/ | |||
function onload () { | |||
function onload() { | |||
let query = new URLSearchParams(window.location.search).get('s') | |||
if (!query) return | |||
input.value = query | |||
@@ -5312,48 +5325,54 @@ | |||
* Search for matches | |||
* @param {String} query The term to search for | |||
*/ | |||
function search (query) { | |||
function search(query) { | |||
// Create a regex for each query | |||
let regMap = query.toLowerCase().split(' ').filter(function (word) { | |||
return word.length && !stopWords.includes(word) | |||
}).map(function (word) { | |||
return new RegExp(word, 'i') | |||
}) | |||
let regMap = query | |||
.toLowerCase() | |||
.split(' ') | |||
.filter(function (word) { | |||
return word.length && !stopWords.includes(word) | |||
}) | |||
.map(function (word) { | |||
return new RegExp(word, 'i') | |||
}) | |||
// Get and sort the results | |||
let results = searchIndex.reduce(function (results, article, index) { | |||
// Setup priority count | |||
let priority = 0 | |||
let results = searchIndex | |||
.reduce(function (results, article, index) { | |||
// Setup priority count | |||
let priority = 0 | |||
// Assign priority | |||
for (let reg of regMap) { | |||
if (reg.test(article.title)) { priority += 100 } | |||
let occurences = article.content.match(reg) | |||
if (occurences) { priority += occurences.length } | |||
} | |||
// If any matches, push to results | |||
if (priority > 0) { | |||
results.push({ | |||
priority: priority, | |||
article: article | |||
}) | |||
} | |||
// Assign priority | |||
for (let reg of regMap) { | |||
if (reg.test(article.title)) { | |||
priority += 100 | |||
} | |||
let occurences = article.content.match(reg) | |||
if (occurences) { | |||
priority += occurences.length | |||
} | |||
} | |||
return results | |||
// If any matches, push to results | |||
if (priority > 0) { | |||
results.push({ | |||
priority: priority, | |||
article: article, | |||
}) | |||
} | |||
}, []).sort(function (article1, article2) { | |||
return article2.priority - article1.priority | |||
}) | |||
return results | |||
}, []) | |||
.sort(function (article1, article2) { | |||
return article2.priority - article1.priority | |||
}) | |||
// Display the results | |||
showResults(results, regMap) | |||
// Update the URL | |||
updateURL(query) | |||
} | |||
/** | |||
@@ -5361,20 +5380,22 @@ | |||
* @param {Array} results The results to display | |||
* @param {List} regMap Regular expressions for the highlights | |||
*/ | |||
function showResults (results, regMap) { | |||
function showResults(results, regMap) { | |||
let status = 'Aucune publication n’a été trouvée 😢' | |||
let searchResults = '' | |||
if (results.length) { | |||
const plural = results.length > 1 ? 's' : '' | |||
status = `${results.length} publication${plural} trouvée${plural} 🙌` | |||
searchResults = results.map(function (result) { | |||
return interpolate(searchResultTemplate.innerHTML, { | |||
url: result.article.url, | |||
title: highlightText(result.article.title, regMap), | |||
date: result.article.date, | |||
content: highlightText(result.article.content, regMap), | |||
searchResults = results | |||
.map(function (result) { | |||
return interpolate(searchResultTemplate.innerHTML, { | |||
url: result.article.url, | |||
title: highlightText(result.article.title, regMap), | |||
date: result.article.date, | |||
content: highlightText(result.article.content, regMap), | |||
}) | |||
}) | |||
}).join('') | |||
.join('') | |||
} | |||
searchStatus.innerHTML = status | |||
resultList.innerHTML = searchResults | |||
@@ -5388,10 +5409,10 @@ | |||
* @param {Object} params The parameters | |||
* @return {String} The interpolated string | |||
*/ | |||
function interpolate (str, params) { | |||
let names = Object.keys(params); | |||
let vals = Object.values(params); | |||
return new Function(...names, `return \`${str}\``)(...vals); | |||
function interpolate(str, params) { | |||
let names = Object.keys(params) | |||
let vals = Object.values(params) | |||
return new Function(...names, `return \`${str}\``)(...vals) | |||
} | |||
/** | |||
@@ -5409,7 +5430,9 @@ | |||
let extracts = [] | |||
for (let reg of regMap) { | |||
const index = text.search(reg) | |||
if (index === -1) { continue } | |||
if (index === -1) { | |||
continue | |||
} | |||
let extract = text.substring( | |||
index - extractBoundariesSize, | |||
index + reg.source.length + extractBoundariesSize | |||
@@ -5417,9 +5440,10 @@ | |||
// TODISCUSS: we replace with the source but in case there is | |||
// an uppercase letter it will disappear from the extract | |||
// (is that confusing or closer to what is expected?) | |||
extract = extract.replace(reg,`<mark>${reg.source}</mark>`) | |||
extract = extract.replace(reg, `<mark>${reg.source}</mark>`) | |||
const prefixEllipsis = index - extractBoundariesSize >= 0 ? '…' : '' | |||
const suffixEllipsis = index + extractBoundariesSize <= textLength ? '…' : '' | |||
const suffixEllipsis = | |||
index + extractBoundariesSize <= textLength ? '…' : '' | |||
extracts.push(`${prefixEllipsis}${extract}${suffixEllipsis}`) | |||
} | |||
if (!extracts.length && textLength < 200) { | |||
@@ -5433,7 +5457,7 @@ | |||
* Update the URL with a query string for the search string | |||
* @param {String} query The search query | |||
*/ | |||
function updateURL (query) { | |||
function updateURL(query) { | |||
// Create the properties | |||
let state = history.state | |||
let title = document.title | |||
@@ -5444,9 +5468,7 @@ | |||
// Update the URL | |||
history.pushState(state, title, url) | |||
} | |||
})() | |||
</script> |
@@ -159,11 +159,15 @@ | |||
* https://gomakethings.com/how-to-create-a-search-page-for-a-static-website-with-vanilla-js/ | |||
* https://gomakethings.com/how-to-update-the-url-of-a-page-without-causing-a-reload-using-vanilla-javascript/ | |||
*/ | |||
(function () { | |||
;(function () { | |||
// Retrieving the search index and stopwords from JSON. | |||
// See https://v8.dev/blog/cost-of-javascript-2019#json | |||
let searchIndex = JSON.parse(document.getElementById('search-index').textContent) | |||
let stopWords = JSON.parse(document.getElementById('search-stop-words').textContent) | |||
let searchIndex = JSON.parse( | |||
document.getElementById('search-index').textContent | |||
) | |||
let stopWords = JSON.parse( | |||
document.getElementById('search-stop-words').textContent | |||
) | |||
// Get the DOM elements | |||
let form = document.querySelector('#form-search') | |||
@@ -173,7 +177,16 @@ | |||
let searchResultTemplate = document.querySelector('#search-result') | |||
// Make sure required content exists | |||
if (!form || !input || !resultList || !searchStatus || !searchIndex || !stopWords || !searchResultTemplate) return | |||
if ( | |||
!form || | |||
!input || | |||
!resultList || | |||
!searchStatus || | |||
!searchIndex || | |||
!stopWords || | |||
!searchResultTemplate | |||
) | |||
return | |||
// Create a submit handler | |||
form.addEventListener('submit', function (event) { | |||
@@ -187,7 +200,7 @@ | |||
}) | |||
// Create a reset handler | |||
form.addEventListener('reset', function() { | |||
form.addEventListener('reset', function () { | |||
search('') | |||
searchStatus.innerHTML = '' | |||
}) | |||
@@ -198,7 +211,7 @@ | |||
/** | |||
* If there's a query string search term, search it on page load | |||
*/ | |||
function onload () { | |||
function onload() { | |||
let query = new URLSearchParams(window.location.search).get('s') | |||
if (!query) return | |||
input.value = query | |||
@@ -209,48 +222,54 @@ | |||
* Search for matches | |||
* @param {String} query The term to search for | |||
*/ | |||
function search (query) { | |||
function search(query) { | |||
// Create a regex for each query | |||
let regMap = query.toLowerCase().split(' ').filter(function (word) { | |||
return word.length && !stopWords.includes(word) | |||
}).map(function (word) { | |||
return new RegExp(word, 'i') | |||
}) | |||
let regMap = query | |||
.toLowerCase() | |||
.split(' ') | |||
.filter(function (word) { | |||
return word.length && !stopWords.includes(word) | |||
}) | |||
.map(function (word) { | |||
return new RegExp(word, 'i') | |||
}) | |||
// Get and sort the results | |||
let results = searchIndex.reduce(function (results, article, index) { | |||
// Setup priority count | |||
let priority = 0 | |||
let results = searchIndex | |||
.reduce(function (results, article, index) { | |||
// Setup priority count | |||
let priority = 0 | |||
// Assign priority | |||
for (let reg of regMap) { | |||
if (reg.test(article.title)) { priority += 100 } | |||
let occurences = article.content.match(reg) | |||
if (occurences) { priority += occurences.length } | |||
} | |||
// If any matches, push to results | |||
if (priority > 0) { | |||
results.push({ | |||
priority: priority, | |||
article: article | |||
}) | |||
} | |||
// Assign priority | |||
for (let reg of regMap) { | |||
if (reg.test(article.title)) { | |||
priority += 100 | |||
} | |||
let occurences = article.content.match(reg) | |||
if (occurences) { | |||
priority += occurences.length | |||
} | |||
} | |||
return results | |||
// If any matches, push to results | |||
if (priority > 0) { | |||
results.push({ | |||
priority: priority, | |||
article: article, | |||
}) | |||
} | |||
}, []).sort(function (article1, article2) { | |||
return article2.priority - article1.priority | |||
}) | |||
return results | |||
}, []) | |||
.sort(function (article1, article2) { | |||
return article2.priority - article1.priority | |||
}) | |||
// Display the results | |||
showResults(results, regMap) | |||
// Update the URL | |||
updateURL(query) | |||
} | |||
/** | |||
@@ -258,20 +277,22 @@ | |||
* @param {Array} results The results to display | |||
* @param {List} regMap Regular expressions for the highlights | |||
*/ | |||
function showResults (results, regMap) { | |||
function showResults(results, regMap) { | |||
let status = 'Aucune publication n’a été trouvée 😢' | |||
let searchResults = '' | |||
if (results.length) { | |||
const plural = results.length > 1 ? 's' : '' | |||
status = `${results.length} publication${plural} trouvée${plural} 🙌` | |||
searchResults = results.map(function (result) { | |||
return interpolate(searchResultTemplate.innerHTML, { | |||
url: result.article.url, | |||
title: highlightText(result.article.title, regMap), | |||
date: result.article.date, | |||
content: highlightText(result.article.content, regMap), | |||
searchResults = results | |||
.map(function (result) { | |||
return interpolate(searchResultTemplate.innerHTML, { | |||
url: result.article.url, | |||
title: highlightText(result.article.title, regMap), | |||
date: result.article.date, | |||
content: highlightText(result.article.content, regMap), | |||
}) | |||
}) | |||
}).join('') | |||
.join('') | |||
} | |||
searchStatus.innerHTML = status | |||
resultList.innerHTML = searchResults | |||
@@ -285,10 +306,10 @@ | |||
* @param {Object} params The parameters | |||
* @return {String} The interpolated string | |||
*/ | |||
function interpolate (str, params) { | |||
let names = Object.keys(params); | |||
let vals = Object.values(params); | |||
return new Function(...names, `return \`${str}\``)(...vals); | |||
function interpolate(str, params) { | |||
let names = Object.keys(params) | |||
let vals = Object.values(params) | |||
return new Function(...names, `return \`${str}\``)(...vals) | |||
} | |||
/** | |||
@@ -306,7 +327,9 @@ | |||
let extracts = [] | |||
for (let reg of regMap) { | |||
const index = text.search(reg) | |||
if (index === -1) { continue } | |||
if (index === -1) { | |||
continue | |||
} | |||
let extract = text.substring( | |||
index - extractBoundariesSize, | |||
index + reg.source.length + extractBoundariesSize | |||
@@ -314,9 +337,10 @@ | |||
// TODISCUSS: we replace with the source but in case there is | |||
// an uppercase letter it will disappear from the extract | |||
// (is that confusing or closer to what is expected?) | |||
extract = extract.replace(reg,`<mark>${reg.source}</mark>`) | |||
extract = extract.replace(reg, `<mark>${reg.source}</mark>`) | |||
const prefixEllipsis = index - extractBoundariesSize >= 0 ? '…' : '' | |||
const suffixEllipsis = index + extractBoundariesSize <= textLength ? '…' : '' | |||
const suffixEllipsis = | |||
index + extractBoundariesSize <= textLength ? '…' : '' | |||
extracts.push(`${prefixEllipsis}${extract}${suffixEllipsis}`) | |||
} | |||
if (!extracts.length && textLength < 200) { | |||
@@ -330,7 +354,7 @@ | |||
* Update the URL with a query string for the search string | |||
* @param {String} query The search query | |||
*/ | |||
function updateURL (query) { | |||
function updateURL(query) { | |||
// Create the properties | |||
let state = history.state | |||
let title = document.title | |||
@@ -341,9 +365,7 @@ | |||
// Update the URL | |||
history.pushState(state, title, url) | |||
} | |||
})() | |||
</script> |