Merge pull request #916 from dalf/pref_infinite_scroll2
Convert the infinite_scroll plugin as a preference (second version)
This commit is contained in:
		
						commit
						8230603f48
					
				| @ -1,9 +0,0 @@ | ||||
| from flask_babel import gettext | ||||
| 
 | ||||
| name = gettext('Infinite scroll') | ||||
| description = gettext('Automatically load next page when scrolling to bottom of current page') | ||||
| default_on = False | ||||
| preference_section = 'ui' | ||||
| 
 | ||||
| js_dependencies = ('plugins/js/infinite_scroll.js',) | ||||
| css_dependencies = ('plugins/css/infinite_scroll.css',) | ||||
| @ -394,6 +394,17 @@ class Preferences: | ||||
|                     'False': False | ||||
|                 } | ||||
|             ), | ||||
|             'infinite_scroll': MapSetting( | ||||
|                 settings['ui']['infinite_scroll'], | ||||
|                 locked=is_locked('infinite_scroll'), | ||||
|                 map={ | ||||
|                     '': settings['ui']['infinite_scroll'], | ||||
|                     '0': False, | ||||
|                     '1': True, | ||||
|                     'True': True, | ||||
|                     'False': False | ||||
|                 } | ||||
|             ), | ||||
|             # fmt: on | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -169,7 +169,6 @@ outgoing: | ||||
| #   - 'Ahmia blacklist'  # activation depends on outgoing.using_tor_proxy | ||||
| #   # these plugins are disabled if nothing is configured .. | ||||
| #   - 'Hostname replace'  # see hostname_replace configuration below | ||||
| #   - 'Infinite scroll' | ||||
| #   - 'Open Access DOI rewrite' | ||||
| #   - 'Vim-like hotkeys' | ||||
| 
 | ||||
|  | ||||
| @ -186,6 +186,7 @@ SCHEMA = { | ||||
|         'results_on_new_tab': SettingsValue(bool, False), | ||||
|         'advanced_search': SettingsValue(bool, False), | ||||
|         'query_in_title': SettingsValue(bool, False), | ||||
|         'infinite_scroll': SettingsValue(bool, False), | ||||
|     }, | ||||
|     'preferences': { | ||||
|         'lock': SettingsValue(list, []), | ||||
|  | ||||
| @ -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(); | ||||
|         } | ||||
|     }); | ||||
| }); | ||||
| @ -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); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     // https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event
 | ||||
|     w.addEventListener('pageshow', throttleAlign); | ||||
|     // https://developer.mozilla.org/en-US/docs/Web/API/FileReader/load_event
 | ||||
|     w.addEventListener('load', throttleAlign); | ||||
|     // https://developer.mozilla.org/en-US/docs/Web/API/Window/resize_event
 | ||||
|     w.addEventListener('resize', throttleAlign); | ||||
| 
 | ||||
|     for (i = 0; i < results_length; i++) { | ||||
|       img = results_nodes[i].querySelector(this.img_selector); | ||||
|       if (img !== null && img !== undefined) { | ||||
|         img.addEventListener('load', throttleAlign); | ||||
|       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', throttleAlign); | ||||
|         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; | ||||
|  | ||||
| @ -382,6 +382,29 @@ | ||||
| .col-stat { | ||||
|   width: 10rem; | ||||
| } | ||||
| @keyframes rotate-forever { | ||||
|   0% { | ||||
|     transform: rotate(0deg); | ||||
|   } | ||||
|   100% { | ||||
|     transform: rotate(360deg); | ||||
|   } | ||||
| } | ||||
| .loading-spinner { | ||||
|   animation-duration: 0.75s; | ||||
|   animation-iteration-count: infinite; | ||||
|   animation-name: rotate-forever; | ||||
|   animation-timing-function: linear; | ||||
|   height: 30px; | ||||
|   width: 30px; | ||||
|   border: 8px solid #666; | ||||
|   border-right-color: transparent; | ||||
|   border-radius: 50% !important; | ||||
|   margin: 0 auto; | ||||
| } | ||||
| html.infinite_scroll #pagination button { | ||||
|   visibility: hidden; | ||||
| } | ||||
| /* | ||||
|    this file is generated automatically by searxng_extra/update/update_pygments.py | ||||
|    using pygments version 2.11.2 | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								searx/static/themes/oscar/css/logicodev-dark.min.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								searx/static/themes/oscar/css/logicodev-dark.min.css
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @ -382,6 +382,29 @@ | ||||
| .col-stat { | ||||
|   width: 10rem; | ||||
| } | ||||
| @keyframes rotate-forever { | ||||
|   0% { | ||||
|     transform: rotate(0deg); | ||||
|   } | ||||
|   100% { | ||||
|     transform: rotate(360deg); | ||||
|   } | ||||
| } | ||||
| .loading-spinner { | ||||
|   animation-duration: 0.75s; | ||||
|   animation-iteration-count: infinite; | ||||
|   animation-name: rotate-forever; | ||||
|   animation-timing-function: linear; | ||||
|   height: 30px; | ||||
|   width: 30px; | ||||
|   border: 8px solid #666; | ||||
|   border-right-color: transparent; | ||||
|   border-radius: 50% !important; | ||||
|   margin: 0 auto; | ||||
| } | ||||
| html.infinite_scroll #pagination button { | ||||
|   visibility: hidden; | ||||
| } | ||||
| /* | ||||
|    this file is generated automatically by searxng_extra/update/update_pygments.py | ||||
|    using pygments version 2.11.2 | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								searx/static/themes/oscar/css/logicodev.min.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								searx/static/themes/oscar/css/logicodev.min.css
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @ -382,6 +382,29 @@ | ||||
| .col-stat { | ||||
|   width: 10rem; | ||||
| } | ||||
| @keyframes rotate-forever { | ||||
|   0% { | ||||
|     transform: rotate(0deg); | ||||
|   } | ||||
|   100% { | ||||
|     transform: rotate(360deg); | ||||
|   } | ||||
| } | ||||
| .loading-spinner { | ||||
|   animation-duration: 0.75s; | ||||
|   animation-iteration-count: infinite; | ||||
|   animation-name: rotate-forever; | ||||
|   animation-timing-function: linear; | ||||
|   height: 30px; | ||||
|   width: 30px; | ||||
|   border: 8px solid #666; | ||||
|   border-right-color: transparent; | ||||
|   border-radius: 50% !important; | ||||
|   margin: 0 auto; | ||||
| } | ||||
| html.infinite_scroll #pagination button { | ||||
|   visibility: hidden; | ||||
| } | ||||
| /* | ||||
|    this file is generated automatically by searxng_extra/update/update_pygments.py | ||||
|    using pygments version 2.11.2 | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								searx/static/themes/oscar/css/pointhi.min.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								searx/static/themes/oscar/css/pointhi.min.css
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @ -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, | ||||
|  | ||||
| @ -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')) | ||||
|     }; | ||||
| @ -189,6 +190,56 @@ $(document).ready(function(){ | ||||
|  * 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(); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| }); | ||||
| ;/** | ||||
|  * @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.addEventListener('load', function() { | ||||
|     // Hide infobox toggle if shrunk size already fits all content.
 | ||||
|     $('.infobox').each(function() { | ||||
| @ -348,7 +399,8 @@ $(document).ready(function(){ | ||||
|     this.verticalMargin = verticalMargin; | ||||
|     this.horizontalMargin = horizontalMargin; | ||||
|     this.maxHeight = maxHeight; | ||||
|     this.isAlignDone = true; | ||||
|     this.trottleCallToAlign = null; | ||||
|     this.alignAfterThrotteling = false; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
| @ -391,12 +443,12 @@ $(document).ready(function(){ | ||||
|         // 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'); | ||||
| @ -431,6 +483,23 @@ $(document).ready(function(){ | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   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); | ||||
| @ -460,9 +529,9 @@ $(document).ready(function(){ | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   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; | ||||
| 
 | ||||
| @ -471,34 +540,53 @@ $(document).ready(function(){ | ||||
|       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); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     // https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event
 | ||||
|     w.addEventListener('pageshow', throttleAlign); | ||||
|     // https://developer.mozilla.org/en-US/docs/Web/API/FileReader/load_event
 | ||||
|     w.addEventListener('load', throttleAlign); | ||||
|     // https://developer.mozilla.org/en-US/docs/Web/API/Window/resize_event
 | ||||
|     w.addEventListener('resize', throttleAlign); | ||||
| 
 | ||||
|     for (i = 0; i < results_length; i++) { | ||||
|       img = results_nodes[i].querySelector(this.img_selector); | ||||
|       if (img !== null && img !== undefined) { | ||||
|         img.addEventListener('load', throttleAlign); | ||||
|       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', throttleAlign); | ||||
|         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; | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								searx/static/themes/oscar/js/searxng.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								searx/static/themes/oscar/js/searxng.min.js
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @ -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')) | ||||
|     }; | ||||
|  | ||||
							
								
								
									
										50
									
								
								searx/static/themes/oscar/src/js/infinite_scroll.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								searx/static/themes/oscar/src/js/infinite_scroll.js
									
									
									
									
									
										Normal file
									
								
							| @ -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(); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| }); | ||||
| @ -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; | ||||
| } | ||||
| @ -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); | ||||
|  | ||||
| @ -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"; | ||||
|  | ||||
| @ -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"; | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								searx/static/themes/simple/css/searxng-rtl.min.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								searx/static/themes/simple/css/searxng-rtl.min.css
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								searx/static/themes/simple/css/searxng.min.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								searx/static/themes/simple/css/searxng.min.css
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								searx/static/themes/simple/js/searxng.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								searx/static/themes/simple/js/searxng.min.js
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @ -59,17 +59,12 @@ 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; } | ||||
|       }; | ||||
| 
 | ||||
|   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 () { | ||||
| @ -89,13 +84,20 @@ window.searxng = (function (w, d) { | ||||
|           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); | ||||
|       } | ||||
| 
 | ||||
|     return promise; | ||||
|     }); | ||||
|   }; | ||||
| 
 | ||||
|   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); | ||||
|  | ||||
							
								
								
									
										88
									
								
								searx/static/themes/simple/src/js/main/infinite_scroll.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								searx/static/themes/simple/src/js/main/infinite_scroll.js
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
|   } | ||||
| 
 | ||||
| }); | ||||
| @ -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); | ||||
|     } | ||||
|     } | ||||
|   }); | ||||
| })(window, document, window.searxng); | ||||
|  | ||||
| @ -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(); | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -100,6 +100,7 @@ | ||||
|     <script src="{{ url_for('static', filename='js/searxng.min.js') }}" | ||||
|             data-method="{{ method or 'POST' }}" | ||||
|             data-autocompleter="{% if autocomplete %}true{% else %}false{% endif %}" | ||||
|             data-infinite-scroll="{% if infinite_scroll %}true{% else %}false{% endif %}" | ||||
|             data-translations="{{ translations }}"></script> | ||||
|     {% for script in scripts %} | ||||
|     {{""}}<script src="{{ url_for('static', filename=script) }}"></script> | ||||
|  | ||||
| @ -248,6 +248,17 @@ | ||||
|                         {{ preferences_item_footer(info, label, rtl) }} | ||||
|                         {% endif %} | ||||
| 
 | ||||
|                         {% if 'infinite_scroll' not in locked_preferences %} | ||||
|                         {% set label = _('Infinite scroll') %} | ||||
|                         {% set info = _('Automatically load next page when scrolling to bottom of current page') %} | ||||
|                         {{ preferences_item_header(info, label, rtl, 'infinite_scroll') }} | ||||
|                             <select class="form-control {{ custom_select_class(rtl) }}" name="infinite_scroll" id="infinite_scroll"> | ||||
|                                 <option value="1" {% if infinite_scroll %}selected="selected"{% endif %}>{{ _('On') }}</option> | ||||
|                                 <option value="0" {% if not infinite_scroll %}selected="selected"{% endif %}>{{ _('Off')}}</option> | ||||
|                             </select> | ||||
|                         {{ preferences_item_footer(info, label, rtl) }} | ||||
|                         {% endif %} | ||||
| 
 | ||||
|                         {{ plugin_of_category('ui' )}} | ||||
|                     </div> | ||||
|                 </fieldset> | ||||
|  | ||||
| @ -23,7 +23,7 @@ | ||||
|           data-method="{{ method or 'POST' }}" | ||||
|           data-autocompleter="{% if autocomplete %}true{% else %}false{% endif %}" | ||||
|           data-search-on-category-select="{{ 'true' if 'plugins/js/search_on_category_select.js' in scripts else 'false'}}" | ||||
|           data-infinite-scroll="{{ 'true' if 'plugins/js/infinite_scroll.js' in scripts else 'false' }}" | ||||
|           data-infinite-scroll="{% if infinite_scroll %}true{% else %}false{% endif %}" | ||||
|           data-hotkeys="{{ 'true' if 'plugins/js/vim_hotkeys.js' in scripts else 'false' }}" | ||||
|           data-static-path="{{ url_for('static', filename='themes/simple') }}/" | ||||
|           data-translations="{{ translations }}"></script> | ||||
|  | ||||
| @ -226,6 +226,18 @@ | ||||
|       <div class="description">{{_('Open result links on new browser tabs') }}</div> | ||||
|     </fieldset> | ||||
|     {% endif %} | ||||
|     {% if 'infinite_scroll' not in locked_preferences %} | ||||
|     <fieldset> | ||||
|       <legend>{{ _('Infinite scroll') }}</legend> | ||||
|       <p class="value"> | ||||
|         <select name='infinite_scroll'> | ||||
|           <option value="1" {% if infinite_scroll %}selected="selected"{% endif %}>{{ _('On') }}</option> | ||||
|           <option value="0" {% if not infinite_scroll %}selected="selected"{% endif %}>{{ _('Off')}}</option> | ||||
|         </select> | ||||
|       </p> | ||||
|       <div class="description">{{ _('Automatically load next page when scrolling to bottom of current page') }}</div> | ||||
|     </fieldset> | ||||
|     {% endif %} | ||||
|     {{ plugin_preferences('ui') }} | ||||
|   {{ tab_footer() }} | ||||
| 
 | ||||
|  | ||||
| @ -431,6 +431,8 @@ def get_translations(): | ||||
|         'no_item_found': gettext('No item found'), | ||||
|         # /preferences: the source of the engine description (wikipedata, wikidata, website) | ||||
|         'Source': gettext('Source'), | ||||
|         # infinite scroll | ||||
|         'error_loading_next_page': gettext('Error loading the next page'), | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| @ -463,6 +465,7 @@ def render(template_name: str, override_theme: str = None, **kwargs): | ||||
|     kwargs['preferences'] = request.preferences | ||||
|     kwargs['method'] = request.preferences.get_value('method') | ||||
|     kwargs['autocomplete'] = request.preferences.get_value('autocomplete') | ||||
|     kwargs['infinite_scroll'] = request.preferences.get_value('infinite_scroll') | ||||
|     kwargs['results_on_new_tab'] = request.preferences.get_value('results_on_new_tab') | ||||
|     kwargs['advanced_search'] = request.preferences.get_value('advanced_search') | ||||
|     kwargs['query_in_title'] = request.preferences.get_value('query_in_title') | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user