120 lines
4.1 KiB
JavaScript
120 lines
4.1 KiB
JavaScript
function encodeHTML(str) {
|
|
return str.replace(/&/g, "&")
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">")
|
|
.replace(/"/g, """)
|
|
.replace(/'/g, "'");
|
|
}
|
|
|
|
function isValidUrl(url) {
|
|
try {
|
|
const parsedUrl = new URL(url, window.location.origin);
|
|
return parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:";
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
let debounceTimeout;
|
|
function searchOnChange(evt) {
|
|
clearTimeout(debounceTimeout);
|
|
debounceTimeout = setTimeout(() => {
|
|
performSearch(evt);
|
|
}, 300); // Debounce delay of 300ms
|
|
}
|
|
|
|
async function performSearch(evt) {
|
|
let searchQuery = evt.target.value.trim().toLowerCase();
|
|
|
|
if (searchQuery !== "") {
|
|
const searchButtonEle = document.querySelectorAll("#search");
|
|
|
|
if (searchButtonEle.length < 2) {
|
|
console.error("Search button elements missing!");
|
|
return;
|
|
}
|
|
|
|
let searchButtonPosition;
|
|
if (window.innerWidth > 768) {
|
|
searchButtonPosition = searchButtonEle[0].getBoundingClientRect();
|
|
document.getElementById("search-content").style.width = "500px";
|
|
} else {
|
|
searchButtonPosition = searchButtonEle[1].getBoundingClientRect();
|
|
document.getElementById("search-content").style.width = "300px";
|
|
}
|
|
|
|
document.getElementById("search-content").style.top =
|
|
searchButtonPosition.top + 50 + "px";
|
|
document.getElementById("search-content").style.left =
|
|
searchButtonPosition.left + "px";
|
|
|
|
try {
|
|
// Use the search index URL provided by Hugo template, fallback to /index.json
|
|
let indexURL = window.searchIndexURL || '/index.json';
|
|
|
|
let response = await fetch(indexURL);
|
|
if (!response.ok) {
|
|
throw new Error("Failed to fetch search data");
|
|
}
|
|
|
|
let searchJson = await response.json();
|
|
console.log("Fetched Data:", searchJson); // Debugging log
|
|
|
|
let searchResults = searchJson.filter((item) => {
|
|
if (!item || typeof item !== "object") return false;
|
|
if (!item.title && !item.description && !item.content) return false;
|
|
|
|
return (
|
|
(item.title && item.title.toLowerCase().includes(searchQuery)) ||
|
|
(item.description && item.description.toLowerCase().includes(searchQuery)) ||
|
|
(item.content && item.content.toLowerCase().includes(searchQuery))
|
|
);
|
|
});
|
|
|
|
const searchResultsContainer = document.getElementById("search-results");
|
|
searchResultsContainer.innerHTML = ""; // Clear previous results
|
|
|
|
if (searchResults.length > 0) {
|
|
searchResults.forEach((item) => {
|
|
if (!item.permalink || !isValidUrl(item.permalink)) {
|
|
console.warn("Skipping invalid search result:", item);
|
|
return;
|
|
}
|
|
|
|
const card = document.createElement("div");
|
|
card.className = "card";
|
|
|
|
const link = document.createElement("a");
|
|
link.href = item.permalink; // Safe, since we validated it
|
|
|
|
const contentDiv = document.createElement("div");
|
|
contentDiv.className = "p-3";
|
|
|
|
const title = document.createElement("h5");
|
|
title.textContent = item.title || "Untitled"; // Use textContent to prevent XSS
|
|
|
|
const description = document.createElement("div");
|
|
description.textContent = item.description || "No description available"; // Safe
|
|
|
|
contentDiv.appendChild(title);
|
|
contentDiv.appendChild(description);
|
|
link.appendChild(contentDiv);
|
|
card.appendChild(link);
|
|
searchResultsContainer.appendChild(card);
|
|
});
|
|
} else {
|
|
const noResultsMessage = document.createElement("p");
|
|
noResultsMessage.className = "text-center py-3";
|
|
noResultsMessage.textContent = `No results found for "${searchQuery}"`;
|
|
searchResultsContainer.appendChild(noResultsMessage);
|
|
}
|
|
|
|
document.getElementById("search-content").style.display = "block";
|
|
} catch (error) {
|
|
console.error("Error fetching search data:", error);
|
|
}
|
|
} else {
|
|
document.getElementById("search-content").style.display = "none";
|
|
document.getElementById("search-results").innerHTML = "";
|
|
}
|
|
}
|