mirror of
https://github.com/iv-org/invidious.git
synced 2026-07-28 00:16:47 -05:00
117 lines
3.2 KiB
JavaScript
117 lines
3.2 KiB
JavaScript
"use strict";
|
|
const searchbox = document.getElementById("searchbox");
|
|
const searchContainer = document.getElementById("search-container");
|
|
const suggestions = document.getElementById("suggestions");
|
|
let selectedSuggestion = -1;
|
|
let suggestionController;
|
|
let queuedSuggestions = 0;
|
|
|
|
function resetSuggestions() {
|
|
suggestions.textContent = "";
|
|
selectedSuggestion = -1;
|
|
}
|
|
|
|
searchbox?.addEventListener("input", function () {
|
|
// If there is already a request in progress,
|
|
// abort. The user has made another input,
|
|
// invalidating the old query.
|
|
if (suggestionController != undefined) {
|
|
suggestionController.abort();
|
|
}
|
|
|
|
// Only make a request for suggestions after
|
|
// there is a delay of at least 150 milliseconds
|
|
// between inputs.
|
|
queuedSuggestions++;
|
|
setTimeout(async function () {
|
|
queuedSuggestions--;
|
|
if (queuedSuggestions != 0) {
|
|
return;
|
|
}
|
|
|
|
// Only continue for queries of at least
|
|
// one character.
|
|
if (searchbox.value.length < 2) {
|
|
resetSuggestions();
|
|
return;
|
|
}
|
|
|
|
// Create the controller which will be
|
|
// used to abort this request if another
|
|
// is made before it finishes.
|
|
suggestionController = new AbortController();
|
|
const signal = suggestionController.signal;
|
|
|
|
// Make the request for suggestions.
|
|
try {
|
|
const resp = await fetch(
|
|
`/api/v1/search/suggestions?q=${encodeURIComponent(searchbox.value)}`,
|
|
{ signal },
|
|
);
|
|
const body = await resp.json();
|
|
|
|
// As a sanity check, ensure the
|
|
// suggestions match the query.
|
|
if (body.query != searchbox.value) {
|
|
return;
|
|
}
|
|
|
|
// Put the results into DOM.
|
|
resetSuggestions();
|
|
const results = body.suggestions;
|
|
for (let i = 0; i < results.length; i++) {
|
|
const s = document.createElement("a");
|
|
s.href = `/search?q=${encodeURIComponent(results[i])}`;
|
|
s.innerText = results[i];
|
|
suggestions.appendChild(s);
|
|
}
|
|
} catch (err) {
|
|
// Discard AbortError as it is expected.
|
|
if (err.name !== "AbortError") {
|
|
console.error(err);
|
|
}
|
|
}
|
|
}, 150);
|
|
});
|
|
|
|
searchbox?.addEventListener("keydown", function (e) {
|
|
const currentSuggestions = suggestions.children;
|
|
|
|
// Navigate suggestions by key.
|
|
switch (e.key) {
|
|
case "ArrowDown":
|
|
selectedSuggestion++;
|
|
e.preventDefault();
|
|
break;
|
|
case "ArrowUp":
|
|
selectedSuggestion--;
|
|
e.preventDefault();
|
|
break;
|
|
case "Enter":
|
|
if (selectedSuggestion != -1) {
|
|
currentSuggestions[selectedSuggestion].click();
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Loop around if you go beyond the end
|
|
// or before -1 (the resting position).
|
|
if (selectedSuggestion >= currentSuggestions.length) {
|
|
selectedSuggestion = -1;
|
|
} else if (selectedSuggestion < -1) {
|
|
selectedSuggestion = currentSuggestions.length - 1;
|
|
}
|
|
|
|
// Remove .active class from all elements
|
|
// except the active one.
|
|
for (let i = 0; i < currentSuggestions.length; i++) {
|
|
if (i == selectedSuggestion) {
|
|
currentSuggestions[i].classList.add("active");
|
|
} else if (currentSuggestions[i].classList.contains("active")) {
|
|
currentSuggestions[i].classList.remove("active");
|
|
}
|
|
}
|
|
});
|