Récupération d'articles d'archives
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
"use strict";
|
||||
|
||||
const HIGHLIGHT_START = "__MEILI_HIGHLIGHT_START__";
|
||||
const HIGHLIGHT_END = "__MEILI_HIGHLIGHT_END__";
|
||||
|
||||
/**
|
||||
* Lit la configuration exposee par le template Hugo.
|
||||
* @param {HTMLElement} root Element principal de la page.
|
||||
@@ -67,14 +70,27 @@ function readSearchQuery(queryParam) {
|
||||
* @param {string} query Texte recherche.
|
||||
* @param {number} limit Nombre de resultats par lot.
|
||||
* @param {number} offset Position de depart.
|
||||
* @returns {{q: string, limit: number, offset: number, attributesToRetrieve: string[]}}
|
||||
* @returns {object}
|
||||
*/
|
||||
function buildSearchPayload(query, limit, offset) {
|
||||
return {
|
||||
q: query,
|
||||
limit,
|
||||
offset,
|
||||
attributesToRetrieve: ["title", "path", "published_at"],
|
||||
attributesToRetrieve: [
|
||||
"title",
|
||||
"description",
|
||||
"content",
|
||||
"path",
|
||||
"section",
|
||||
"published_at",
|
||||
],
|
||||
attributesToHighlight: ["title", "description", "content"],
|
||||
attributesToCrop: ["content"],
|
||||
cropLength: 24,
|
||||
cropMarker: "...",
|
||||
highlightPreTag: HIGHLIGHT_START,
|
||||
highlightPostTag: HIGHLIGHT_END,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -179,7 +195,7 @@ function setSectionVisibility(section, visible) {
|
||||
/**
|
||||
* Convertit un hit brut en resultat exploitable.
|
||||
* @param {any} hit Resultat brut.
|
||||
* @returns {{title: string, path: string, published_at: string} | null}
|
||||
* @returns {{title: string, titleMarkup: string, summary: string, summaryMarkup: string, path: string, section: string, published_at: string} | null}
|
||||
*/
|
||||
function normalizeHit(hit) {
|
||||
if (typeof hit !== "object" || hit === null) {
|
||||
@@ -194,19 +210,110 @@ function normalizeHit(hit) {
|
||||
if (typeof hit.title === "string" && hit.title.trim().length > 0) {
|
||||
title = hit.title.trim();
|
||||
}
|
||||
const titleMarkup = readFormattedField(hit, "title") || title;
|
||||
|
||||
let publishedAt = "";
|
||||
if (typeof hit.published_at === "string" && hit.published_at.trim().length > 0) {
|
||||
publishedAt = hit.published_at.trim();
|
||||
}
|
||||
|
||||
let section = "";
|
||||
if (typeof hit.section === "string" && hit.section.trim().length > 0) {
|
||||
section = hit.section.trim();
|
||||
}
|
||||
|
||||
const summaryMarkup = selectSummaryMarkup(hit);
|
||||
const summary = stripHighlightMarkers(summaryMarkup);
|
||||
|
||||
return {
|
||||
title,
|
||||
titleMarkup,
|
||||
summary,
|
||||
summaryMarkup,
|
||||
path: hit.path.trim(),
|
||||
section,
|
||||
published_at: publishedAt,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Lit un champ formate par Meilisearch.
|
||||
* @param {any} hit Resultat brut.
|
||||
* @param {string} attribute Nom de l'attribut recherche.
|
||||
* @returns {string}
|
||||
*/
|
||||
function readFormattedField(hit, attribute) {
|
||||
if (typeof hit !== "object" || hit === null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const formatted = hit._formatted;
|
||||
if (typeof formatted !== "object" || formatted === null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const rawValue = formatted[attribute];
|
||||
if (typeof rawValue !== "string") {
|
||||
return "";
|
||||
}
|
||||
|
||||
return rawValue.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indique si une chaine contient des marqueurs de surlignage.
|
||||
* @param {string} value Texte formate.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function containsHighlightMarkers(value) {
|
||||
return value.includes(HIGHLIGHT_START) && value.includes(HIGHLIGHT_END);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selectionne le meilleur extrait a afficher pour un resultat.
|
||||
* @param {any} hit Resultat brut.
|
||||
* @returns {string}
|
||||
*/
|
||||
function selectSummaryMarkup(hit) {
|
||||
const formattedDescription = readFormattedField(hit, "description");
|
||||
const formattedContent = readFormattedField(hit, "content");
|
||||
|
||||
if (containsHighlightMarkers(formattedDescription)) {
|
||||
return formattedDescription;
|
||||
}
|
||||
|
||||
if (containsHighlightMarkers(formattedContent)) {
|
||||
return formattedContent;
|
||||
}
|
||||
|
||||
if (typeof hit.description === "string" && hit.description.trim().length > 0) {
|
||||
return hit.description.trim();
|
||||
}
|
||||
|
||||
if (formattedDescription.length > 0) {
|
||||
return formattedDescription;
|
||||
}
|
||||
|
||||
if (formattedContent.length > 0) {
|
||||
return formattedContent;
|
||||
}
|
||||
|
||||
if (typeof hit.content === "string" && hit.content.trim().length > 0) {
|
||||
return hit.content.trim();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Retire les marqueurs de surlignage d'un texte.
|
||||
* @param {string} value Texte formate.
|
||||
* @returns {string}
|
||||
*/
|
||||
function stripHighlightMarkers(value) {
|
||||
return value.split(HIGHLIGHT_START).join("").split(HIGHLIGHT_END).join("").trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formate la date en ignorant l'heure de publication.
|
||||
* @param {unknown} rawDate Date brute.
|
||||
@@ -240,58 +347,107 @@ function buildDateInfo(rawDate) {
|
||||
|
||||
/**
|
||||
* Extrait un libelle de section a partir du chemin Hugo.
|
||||
* @param {string} section Section brute renvoyee par l'index.
|
||||
* @param {string} path Chemin relatif de l'article.
|
||||
* @returns {string}
|
||||
*/
|
||||
function extractSectionLabel(path) {
|
||||
const segments = path.split("/").filter(Boolean);
|
||||
if (segments.length === 0) {
|
||||
return "Section inconnue";
|
||||
function extractSectionLabel(section, path) {
|
||||
let sectionKey = "";
|
||||
if (typeof section === "string" && section.trim().length > 0) {
|
||||
sectionKey = section.trim();
|
||||
} else {
|
||||
const segments = path.split("/").filter(Boolean);
|
||||
if (segments.length === 0) {
|
||||
return "Section inconnue";
|
||||
}
|
||||
|
||||
sectionKey = segments[0];
|
||||
}
|
||||
|
||||
const firstSegment = segments[0];
|
||||
if (firstSegment === "collections") {
|
||||
if (sectionKey === "collections") {
|
||||
return "Collections";
|
||||
}
|
||||
|
||||
if (firstSegment === "critiques") {
|
||||
if (sectionKey === "critiques") {
|
||||
return "Critiques";
|
||||
}
|
||||
|
||||
if (firstSegment === "interets") {
|
||||
if (sectionKey === "interets") {
|
||||
return "Intérêts";
|
||||
}
|
||||
|
||||
if (firstSegment === "stats") {
|
||||
if (sectionKey === "stats") {
|
||||
return "Statistiques";
|
||||
}
|
||||
|
||||
if (firstSegment === "taxonomies") {
|
||||
if (sectionKey === "taxonomies") {
|
||||
return "Taxonomies";
|
||||
}
|
||||
|
||||
if (firstSegment === "contact") {
|
||||
if (sectionKey === "contact") {
|
||||
return "Contact";
|
||||
}
|
||||
|
||||
if (firstSegment === "manifeste") {
|
||||
if (sectionKey === "manifeste") {
|
||||
return "Manifeste";
|
||||
}
|
||||
|
||||
if (firstSegment === "liens-morts") {
|
||||
if (sectionKey === "liens-morts") {
|
||||
return "Liens morts";
|
||||
}
|
||||
|
||||
if (firstSegment === "revue-de-presse") {
|
||||
if (sectionKey === "revue-de-presse") {
|
||||
return "Revue de presse";
|
||||
}
|
||||
|
||||
return firstSegment;
|
||||
return sectionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit un fragment DOM avec les passages surlignes.
|
||||
* @param {string} rawText Texte potentiellement marque.
|
||||
* @returns {DocumentFragment}
|
||||
*/
|
||||
function buildHighlightedFragment(rawText) {
|
||||
const fragment = document.createDocumentFragment();
|
||||
|
||||
if (typeof rawText !== "string" || rawText.length === 0) {
|
||||
return fragment;
|
||||
}
|
||||
|
||||
let remaining = rawText;
|
||||
|
||||
while (remaining.length > 0) {
|
||||
const startIndex = remaining.indexOf(HIGHLIGHT_START);
|
||||
if (startIndex === -1) {
|
||||
fragment.append(remaining);
|
||||
break;
|
||||
}
|
||||
|
||||
if (startIndex > 0) {
|
||||
fragment.append(remaining.slice(0, startIndex));
|
||||
}
|
||||
|
||||
remaining = remaining.slice(startIndex + HIGHLIGHT_START.length);
|
||||
|
||||
const endIndex = remaining.indexOf(HIGHLIGHT_END);
|
||||
if (endIndex === -1) {
|
||||
fragment.append(remaining);
|
||||
break;
|
||||
}
|
||||
|
||||
const mark = document.createElement("mark");
|
||||
mark.textContent = remaining.slice(0, endIndex);
|
||||
fragment.append(mark);
|
||||
remaining = remaining.slice(endIndex + HIGHLIGHT_END.length);
|
||||
}
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit un article de resultat simple.
|
||||
* @param {{title: string, path: string, published_at?: string}} hit Resultat.
|
||||
* @param {{title: string, titleMarkup: string, summary: string, summaryMarkup: string, path: string, section: string, published_at?: string}} hit Resultat.
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
function createResultArticle(hit) {
|
||||
@@ -300,7 +456,7 @@ function createResultArticle(hit) {
|
||||
const title = document.createElement("h3");
|
||||
const link = document.createElement("a");
|
||||
link.href = hit.path;
|
||||
link.textContent = hit.title;
|
||||
link.append(buildHighlightedFragment(hit.titleMarkup));
|
||||
title.append(link);
|
||||
article.append(title);
|
||||
|
||||
@@ -324,11 +480,18 @@ function createResultArticle(hit) {
|
||||
meta.append(separator);
|
||||
|
||||
const section = document.createElement("span");
|
||||
section.textContent = extractSectionLabel(hit.path);
|
||||
section.textContent = extractSectionLabel(hit.section, hit.path);
|
||||
meta.append(section);
|
||||
|
||||
article.append(meta);
|
||||
|
||||
if (hit.summary.length > 0) {
|
||||
const summary = document.createElement("p");
|
||||
summary.className = "search-result-summary";
|
||||
summary.append(buildHighlightedFragment(hit.summaryMarkup));
|
||||
article.append(summary);
|
||||
}
|
||||
|
||||
const path = document.createElement("p");
|
||||
path.className = "search-result-path";
|
||||
path.textContent = hit.path;
|
||||
@@ -340,7 +503,7 @@ function createResultArticle(hit) {
|
||||
/**
|
||||
* Rend la liste des resultats.
|
||||
* @param {HTMLOListElement} listNode Liste cible.
|
||||
* @param {Array<{title: string, path: string, published_at?: string}>} hits Resultats a afficher.
|
||||
* @param {Array<{title: string, titleMarkup: string, summary: string, summaryMarkup: string, path: string, section: string, published_at?: string}>} hits Resultats a afficher.
|
||||
*/
|
||||
function renderListing(listNode, hits) {
|
||||
clearNode(listNode);
|
||||
|
||||
Reference in New Issue
Block a user