/** * @source: ./script.js (originally from araa-search on Github) * * @licstart The following is the entire license notice for the * JavaScript code in this page. * * Copyright (C) 2023 Extravi * * The JavaScript code in this page is free software: you can * redistribute it and/or modify it under the terms of the GNU Affero * General Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later version. * * The code is distributed WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Affero General Public License for more details. * * As additional permission under GNU Affero General Public License * section 7, you may distribute non-source (e.g., minimized or compacted) * forms of that code without the copy of the GNU Affero General Public * License normally required by section 4, provided you include this * license notice and a URL through which recipients can access the * Corresponding Source. * * @licend The above is the entire license notice * for the JavaScript code in this page. */ // Removes the 'Apply Settings' button for Javascript users, // since changing any of the elements causes the settings to apply // automatically. let resultsSave = document.querySelector(".results-save"); if (resultsSave != null) { resultsSave.style.display = "none"; } const searchInput = document.getElementById('search-input'); const searchWrapper = document.querySelectorAll('.wrapper, .wrapper-results')[0]; const resultsWrapper = document.querySelector('.autocomplete'); // const clearSearch = document.querySelector("#clearSearch"); async function getSuggestions(query) { try { const params = new URLSearchParams({ "q": query }).toString(); const response = await fetch(`/suggestions?${params}`); const data = await response.json(); return data[1]; // Return only the array of suggestion strings } catch (error) { console.error(error); } } let currentIndex = -1; // Keep track of the currently selected suggestion let results = []; searchInput.addEventListener('input', async () => { let input = searchInput.value; if (input.length) { results = await getSuggestions(input); } renderResults(results); currentIndex = -1; // Reset index when we return new results }); searchInput.addEventListener("focus", async () => { let input = searchInput.value; if (results.length === 0 && input.length != 0) { results = await getSuggestions(input); } renderResults(results); }) // clearSearch.style.visibility = "visible"; // Only show the clear search button for JS users. // clearSearch.addEventListener("click", () => { // searchInput.value = ""; // searchInput.focus(); // }) searchInput.addEventListener('keydown', (event) => { if (event.key === 'ArrowUp' || event.key === 'ArrowDown' || event.key === 'Tab') { event.preventDefault(); // Prevent the default behavior, such as moving the cursor // Find the currently selected suggestion element const selectedSuggestion = resultsWrapper.querySelector('.selected'); if (selectedSuggestion) { selectedSuggestion.classList.remove('selected'); // Deselect the currently selected suggestion } // Increment current index when ArrowUp is pressed otherwise hen Tab OR ArrowDown decrement if (event.key === 'ArrowUp') { currentIndex--; } else { currentIndex++; } // Wrap around the index if it goes out of bounds if (currentIndex < 0) { currentIndex = resultsWrapper.querySelectorAll('li').length - 1; } else if (currentIndex >= resultsWrapper.querySelectorAll('li').length) { currentIndex = 0; } // Select the new suggestion resultsWrapper.querySelectorAll('li')[currentIndex].classList.add('selected'); // Update the value of the search input searchInput.value = resultsWrapper.querySelectorAll('li')[currentIndex].textContent; } }); // Default to the currently selected type or fallback to 'text' let selectedType = document.querySelector('.search-active')?.value || 'text'; // Function to render results function renderResults(results) { if (!results || !results.length || !searchInput.value) { searchWrapper.classList.remove('show'); return; } let content = ''; results.forEach((item) => { content += `
  • ${item}
  • `; }); if (searchInput.value) { searchWrapper.classList.add('show'); } resultsWrapper.innerHTML = ``; } // Function to handle search input searchInput.addEventListener('input', async () => { let input = searchInput.value; if (input.length) { const results = await getSuggestions(input); renderResults(results); } }); // Handle click events on the type buttons const typeButtons = document.querySelectorAll('[name="t"]'); typeButtons.forEach(button => { button.addEventListener('click', function() { selectedType = this.value; typeButtons.forEach(btn => btn.classList.remove('search-active')); this.classList.add('search-active'); }); }); // Handle clicks on search results resultsWrapper.addEventListener('click', (event) => { if (event.target.tagName === 'LI') { const query = event.target.textContent; window.location.href = `/search?q=${encodeURIComponent(query)}&t=${encodeURIComponent(selectedType)}`; } }); document.addEventListener("keypress", (event) => { if (document.activeElement == searchInput) { // Allow the '/' character to be pressed when searchInput is active } else if (document.querySelector(".calc") != null) { // Do nothing if the calculator is available, so the division keybinding // will still work } else if (event.key == "/") { event.preventDefault(); searchInput.focus(); searchInput.selectionStart = searchInput.selectionEnd = searchInput.value.length; } }) // Add event listener to hide autocomplete suggestions when clicking outside of search-input or wrapper document.addEventListener('click', (event) => { // Check if the target of the event is the search-input or any of its ancestors if (!searchInput.contains(event.target) && !searchWrapper.contains(event.target)) { // Remove the show class from the search wrapper searchWrapper.classList.remove('show'); } }); // Update visual feedback for selected type on page load document.addEventListener("DOMContentLoaded", () => { const activeButton = document.querySelector(`[name="t"][value="${selectedType}"]`); if (activeButton) { typeButtons.forEach(btn => btn.classList.remove('search-active')); activeButton.classList.add('search-active'); } });