results.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. /* SPDX-License-Identifier: AGPL-3.0-or-later */
  2. import "../../../node_modules/swiped-events/src/swiped-events.js";
  3. ((w, d, searxng) => {
  4. if (searxng.endpoint !== "results") {
  5. return;
  6. }
  7. searxng.ready(() => {
  8. d.querySelectorAll("#urls img").forEach((img) =>
  9. img.addEventListener(
  10. "error",
  11. () => {
  12. // console.log("ERROR can't load: " + img.src);
  13. img.src = window.searxng.settings.theme_static_path + "/img/img_load_error.svg";
  14. },
  15. { once: true }
  16. )
  17. );
  18. if (d.querySelector("#search_url button#copy_url")) {
  19. d.querySelector("#search_url button#copy_url").style.display = "block";
  20. }
  21. searxng.on(".btn-collapse", "click", function () {
  22. var btnLabelCollapsed = this.getAttribute("data-btn-text-collapsed");
  23. var btnLabelNotCollapsed = this.getAttribute("data-btn-text-not-collapsed");
  24. var target = this.getAttribute("data-target");
  25. var targetElement = d.querySelector(target);
  26. var html = this.innerHTML;
  27. if (this.classList.contains("collapsed")) {
  28. html = html.replace(btnLabelCollapsed, btnLabelNotCollapsed);
  29. } else {
  30. html = html.replace(btnLabelNotCollapsed, btnLabelCollapsed);
  31. }
  32. this.innerHTML = html;
  33. this.classList.toggle("collapsed");
  34. targetElement.classList.toggle("invisible");
  35. });
  36. searxng.on(".media-loader", "click", function () {
  37. var target = this.getAttribute("data-target");
  38. var iframe_load = d.querySelector(target + " > iframe");
  39. var srctest = iframe_load.getAttribute("src");
  40. if (srctest === null || srctest === undefined || srctest === false) {
  41. iframe_load.setAttribute("src", iframe_load.getAttribute("data-src"));
  42. }
  43. });
  44. searxng.on("#copy_url", "click", function () {
  45. var target = this.parentElement.querySelector("pre");
  46. navigator.clipboard.writeText(target.innerText);
  47. this.innerText = this.dataset.copiedText;
  48. });
  49. // searxng.selectImage (gallery)
  50. // -----------------------------
  51. // setTimeout() ID, needed to cancel *last* loadImage
  52. let imgTimeoutID;
  53. // progress spinner, while an image is loading
  54. const imgLoaderSpinner = d.createElement("div");
  55. imgLoaderSpinner.classList.add("loader");
  56. // singleton image object, which is used for all loading processes of a
  57. // detailed image
  58. const imgLoader = new Image();
  59. const loadImage = (imgSrc, onSuccess) => {
  60. // if defered image load exists, stop defered task.
  61. if (imgTimeoutID) clearTimeout(imgTimeoutID);
  62. // defer load of the detail image for 1 sec
  63. imgTimeoutID = setTimeout(() => {
  64. imgLoader.src = imgSrc;
  65. }, 1000);
  66. // set handlers in the on-properties
  67. imgLoader.onload = () => {
  68. onSuccess();
  69. imgLoaderSpinner.remove();
  70. };
  71. imgLoader.onerror = () => {
  72. imgLoaderSpinner.remove();
  73. };
  74. };
  75. searxng.selectImage = (resultElement) => {
  76. // add a class that can be evaluated in the CSS and indicates that the
  77. // detail view is open
  78. d.getElementById("results").classList.add("image-detail-open");
  79. // add a hash to the browser history so that pressing back doesn't return
  80. // to the previous page this allows us to dismiss the image details on
  81. // pressing the back button on mobile devices
  82. window.location.hash = "#image-viewer";
  83. searxng.scrollPageToSelected();
  84. // if there is none element given by the caller, stop here
  85. if (!resultElement) return;
  86. // find <img> object in the element, if there is none, stop here.
  87. const img = resultElement.querySelector(".result-images-source img");
  88. if (!img) return;
  89. // <img src="" data-src="http://example.org/image.jpg">
  90. const src = img.getAttribute("data-src");
  91. // already loaded high-res image or no high-res image available
  92. if (!src) return;
  93. // use the image thumbnail until the image is fully loaded
  94. const thumbnail = resultElement.querySelector(".image_thumbnail");
  95. img.src = thumbnail.src;
  96. // show a progress spinner
  97. const detailElement = resultElement.querySelector(".detail");
  98. detailElement.appendChild(imgLoaderSpinner);
  99. // load full size image in background
  100. loadImage(src, () => {
  101. // after the singelton loadImage has loaded the detail image into the
  102. // cache, it can be used in the origin <img> as src property.
  103. img.src = src;
  104. img.removeAttribute("data-src");
  105. });
  106. };
  107. searxng.closeDetail = () => {
  108. d.getElementById("results").classList.remove("image-detail-open");
  109. // remove #image-viewer hash from url by navigating back
  110. if (window.location.hash === "#image-viewer") window.history.back();
  111. searxng.scrollPageToSelected();
  112. };
  113. searxng.on(".result-detail-close", "click", (e) => {
  114. e.preventDefault();
  115. searxng.closeDetail();
  116. });
  117. searxng.on(".result-detail-previous", "click", (e) => {
  118. e.preventDefault();
  119. searxng.selectPrevious(false);
  120. });
  121. searxng.on(".result-detail-next", "click", (e) => {
  122. e.preventDefault();
  123. searxng.selectNext(false);
  124. });
  125. // listen for the back button to be pressed and dismiss the image details when called
  126. window.addEventListener("hashchange", () => {
  127. if (window.location.hash !== "#image-viewer") searxng.closeDetail();
  128. });
  129. d.querySelectorAll(".swipe-horizontal").forEach((obj) => {
  130. obj.addEventListener("swiped-left", () => {
  131. searxng.selectNext(false);
  132. });
  133. obj.addEventListener("swiped-right", () => {
  134. searxng.selectPrevious(false);
  135. });
  136. });
  137. w.addEventListener(
  138. "scroll",
  139. () => {
  140. var e = d.getElementById("backToTop"),
  141. scrollTop = document.documentElement.scrollTop || document.body.scrollTop,
  142. results = d.getElementById("results");
  143. if (e !== null) {
  144. if (scrollTop >= 100) {
  145. results.classList.add("scrolling");
  146. } else {
  147. results.classList.remove("scrolling");
  148. }
  149. }
  150. },
  151. true
  152. );
  153. });
  154. })(window, document, window.searxng);