diff options
| author | Markus Heiser <markus.heiser@darmarit.de> | 2025-01-23 11:10:40 +0100 |
|---|---|---|
| committer | Markus Heiser <markus.heiser@darmarIT.de> | 2025-02-28 12:27:41 +0100 |
| commit | a1132deaa4618f228e82252397247a150081a5f3 (patch) | |
| tree | 0445fbe04c8932acdfbe5362db40ea1782f38539 /searx/static/themes/simple/src/js | |
| parent | b6487b70aaa199aba6ae999a9c99b340b5e98884 (diff) | |
[web-client] simple theme: move sources to client/simple/src
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
Diffstat (limited to 'searx/static/themes/simple/src/js')
| -rw-r--r-- | searx/static/themes/simple/src/js/head/00_init.js | 20 | ||||
| -rw-r--r-- | searx/static/themes/simple/src/js/main/00_toolkit.js | 165 | ||||
| -rw-r--r-- | searx/static/themes/simple/src/js/main/infinite_scroll.js | 88 | ||||
| -rw-r--r-- | searx/static/themes/simple/src/js/main/keyboard.js | 461 | ||||
| -rw-r--r-- | searx/static/themes/simple/src/js/main/mapresult.js | 74 | ||||
| -rw-r--r-- | searx/static/themes/simple/src/js/main/preferences.js | 53 | ||||
| -rw-r--r-- | searx/static/themes/simple/src/js/main/results.js | 181 | ||||
| -rw-r--r-- | searx/static/themes/simple/src/js/main/search.js | 207 |
8 files changed, 0 insertions, 1249 deletions
diff --git a/searx/static/themes/simple/src/js/head/00_init.js b/searx/static/themes/simple/src/js/head/00_init.js deleted file mode 100644 index a7c61c43e..000000000 --- a/searx/static/themes/simple/src/js/head/00_init.js +++ /dev/null @@ -1,20 +0,0 @@ -/* SPDX-License-Identifier: AGPL-3.0-or-later */ -(function (w, d) { - 'use strict'; - - // add data- properties - var script = d.currentScript || (function () { - var scripts = d.getElementsByTagName('script'); - return scripts[scripts.length - 1]; - })(); - - w.searxng = { - settings: JSON.parse(atob(script.getAttribute('client_settings'))) - }; - - // update the css - var htmlElement = d.getElementsByTagName("html")[0]; - htmlElement.classList.remove('no-js'); - htmlElement.classList.add('js'); - -})(window, document); diff --git a/searx/static/themes/simple/src/js/main/00_toolkit.js b/searx/static/themes/simple/src/js/main/00_toolkit.js deleted file mode 100644 index 4e374a019..000000000 --- a/searx/static/themes/simple/src/js/main/00_toolkit.js +++ /dev/null @@ -1,165 +0,0 @@ -/** - * @license - * (C) Copyright Contributors to the SearXNG project. - * (C) Copyright Contributors to the searx project (2014 - 2021). - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -window.searxng = (function (w, d) { - - 'use strict'; - - // not invented here toolkit with bugs fixed elsewhere - // purposes : be just good enough and as small as possible - - // from https://plainjs.com/javascript/events/live-binding-event-handlers-14/ - if (w.Element) { - (function (ElementPrototype) { - ElementPrototype.matches = ElementPrototype.matches || - ElementPrototype.matchesSelector || - ElementPrototype.webkitMatchesSelector || - ElementPrototype.msMatchesSelector || - function (selector) { - var node = this, nodes = (node.parentNode || node.document).querySelectorAll(selector), i = -1; - while (nodes[++i] && nodes[i] != node); - return !!nodes[i]; - }; - })(Element.prototype); - } - - function callbackSafe (callback, el, e) { - try { - callback.call(el, e); - } catch (exception) { - console.log(exception); - } - } - - var searxng = window.searxng || {}; - - searxng.on = function (obj, eventType, callback, useCapture) { - useCapture = useCapture || false; - if (typeof obj !== 'string') { - // obj HTMLElement, HTMLDocument - obj.addEventListener(eventType, callback, useCapture); - } else { - // obj is a selector - d.addEventListener(eventType, function (e) { - var el = e.target || e.srcElement, found = false; - while (el && el.matches && el !== d && !(found = el.matches(obj))) el = el.parentElement; - if (found) callbackSafe(callback, el, e); - }, useCapture); - } - }; - - searxng.ready = function (callback) { - if (document.readyState != 'loading') { - callback.call(w); - } else { - w.addEventListener('DOMContentLoaded', callback.bind(w)); - } - }; - - searxng.http = function (method, url, data = null) { - return new Promise(function (resolve, reject) { - try { - var req = new XMLHttpRequest(); - req.open(method, url, true); - req.timeout = 20000; - - // On load - req.onload = function () { - if (req.status == 200) { - resolve(req.response, req.responseType); - } else { - reject(Error(req.statusText)); - } - }; - - // Handle network errors - req.onerror = function () { - reject(Error("Network Error")); - }; - - req.onabort = function () { - reject(Error("Transaction is aborted")); - }; - - req.ontimeout = function () { - reject(Error("Timeout")); - } - - // Make the request - if (data) { - req.send(data) - } else { - req.send(); - } - } catch (ex) { - reject(ex); - } - }); - }; - - searxng.loadStyle = function (src) { - var path = searxng.settings.theme_static_path + "/" + src, - id = "style_" + src.replace('.', '_'), - s = d.getElementById(id); - if (s === null) { - s = d.createElement('link'); - s.setAttribute('id', id); - s.setAttribute('rel', 'stylesheet'); - s.setAttribute('type', 'text/css'); - s.setAttribute('href', path); - d.body.appendChild(s); - } - }; - - searxng.loadScript = function (src, callback) { - var path = searxng.settings.theme_static_path + "/" + src, - id = "script_" + src.replace('.', '_'), - s = d.getElementById(id); - if (s === null) { - s = d.createElement('script'); - s.setAttribute('id', id); - s.setAttribute('src', path); - s.onload = callback; - s.onerror = function () { - s.setAttribute('error', '1'); - }; - d.body.appendChild(s); - } else if (!s.hasAttribute('error')) { - try { - callback.apply(s, []); - } catch (exception) { - console.log(exception); - } - } else { - console.log("callback not executed : script '" + path + "' not loaded."); - } - }; - - searxng.insertBefore = function (newNode, referenceNode) { - referenceNode.parentNode.insertBefore(newNode, referenceNode); - }; - - searxng.insertAfter = function (newNode, referenceNode) { - referenceNode.parentNode.insertAfter(newNode, referenceNode.nextSibling); - }; - - searxng.on('.close', 'click', function () { - this.parentNode.classList.add('invisible'); - }); - - function getEndpoint () { - for (var className of d.getElementsByTagName('body')[0].classList.values()) { - if (className.endsWith('_endpoint')) { - return className.split('_')[0]; - } - } - return ''; - } - - searxng.endpoint = getEndpoint(); - - return searxng; -})(window, document); diff --git a/searx/static/themes/simple/src/js/main/infinite_scroll.js b/searx/static/themes/simple/src/js/main/infinite_scroll.js deleted file mode 100644 index 07db3305a..000000000 --- a/searx/static/themes/simple/src/js/main/infinite_scroll.js +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later - -/* global searxng */ - -searxng.ready(function () { - 'use strict'; - - searxng.infinite_scroll_supported = ( - 'IntersectionObserver' in window && - 'IntersectionObserverEntry' in window && - 'intersectionRatio' in window.IntersectionObserverEntry.prototype); - - if (searxng.endpoint !== 'results') { - return; - } - - if (!searxng.infinite_scroll_supported) { - console.log('IntersectionObserver not supported'); - return; - } - - let d = document; - var onlyImages = d.getElementById('results').classList.contains('only_template_images'); - - function newLoadSpinner () { - var loader = d.createElement('div'); - loader.classList.add('loader'); - return loader; - } - - function replaceChildrenWith (element, children) { - element.textContent = ''; - children.forEach(child => element.appendChild(child)); - } - - function loadNextPage (callback) { - var form = d.querySelector('#pagination form.next_page'); - if (!form) { - return - } - replaceChildrenWith(d.querySelector('#pagination'), [ newLoadSpinner() ]); - var formData = new FormData(form); - searxng.http('POST', d.querySelector('#search').getAttribute('action'), formData).then( - function (response) { - var nextPageDoc = new DOMParser().parseFromString(response, 'text/html'); - var articleList = nextPageDoc.querySelectorAll('#urls article'); - var paginationElement = nextPageDoc.querySelector('#pagination'); - d.querySelector('#pagination').remove(); - if (articleList.length > 0 && !onlyImages) { - // do not add <hr> element when there are only images - d.querySelector('#urls').appendChild(d.createElement('hr')); - } - articleList.forEach(articleElement => { - d.querySelector('#urls').appendChild(articleElement); - }); - if (paginationElement) { - d.querySelector('#results').appendChild(paginationElement); - callback(); - } - } - ).catch( - function (err) { - console.log(err); - var e = d.createElement('div'); - e.textContent = searxng.settings.translations.error_loading_next_page; - e.classList.add('dialog-error'); - e.setAttribute('role', 'alert'); - replaceChildrenWith(d.querySelector('#pagination'), [ e ]); - } - ) - } - - if (searxng.settings.infinite_scroll && searxng.infinite_scroll_supported) { - const intersectionObserveOptions = { - rootMargin: "20rem", - }; - const observedSelector = 'article.result:last-child'; - const observer = new IntersectionObserver(entries => { - const paginationEntry = entries[0]; - if (paginationEntry.isIntersecting) { - observer.unobserve(paginationEntry.target); - loadNextPage(() => observer.observe(d.querySelector(observedSelector), intersectionObserveOptions)); - } - }); - observer.observe(d.querySelector(observedSelector), intersectionObserveOptions); - } - -}); diff --git a/searx/static/themes/simple/src/js/main/keyboard.js b/searx/static/themes/simple/src/js/main/keyboard.js deleted file mode 100644 index e16134579..000000000 --- a/searx/static/themes/simple/src/js/main/keyboard.js +++ /dev/null @@ -1,461 +0,0 @@ -/* SPDX-License-Identifier: AGPL-3.0-or-later */ -/* global searxng */ - -searxng.ready(function () { - - 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 && resultElement.classList.contains('result-images'); - } - - searxng.on('.result', 'click', function (e) { - if (!isElementInDetail(e.target)) { - highlightResult(this)(true, true); - let resultElement = getResultElement(e.target); - if (isImageResult(resultElement)) { - e.preventDefault(); - searxng.selectImage(resultElement); - } - } - }); - - searxng.on('.result a', 'focus', function (e) { - if (!isElementInDetail(e.target)) { - let 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 */ - var 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' - }, - }; - var 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) - } - - var keyBindings = keyBindingLayouts[searxng.settings.hotkeys] || keyBindingLayouts.default; - - searxng.on(document, "keydown", function (e) { - // check for modifiers so we don't break browser's hotkeys - if ( - Object.prototype.hasOwnProperty.call(keyBindings, e.key) - && !e.ctrlKey && !e.altKey - && !e.shiftKey && !e.metaKey - ) { - var 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 function (noScroll, keepFocus) { - var 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; - } - } - - var 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': - var top = document.documentElement.scrollTop || document.body.scrollTop; - var bot = top + document.documentElement.clientHeight; - - for (var i = 0; i < results.length; i++) { - next = results[i]; - var etop = next.offsetTop; - var 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; - case 'top': - /* falls through */ - default: - next = results[0]; - } - } - - if (next) { - current.removeAttribute('data-vim-selected'); - next.setAttribute('data-vim-selected', 'true'); - if (!keepFocus) { - var 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 function () { - var 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 () { - var sel = document.querySelector('.result[data-vim-selected]'); - if (sel === null) { - return; - } - var 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 { - var wbot = wtop + wheight; - if (wbot < (ebot + offset)) { - window.scroll(window.scrollX, ebot - wheight + offset); - } - } - } - - function scrollPage (amount) { - return function () { - window.scrollBy(0, amount); - highlightResult('visible')(); - }; - } - - function scrollPageTo (position, nav) { - return function () { - window.scrollTo(0, position); - highlightResult(nav)(); - }; - } - - function searchInputFocus () { - window.scrollTo(0, 0); - var q = document.querySelector('#q'); - q.focus(); - if (q.setSelectionRange) { - var len = q.value.length; - q.setSelectionRange(len, len); - } - } - - function openResult (newTab) { - return function () { - var link = document.querySelector('.result[data-vim-selected] h3 a'); - if (link === null) { - link = document.querySelector('.result[data-vim-selected] > a'); - } - if (link !== null) { - var url = link.getAttribute('href'); - if (newTab) { - window.open(url); - } else { - window.location.href = url; - } - } - }; - } - - function initHelpContent (divElement) { - var categories = {}; - - for (var k in keyBindings) { - var key = keyBindings[k]; - categories[key.cat] = categories[key.cat] || []; - categories[key.cat].push(key); - } - - var sorted = Object.keys(categories).sort(function (a, b) { - return categories[b].length - categories[a].length; - }); - - if (sorted.length === 0) { - return; - } - - var html = '<a href="#" class="close" aria-label="close" title="close">×</a>'; - html += '<h3>How to navigate SearXNG with hotkeys</h3>'; - html += '<table>'; - - for (var i = 0; i < sorted.length; i++) { - var cat = categories[sorted[i]]; - - var lastCategory = i === (sorted.length - 1); - var first = i % 2 === 0; - - if (first) { - html += '<tr>'; - } - html += '<td>'; - - html += '<h4>' + cat[0].cat + '</h4>'; - html += '<ul class="list-unstyled">'; - - for (var 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 () { - var 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); - var body = document.getElementsByTagName('body')[0]; - body.appendChild(helpPanel); - } else { - // toggle hidden - helpPanel.classList.toggle('invisible'); - return; - } - } - - function copyURLToClipboard () { - var 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'); -}); diff --git a/searx/static/themes/simple/src/js/main/mapresult.js b/searx/static/themes/simple/src/js/main/mapresult.js deleted file mode 100644 index 2c3777678..000000000 --- a/searx/static/themes/simple/src/js/main/mapresult.js +++ /dev/null @@ -1,74 +0,0 @@ -/* SPDX-License-Identifier: AGPL-3.0-or-later */ -/* global L */ -(function (w, d, searxng) { - 'use strict'; - - searxng.ready(function () { - searxng.on('.searxng_init_map', 'click', function (event) { - // no more request - this.classList.remove("searxng_init_map"); - - // - var leaflet_target = this.dataset.leafletTarget; - var map_lon = parseFloat(this.dataset.mapLon); - var map_lat = parseFloat(this.dataset.mapLat); - var map_zoom = parseFloat(this.dataset.mapZoom); - var map_boundingbox = JSON.parse(this.dataset.mapBoundingbox); - var map_geojson = JSON.parse(this.dataset.mapGeojson); - - searxng.loadStyle('css/leaflet.css'); - searxng.loadScript('js/leaflet.js', function () { - var map_bounds = null; - if (map_boundingbox) { - var southWest = L.latLng(map_boundingbox[0], map_boundingbox[2]); - var northEast = L.latLng(map_boundingbox[1], map_boundingbox[3]); - map_bounds = L.latLngBounds(southWest, northEast); - } - - // init map - var map = L.map(leaflet_target); - // create the tile layer with correct attribution - var osmMapnikUrl = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'; - var osmMapnikAttrib = 'Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors'; - var osmMapnik = new L.TileLayer(osmMapnikUrl, {minZoom: 1, maxZoom: 19, attribution: osmMapnikAttrib}); - var osmWikimediaUrl = 'https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png'; - var osmWikimediaAttrib = 'Wikimedia maps | Maps data © <a href="https://openstreetmap.org">OpenStreetMap contributors</a>'; - var osmWikimedia = new L.TileLayer(osmWikimediaUrl, {minZoom: 1, maxZoom: 19, attribution: osmWikimediaAttrib}); - // init map view - if (map_bounds) { - // TODO hack: https://github.com/Leaflet/Leaflet/issues/2021 - // Still useful ? - setTimeout(function () { - map.fitBounds(map_bounds, { - maxZoom: 17 - }); - }, 0); - } else if (map_lon && map_lat) { - if (map_zoom) { - map.setView(new L.latLng(map_lat, map_lon), map_zoom); - } else { - map.setView(new L.latLng(map_lat, map_lon), 8); - } - } - - map.addLayer(osmMapnik); - - var baseLayers = { - "OSM Mapnik": osmMapnik, - "OSM Wikimedia": osmWikimedia, - }; - - L.control.layers(baseLayers).addTo(map); - - if (map_geojson) { - L.geoJson(map_geojson).addTo(map); - } /* else if(map_bounds) { - L.rectangle(map_bounds, {color: "#ff7800", weight: 3, fill:false}).addTo(map); - } */ - }); - - // this event occur only once per element - event.preventDefault(); - }); - }); -})(window, document, window.searxng); diff --git a/searx/static/themes/simple/src/js/main/preferences.js b/searx/static/themes/simple/src/js/main/preferences.js deleted file mode 100644 index a0b853d61..000000000 --- a/searx/static/themes/simple/src/js/main/preferences.js +++ /dev/null @@ -1,53 +0,0 @@ -/* SPDX-License-Identifier: AGPL-3.0-or-later */ -(function (w, d, searxng) { - 'use strict'; - - if (searxng.endpoint !== 'preferences') { - return; - } - - searxng.ready(function () { - let engine_descriptions = null; - function load_engine_descriptions () { - if (engine_descriptions == null) { - searxng.http("GET", "engine_descriptions.json").then(function (content) { - engine_descriptions = JSON.parse(content); - for (const [engine_name, description] of Object.entries(engine_descriptions)) { - let elements = d.querySelectorAll('[data-engine-name="' + engine_name + '"] .engine-description'); - for (const element of elements) { - let source = ' (<i>' + searxng.settings.translations.Source + ': ' + description[1] + '</i>)'; - element.innerHTML = description[0] + source; - } - } - }); - } - } - - for (const el of d.querySelectorAll('[data-engine-name]')) { - searxng.on(el, 'mouseenter', load_engine_descriptions); - } - - const enableAllEngines = d.querySelectorAll(".enable-all-engines"); - const disableAllEngines = d.querySelectorAll(".disable-all-engines"); - const engineToggles = d.querySelectorAll('tbody input[type=checkbox][class~=checkbox-onoff]'); - const toggleEngines = (enable) => { - for (const el of engineToggles) { - // check if element visible, so that only engines of the current category are modified - if (el.offsetParent !== null) el.checked = !enable; - } - }; - for (const el of enableAllEngines) { - searxng.on(el, 'click', () => toggleEngines(true)); - } - for (const el of disableAllEngines) { - searxng.on(el, 'click', () => toggleEngines(false)); - } - - const copyHashButton = d.querySelector("#copy-hash"); - searxng.on(copyHashButton, 'click', (e) => { - e.preventDefault(); - navigator.clipboard.writeText(copyHashButton.dataset.hash); - copyHashButton.innerText = copyHashButton.dataset.copiedText; - }); - }); -})(window, document, window.searxng); diff --git a/searx/static/themes/simple/src/js/main/results.js b/searx/static/themes/simple/src/js/main/results.js deleted file mode 100644 index cc7c7efcd..000000000 --- a/searx/static/themes/simple/src/js/main/results.js +++ /dev/null @@ -1,181 +0,0 @@ -/* SPDX-License-Identifier: AGPL-3.0-or-later */ -(function (w, d, searxng) { - 'use strict'; - - if (searxng.endpoint !== 'results') { - return; - } - - searxng.ready(function () { - d.querySelectorAll('#urls img').forEach( - img => - img.addEventListener( - 'error', () => { - // console.log("ERROR can't load: " + img.src); - img.src = window.searxng.settings.theme_static_path + "/img/img_load_error.svg"; - }, - {once: true} - )); - - if (d.querySelector('#search_url button#copy_url')) { - d.querySelector('#search_url button#copy_url').style.display = "block"; - } - - searxng.on('.btn-collapse', 'click', function () { - var btnLabelCollapsed = this.getAttribute('data-btn-text-collapsed'); - var btnLabelNotCollapsed = this.getAttribute('data-btn-text-not-collapsed'); - var target = this.getAttribute('data-target'); - var targetElement = d.querySelector(target); - var html = this.innerHTML; - if (this.classList.contains('collapsed')) { - html = html.replace(btnLabelCollapsed, btnLabelNotCollapsed); - } else { - html = html.replace(btnLabelNotCollapsed, btnLabelCollapsed); - } - this.innerHTML = html; - this.classList.toggle('collapsed'); - targetElement.classList.toggle('invisible'); - }); - - searxng.on('.media-loader', 'click', function () { - var target = this.getAttribute('data-target'); - var iframe_load = d.querySelector(target + ' > iframe'); - var srctest = iframe_load.getAttribute('src'); - if (srctest === null || srctest === undefined || srctest === false) { - iframe_load.setAttribute('src', iframe_load.getAttribute('data-src')); - } - }); - - searxng.on('#copy_url', 'click', function () { - var target = this.parentElement.querySelector('pre'); - navigator.clipboard.writeText(target.innerText); - this.innerText = this.dataset.copiedText; - }); - - // searxng.selectImage (gallery) - // ----------------------------- - - // setTimeout() ID, needed to cancel *last* loadImage - let imgTimeoutID; - - // progress spinner, while an image is loading - const imgLoaderSpinner = d.createElement('div'); - imgLoaderSpinner.classList.add('loader'); - - // singleton image object, which is used for all loading processes of a - // detailed image - const imgLoader = new Image(); - - const loadImage = (imgSrc, onSuccess) => { - // if defered image load exists, stop defered task. - if (imgTimeoutID) clearTimeout(imgTimeoutID); - - // defer load of the detail image for 1 sec - imgTimeoutID = setTimeout(() => { - imgLoader.src = imgSrc; - }, 1000); - - // set handlers in the on-properties - imgLoader.onload = () => { - onSuccess(); - imgLoaderSpinner.remove(); - }; - imgLoader.onerror = () => { - imgLoaderSpinner.remove(); - }; - }; - - searxng.selectImage = (resultElement) => { - - // add a class that can be evaluated in the CSS and indicates that the - // detail view is open - d.getElementById('results').classList.add('image-detail-open'); - - // add a hash to the browser history so that pressing back doesn't return - // to the previous page this allows us to dismiss the image details on - // pressing the back button on mobile devices - window.location.hash = '#image-viewer'; - - searxng.scrollPageToSelected(); - - // if there is none element given by the caller, stop here - if (!resultElement) return; - - // find <img> object in the element, if there is none, stop here. - const img = resultElement.querySelector('.result-images-source img'); - if (!img) return; - - // <img src="" data-src="http://example.org/image.jpg"> - const src = img.getAttribute('data-src'); - - // already loaded high-res image or no high-res image available - if (!src) return; - - // use the image thumbnail until the image is fully loaded - const thumbnail = resultElement.querySelector('.image_thumbnail'); - img.src = thumbnail.src; - - // show a progress spinner - const detailElement = resultElement.querySelector('.detail'); - detailElement.appendChild(imgLoaderSpinner); - - // load full size image in background - loadImage(src, () => { - // after the singelton loadImage has loaded the detail image into the - // cache, it can be used in the origin <img> as src property. - img.src = src; - img.removeAttribute('data-src'); - }); - }; - - searxng.closeDetail = function () { - d.getElementById('results').classList.remove('image-detail-open'); - // remove #image-viewer hash from url by navigating back - if (window.location.hash == '#image-viewer') window.history.back(); - searxng.scrollPageToSelected(); - }; - searxng.on('.result-detail-close', 'click', e => { - e.preventDefault(); - searxng.closeDetail(); - }); - searxng.on('.result-detail-previous', 'click', e => { - e.preventDefault(); - searxng.selectPrevious(false); - }); - searxng.on('.result-detail-next', 'click', e => { - e.preventDefault(); - searxng.selectNext(false); - }); - - // listen for the back button to be pressed and dismiss the image details when called - window.addEventListener('hashchange', () => { - if (window.location.hash != '#image-viewer') searxng.closeDetail(); - }); - - d.querySelectorAll('.swipe-horizontal').forEach( - obj => { - obj.addEventListener('swiped-left', function () { - searxng.selectNext(false); - }); - obj.addEventListener('swiped-right', function () { - searxng.selectPrevious(false); - }); - } - ); - - w.addEventListener('scroll', function () { - var e = d.getElementById('backToTop'), - scrollTop = document.documentElement.scrollTop || document.body.scrollTop, - results = d.getElementById('results'); - if (e !== null) { - if (scrollTop >= 100) { - results.classList.add('scrolling'); - } else { - results.classList.remove('scrolling'); - } - } - }, true); - - }); - -})(window, document, window.searxng); diff --git a/searx/static/themes/simple/src/js/main/search.js b/searx/static/themes/simple/src/js/main/search.js deleted file mode 100644 index 1a53713c0..000000000 --- a/searx/static/themes/simple/src/js/main/search.js +++ /dev/null @@ -1,207 +0,0 @@ -/* SPDX-License-Identifier: AGPL-3.0-or-later */ -/* global AutoComplete */ -(function (w, d, searxng) { - 'use strict'; - - var qinput_id = "q", qinput; - - const isMobile = window.matchMedia("only screen and (max-width: 50em)").matches; - - function submitIfQuery () { - if (qinput.value.length > 0) { - var search = document.getElementById('search'); - setTimeout(search.submit.bind(search), 0); - } - } - - function createClearButton (qinput) { - var cs = document.getElementById('clear_search'); - var updateClearButton = function () { - if (qinput.value.length === 0) { - cs.classList.add("empty"); - } else { - cs.classList.remove("empty"); - } - }; - - // update status, event listener - updateClearButton(); - cs.addEventListener('click', function (ev) { - qinput.value = ''; - qinput.focus(); - updateClearButton(); - ev.preventDefault(); - }); - qinput.addEventListener('input', updateClearButton, false); - } - - searxng.ready(function () { - qinput = d.getElementById(qinput_id); - - if (qinput !== null) { - // clear button - createClearButton(qinput); - - // autocompleter - if (searxng.settings.autocomplete) { - searxng.autocomplete = AutoComplete.call(w, { - Url: "./autocompleter", - EmptyMessage: searxng.settings.translations.no_item_found, - HttpMethod: searxng.settings.method, - HttpHeaders: { - "Content-type": "application/x-www-form-urlencoded", - "X-Requested-With": "XMLHttpRequest" - }, - MinChars: searxng.settings.autocomplete_min, - Delay: 300, - _Position: function () {}, - _Open: function () { - var params = this; - Array.prototype.forEach.call(this.DOMResults.getElementsByTagName("li"), function (li) { - if (li.getAttribute("class") != "locked") { - li.onmousedown = function () { - params._Select(li); - }; - } - }); - }, - _Select: function (item) { - AutoComplete.defaults._Select.call(this, item); - var form = item.closest('form'); - if (form) { - form.submit(); - } - }, - _MinChars: function () { - if (this.Input.value.indexOf('!') > -1) { - return 0; - } else { - return AutoComplete.defaults._MinChars.call(this); - } - }, - KeyboardMappings: Object.assign({}, AutoComplete.defaults.KeyboardMappings, { - "KeyUpAndDown_up": Object.assign({}, AutoComplete.defaults.KeyboardMappings.KeyUpAndDown_up, { - Callback: function (event) { - AutoComplete.defaults.KeyboardMappings.KeyUpAndDown_up.Callback.call(this, event); - var liActive = this.DOMResults.querySelector("li.active"); - if (liActive) { - AutoComplete.defaults._Select.call(this, liActive); - } - }, - }), - "Tab": Object.assign({}, AutoComplete.defaults.KeyboardMappings.Enter, { - Conditions: [{ - Is: 9, - Not: false - }], - Callback: function (event) { - if (this.DOMResults.getAttribute("class").indexOf("open") != -1) { - var liActive = this.DOMResults.querySelector("li.active"); - if (liActive !== null) { - AutoComplete.defaults._Select.call(this, liActive); - event.preventDefault(); - } - } - }, - }) - }), - }, "#" + qinput_id); - } - - /* - Monkey patch autocomplete.js to fix a bug - With the POST method, the values are not URL encoded: query like "1 + 1" are sent as "1 1" since space are URL encoded as plus. - See HTML specifications: - * HTML5: https://url.spec.whatwg.org/#concept-urlencoded-serializer - * HTML4: https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 - - autocomplete.js does not URL encode the name and values: - https://github.com/autocompletejs/autocomplete.js/blob/87069524f3b95e68f1b54d8976868e0eac1b2c83/src/autocomplete.ts#L665 - - The monkey patch overrides the compiled version of the ajax function. - See https://github.com/autocompletejs/autocomplete.js/blob/87069524f3b95e68f1b54d8976868e0eac1b2c83/dist/autocomplete.js#L143-L158 - The patch changes only the line 156 from - params.Request.send(params._QueryArg() + "=" + params._Pre()); - to - params.Request.send(encodeURIComponent(params._QueryArg()) + "=" + encodeURIComponent(params._Pre())); - - Related to: - * https://github.com/autocompletejs/autocomplete.js/issues/78 - * https://github.com/searxng/searxng/issues/1695 - */ - AutoComplete.prototype.ajax = function (params, request, timeout) { - if (timeout === void 0) { timeout = true; } - if (params.$AjaxTimer) { - window.clearTimeout(params.$AjaxTimer); - } - if (timeout === true) { - params.$AjaxTimer = window.setTimeout(AutoComplete.prototype.ajax.bind(null, params, request, false), params.Delay); - } else { - if (params.Request) { - params.Request.abort(); - } - params.Request = request; - params.Request.send(encodeURIComponent(params._QueryArg()) + "=" + encodeURIComponent(params._Pre())); - } - }; - - if (!isMobile && document.querySelector('.index_endpoint')) { - qinput.focus(); - } - } - - // 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 ( - qinput !== null - && 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 - && d.querySelector(".search_filters") != null - ) { - searxng.on(d.getElementById('safesearch'), 'change', submitIfQuery); - searxng.on(d.getElementById('time_range'), 'change', submitIfQuery); - searxng.on(d.getElementById('language'), 'change', submitIfQuery); - } - - const categoryButtons = d.querySelectorAll("button.category_button"); - for (let button of categoryButtons) { - searxng.on(button, 'click', (event) => { - if (event.shiftKey) { - event.preventDefault(); - button.classList.toggle("selected"); - return; - } - - // manually deselect the old selection when a new category is selected - const selectedCategories = d.querySelectorAll("button.category_button.selected"); - for (let categoryButton of selectedCategories) { - categoryButton.classList.remove("selected"); - } - button.classList.add("selected"); - }) - } - - // override form submit action to update the actually selected categories - const form = d.querySelector("#search"); - if (form != null) { - searxng.on(form, 'submit', (event) => { - event.preventDefault(); - const categoryValuesInput = d.querySelector("#selected-categories"); - if (categoryValuesInput) { - let categoryValues = []; - for (let categoryButton of categoryButtons) { - if (categoryButton.classList.contains("selected")) { - categoryValues.push(categoryButton.name.replace("category_", "")); - } - } - categoryValuesInput.value = categoryValues.join(","); - } - form.submit(); - }); - } - }); - -})(window, document, window.searxng); |