Browse Source

Display search results with a dedicated template

master
David Larlet 2 years ago
parent
commit
9988a3b959
2 changed files with 102 additions and 38 deletions
  1. 51
    19
      david/recherche/index.html
  2. 51
    19
      david/templates/recherche.html

+ 51
- 19
david/recherche/index.html View File

@@ -80,12 +80,13 @@
<button type="reset">Annuler</button>
</form>

<div id="search-status" role="status"></div>
<p id="search-status" role="status"></p>
<div id="search-results"></div>

<hr>

<p>
Seuls les écrits de ces dernières années sont indexés.
Vous pouvez aussi consulter les archives chronologiques de
<a href="/david/2022/">2022</a>,
<a href="/david/2021/">2021</a>,
@@ -221,6 +222,12 @@
})
</script>

<template id="search-result">
<h2>
<a href="${url}">${title}</a> (${date})
</h2>
<p>${content}</p>
</template>
<script id="search-index" type="application/json">
[
{
@@ -5266,9 +5273,10 @@
let input = document.querySelector('#input-search')
let resultList = document.querySelector('#search-results')
let searchStatus = document.querySelector('#search-status')
let searchResultTemplate = document.querySelector('#search-result')

// Make sure required content exists
if (!form || !input || !resultList || !searchStatus || !searchIndex || !stopWords) return
if (!form || !input || !resultList || !searchStatus || !searchIndex || !stopWords || !searchResultTemplate) return

// Create a submit handler
form.addEventListener('submit', function (event) {
@@ -5351,38 +5359,58 @@
/**
* Show the search results in the UI
* @param {Array} results The results to display
* @param {List} regMap Regular expressions for the highlights
*/
function showResults (results, regMap) {
let status = 'Aucune publication n’a été trouvée 😢'
let searchResults = ''
if (results.length) {
const plural = results.length > 1 ? 's' : ''
searchStatus.innerHTML = `<p>${results.length} publication${plural} trouvée${plural} 🙌</p>`
resultList.innerHTML = results.map(function (result) {
return `
<h2>
<a href="${result.article.url}">${result.article.title}</a>
(${result.article.date})
</h2>
<p>${highlightContent(result.article.content, regMap)}</p>
`
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),
})
}).join('')
} else {
searchStatus.innerHTML = '<p>Aucune publication n’a été trouvée 😢<br>Seuls les écrits de ces dernières années sont indexés.</p>'
resultList.innerHTML = ''
}
searchStatus.innerHTML = status
resultList.innerHTML = searchResults
}

/**
* Get a template from a string
* https://stackoverflow.com/a/41015840
* https://gomakethings.com/html-templates-with-vanilla-javascript/
* @param {String} str The string to interpolate
* @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 highlightContent(content, regMap) {
/**
* Highlight the text in the UI
* @param {String} text The content to highlight
* @param {List} regMap Regular expressions for the highlights
*/
function highlightText(text, regMap) {
// TODO: deal with close matches when multiple words are looked for,
// it does not look trivial because you have to memorize positions
// then create extracts.
// For instance: `microsoft github`
const extractBoundariesSize = 100
const contentLength = content.length
const textLength = text.length
let extracts = []
for (let reg of regMap) {
const index = content.search(reg)
const index = text.search(reg)
if (index === -1) { continue }
let extract = content.substring(
let extract = text.substring(
index - extractBoundariesSize,
index + reg.source.length + extractBoundariesSize
)
@@ -5391,9 +5419,13 @@
// (is that confusing or closer to what is expected?)
extract = extract.replace(reg,`<mark>${reg.source}</mark>`)
const prefixEllipsis = index - extractBoundariesSize >= 0 ? '…' : ''
const suffixEllipsis = index - extractBoundariesSize <= contentLength ? '…' : ''
const suffixEllipsis = index + extractBoundariesSize <= textLength ? '…' : ''
extracts.push(`${prefixEllipsis}${extract}${suffixEllipsis}`)
}
if (!extracts.length && textLength < 200) {
// If there is no match but it's a short title, return it.
return text
}
return extracts.join('')
}


+ 51
- 19
david/templates/recherche.html View File

@@ -30,12 +30,13 @@
<button type="reset">Annuler</button>
</form>

<div id="search-status" role="status"></div>
<p id="search-status" role="status"></p>
<div id="search-results"></div>

<hr>

<p>
Seuls les écrits de ces dernières années sont indexés.
Vous pouvez aussi consulter les archives chronologiques de
<a href="/david/2022/">2022</a>,
<a href="/david/2021/">2021</a>,
@@ -45,6 +46,12 @@
{% endblock content %}

{% block extra_body %}
<template id="search-result">
<h2>
<a href="${url}">${title}</a> (${date})
</h2>
<p>${content}</p>
</template>
<script id="search-index" type="application/json">
{{ search_index }}
</script>
@@ -163,9 +170,10 @@
let input = document.querySelector('#input-search')
let resultList = document.querySelector('#search-results')
let searchStatus = document.querySelector('#search-status')
let searchResultTemplate = document.querySelector('#search-result')

// Make sure required content exists
if (!form || !input || !resultList || !searchStatus || !searchIndex || !stopWords) return
if (!form || !input || !resultList || !searchStatus || !searchIndex || !stopWords || !searchResultTemplate) return

// Create a submit handler
form.addEventListener('submit', function (event) {
@@ -248,38 +256,58 @@
/**
* Show the search results in the UI
* @param {Array} results The results to display
* @param {List} regMap Regular expressions for the highlights
*/
function showResults (results, regMap) {
let status = 'Aucune publication n’a été trouvée 😢'
let searchResults = ''
if (results.length) {
const plural = results.length > 1 ? 's' : ''
searchStatus.innerHTML = `<p>${results.length} publication${plural} trouvée${plural} 🙌</p>`
resultList.innerHTML = results.map(function (result) {
return `
<h2>
<a href="${result.article.url}">${result.article.title}</a>
(${result.article.date})
</h2>
<p>${highlightContent(result.article.content, regMap)}</p>
`
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),
})
}).join('')
} else {
searchStatus.innerHTML = '<p>Aucune publication n’a été trouvée 😢<br>Seuls les écrits de ces dernières années sont indexés.</p>'
resultList.innerHTML = ''
}
searchStatus.innerHTML = status
resultList.innerHTML = searchResults
}

/**
* Get a template from a string
* https://stackoverflow.com/a/41015840
* https://gomakethings.com/html-templates-with-vanilla-javascript/
* @param {String} str The string to interpolate
* @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 highlightContent(content, regMap) {
/**
* Highlight the text in the UI
* @param {String} text The content to highlight
* @param {List} regMap Regular expressions for the highlights
*/
function highlightText(text, regMap) {
// TODO: deal with close matches when multiple words are looked for,
// it does not look trivial because you have to memorize positions
// then create extracts.
// For instance: `microsoft github`
const extractBoundariesSize = 100
const contentLength = content.length
const textLength = text.length
let extracts = []
for (let reg of regMap) {
const index = content.search(reg)
const index = text.search(reg)
if (index === -1) { continue }
let extract = content.substring(
let extract = text.substring(
index - extractBoundariesSize,
index + reg.source.length + extractBoundariesSize
)
@@ -288,9 +316,13 @@
// (is that confusing or closer to what is expected?)
extract = extract.replace(reg,`<mark>${reg.source}</mark>`)
const prefixEllipsis = index - extractBoundariesSize >= 0 ? '…' : ''
const suffixEllipsis = index - extractBoundariesSize <= contentLength ? '…' : ''
const suffixEllipsis = index + extractBoundariesSize <= textLength ? '…' : ''
extracts.push(`${prefixEllipsis}${extract}${suffixEllipsis}`)
}
if (!extracts.length && textLength < 200) {
// If there is no match but it's a short title, return it.
return text
}
return extracts.join('')
}


Loading…
Cancel
Save