Logo update WIP
This commit is contained in:
parent
787816d0ab
commit
9b92632cd6
11 changed files with 932 additions and 285 deletions
|
@ -1,180 +1,120 @@
|
|||
/**
|
||||
* @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.
|
||||
*/
|
||||
// Wait for the DOM to load
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const searchInput = document.getElementById('search-input');
|
||||
const searchWrapper = document.querySelector('.wrapper');
|
||||
const resultsWrapper = document.querySelector('.autocomplete');
|
||||
|
||||
// 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";
|
||||
}
|
||||
let suggestions = [];
|
||||
let currentIndex = -1; // Keep track of the currently selected suggestion
|
||||
|
||||
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
|
||||
|
||||
// Handle click events on the type buttons
|
||||
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
|
||||
// Fetch suggestions from the server
|
||||
async function getSuggestions(query) {
|
||||
try {
|
||||
const response = await fetch(`/suggestions?q=${encodeURIComponent(query)}`);
|
||||
const data = await response.json();
|
||||
return data[1]; // Return only the array of suggestion strings
|
||||
} catch (error) {
|
||||
console.error('Error fetching suggestions:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Increment current index when ArrowUp is pressed otherwise hen Tab OR ArrowDown decrement
|
||||
if (event.key === 'ArrowUp') {
|
||||
currentIndex--;
|
||||
} else {
|
||||
currentIndex++;
|
||||
// Function to render results
|
||||
function renderSuggestions(results) {
|
||||
if (!results || !results.length) {
|
||||
searchWrapper.classList.remove('show');
|
||||
resultsWrapper.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
let content = '';
|
||||
results.forEach((item, index) => {
|
||||
content += `<li data-index="${index}">${item}</li>`;
|
||||
});
|
||||
|
||||
resultsWrapper.innerHTML = `<ul>${content}</ul>`;
|
||||
searchWrapper.classList.add('show');
|
||||
}
|
||||
|
||||
// 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;
|
||||
// Fetch suggestions when input is focused
|
||||
searchInput.addEventListener('focus', async () => {
|
||||
const query = searchInput.value.trim();
|
||||
suggestions = await getSuggestions(query);
|
||||
renderSuggestions(suggestions);
|
||||
currentIndex = -1;
|
||||
});
|
||||
|
||||
// Handle input event
|
||||
searchInput.addEventListener('input', async () => {
|
||||
const query = searchInput.value.trim();
|
||||
if (query.length > 0) {
|
||||
suggestions = await getSuggestions(query);
|
||||
} else {
|
||||
suggestions = [];
|
||||
}
|
||||
currentIndex = -1; // Reset index when new results come in
|
||||
renderSuggestions(suggestions);
|
||||
});
|
||||
|
||||
// Handle keydown events for navigation
|
||||
searchInput.addEventListener('keydown', (event) => {
|
||||
const items = resultsWrapper.querySelectorAll('li');
|
||||
if (event.key === 'ArrowUp' || event.key === 'ArrowDown' || event.key === 'Tab') {
|
||||
event.preventDefault();
|
||||
|
||||
// Remove 'selected' class from the current item
|
||||
if (items[currentIndex]) {
|
||||
items[currentIndex].classList.remove('selected');
|
||||
}
|
||||
|
||||
if (event.key === 'ArrowUp') {
|
||||
currentIndex = (currentIndex > 0) ? currentIndex - 1 : items.length - 1;
|
||||
} else {
|
||||
currentIndex = (currentIndex + 1) % items.length;
|
||||
}
|
||||
|
||||
// Add 'selected' class to the new item and update the input value
|
||||
if (items[currentIndex]) {
|
||||
items[currentIndex].classList.add('selected');
|
||||
searchInput.value = items[currentIndex].textContent;
|
||||
}
|
||||
} else if (event.key === 'Enter') {
|
||||
if (currentIndex > -1 && items[currentIndex]) {
|
||||
event.preventDefault();
|
||||
selectSuggestion(items[currentIndex]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Function to handle suggestion selection
|
||||
function selectSuggestion(item) {
|
||||
const query = item.textContent;
|
||||
searchInput.value = query;
|
||||
resultsWrapper.innerHTML = '';
|
||||
searchWrapper.classList.remove('show');
|
||||
|
||||
// Submit the form or navigate to search results
|
||||
const form = searchInput.closest('form');
|
||||
if (form) {
|
||||
form.submit();
|
||||
} else {
|
||||
window.location.href = `/search?q=${encodeURIComponent(query)}&t=web`;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
// Handle clicks on search suggestions
|
||||
resultsWrapper.addEventListener('click', (event) => {
|
||||
if (event.target.tagName === 'LI') {
|
||||
selectSuggestion(event.target);
|
||||
}
|
||||
});
|
||||
|
||||
// Close the suggestions when clicking outside
|
||||
document.addEventListener('click', (event) => {
|
||||
if (!searchWrapper.contains(event.target)) {
|
||||
searchWrapper.classList.remove('show');
|
||||
resultsWrapper.innerHTML = '';
|
||||
currentIndex = -1;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 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 += `<li>${item}</li>`;
|
||||
});
|
||||
|
||||
if (searchInput.value) {
|
||||
searchWrapper.classList.add('show');
|
||||
}
|
||||
resultsWrapper.innerHTML = `<ul>${content}</ul>`;
|
||||
}
|
||||
|
||||
// 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');
|
||||
// }
|
||||
// });
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue