diff options
author | Alexandre Flament <alex@al-f.net> | 2022-01-23 11:37:57 +0100 |
---|---|---|
committer | Alexandre Flament <alex@al-f.net> | 2022-02-20 22:58:51 +0100 |
commit | 56e34947a6368e6154064c52fa23d21ecda7ab4c (patch) | |
tree | bad1463a0c3056896cfacb205039586b85a2c04d /searx/static | |
parent | 36aee70c247fe347c69abb17ec3bdc31781204c6 (diff) | |
download | searxng-56e34947a6368e6154064c52fa23d21ecda7ab4c.tar.gz searxng-56e34947a6368e6154064c52fa23d21ecda7ab4c.zip |
[mod] infinite_scroll as preference
* oscar theme: code from searx/plugins/infinite_scroll.py
* simple theme: new implementation
Co-authored-by: Markus Heiser <markus.heiser@darmarIT.de>
Diffstat (limited to 'searx/static')
-rw-r--r-- | searx/static/plugins/js/infinite_scroll.js | 40 | ||||
-rw-r--r-- | searx/static/themes/__common__/js/image_layout.js | 93 | ||||
-rw-r--r-- | searx/static/themes/oscar/gruntfile.js | 2 | ||||
-rw-r--r-- | searx/static/themes/oscar/src/js/01_init.js | 1 | ||||
-rw-r--r-- | searx/static/themes/oscar/src/js/infinite_scroll.js | 50 | ||||
-rw-r--r-- | searx/static/themes/oscar/src/less/infinite_scroll.less (renamed from searx/static/plugins/css/infinite_scroll.css) | 4 | ||||
-rw-r--r-- | searx/static/themes/oscar/src/less/logicodev-dark/oscar.less | 1 | ||||
-rw-r--r-- | searx/static/themes/oscar/src/less/logicodev/oscar.less | 1 | ||||
-rw-r--r-- | searx/static/themes/oscar/src/less/pointhi/oscar.less | 1 | ||||
-rw-r--r-- | searx/static/themes/simple/src/js/main/00_toolkit.js | 81 | ||||
-rw-r--r-- | searx/static/themes/simple/src/js/main/infinite_scroll.js | 88 | ||||
-rw-r--r-- | searx/static/themes/simple/src/js/main/preferences.js | 10 | ||||
-rw-r--r-- | searx/static/themes/simple/src/js/main/results.js | 4 | ||||
-rw-r--r-- | searx/static/themes/simple/src/less/style.less | 8 |
14 files changed, 274 insertions, 110 deletions
diff --git a/searx/static/plugins/js/infinite_scroll.js b/searx/static/plugins/js/infinite_scroll.js deleted file mode 100644 index cd8096571..000000000 --- a/searx/static/plugins/js/infinite_scroll.js +++ /dev/null @@ -1,40 +0,0 @@ -function hasScrollbar() { - var root = document.compatMode=='BackCompat'? document.body : document.documentElement; - return root.scrollHeight>root.clientHeight; -} - -function loadNextPage() { - var formData = $('#pagination form:last').serialize(); - if (formData) { - $('#pagination').html('<div class="loading-spinner"></div>'); - $.ajax({ - type: "POST", - url: $('#search_form').prop('action'), - data: formData, - dataType: 'html', - success: function(data) { - var body = $(data); - $('#pagination').remove(); - $('#main_results').append('<hr/>'); - $('#main_results').append(body.find('.result')); - $('#main_results').append(body.find('#pagination')); - if(!hasScrollbar()) { - loadNextPage(); - } - } - }); - } -} - -$(document).ready(function() { - var win = $(window); - if(!hasScrollbar()) { - loadNextPage(); - } - win.scroll(function() { - $("#pagination button").css("visibility", "hidden"); - if ($(document).height() - win.height() - win.scrollTop() < 150) { - loadNextPage(); - } - }); -}); diff --git a/searx/static/themes/__common__/js/image_layout.js b/searx/static/themes/__common__/js/image_layout.js index e37058dfa..329fa46a8 100644 --- a/searx/static/themes/__common__/js/image_layout.js +++ b/searx/static/themes/__common__/js/image_layout.js @@ -29,7 +29,8 @@ this.verticalMargin = verticalMargin; this.horizontalMargin = horizontalMargin; this.maxHeight = maxHeight; - this.isAlignDone = true; + this.trottleCallToAlign = null; + this.alignAfterThrotteling = false; } /** @@ -72,12 +73,12 @@ // not loaded image : make it square as _getHeigth said it imgWidth = height; } - img.style.width = imgWidth + 'px'; - img.style.height = height + 'px'; - img.style.marginLeft = this.horizontalMargin + 'px'; - img.style.marginTop = this.horizontalMargin + 'px'; - img.style.marginRight = this.verticalMargin - 7 + 'px'; // -4 is the negative margin of the inline element - img.style.marginBottom = this.verticalMargin - 7 + 'px'; + img.setAttribute('width', Math.round(imgWidth)); + img.setAttribute('height', Math.round(height)); + img.style.marginLeft = Math.round(this.horizontalMargin) + 'px'; + img.style.marginTop = Math.round(this.horizontalMargin) + 'px'; + img.style.marginRight = Math.round(this.verticalMargin - 7) + 'px'; // -4 is the negative margin of the inline element + img.style.marginBottom = Math.round(this.verticalMargin - 7) + 'px'; resultNode = img.parentNode.parentNode; if (!resultNode.classList.contains('js')) { resultNode.classList.add('js'); @@ -112,6 +113,23 @@ } }; + ImageLayout.prototype.throttleAlign = function () { + var obj = this; + if (obj.trottleCallToAlign) { + obj.alignAfterThrotteling = true; + } else { + obj.alignAfterThrotteling = false; + obj.align(); + obj.trottleCallToAlign = setTimeout(function () { + if (obj.alignAfterThrotteling) { + obj.align(); + } + obj.alignAfterThrotteling = false; + obj.trottleCallToAlign = null; + }, 20); + } + } + ImageLayout.prototype.align = function () { var i; var results_selectorNode = d.querySelectorAll(this.results_selector); @@ -141,9 +159,9 @@ } }; - ImageLayout.prototype.watch = function () { + ImageLayout.prototype._monitorImages = function () { var i, img; - var obj = this; + var objthrottleAlign = this.throttleAlign.bind(this); var results_nodes = d.querySelectorAll(this.results_selector); var results_length = results_nodes.length; @@ -152,34 +170,53 @@ event.originalTarget.src = w.searxng.static_path + w.searxng.theme.img_load_error; } - function throttleAlign () { - if (obj.isAlignDone) { - obj.isAlignDone = false; - setTimeout(function () { - obj.align(); - obj.isAlignDone = true; - }, 100); + for (i = 0; i < results_length; i++) { + img = results_nodes[i].querySelector(this.img_selector); + if (img !== null && img !== undefined && !img.classList.contains('aligned')) { + img.addEventListener('load', objthrottleAlign); + // https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror + img.addEventListener('error', objthrottleAlign); + img.addEventListener('timeout', objthrottleAlign); + if (w.searxng.theme.img_load_error) { + img.addEventListener('error', img_load_error, {once: true}); + } + img.classList.add('aligned'); } } + } + + ImageLayout.prototype.watch = function () { + var objthrottleAlign = this.throttleAlign.bind(this); // https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event - w.addEventListener('pageshow', throttleAlign); + w.addEventListener('pageshow', objthrottleAlign); // https://developer.mozilla.org/en-US/docs/Web/API/FileReader/load_event - w.addEventListener('load', throttleAlign); + w.addEventListener('load', objthrottleAlign); // https://developer.mozilla.org/en-US/docs/Web/API/Window/resize_event - w.addEventListener('resize', throttleAlign); + w.addEventListener('resize', objthrottleAlign); - for (i = 0; i < results_length; i++) { - img = results_nodes[i].querySelector(this.img_selector); - if (img !== null && img !== undefined) { - img.addEventListener('load', throttleAlign); - // https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror - img.addEventListener('error', throttleAlign); - if (w.searxng.theme.img_load_error) { - img.addEventListener('error', img_load_error, {once: true}); + this._monitorImages(); + + var obj = this; + + let observer = new MutationObserver(entries => { + let newElement = false; + for (let i = 0; i < entries.length; i++) { + if (entries[i].addedNodes.length > 0 && entries[i].addedNodes[0].classList.contains('result')) { + newElement = true; + break; } } - } + if (newElement) { + obj._monitorImages(); + } + }); + observer.observe(d.querySelector(this.container_selector), { + childList: true, + subtree: true, + attributes: false, + characterData: false, + }) }; w.searxng.ImageLayout = ImageLayout; diff --git a/searx/static/themes/oscar/gruntfile.js b/searx/static/themes/oscar/gruntfile.js index 8e118afd6..2f87e289f 100644 --- a/searx/static/themes/oscar/gruntfile.js +++ b/searx/static/themes/oscar/gruntfile.js @@ -78,7 +78,7 @@ module.exports = function(grunt) { } }, jshint: { - files: ['gruntfile.js', 'src/js/*.js', '../__common__/js/image_layout.js'], + files: ['gruntfile.js', 'src/js/*.js'], // files in __common__ are linted by es lint in simple theme options: { reporterOutput: "", esversion: 6, diff --git a/searx/static/themes/oscar/src/js/01_init.js b/searx/static/themes/oscar/src/js/01_init.js index 8853d9909..f72b0078b 100644 --- a/searx/static/themes/oscar/src/js/01_init.js +++ b/searx/static/themes/oscar/src/js/01_init.js @@ -19,6 +19,7 @@ window.searxng = (function(d) { return { autocompleter: script.getAttribute('data-autocompleter') === 'true', + infinite_scroll: script.getAttribute('data-infinite-scroll') === 'true', method: script.getAttribute('data-method'), translations: JSON.parse(script.getAttribute('data-translations')) }; diff --git a/searx/static/themes/oscar/src/js/infinite_scroll.js b/searx/static/themes/oscar/src/js/infinite_scroll.js new file mode 100644 index 000000000..6dbff5fef --- /dev/null +++ b/searx/static/themes/oscar/src/js/infinite_scroll.js @@ -0,0 +1,50 @@ +/** + * @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 + */ + +$(document).ready(function() { + function hasScrollbar() { + var root = document.compatMode=='BackCompat'? document.body : document.documentElement; + return root.scrollHeight>root.clientHeight; + } + + function loadNextPage() { + var formData = $('#pagination form:last').serialize(); + if (formData) { + $('#pagination').html('<div class="loading-spinner"></div>'); + $.ajax({ + type: "POST", + url: $('#search_form').prop('action'), + data: formData, + dataType: 'html', + success: function(data) { + var body = $(data); + $('#pagination').remove(); + $('#main_results').append('<hr/>'); + $('#main_results').append(body.find('.result')); + $('#main_results').append(body.find('#pagination')); + if(!hasScrollbar()) { + loadNextPage(); + } + } + }); + } + } + + if (searxng.infinite_scroll) { + var win = $(window); + $("html").addClass('infinite_scroll'); + if(!hasScrollbar()) { + loadNextPage(); + } + win.on('scroll', function() { + if ($(document).height() - win.height() - win.scrollTop() < 150) { + loadNextPage(); + } + }); + } + +}); diff --git a/searx/static/plugins/css/infinite_scroll.css b/searx/static/themes/oscar/src/less/infinite_scroll.less index 07b9f6de9..f66373651 100644 --- a/searx/static/plugins/css/infinite_scroll.css +++ b/searx/static/themes/oscar/src/less/infinite_scroll.less @@ -2,6 +2,7 @@ 0% { transform: rotate(0deg) } 100% { transform: rotate(360deg) } } + .loading-spinner { animation-duration: 0.75s; animation-iteration-count: infinite; @@ -14,6 +15,7 @@ border-radius: 50% !important; margin: 0 auto; } -#pagination button { + +html.infinite_scroll #pagination button { visibility: hidden; } diff --git a/searx/static/themes/oscar/src/less/logicodev-dark/oscar.less b/searx/static/themes/oscar/src/less/logicodev-dark/oscar.less index 14f23111f..71821a259 100644 --- a/searx/static/themes/oscar/src/less/logicodev-dark/oscar.less +++ b/searx/static/themes/oscar/src/less/logicodev-dark/oscar.less @@ -4,6 +4,7 @@ @import "../../../../__common__/less/result_templates.less"; @import "../../less/result_templates.less"; @import "../../less/preferences.less"; +@import "../infinite_scroll.less"; @import "../../generated/pygments-logicodev.less"; @stacked-bar-chart: rgb(213, 216, 215, 1); diff --git a/searx/static/themes/oscar/src/less/logicodev/oscar.less b/searx/static/themes/oscar/src/less/logicodev/oscar.less index 187368f71..61e03745b 100644 --- a/searx/static/themes/oscar/src/less/logicodev/oscar.less +++ b/searx/static/themes/oscar/src/less/logicodev/oscar.less @@ -4,6 +4,7 @@ @import "../../../../__common__/less/result_templates.less"; @import "../../less/result_templates.less"; @import "../../less/preferences.less"; +@import "../infinite_scroll.less"; @import "../../generated/pygments-logicodev.less"; @import "navbar.less"; diff --git a/searx/static/themes/oscar/src/less/pointhi/oscar.less b/searx/static/themes/oscar/src/less/pointhi/oscar.less index e9851458d..d54fa28d9 100644 --- a/searx/static/themes/oscar/src/less/pointhi/oscar.less +++ b/searx/static/themes/oscar/src/less/pointhi/oscar.less @@ -4,6 +4,7 @@ @import "../../../../__common__/less/result_templates.less"; @import "../../less/result_templates.less"; @import "../../less/preferences.less"; +@import "../infinite_scroll.less"; @import "../../generated/pygments-pointhi.less"; @import "footer.less"; diff --git a/searx/static/themes/simple/src/js/main/00_toolkit.js b/searx/static/themes/simple/src/js/main/00_toolkit.js index c5b7fe578..f53842d72 100644 --- a/searx/static/themes/simple/src/js/main/00_toolkit.js +++ b/searx/static/themes/simple/src/js/main/00_toolkit.js @@ -59,43 +59,45 @@ window.searxng = (function (w, d) { } }; - searxng.http = function (method, url) { - var req = new XMLHttpRequest(), - resolve = function () {}, - reject = function () {}, - promise = { - then: function (callback) { resolve = callback; return promise; }, - catch: function (callback) { reject = callback; return promise; } - }; - - try { - req.open(method, url, true); + 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")); + } - // On load - req.onload = function () { - if (req.status == 200) { - resolve(req.response, req.responseType); + // Make the request + if (data) { + req.send(data) } else { - reject(Error(req.statusText)); + req.send(); } - }; - - // Handle network errors - req.onerror = function () { - reject(Error("Network Error")); - }; - - req.onabort = function () { - reject(Error("Transaction is aborted")); - }; - - // Make the request - req.send(); - } catch (ex) { - reject(ex); - } - - return promise; + } catch (ex) { + reject(ex); + } + }); }; searxng.loadStyle = function (src) { @@ -148,5 +150,16 @@ window.searxng = (function (w, d) { 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 new file mode 100644 index 000000000..b900e66e2 --- /dev/null +++ b/searx/static/themes/simple/src/js/main/infinite_scroll.js @@ -0,0 +1,88 @@ +// 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.translations.error_loading_next_page; + e.classList.add('dialog-error'); + e.setAttribute('role', 'alert'); + replaceChildrenWith(d.querySelector('#pagination'), [ e ]); + } + ) + } + + if (searxng.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/preferences.js b/searx/static/themes/simple/src/js/main/preferences.js index 343f20826..09f9cdde4 100644 --- a/searx/static/themes/simple/src/js/main/preferences.js +++ b/searx/static/themes/simple/src/js/main/preferences.js @@ -2,6 +2,10 @@ (function (w, d, searxng) { 'use strict'; + if (searxng.endpoint !== 'preferences') { + return; + } + searxng.ready(function () { let engine_descriptions = null; function load_engine_descriptions () { @@ -19,10 +23,8 @@ } } - if (d.querySelector('body[class="preferences_endpoint"]')) { - for (const el of d.querySelectorAll('[data-engine-name]')) { - searxng.on(el, 'mouseenter', load_engine_descriptions); - } + for (const el of d.querySelectorAll('[data-engine-name]')) { + searxng.on(el, 'mouseenter', load_engine_descriptions); } }); })(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 index b9bd43394..609bd8ecd 100644 --- a/searx/static/themes/simple/src/js/main/results.js +++ b/searx/static/themes/simple/src/js/main/results.js @@ -2,6 +2,10 @@ (function (w, d, searxng) { 'use strict'; + if (searxng.endpoint !== 'results') { + return; + } + searxng.ready(function () { searxng.image_thumbnail_layout = new searxng.ImageLayout('#urls', '#urls .result-images', 'img.image_thumbnail', 14, 6, 200); searxng.image_thumbnail_layout.watch(); diff --git a/searx/static/themes/simple/src/less/style.less b/searx/static/themes/simple/src/less/style.less index dd038cdf7..29cf554b0 100644 --- a/searx/static/themes/simple/src/less/style.less +++ b/searx/static/themes/simple/src/less/style.less @@ -771,15 +771,19 @@ article[data-vim-selected].category-social { margin: 1rem @results-tablet-offset 0 @results-tablet-offset; display: grid; grid-template-columns: 100%; - grid-template-rows: min-content min-content 1fr min-content min-content; + grid-template-rows: min-content min-content min-content 1fr min-content; gap: 0; grid-template-areas: "corrections" - "urls" "answers" "sidebar" + "urls" "pagination"; + #sidebar { + display: none; + } + #urls { width: inherit; margin: 0; |