diff --git a/searx/results.py b/searx/results.py index 1ac6a5ab5..ab242b838 100644 --- a/searx/results.py +++ b/searx/results.py @@ -278,12 +278,18 @@ class ResultContainer: result['parsed_url'] = result['parsed_url']._replace(scheme="http") result['url'] = result['parsed_url'].geturl() + # avoid duplicate content between the content and title fields + if result.get('content') == result.get('title'): + del result['content'] + + # make sure there is a template + if 'template' not in result: + result['template'] = 'default.html' + # strip multiple spaces and cariage returns from content if result.get('content'): result['content'] = WHITESPACE_REGEX.sub(' ', result['content']) - return True - def __merge_url_result(self, result, position): result['engines'] = set([result['engine']]) with self._lock: diff --git a/searx/static/themes/oscar/css/logicodev-dark.min.css.map b/searx/static/themes/oscar/css/logicodev-dark.min.css.map index e9928c467..6cb083bcf 100644 Binary files a/searx/static/themes/oscar/css/logicodev-dark.min.css.map and b/searx/static/themes/oscar/css/logicodev-dark.min.css.map differ diff --git a/searx/static/themes/oscar/gruntfile.js b/searx/static/themes/oscar/gruntfile.js index 2f87e289f..988d42c3d 100644 --- a/searx/static/themes/oscar/gruntfile.js +++ b/searx/static/themes/oscar/gruntfile.js @@ -60,7 +60,7 @@ module.exports = function(grunt) { separator: ';' }, dist: { - src: ['src/js/*.js', '../__common__/js/image_layout.js'], + src: ['src/js/*.js'], dest: 'js/searxng.js' } }, diff --git a/searx/static/themes/oscar/js/searxng.js b/searx/static/themes/oscar/js/searxng.js index edeeaee84..29423e1dd 100644 --- a/searx/static/themes/oscar/js/searxng.js +++ b/searx/static/themes/oscar/js/searxng.js @@ -183,6 +183,230 @@ $(document).ready(function(){ searxng.image_thumbnail_layout = new searxng.ImageLayout('#main_results', '#main_results .result-images', 'img.img-thumbnail', 15, 3, 200); searxng.image_thumbnail_layout.watch(); }); +;/** +* +* Google Image Layout v0.0.1 +* Description, by Anh Trinh. +* Heavily modified for searx +* https://ptgamr.github.io/2014-09-12-google-image-layout/ +* https://ptgamr.github.io/google-image-layout/src/google-image-layout.js +* +* @license Free to use under the MIT License. +* +* @example Example usage of searxng.ImageLayout class. +* searxng.image_thumbnail_layout = new searxng.ImageLayout( +* '#urls', // container_selector +* '#urls .result-images', // results_selector +* 'img.image_thumbnail', // img_selector +* 14, // verticalMargin +* 6, // horizontalMargin +* 200 // maxHeight +* ); +* searxng.image_thumbnail_layout.watch(); +*/ + + +(function (w, d) { + function ImageLayout (container_selector, results_selector, img_selector, verticalMargin, horizontalMargin, maxHeight) { + this.container_selector = container_selector; + this.results_selector = results_selector; + this.img_selector = img_selector; + this.verticalMargin = verticalMargin; + this.horizontalMargin = horizontalMargin; + this.maxHeight = maxHeight; + this.trottleCallToAlign = null; + this.alignAfterThrotteling = false; + } + + /** + * Get the height that make all images fit the container + * + * width = w1 + w2 + w3 + ... = r1*h + r2*h + r3*h + ... + * + * @param {[type]} images the images to be calculated + * @param {[type]} width the container witdth + * @param {[type]} margin the margin between each image + * + * @return {[type]} the height + */ + ImageLayout.prototype._getHeigth = function (images, width) { + var i, img; + var r = 0; + + for (i = 0; i < images.length; i++) { + img = images[i]; + if ((img.naturalWidth > 0) && (img.naturalHeight > 0)) { + r += img.naturalWidth / img.naturalHeight; + } else { + // assume that not loaded images are square + r += 1; + } + } + + return (width - images.length * this.verticalMargin) / r; // have to round down because Firefox will automatically roundup value with number of decimals > 3 + }; + + ImageLayout.prototype._setSize = function (images, height) { + var i, img, imgWidth; + var imagesLength = images.length, resultNode; + + for (i = 0; i < imagesLength; i++) { + img = images[i]; + if ((img.naturalWidth > 0) && (img.naturalHeight > 0)) { + imgWidth = height * img.naturalWidth / img.naturalHeight; + } else { + // not loaded image : make it square as _getHeigth said it + imgWidth = height; + } + 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'); + } + } + }; + + ImageLayout.prototype._alignImgs = function (imgGroup) { + var isSearching, slice, i, h; + var containerElement = d.querySelector(this.container_selector); + var containerCompStyles = window.getComputedStyle(containerElement); + var containerPaddingLeft = parseInt(containerCompStyles.getPropertyValue('padding-left'), 10); + var containerPaddingRight = parseInt(containerCompStyles.getPropertyValue('padding-right'), 10); + var containerWidth = containerElement.clientWidth - containerPaddingLeft - containerPaddingRight; + + while (imgGroup.length > 0) { + isSearching = true; + for (i = 1; i <= imgGroup.length && isSearching; i++) { + slice = imgGroup.slice(0, i); + h = this._getHeigth(slice, containerWidth); + if (h < this.maxHeight) { + this._setSize(slice, h); + // continue with the remaining images + imgGroup = imgGroup.slice(i); + isSearching = false; + } + } + if (isSearching) { + this._setSize(slice, Math.min(this.maxHeight, h)); + break; + } + } + }; + + 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); + var results_length = results_selectorNode.length; + var previous = null; + var current = null; + var imgGroup = []; + + for (i = 0; i < results_length; i++) { + current = results_selectorNode[i]; + if (current.previousElementSibling !== previous && imgGroup.length > 0) { + // the current image is not connected to previous one + // so the current image is the start of a new group of images. + // so call _alignImgs to align the current group + this._alignImgs(imgGroup); + // and start a new empty group of images + imgGroup = []; + } + // add the current image to the group (only the img tag) + imgGroup.push(current.querySelector(this.img_selector)); + // update the previous variable + previous = current; + } + // align the remaining images + if (imgGroup.length > 0) { + this._alignImgs(imgGroup); + } + }; + + ImageLayout.prototype._monitorImages = function () { + var i, img; + var objthrottleAlign = this.throttleAlign.bind(this); + var results_nodes = d.querySelectorAll(this.results_selector); + var results_length = results_nodes.length; + + function img_load_error (event) { + // console.log("ERROR can't load: " + event.originalTarget.src); + event.originalTarget.src = w.searxng.static_path + w.searxng.theme.img_load_error; + } + + 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', objthrottleAlign); + // https://developer.mozilla.org/en-US/docs/Web/API/FileReader/load_event + w.addEventListener('load', objthrottleAlign); + // https://developer.mozilla.org/en-US/docs/Web/API/Window/resize_event + w.addEventListener('resize', objthrottleAlign); + + 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; + +}(window, document)); ;/** * @license * (C) Copyright Contributors to the SearXNG project. @@ -368,227 +592,3 @@ $(document).ready(function(){ }); }); -;/** -* -* Google Image Layout v0.0.1 -* Description, by Anh Trinh. -* Heavily modified for searx -* https://ptgamr.github.io/2014-09-12-google-image-layout/ -* https://ptgamr.github.io/google-image-layout/src/google-image-layout.js -* -* @license Free to use under the MIT License. -* -* @example Example usage of searxng.ImageLayout class. -* searxng.image_thumbnail_layout = new searxng.ImageLayout( -* '#urls', // container_selector -* '#urls .result-images', // results_selector -* 'img.image_thumbnail', // img_selector -* 14, // verticalMargin -* 6, // horizontalMargin -* 200 // maxHeight -* ); -* searxng.image_thumbnail_layout.watch(); -*/ - - -(function (w, d) { - function ImageLayout (container_selector, results_selector, img_selector, verticalMargin, horizontalMargin, maxHeight) { - this.container_selector = container_selector; - this.results_selector = results_selector; - this.img_selector = img_selector; - this.verticalMargin = verticalMargin; - this.horizontalMargin = horizontalMargin; - this.maxHeight = maxHeight; - this.trottleCallToAlign = null; - this.alignAfterThrotteling = false; - } - - /** - * Get the height that make all images fit the container - * - * width = w1 + w2 + w3 + ... = r1*h + r2*h + r3*h + ... - * - * @param {[type]} images the images to be calculated - * @param {[type]} width the container witdth - * @param {[type]} margin the margin between each image - * - * @return {[type]} the height - */ - ImageLayout.prototype._getHeigth = function (images, width) { - var i, img; - var r = 0; - - for (i = 0; i < images.length; i++) { - img = images[i]; - if ((img.naturalWidth > 0) && (img.naturalHeight > 0)) { - r += img.naturalWidth / img.naturalHeight; - } else { - // assume that not loaded images are square - r += 1; - } - } - - return (width - images.length * this.verticalMargin) / r; // have to round down because Firefox will automatically roundup value with number of decimals > 3 - }; - - ImageLayout.prototype._setSize = function (images, height) { - var i, img, imgWidth; - var imagesLength = images.length, resultNode; - - for (i = 0; i < imagesLength; i++) { - img = images[i]; - if ((img.naturalWidth > 0) && (img.naturalHeight > 0)) { - imgWidth = height * img.naturalWidth / img.naturalHeight; - } else { - // not loaded image : make it square as _getHeigth said it - imgWidth = height; - } - 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'); - } - } - }; - - ImageLayout.prototype._alignImgs = function (imgGroup) { - var isSearching, slice, i, h; - var containerElement = d.querySelector(this.container_selector); - var containerCompStyles = window.getComputedStyle(containerElement); - var containerPaddingLeft = parseInt(containerCompStyles.getPropertyValue('padding-left'), 10); - var containerPaddingRight = parseInt(containerCompStyles.getPropertyValue('padding-right'), 10); - var containerWidth = containerElement.clientWidth - containerPaddingLeft - containerPaddingRight; - - while (imgGroup.length > 0) { - isSearching = true; - for (i = 1; i <= imgGroup.length && isSearching; i++) { - slice = imgGroup.slice(0, i); - h = this._getHeigth(slice, containerWidth); - if (h < this.maxHeight) { - this._setSize(slice, h); - // continue with the remaining images - imgGroup = imgGroup.slice(i); - isSearching = false; - } - } - if (isSearching) { - this._setSize(slice, Math.min(this.maxHeight, h)); - break; - } - } - }; - - 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); - var results_length = results_selectorNode.length; - var previous = null; - var current = null; - var imgGroup = []; - - for (i = 0; i < results_length; i++) { - current = results_selectorNode[i]; - if (current.previousElementSibling !== previous && imgGroup.length > 0) { - // the current image is not connected to previous one - // so the current image is the start of a new group of images. - // so call _alignImgs to align the current group - this._alignImgs(imgGroup); - // and start a new empty group of images - imgGroup = []; - } - // add the current image to the group (only the img tag) - imgGroup.push(current.querySelector(this.img_selector)); - // update the previous variable - previous = current; - } - // align the remaining images - if (imgGroup.length > 0) { - this._alignImgs(imgGroup); - } - }; - - ImageLayout.prototype._monitorImages = function () { - var i, img; - var objthrottleAlign = this.throttleAlign.bind(this); - var results_nodes = d.querySelectorAll(this.results_selector); - var results_length = results_nodes.length; - - function img_load_error (event) { - // console.log("ERROR can't load: " + event.originalTarget.src); - event.originalTarget.src = w.searxng.static_path + w.searxng.theme.img_load_error; - } - - 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', objthrottleAlign); - // https://developer.mozilla.org/en-US/docs/Web/API/FileReader/load_event - w.addEventListener('load', objthrottleAlign); - // https://developer.mozilla.org/en-US/docs/Web/API/Window/resize_event - w.addEventListener('resize', objthrottleAlign); - - 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; - -}(window, document)); diff --git a/searx/static/themes/oscar/js/searxng.min.js b/searx/static/themes/oscar/js/searxng.min.js index e0573b888..ab130cdad 100644 Binary files a/searx/static/themes/oscar/js/searxng.min.js and b/searx/static/themes/oscar/js/searxng.min.js differ diff --git a/searx/static/themes/oscar/js/searxng.min.js.map b/searx/static/themes/oscar/js/searxng.min.js.map index 6156eea02..8fe21b87a 100644 Binary files a/searx/static/themes/oscar/js/searxng.min.js.map and b/searx/static/themes/oscar/js/searxng.min.js.map differ diff --git a/searx/static/themes/__common__/js/image_layout.js b/searx/static/themes/oscar/src/js/image_layout.js similarity index 99% rename from searx/static/themes/__common__/js/image_layout.js rename to searx/static/themes/oscar/src/js/image_layout.js index 329fa46a8..c65351f7b 100644 --- a/searx/static/themes/__common__/js/image_layout.js +++ b/searx/static/themes/oscar/src/js/image_layout.js @@ -128,7 +128,7 @@ obj.trottleCallToAlign = null; }, 20); } - } + }; ImageLayout.prototype.align = function () { var i; @@ -183,7 +183,7 @@ img.classList.add('aligned'); } } - } + }; ImageLayout.prototype.watch = function () { var objthrottleAlign = this.throttleAlign.bind(this); @@ -216,7 +216,7 @@ subtree: true, attributes: false, characterData: false, - }) + }); }; w.searxng.ImageLayout = ImageLayout; diff --git a/searx/static/themes/simple/css/searxng-rtl.min.css b/searx/static/themes/simple/css/searxng-rtl.min.css index 64c1ac234..267545e7c 100644 Binary files a/searx/static/themes/simple/css/searxng-rtl.min.css and b/searx/static/themes/simple/css/searxng-rtl.min.css differ diff --git a/searx/static/themes/simple/css/searxng-rtl.min.css.map b/searx/static/themes/simple/css/searxng-rtl.min.css.map index b4ce34408..b8828a7d8 100644 Binary files a/searx/static/themes/simple/css/searxng-rtl.min.css.map and b/searx/static/themes/simple/css/searxng-rtl.min.css.map differ diff --git a/searx/static/themes/simple/css/searxng.min.css b/searx/static/themes/simple/css/searxng.min.css index f300d236c..d5162c65b 100644 Binary files a/searx/static/themes/simple/css/searxng.min.css and b/searx/static/themes/simple/css/searxng.min.css differ diff --git a/searx/static/themes/simple/css/searxng.min.css.map b/searx/static/themes/simple/css/searxng.min.css.map index 4929bedc9..842cd6748 100644 Binary files a/searx/static/themes/simple/css/searxng.min.css.map and b/searx/static/themes/simple/css/searxng.min.css.map differ diff --git a/searx/static/themes/simple/gruntfile.js b/searx/static/themes/simple/gruntfile.js index 52c460494..25883e384 100644 --- a/searx/static/themes/simple/gruntfile.js +++ b/searx/static/themes/simple/gruntfile.js @@ -108,7 +108,6 @@ module.exports = function (grunt) { 'js/searxng.head.min.js': ['src/js/head/*.js'], 'js/searxng.min.js': [ 'src/js/main/*.js', - '../__common__/js/*.js', './node_modules/autocomplete-js/dist/autocomplete.js' ] } diff --git a/searx/static/themes/simple/js/searxng.min.js b/searx/static/themes/simple/js/searxng.min.js index 7a74e677f..ebf718f30 100644 Binary files a/searx/static/themes/simple/js/searxng.min.js and b/searx/static/themes/simple/js/searxng.min.js differ diff --git a/searx/static/themes/simple/js/searxng.min.js.map b/searx/static/themes/simple/js/searxng.min.js.map index 1f64e949e..42b9f8cad 100644 Binary files a/searx/static/themes/simple/js/searxng.min.js.map and b/searx/static/themes/simple/js/searxng.min.js.map differ diff --git a/searx/static/themes/simple/src/js/main/results.js b/searx/static/themes/simple/src/js/main/results.js index 609bd8ecd..4eae58435 100644 --- a/searx/static/themes/simple/src/js/main/results.js +++ b/searx/static/themes/simple/src/js/main/results.js @@ -7,9 +7,6 @@ } 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(); - d.querySelectorAll('#urls img.image').forEach( img => img.addEventListener( @@ -74,13 +71,11 @@ } } d.getElementById('results').classList.add('image-detail-open'); - searxng.image_thumbnail_layout.align(); searxng.scrollPageToSelected(); } searxng.closeDetail = function (e) { d.getElementById('results').classList.remove('image-detail-open'); - searxng.image_thumbnail_layout.align(); searxng.scrollPageToSelected(); } searxng.on('.result-detail-close', 'click', e => { diff --git a/searx/static/themes/simple/src/less/definitions.less b/searx/static/themes/simple/src/less/definitions.less index abda5e37c..7de0d7ef9 100644 --- a/searx/static/themes/simple/src/less/definitions.less +++ b/searx/static/themes/simple/src/less/definitions.less @@ -68,8 +68,8 @@ --color-result-search-url-border: #ddd; --color-result-search-url-font: #000; // Images Colors - --color-result-image-span-background-hover: rgba(0, 0, 0, 0.6); - --color-result-image-span-font: #fff; + --color-result-image-span-font: #444; + --color-result-image-span-font-selected: #fff; --color-result-image-background: #fff; /// Settings Colors --color-settings-tr-hover: #f7f7f7; @@ -179,8 +179,8 @@ --color-result-detail-loader-border: rgba(255, 255, 255, 0.2); --color-result-detail-loader-borderleft: rgba(0, 0, 0, 0); // Images Colors - --color-result-image-span-background-hover: rgba(0, 0, 0, 0.6); - --color-result-image-span-font: #fff; + --color-result-image-span-font: #bbb; + --color-result-image-span-font-selected: #222; --color-result-image-background: #222; /// Settings Colors --color-settings-tr-hover: #2d2d2d; @@ -230,9 +230,11 @@ @results-gap: 5rem; @results-margin: 0.125rem; @result-padding: 1rem; +@results-image-row-height: 12rem; +@results-image-row-height-phone: 6rem; @search-width: 40rem; // heigh of #search, see detail.less -@search-height: 7.75rem; +@search-height: 7.6rem; /// Device Size /// @desktop > @tablet diff --git a/searx/static/themes/simple/src/less/detail.less b/searx/static/themes/simple/src/less/detail.less index 5e3cfaca6..b63e65206 100644 --- a/searx/static/themes/simple/src/less/detail.less +++ b/searx/static/themes/simple/src/less/detail.less @@ -39,6 +39,7 @@ article.result-images .detail { border: none; object-fit: contain; width: inherit; + height: inherit; max-width: 100%; min-height: inherit; max-height: calc(100vh - 25rem - 7rem); diff --git a/searx/static/themes/simple/src/less/style-rtl.less b/searx/static/themes/simple/src/less/style-rtl.less index 6d3e9f525..bbaa48748 100644 --- a/searx/static/themes/simple/src/less/style-rtl.less +++ b/searx/static/themes/simple/src/less/style-rtl.less @@ -83,6 +83,11 @@ } } +// Image flexbox +#main_results div#results.only_template_images #urls { + direction: rtl; +} + // Image detail #results.image-detail-open article.result-images[data-vim-selected] .detail .result-images-labels p { direction: rtl; diff --git a/searx/static/themes/simple/src/less/style.less b/searx/static/themes/simple/src/less/style.less index 29cf554b0..7640b253c 100644 --- a/searx/static/themes/simple/src/less/style.less +++ b/searx/static/themes/simple/src/less/style.less @@ -143,6 +143,10 @@ article.result-images[data-vim-selected] { .image_thumbnail { filter: opacity(60%); } + + span.title { + color: var(--color-result-image-span-font-selected); + } } article[data-vim-selected].category-videos, @@ -284,6 +288,16 @@ article[data-vim-selected].category-social { } } +.template_group_images { + display: flex; + flex-wrap: wrap; +} + +.template_group_images::after { + flex-grow: 10; + content: ""; +} + .category-videos, .category-news, .category-map, @@ -357,40 +371,38 @@ article[data-vim-selected].category-social { } .result-images { - display: inline-block; - margin: 0; - padding: 0; - position: relative; - max-height: 200px; + flex-grow: 1; + padding: 0.5rem 0.5rem 2rem 0.5rem; + margin: 0.25rem; border: none !important; + height: @results-image-row-height; + + & > a { + position: relative; + } img { - float: inherit; - margin: 0.125rem; + margin: 0; padding: 0; border: none; - max-height: 200px; + height: 100%; + width: 100%; + object-fit: cover; + vertical-align: bottom; background: var(--color-result-image-background); } span.title { - display: none; - color: var(--color-result-image-span-font); - } - - &:hover span.title { display: block; position: absolute; - bottom: 0; - .ltr-right(0); - padding: 4px; - margin-top: 0; - .ltr-margin-right(0); - margin-bottom: 4px; - .ltr-margin-left(4px); - // color: @color-result-image-span-font; - background-color: var(--color-result-image-span-background-hover); - font-size: 0.7em; + + width: 100%; + font-size: 0.9rem; + color: var(--color-result-image-span-font); + padding: 0.5rem 0 0 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } } @@ -785,8 +797,14 @@ article[data-vim-selected].category-social { } #urls { - width: inherit; margin: 0; + display: flex; + flex-wrap: wrap; + } + + #urls::after { + flex-grow: 10; + content: ""; } #backToTop { @@ -837,8 +855,7 @@ article[data-vim-selected].category-social { .result-images { margin: 0; - padding: 0; - border: none; + height: @results-image-row-height-phone; } } diff --git a/searx/templates/simple/results.html b/searx/templates/simple/results.html index 4149f83c3..ee47db662 100644 --- a/searx/templates/simple/results.html +++ b/searx/templates/simple/results.html @@ -15,7 +15,7 @@ {% if results and results|map(attribute='template')|unique|list|count == 1 %} {% set only_template = 'only_template_' + results[0]['template']|default('default')|replace('.html', '') %} {% else %} - {% set unique_template = '' %} + {% set only_template = '' %} {% endif %}
@@ -131,12 +131,10 @@
{% for result in results %} + {% if result.open_group and not only_template %}
{% endif %} {% set index = loop.index %} - {% if result['template'] %} - {% include get_result_template('simple', result['template']) %} - {% else %} - {% include 'simple/result_templates/default.html' %} - {% endif %} + {% include get_result_template('simple', result['template']) %} + {% if result.close_group and not only_template %}
{% endif %} {% endfor %} {% if not results and not answers %} {% include 'simple/messages/no_results.html' %} diff --git a/searx/webapp.py b/searx/webapp.py index 1314fc276..df717a7b1 100755 --- a/searx/webapp.py +++ b/searx/webapp.py @@ -726,6 +726,9 @@ def search(): # Server-Timing header request.timings = result_container.get_timings() # pylint: disable=assigning-non-slot + current_template = None + previous_result = None + # output for result in results: if output_format == 'html': @@ -762,6 +765,18 @@ def search(): else: result['publishedDate'] = format_date(result['publishedDate']) + # set result['open_group'] = True when the template changes from the previous result + # set result['close_group'] = True when the template changes on the next result + if current_template != result.get('template'): + result['open_group'] = True + if previous_result: + previous_result['close_group'] = True # pylint: disable=unsupported-assignment-operation + current_template = result.get('template') + previous_result = result + + if previous_result: + previous_result['close_group'] = True + if output_format == 'json': x = { 'query': search_query.query, diff --git a/tests/unit/test_webapp.py b/tests/unit/test_webapp.py index a50bd5c78..aed6f2dd4 100644 --- a/tests/unit/test_webapp.py +++ b/tests/unit/test_webapp.py @@ -33,7 +33,8 @@ class ViewsTestCase(SearxTestCase): 'engine': 'startpage', 'parsed_url': ParseResult( scheme='http', netloc='first.test.xyz', path='/', params='', query='', fragment='' - ), # noqa + ), + 'template': 'default.html', }, { 'content': 'second test content', @@ -43,7 +44,8 @@ class ViewsTestCase(SearxTestCase): 'engine': 'youtube', 'parsed_url': ParseResult( scheme='http', netloc='second.test.xyz', path='/', params='', query='', fragment='' - ), # noqa + ), + 'template': 'default.html', }, ]