00_toolkit.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /**
  2. * @license
  3. * (C) Copyright Contributors to the SearXNG project.
  4. * (C) Copyright Contributors to the searx project (2014 - 2021).
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. window.searxng = ((w, d) => {
  8. // not invented here toolkit with bugs fixed elsewhere
  9. // purposes : be just good enough and as small as possible
  10. // from https://plainjs.com/javascript/events/live-binding-event-handlers-14/
  11. if (w.Element) {
  12. ((ElementPrototype) => {
  13. ElementPrototype.matches =
  14. ElementPrototype.matches ||
  15. ElementPrototype.matchesSelector ||
  16. ElementPrototype.webkitMatchesSelector ||
  17. ElementPrototype.msMatchesSelector ||
  18. function (selector) {
  19. const nodes = (this.parentNode || this.document).querySelectorAll(selector);
  20. let i = -1;
  21. while (nodes[++i] && nodes[i] !== this);
  22. return !!nodes[i];
  23. };
  24. })(Element.prototype);
  25. }
  26. function callbackSafe(callback, el, e) {
  27. try {
  28. callback.call(el, e);
  29. } catch (exception) {
  30. console.log(exception);
  31. }
  32. }
  33. const searxng = window.searxng || {};
  34. searxng.on = (obj, eventType, callback, useCapture) => {
  35. useCapture = useCapture || false;
  36. if (typeof obj !== "string") {
  37. // obj HTMLElement, HTMLDocument
  38. obj.addEventListener(eventType, callback, useCapture);
  39. } else {
  40. // obj is a selector
  41. d.addEventListener(
  42. eventType,
  43. (e) => {
  44. let el = e.target || e.srcElement;
  45. let found = false;
  46. while (el?.matches && el !== d) {
  47. found = el.matches(obj);
  48. if (found) break;
  49. el = el.parentElement;
  50. }
  51. if (found) {
  52. callbackSafe(callback, el, e);
  53. }
  54. },
  55. useCapture
  56. );
  57. }
  58. };
  59. searxng.ready = (callback) => {
  60. if (document.readyState !== "loading") {
  61. callback.call(w);
  62. } else {
  63. w.addEventListener("DOMContentLoaded", callback.bind(w));
  64. }
  65. };
  66. searxng.http = (method, url, data = null) =>
  67. new Promise((resolve, reject) => {
  68. try {
  69. const req = new XMLHttpRequest();
  70. req.open(method, url, true);
  71. req.timeout = 20000;
  72. // On load
  73. req.onload = () => {
  74. if (req.status === 200) {
  75. resolve(req.response, req.responseType);
  76. } else {
  77. reject(Error(req.statusText));
  78. }
  79. };
  80. // Handle network errors
  81. req.onerror = () => {
  82. reject(Error("Network Error"));
  83. };
  84. req.onabort = () => {
  85. reject(Error("Transaction is aborted"));
  86. };
  87. req.ontimeout = () => {
  88. reject(Error("Timeout"));
  89. };
  90. // Make the request
  91. if (data) {
  92. req.send(data);
  93. } else {
  94. req.send();
  95. }
  96. } catch (ex) {
  97. reject(ex);
  98. }
  99. });
  100. searxng.loadStyle = (src) => {
  101. const path = `${searxng.settings.theme_static_path}/${src}`;
  102. const id = `style_${src.replace(".", "_")}`;
  103. let s = d.getElementById(id);
  104. if (s === null) {
  105. s = d.createElement("link");
  106. s.setAttribute("id", id);
  107. s.setAttribute("rel", "stylesheet");
  108. s.setAttribute("type", "text/css");
  109. s.setAttribute("href", path);
  110. d.body.appendChild(s);
  111. }
  112. };
  113. searxng.loadScript = (src, callback) => {
  114. const path = `${searxng.settings.theme_static_path}/${src}`;
  115. const id = `script_${src.replace(".", "_")}`;
  116. let s = d.getElementById(id);
  117. if (s === null) {
  118. s = d.createElement("script");
  119. s.setAttribute("id", id);
  120. s.setAttribute("src", path);
  121. s.onload = callback;
  122. s.onerror = () => {
  123. s.setAttribute("error", "1");
  124. };
  125. d.body.appendChild(s);
  126. } else if (!s.hasAttribute("error")) {
  127. try {
  128. callback.apply(s, []);
  129. } catch (exception) {
  130. console.log(exception);
  131. }
  132. } else {
  133. console.log(`callback not executed : script '${path}' not loaded.`);
  134. }
  135. };
  136. searxng.insertBefore = (newNode, referenceNode) => {
  137. referenceNode.parentNode.insertBefore(newNode, referenceNode);
  138. };
  139. searxng.insertAfter = (newNode, referenceNode) => {
  140. referenceNode.parentNode.insertAfter(newNode, referenceNode.nextSibling);
  141. };
  142. searxng.on(".close", "click", function () {
  143. this.parentNode.classList.add("invisible");
  144. });
  145. function getEndpoint() {
  146. for (const className of d.getElementsByTagName("body")[0].classList.values()) {
  147. if (className.endsWith("_endpoint")) {
  148. return className.split("_")[0];
  149. }
  150. }
  151. return "";
  152. }
  153. searxng.endpoint = getEndpoint();
  154. return searxng;
  155. })(window, document);