summaryrefslogtreecommitdiff
path: root/client/simple/src/js/main/keyboard.js
diff options
context:
space:
mode:
Diffstat (limited to 'client/simple/src/js/main/keyboard.js')
-rw-r--r--client/simple/src/js/main/keyboard.js473
1 files changed, 0 insertions, 473 deletions
diff --git a/client/simple/src/js/main/keyboard.js b/client/simple/src/js/main/keyboard.js
deleted file mode 100644
index e707c397f..000000000
--- a/client/simple/src/js/main/keyboard.js
+++ /dev/null
@@ -1,473 +0,0 @@
-/* SPDX-License-Identifier: AGPL-3.0-or-later */
-/* global searxng */
-
-searxng.ready(() => {
- function isElementInDetail(el) {
- while (el !== undefined) {
- if (el.classList.contains("detail")) {
- return true;
- }
- if (el.classList.contains("result")) {
- // we found a result, no need to go to the root of the document:
- // el is not inside a <div class="detail"> element
- return false;
- }
- el = el.parentNode;
- }
- return false;
- }
-
- function getResultElement(el) {
- while (el !== undefined) {
- if (el.classList.contains("result")) {
- return el;
- }
- el = el.parentNode;
- }
- return undefined;
- }
-
- function isImageResult(resultElement) {
- return resultElement?.classList.contains("result-images");
- }
-
- searxng.on(".result", "click", function (e) {
- if (!isElementInDetail(e.target)) {
- highlightResult(this)(true, true);
- const resultElement = getResultElement(e.target);
- if (isImageResult(resultElement)) {
- e.preventDefault();
- searxng.selectImage(resultElement);
- }
- }
- });
-
- searxng.on(
- ".result a",
- "focus",
- (e) => {
- if (!isElementInDetail(e.target)) {
- const resultElement = getResultElement(e.target);
- if (resultElement && resultElement.getAttribute("data-vim-selected") === null) {
- highlightResult(resultElement)(true);
- }
- if (isImageResult(resultElement)) {
- searxng.selectImage(resultElement);
- }
- }
- },
- true
- );
-
- /* common base for layouts */
- const baseKeyBinding = {
- Escape: {
- key: "ESC",
- fun: removeFocus,
- des: "remove focus from the focused input",
- cat: "Control"
- },
- c: {
- key: "c",
- fun: copyURLToClipboard,
- des: "copy url of the selected result to the clipboard",
- cat: "Results"
- },
- h: {
- key: "h",
- fun: toggleHelp,
- des: "toggle help window",
- cat: "Other"
- },
- i: {
- key: "i",
- fun: searchInputFocus,
- des: "focus on the search input",
- cat: "Control"
- },
- n: {
- key: "n",
- fun: GoToNextPage(),
- des: "go to next page",
- cat: "Results"
- },
- o: {
- key: "o",
- fun: openResult(false),
- des: "open search result",
- cat: "Results"
- },
- p: {
- key: "p",
- fun: GoToPreviousPage(),
- des: "go to previous page",
- cat: "Results"
- },
- r: {
- key: "r",
- fun: reloadPage,
- des: "reload page from the server",
- cat: "Control"
- },
- t: {
- key: "t",
- fun: openResult(true),
- des: "open the result in a new tab",
- cat: "Results"
- }
- };
- const keyBindingLayouts = {
- default: Object.assign(
- {
- /* SearXNG layout */
- ArrowLeft: {
- key: "←",
- fun: highlightResult("up"),
- des: "select previous search result",
- cat: "Results"
- },
- ArrowRight: {
- key: "→",
- fun: highlightResult("down"),
- des: "select next search result",
- cat: "Results"
- }
- },
- baseKeyBinding
- ),
-
- vim: Object.assign(
- {
- /* Vim-like Key Layout. */
- b: {
- key: "b",
- fun: scrollPage(-window.innerHeight),
- des: "scroll one page up",
- cat: "Navigation"
- },
- f: {
- key: "f",
- fun: scrollPage(window.innerHeight),
- des: "scroll one page down",
- cat: "Navigation"
- },
- u: {
- key: "u",
- fun: scrollPage(-window.innerHeight / 2),
- des: "scroll half a page up",
- cat: "Navigation"
- },
- d: {
- key: "d",
- fun: scrollPage(window.innerHeight / 2),
- des: "scroll half a page down",
- cat: "Navigation"
- },
- g: {
- key: "g",
- fun: scrollPageTo(-document.body.scrollHeight, "top"),
- des: "scroll to the top of the page",
- cat: "Navigation"
- },
- v: {
- key: "v",
- fun: scrollPageTo(document.body.scrollHeight, "bottom"),
- des: "scroll to the bottom of the page",
- cat: "Navigation"
- },
- k: {
- key: "k",
- fun: highlightResult("up"),
- des: "select previous search result",
- cat: "Results"
- },
- j: {
- key: "j",
- fun: highlightResult("down"),
- des: "select next search result",
- cat: "Results"
- },
- y: {
- key: "y",
- fun: copyURLToClipboard,
- des: "copy url of the selected result to the clipboard",
- cat: "Results"
- }
- },
- baseKeyBinding
- )
- };
-
- const keyBindings = keyBindingLayouts[searxng.settings.hotkeys] || keyBindingLayouts.default;
-
- searxng.on(document, "keydown", (e) => {
- // check for modifiers so we don't break browser's hotkeys
- if (
- // biome-ignore lint/suspicious/noPrototypeBuiltins: FIXME: support for Chromium 93-87, Firefox 92-78, Safari 15.4-14
- Object.prototype.hasOwnProperty.call(keyBindings, e.key) &&
- !e.ctrlKey &&
- !e.altKey &&
- !e.shiftKey &&
- !e.metaKey
- ) {
- const tagName = e.target.tagName.toLowerCase();
- if (e.key === "Escape") {
- keyBindings[e.key].fun(e);
- } else {
- if (e.target === document.body || tagName === "a" || tagName === "button") {
- e.preventDefault();
- keyBindings[e.key].fun();
- }
- }
- }
- });
-
- function highlightResult(which) {
- return (noScroll, keepFocus) => {
- let current = document.querySelector(".result[data-vim-selected]"),
- effectiveWhich = which;
- if (current === null) {
- // no selection : choose the first one
- current = document.querySelector(".result");
- if (current === null) {
- // no first one : there are no results
- return;
- }
- // replace up/down actions by selecting first one
- if (which === "down" || which === "up") {
- effectiveWhich = current;
- }
- }
-
- let next,
- results = document.querySelectorAll(".result");
- results = Array.from(results); // convert NodeList to Array for further use
-
- if (typeof effectiveWhich !== "string") {
- next = effectiveWhich;
- } else {
- switch (effectiveWhich) {
- case "visible": {
- const top = document.documentElement.scrollTop || document.body.scrollTop;
- const bot = top + document.documentElement.clientHeight;
-
- for (let i = 0; i < results.length; i++) {
- next = results[i];
- const etop = next.offsetTop;
- const ebot = etop + next.clientHeight;
-
- if (ebot <= bot && etop > top) {
- break;
- }
- }
- break;
- }
- case "down":
- next = results[results.indexOf(current) + 1] || current;
- break;
- case "up":
- next = results[results.indexOf(current) - 1] || current;
- break;
- case "bottom":
- next = results[results.length - 1];
- break;
- // biome-ignore lint/complexity/noUselessSwitchCase: fallthrough is intended
- case "top":
- /* falls through */
- default:
- next = results[0];
- }
- }
-
- if (next) {
- current.removeAttribute("data-vim-selected");
- next.setAttribute("data-vim-selected", "true");
- if (!keepFocus) {
- const link = next.querySelector("h3 a") || next.querySelector("a");
- if (link !== null) {
- link.focus();
- }
- }
- if (!noScroll) {
- scrollPageToSelected();
- }
- }
- };
- }
-
- function reloadPage() {
- document.location.reload(true);
- }
-
- function removeFocus(e) {
- const tagName = e.target.tagName.toLowerCase();
- if (document.activeElement && (tagName === "input" || tagName === "select" || tagName === "textarea")) {
- document.activeElement.blur();
- } else {
- searxng.closeDetail();
- }
- }
-
- function pageButtonClick(css_selector) {
- return () => {
- const button = document.querySelector(css_selector);
- if (button) {
- button.click();
- }
- };
- }
-
- function GoToNextPage() {
- return pageButtonClick('nav#pagination .next_page button[type="submit"]');
- }
-
- function GoToPreviousPage() {
- return pageButtonClick('nav#pagination .previous_page button[type="submit"]');
- }
-
- function scrollPageToSelected() {
- const sel = document.querySelector(".result[data-vim-selected]");
- if (sel === null) {
- return;
- }
- const wtop = document.documentElement.scrollTop || document.body.scrollTop,
- wheight = document.documentElement.clientHeight,
- etop = sel.offsetTop,
- ebot = etop + sel.clientHeight,
- offset = 120;
- // first element ?
- if (sel.previousElementSibling === null && ebot < wheight) {
- // set to the top of page if the first element
- // is fully included in the viewport
- window.scroll(window.scrollX, 0);
- return;
- }
- if (wtop > etop - offset) {
- window.scroll(window.scrollX, etop - offset);
- } else {
- const wbot = wtop + wheight;
- if (wbot < ebot + offset) {
- window.scroll(window.scrollX, ebot - wheight + offset);
- }
- }
- }
-
- function scrollPage(amount) {
- return () => {
- window.scrollBy(0, amount);
- highlightResult("visible")();
- };
- }
-
- function scrollPageTo(position, nav) {
- return () => {
- window.scrollTo(0, position);
- highlightResult(nav)();
- };
- }
-
- function searchInputFocus() {
- window.scrollTo(0, 0);
- const q = document.querySelector("#q");
- q.focus();
- if (q.setSelectionRange) {
- const len = q.value.length;
- q.setSelectionRange(len, len);
- }
- }
-
- function openResult(newTab) {
- return () => {
- let link = document.querySelector(".result[data-vim-selected] h3 a");
- if (link === null) {
- link = document.querySelector(".result[data-vim-selected] > a");
- }
- if (link !== null) {
- const url = link.getAttribute("href");
- if (newTab) {
- window.open(url);
- } else {
- window.location.href = url;
- }
- }
- };
- }
-
- function initHelpContent(divElement) {
- const categories = {};
-
- for (const k in keyBindings) {
- const key = keyBindings[k];
- categories[key.cat] = categories[key.cat] || [];
- categories[key.cat].push(key);
- }
-
- const sorted = Object.keys(categories).sort((a, b) => categories[b].length - categories[a].length);
-
- if (sorted.length === 0) {
- return;
- }
-
- let html = '<a href="#" class="close" aria-label="close" title="close">×</a>';
- html += "<h3>How to navigate SearXNG with hotkeys</h3>";
- html += "<table>";
-
- for (let i = 0; i < sorted.length; i++) {
- const cat = categories[sorted[i]];
-
- const lastCategory = i === sorted.length - 1;
- const first = i % 2 === 0;
-
- if (first) {
- html += "<tr>";
- }
- html += "<td>";
-
- html += `<h4>${cat[0].cat}</h4>`;
- html += '<ul class="list-unstyled">';
-
- for (const cj in cat) {
- html += `<li><kbd>${cat[cj].key}</kbd> ${cat[cj].des}</li>`;
- }
-
- html += "</ul>";
- html += "</td>"; // col-sm-*
-
- if (!first || lastCategory) {
- html += "</tr>"; // row
- }
- }
-
- html += "</table>";
-
- divElement.innerHTML = html;
- }
-
- function toggleHelp() {
- let helpPanel = document.querySelector("#vim-hotkeys-help");
- if (helpPanel === undefined || helpPanel === null) {
- // first call
- helpPanel = document.createElement("div");
- helpPanel.id = "vim-hotkeys-help";
- helpPanel.className = "dialog-modal";
- initHelpContent(helpPanel);
- const body = document.getElementsByTagName("body")[0];
- body.appendChild(helpPanel);
- } else {
- // toggle hidden
- helpPanel.classList.toggle("invisible");
- }
- }
-
- function copyURLToClipboard() {
- const currentUrlElement = document.querySelector(".result[data-vim-selected] h3 a");
- if (currentUrlElement === null) return;
-
- const url = currentUrlElement.getAttribute("href");
- navigator.clipboard.writeText(url);
- }
-
- searxng.scrollPageToSelected = scrollPageToSelected;
- searxng.selectNext = highlightResult("down");
- searxng.selectPrevious = highlightResult("up");
-});