diff options
| author | Ivan Gabaldon <igabaldon@inetol.net> | 2025-07-06 12:27:28 +0200 |
|---|---|---|
| committer | Markus Heiser <markus.heiser@darmarIT.de> | 2025-08-18 16:38:32 +0200 |
| commit | 60bd8b90f04d5d825fc8ac279cb7fdfde9fe78ea (patch) | |
| tree | 19b2639638e7845597f9aa839eda39a456188a1c /client/simple/src/js/main/search.ts | |
| parent | adc4361eb919604889dc0661e75ef6ac8cfc4d23 (diff) | |
[enh] theme/simple: custom router
Lay the foundation for loading scripts granularly depending on the endpoint it's
on.
Remove vendor specific prefixes as there are now managed by browserslist and
LightningCSS.
Enabled quite a few rules in Biome that don't come in recommended to better
catch issues and improve consistency.
Related:
- https://github.com/searxng/searxng/pull/5073#discussion_r2256037965
- https://github.com/searxng/searxng/pull/5073#discussion_r2256057100
Diffstat (limited to 'client/simple/src/js/main/search.ts')
| -rw-r--r-- | client/simple/src/js/main/search.ts | 263 |
1 files changed, 67 insertions, 196 deletions
diff --git a/client/simple/src/js/main/search.ts b/client/simple/src/js/main/search.ts index 5e68965b1..508dc702a 100644 --- a/client/simple/src/js/main/search.ts +++ b/client/simple/src/js/main/search.ts @@ -1,4 +1,4 @@ -import { assertElement, searxng } from "./00_toolkit.ts"; +import { assertElement, listen, settings } from "../core/toolkit.ts"; const submitIfQuery = (qInput: HTMLInputElement): void => { if (qInput.value.length > 0) { @@ -17,217 +17,88 @@ const createClearButton = (qInput: HTMLInputElement): void => { updateClearButton(qInput, cs); - searxng.listen("click", cs, (event: MouseEvent) => { + listen("click", cs, (event: MouseEvent) => { event.preventDefault(); qInput.value = ""; qInput.focus(); updateClearButton(qInput, cs); }); - searxng.listen("input", qInput, () => updateClearButton(qInput, cs), { passive: true }); + listen("input", qInput, () => updateClearButton(qInput, cs), { passive: true }); }; -const fetchResults = async (qInput: HTMLInputElement, query: string): Promise<void> => { - try { - let res: Response; - - if (searxng.settings.method === "GET") { - res = await searxng.http("GET", `./autocompleter?q=${query}`); - } else { - res = await searxng.http("POST", "./autocompleter", new URLSearchParams({ q: query })); - } - - const results = await res.json(); - - const autocomplete = document.querySelector<HTMLElement>(".autocomplete"); - assertElement(autocomplete); - - const autocompleteList = document.querySelector<HTMLUListElement>(".autocomplete ul"); - assertElement(autocompleteList); - - autocomplete.classList.add("open"); - autocompleteList.replaceChildren(); - - // show an error message that no result was found - if (!results?.[1]?.length) { - const noItemFoundMessage = Object.assign(document.createElement("li"), { - className: "no-item-found", - textContent: searxng.settings.translations?.no_item_found ?? "No results found" - }); - autocompleteList.append(noItemFoundMessage); - return; - } - - const fragment = new DocumentFragment(); - - for (const result of results[1]) { - const li = Object.assign(document.createElement("li"), { textContent: result }); - - searxng.listen("mousedown", li, () => { - qInput.value = result; - - const form = document.querySelector<HTMLFormElement>("#search"); - form?.submit(); - - autocomplete.classList.remove("open"); - }); - - fragment.append(li); - } - - autocompleteList.append(fragment); - } catch (error) { - console.error("Error fetching autocomplete results:", error); +const qInput = document.getElementById("q") as HTMLInputElement | null; +assertElement(qInput); + +const isMobile: boolean = window.matchMedia("(max-width: 50em)").matches; +const isResultsPage: boolean = document.querySelector("main")?.id === "main_results"; + +// focus search input on large screens +if (!(isMobile || isResultsPage)) { + qInput.focus(); +} + +createClearButton(qInput); + +// Additionally to searching when selecting a new category, we also +// automatically start a new search request when the user changes a search +// filter (safesearch, time range or language) (this requires JavaScript +// though) +if ( + settings.search_on_category_select && + // If .search_filters is undefined (invisible) we are on the homepage and + // hence don't have to set any listeners + document.querySelector(".search_filters") +) { + const safesearchElement = document.getElementById("safesearch"); + if (safesearchElement) { + listen("change", safesearchElement, () => submitIfQuery(qInput)); } -}; - -searxng.ready( - () => { - const qInput = document.getElementById("q") as HTMLInputElement | null; - assertElement(qInput); - const isMobile = window.matchMedia("(max-width: 50em)").matches; - const isResultsPage = document.querySelector("main")?.id === "main_results"; - - // focus search input on large screens - if (!isMobile && !isResultsPage) { - qInput.focus(); - } - - createClearButton(qInput); - - // autocompleter - if (searxng.settings.autocomplete) { - let timeoutId: number; - - searxng.listen("input", qInput, () => { - clearTimeout(timeoutId); - - const query = qInput.value; - const minLength = searxng.settings.autocomplete_min ?? 2; - - if (query.length < minLength) return; - - timeoutId = window.setTimeout(async () => { - if (query === qInput.value) { - await fetchResults(qInput, query); - } - }, 300); - }); - - const autocomplete = document.querySelector<HTMLElement>(".autocomplete"); - const autocompleteList = document.querySelector<HTMLUListElement>(".autocomplete ul"); - if (autocompleteList) { - searxng.listen("keyup", qInput, (event: KeyboardEvent) => { - const listItems = [...autocompleteList.children] as HTMLElement[]; - - const currentIndex = listItems.findIndex((item) => item.classList.contains("active")); - let newCurrentIndex = -1; - - switch (event.key) { - case "ArrowUp": { - const currentItem = listItems[currentIndex]; - if (currentItem && currentIndex >= 0) { - currentItem.classList.remove("active"); - } - // we need to add listItems.length to the index calculation here because the JavaScript modulos - // operator doesn't work with negative numbers - newCurrentIndex = (currentIndex - 1 + listItems.length) % listItems.length; - break; - } - case "ArrowDown": { - const currentItem = listItems[currentIndex]; - if (currentItem && currentIndex >= 0) { - currentItem.classList.remove("active"); - } - newCurrentIndex = (currentIndex + 1) % listItems.length; - break; - } - case "Tab": - case "Enter": - if (autocomplete) { - autocomplete.classList.remove("open"); - } - break; - } - - if (newCurrentIndex !== -1) { - const selectedItem = listItems[newCurrentIndex]; - if (selectedItem) { - selectedItem.classList.add("active"); - - if (!selectedItem.classList.contains("no-item-found")) { - const qInput = document.getElementById("q") as HTMLInputElement | null; - if (qInput) { - qInput.value = selectedItem.textContent ?? ""; - } - } - } - } - }); - } - } + const timeRangeElement = document.getElementById("time_range"); + if (timeRangeElement) { + listen("change", timeRangeElement, () => submitIfQuery(qInput)); + } - // Additionally to searching when selecting a new category, we also - // automatically start a new search request when the user changes a search - // filter (safesearch, time range or language) (this requires JavaScript - // though) - if ( - searxng.settings.search_on_category_select && - // If .search_filters is undefined (invisible) we are on the homepage and - // hence don't have to set any listeners - document.querySelector(".search_filters") - ) { - const safesearchElement = document.getElementById("safesearch"); - if (safesearchElement) { - searxng.listen("change", safesearchElement, () => submitIfQuery(qInput)); - } - - const timeRangeElement = document.getElementById("time_range"); - if (timeRangeElement) { - searxng.listen("change", timeRangeElement, () => submitIfQuery(qInput)); - } - - const languageElement = document.getElementById("language"); - if (languageElement) { - searxng.listen("change", languageElement, () => submitIfQuery(qInput)); - } + const languageElement = document.getElementById("language"); + if (languageElement) { + listen("change", languageElement, () => submitIfQuery(qInput)); + } +} + +const categoryButtons: HTMLButtonElement[] = [ + ...document.querySelectorAll<HTMLButtonElement>("button.category_button") +]; +for (const button of categoryButtons) { + listen("click", button, (event: MouseEvent) => { + if (event.shiftKey) { + event.preventDefault(); + button.classList.toggle("selected"); + return; } - const categoryButtons = [...document.querySelectorAll<HTMLButtonElement>("button.category_button")]; - for (const button of categoryButtons) { - searxng.listen("click", button, (event: MouseEvent) => { - if (event.shiftKey) { - event.preventDefault(); - button.classList.toggle("selected"); - return; - } - - // deselect all other categories - for (const categoryButton of categoryButtons) { - categoryButton.classList.toggle("selected", categoryButton === button); - } - }); + // deselect all other categories + for (const categoryButton of categoryButtons) { + categoryButton.classList.toggle("selected", categoryButton === button); } + }); +} - const form = document.querySelector<HTMLFormElement>("#search"); - assertElement(form); +const form: HTMLFormElement | null = document.querySelector<HTMLFormElement>("#search"); +assertElement(form); - // override form submit action to update the actually selected categories - searxng.listen("submit", form, (event: Event) => { - event.preventDefault(); +// override form submit action to update the actually selected categories +listen("submit", form, (event: Event) => { + event.preventDefault(); - const categoryValuesInput = document.querySelector<HTMLInputElement>("#selected-categories"); - if (categoryValuesInput) { - const categoryValues = categoryButtons - .filter((button) => button.classList.contains("selected")) - .map((button) => button.name.replace("category_", "")); + const categoryValuesInput = document.querySelector<HTMLInputElement>("#selected-categories"); + if (categoryValuesInput) { + const categoryValues = categoryButtons + .filter((button) => button.classList.contains("selected")) + .map((button) => button.name.replace("category_", "")); - categoryValuesInput.value = categoryValues.join(","); - } + categoryValuesInput.value = categoryValues.join(","); + } - form.submit(); - }); - }, - { on: [searxng.endpoint === "index" || searxng.endpoint === "results"] } -); + form.submit(); +}); |