searx.js 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534
  1. /**
  2. * searx is free software: you can redistribute it and/or modify
  3. * it under the terms of the GNU Affero General Public License as published by
  4. * the Free Software Foundation, either version 3 of the License, or
  5. * (at your option) any later version.
  6. *
  7. * searx is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU Affero General Public License for more details.
  11. *
  12. * You should have received a copy of the GNU Affero General Public License
  13. * along with searx. If not, see < http://www.gnu.org/licenses/ >.
  14. *
  15. * (C) 2017 by Alexandre Flament, <alex@al-f.net>
  16. *
  17. */
  18. (function(w, d, searx) {
  19. 'use strict';
  20. // not invented here tookit with bugs fixed elsewhere
  21. // purposes : be just good enough and as small as possible
  22. // from https://plainjs.com/javascript/events/live-binding-event-handlers-14/
  23. if (w.Element) {
  24. (function(ElementPrototype) {
  25. ElementPrototype.matches = ElementPrototype.matches ||
  26. ElementPrototype.matchesSelector ||
  27. ElementPrototype.webkitMatchesSelector ||
  28. ElementPrototype.msMatchesSelector ||
  29. function(selector) {
  30. var node = this, nodes = (node.parentNode || node.document).querySelectorAll(selector), i = -1;
  31. while (nodes[++i] && nodes[i] != node);
  32. return !!nodes[i];
  33. };
  34. })(Element.prototype);
  35. }
  36. function callbackSafe(callback, el, e) {
  37. try {
  38. callback.call(el, e);
  39. } catch (exception) {
  40. console.log(exception);
  41. }
  42. }
  43. searx = searx || {};
  44. searx.on = function(obj, eventType, callback, useCapture) {
  45. useCapture = useCapture || false;
  46. if (typeof obj !== 'string') {
  47. // obj HTMLElement, HTMLDocument
  48. obj.addEventListener(eventType, callback, useCapture);
  49. } else {
  50. // obj is a selector
  51. d.addEventListener(eventType, function(e) {
  52. var el = e.target || e.srcElement, found = false;
  53. while (el && el.matches && el !== d && !(found = el.matches(obj))) el = el.parentElement;
  54. if (found) callbackSafe(callback, el, e);
  55. }, useCapture);
  56. }
  57. };
  58. searx.ready = function(callback) {
  59. if (document.readyState != 'loading') {
  60. callback.call(w);
  61. } else {
  62. w.addEventListener('DOMContentLoaded', callback.bind(w));
  63. }
  64. };
  65. searx.http = function(method, url, callback) {
  66. var req = new XMLHttpRequest(),
  67. resolve = function() {},
  68. reject = function() {},
  69. promise = {
  70. then: function(callback) { resolve = callback; return promise; },
  71. catch: function(callback) { reject = callback; return promise; }
  72. };
  73. try {
  74. req.open(method, url, true);
  75. // On load
  76. req.onload = function() {
  77. if (req.status == 200) {
  78. resolve(req.response, req.responseType);
  79. } else {
  80. reject(Error(req.statusText));
  81. }
  82. };
  83. // Handle network errors
  84. req.onerror = function() {
  85. reject(Error("Network Error"));
  86. };
  87. req.onabort = function() {
  88. reject(Error("Transaction is aborted"));
  89. };
  90. // Make the request
  91. req.send();
  92. } catch (ex) {
  93. reject(ex);
  94. }
  95. return promise;
  96. };
  97. searx.loadStyle = function(src) {
  98. var path = searx.staticPath + src,
  99. id = "style_" + src.replace('.', '_'),
  100. s = d.getElementById(id);
  101. if (s === null) {
  102. s = d.createElement('link');
  103. s.setAttribute('id', id);
  104. s.setAttribute('rel', 'stylesheet');
  105. s.setAttribute('type', 'text/css');
  106. s.setAttribute('href', path);
  107. d.body.appendChild(s);
  108. }
  109. };
  110. searx.loadScript = function(src, callback) {
  111. var path = searx.staticPath + src,
  112. id = "script_" + src.replace('.', '_'),
  113. s = d.getElementById(id);
  114. if (s === null) {
  115. s = d.createElement('script');
  116. s.setAttribute('id', id);
  117. s.setAttribute('src', path);
  118. s.onload = callback;
  119. s.onerror = function() {
  120. s.setAttribute('error', '1');
  121. };
  122. d.body.appendChild(s);
  123. } else if (!s.hasAttribute('error')) {
  124. try {
  125. callback.apply(s, []);
  126. } catch (exception) {
  127. console.log(exception);
  128. }
  129. } else {
  130. console.log("callback not executed : script '" + path + "' not loaded.");
  131. }
  132. };
  133. searx.on('.close', 'click', function(e) {
  134. var el = e.target || e.srcElement;
  135. this.parentNode.style.display="None";
  136. });
  137. return searx;
  138. })(window, document, window.searx);
  139. ;(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.AutoComplete = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  140. /*
  141. * @license MIT
  142. *
  143. * Autocomplete.js v2.6.3
  144. * Developed by Baptiste Donaux
  145. * http://autocomplete-js.com
  146. *
  147. * (c) 2017, Baptiste Donaux
  148. */
  149. "use strict";
  150. var ConditionOperator;
  151. (function (ConditionOperator) {
  152. ConditionOperator[ConditionOperator["AND"] = 0] = "AND";
  153. ConditionOperator[ConditionOperator["OR"] = 1] = "OR";
  154. })(ConditionOperator || (ConditionOperator = {}));
  155. var EventType;
  156. (function (EventType) {
  157. EventType[EventType["KEYDOWN"] = 0] = "KEYDOWN";
  158. EventType[EventType["KEYUP"] = 1] = "KEYUP";
  159. })(EventType || (EventType = {}));
  160. /**
  161. * Core
  162. *
  163. * @class
  164. * @author Baptiste Donaux <baptiste.donaux@gmail.com> @baptistedonaux
  165. */
  166. var AutoComplete = (function () {
  167. // Constructor
  168. function AutoComplete(params, selector) {
  169. if (params === void 0) { params = {}; }
  170. if (selector === void 0) { selector = "[data-autocomplete]"; }
  171. if (Array.isArray(selector)) {
  172. selector.forEach(function (s) {
  173. new AutoComplete(params, s);
  174. });
  175. }
  176. else if (typeof selector == "string") {
  177. var elements = document.querySelectorAll(selector);
  178. Array.prototype.forEach.call(elements, function (input) {
  179. new AutoComplete(params, input);
  180. });
  181. }
  182. else {
  183. var specificParams = AutoComplete.merge(AutoComplete.defaults, params, {
  184. DOMResults: document.createElement("div")
  185. });
  186. AutoComplete.prototype.create(specificParams, selector);
  187. return specificParams;
  188. }
  189. }
  190. AutoComplete.prototype.create = function (params, element) {
  191. params.Input = element;
  192. if (params.Input.nodeName.match(/^INPUT$/i) && (params.Input.hasAttribute("type") === false || params.Input.getAttribute("type").match(/^TEXT|SEARCH$/i))) {
  193. params.Input.setAttribute("autocomplete", "off");
  194. params._Position(params);
  195. params.Input.parentNode.appendChild(params.DOMResults);
  196. params.$Listeners = {
  197. blur: params._Blur.bind(params),
  198. destroy: AutoComplete.prototype.destroy.bind(null, params),
  199. focus: params._Focus.bind(params),
  200. keyup: AutoComplete.prototype.event.bind(null, params, EventType.KEYUP),
  201. keydown: AutoComplete.prototype.event.bind(null, params, EventType.KEYDOWN),
  202. position: params._Position.bind(params)
  203. };
  204. for (var event in params.$Listeners) {
  205. params.Input.addEventListener(event, params.$Listeners[event]);
  206. }
  207. }
  208. };
  209. AutoComplete.prototype.getEventsByType = function (params, type) {
  210. var mappings = {};
  211. for (var key in params.KeyboardMappings) {
  212. var event = EventType.KEYUP;
  213. if (params.KeyboardMappings[key].Event !== undefined) {
  214. event = params.KeyboardMappings[key].Event;
  215. }
  216. if (event == type) {
  217. mappings[key] = params.KeyboardMappings[key];
  218. }
  219. }
  220. return mappings;
  221. };
  222. AutoComplete.prototype.event = function (params, type, event) {
  223. var eventIdentifier = function (condition) {
  224. if ((match === true && mapping.Operator == ConditionOperator.AND) || (match === false && mapping.Operator == ConditionOperator.OR)) {
  225. condition = AutoComplete.merge({
  226. Not: false
  227. }, condition);
  228. if (condition.hasOwnProperty("Is")) {
  229. if (condition.Is == event.keyCode) {
  230. match = !condition.Not;
  231. }
  232. else {
  233. match = condition.Not;
  234. }
  235. }
  236. else if (condition.hasOwnProperty("From") && condition.hasOwnProperty("To")) {
  237. if (event.keyCode >= condition.From && event.keyCode <= condition.To) {
  238. match = !condition.Not;
  239. }
  240. else {
  241. match = condition.Not;
  242. }
  243. }
  244. }
  245. };
  246. for (var name in AutoComplete.prototype.getEventsByType(params, type)) {
  247. var mapping = AutoComplete.merge({
  248. Operator: ConditionOperator.AND
  249. }, params.KeyboardMappings[name]), match = ConditionOperator.AND == mapping.Operator;
  250. mapping.Conditions.forEach(eventIdentifier);
  251. if (match === true) {
  252. mapping.Callback.call(params, event);
  253. }
  254. }
  255. };
  256. AutoComplete.prototype.makeRequest = function (params, callback) {
  257. var propertyHttpHeaders = Object.getOwnPropertyNames(params.HttpHeaders), request = new XMLHttpRequest(), method = params._HttpMethod(), url = params._Url(), queryParams = params._Pre(), queryParamsStringify = encodeURIComponent(params._QueryArg()) + "=" + encodeURIComponent(queryParams);
  258. if (method.match(/^GET$/i)) {
  259. if (url.indexOf("?") !== -1) {
  260. url += "&" + queryParamsStringify;
  261. }
  262. else {
  263. url += "?" + queryParamsStringify;
  264. }
  265. }
  266. request.open(method, url, true);
  267. for (var i = propertyHttpHeaders.length - 1; i >= 0; i--) {
  268. request.setRequestHeader(propertyHttpHeaders[i], params.HttpHeaders[propertyHttpHeaders[i]]);
  269. }
  270. request.onreadystatechange = function () {
  271. if (request.readyState == 4 && request.status == 200) {
  272. params.$Cache[queryParams] = request.response;
  273. callback(request.response);
  274. }
  275. };
  276. return request;
  277. };
  278. AutoComplete.prototype.ajax = function (params, request, timeout) {
  279. if (timeout === void 0) { timeout = true; }
  280. if (params.$AjaxTimer) {
  281. window.clearTimeout(params.$AjaxTimer);
  282. }
  283. if (timeout === true) {
  284. params.$AjaxTimer = window.setTimeout(AutoComplete.prototype.ajax.bind(null, params, request, false), params.Delay);
  285. }
  286. else {
  287. if (params.Request) {
  288. params.Request.abort();
  289. }
  290. params.Request = request;
  291. params.Request.send(params._QueryArg() + "=" + params._Pre());
  292. }
  293. };
  294. AutoComplete.prototype.cache = function (params, callback) {
  295. var response = params._Cache(params._Pre());
  296. if (response === undefined) {
  297. var request = AutoComplete.prototype.makeRequest(params, callback);
  298. AutoComplete.prototype.ajax(params, request);
  299. }
  300. else {
  301. callback(response);
  302. }
  303. };
  304. AutoComplete.prototype.destroy = function (params) {
  305. for (var event in params.$Listeners) {
  306. params.Input.removeEventListener(event, params.$Listeners[event]);
  307. }
  308. params.DOMResults.parentNode.removeChild(params.DOMResults);
  309. };
  310. return AutoComplete;
  311. }());
  312. AutoComplete.merge = function () {
  313. var merge = {}, tmp;
  314. for (var i = 0; i < arguments.length; i++) {
  315. for (tmp in arguments[i]) {
  316. merge[tmp] = arguments[i][tmp];
  317. }
  318. }
  319. return merge;
  320. };
  321. AutoComplete.defaults = {
  322. Delay: 150,
  323. EmptyMessage: "No result here",
  324. Highlight: {
  325. getRegex: function (value) {
  326. return new RegExp(value, "ig");
  327. },
  328. transform: function (value) {
  329. return "<strong>" + value + "</strong>";
  330. }
  331. },
  332. HttpHeaders: {
  333. "Content-type": "application/x-www-form-urlencoded"
  334. },
  335. Limit: 0,
  336. MinChars: 0,
  337. HttpMethod: "GET",
  338. QueryArg: "q",
  339. Url: null,
  340. KeyboardMappings: {
  341. "Enter": {
  342. Conditions: [{
  343. Is: 13,
  344. Not: false
  345. }],
  346. Callback: function (event) {
  347. if (this.DOMResults.getAttribute("class").indexOf("open") != -1) {
  348. var liActive = this.DOMResults.querySelector("li.active");
  349. if (liActive !== null) {
  350. event.preventDefault();
  351. this._Select(liActive);
  352. this.DOMResults.setAttribute("class", "autocomplete");
  353. }
  354. }
  355. },
  356. Operator: ConditionOperator.AND,
  357. Event: EventType.KEYDOWN
  358. },
  359. "KeyUpAndDown_down": {
  360. Conditions: [{
  361. Is: 38,
  362. Not: false
  363. },
  364. {
  365. Is: 40,
  366. Not: false
  367. }],
  368. Callback: function (event) {
  369. event.preventDefault();
  370. },
  371. Operator: ConditionOperator.OR,
  372. Event: EventType.KEYDOWN
  373. },
  374. "KeyUpAndDown_up": {
  375. Conditions: [{
  376. Is: 38,
  377. Not: false
  378. },
  379. {
  380. Is: 40,
  381. Not: false
  382. }],
  383. Callback: function (event) {
  384. event.preventDefault();
  385. var first = this.DOMResults.querySelector("li:first-child:not(.locked)"), last = this.DOMResults.querySelector("li:last-child:not(.locked)"), active = this.DOMResults.querySelector("li.active");
  386. if (active) {
  387. var currentIndex = Array.prototype.indexOf.call(active.parentNode.children, active), position = currentIndex + (event.keyCode - 39), lisCount = this.DOMResults.getElementsByTagName("li").length;
  388. if (position < 0) {
  389. position = lisCount - 1;
  390. }
  391. else if (position >= lisCount) {
  392. position = 0;
  393. }
  394. active.classList.remove("active");
  395. active.parentElement.children.item(position).classList.add("active");
  396. }
  397. else if (last && event.keyCode == 38) {
  398. last.classList.add("active");
  399. }
  400. else if (first) {
  401. first.classList.add("active");
  402. }
  403. },
  404. Operator: ConditionOperator.OR,
  405. Event: EventType.KEYUP
  406. },
  407. "AlphaNum": {
  408. Conditions: [{
  409. Is: 13,
  410. Not: true
  411. }, {
  412. From: 35,
  413. To: 40,
  414. Not: true
  415. }],
  416. Callback: function () {
  417. var oldValue = this.Input.getAttribute("data-autocomplete-old-value"), currentValue = this._Pre();
  418. if (currentValue !== "" && currentValue.length >= this._MinChars()) {
  419. if (!oldValue || currentValue != oldValue) {
  420. this.DOMResults.setAttribute("class", "autocomplete open");
  421. }
  422. AutoComplete.prototype.cache(this, function (response) {
  423. this._Render(this._Post(response));
  424. this._Open();
  425. }.bind(this));
  426. }
  427. },
  428. Operator: ConditionOperator.AND,
  429. Event: EventType.KEYUP
  430. }
  431. },
  432. DOMResults: null,
  433. Request: null,
  434. Input: null,
  435. /**
  436. * Return the message when no result returns
  437. */
  438. _EmptyMessage: function () {
  439. var emptyMessage = "";
  440. if (this.Input.hasAttribute("data-autocomplete-empty-message")) {
  441. emptyMessage = this.Input.getAttribute("data-autocomplete-empty-message");
  442. }
  443. else if (this.EmptyMessage !== false) {
  444. emptyMessage = this.EmptyMessage;
  445. }
  446. else {
  447. emptyMessage = "";
  448. }
  449. return emptyMessage;
  450. },
  451. /**
  452. * Returns the maximum number of results
  453. */
  454. _Limit: function () {
  455. var limit = this.Input.getAttribute("data-autocomplete-limit");
  456. if (isNaN(limit) || limit === null) {
  457. return this.Limit;
  458. }
  459. return parseInt(limit, 10);
  460. },
  461. /**
  462. * Returns the minimum number of characters entered before firing ajax
  463. */
  464. _MinChars: function () {
  465. var minchars = this.Input.getAttribute("data-autocomplete-minchars");
  466. if (isNaN(minchars) || minchars === null) {
  467. return this.MinChars;
  468. }
  469. return parseInt(minchars, 10);
  470. },
  471. /**
  472. * Apply transformation on labels response
  473. */
  474. _Highlight: function (label) {
  475. return label.replace(this.Highlight.getRegex(this._Pre()), this.Highlight.transform);
  476. },
  477. /**
  478. * Returns the HHTP method to use
  479. */
  480. _HttpMethod: function () {
  481. if (this.Input.hasAttribute("data-autocomplete-method")) {
  482. return this.Input.getAttribute("data-autocomplete-method");
  483. }
  484. return this.HttpMethod;
  485. },
  486. /**
  487. * Returns the query param to use
  488. */
  489. _QueryArg: function () {
  490. if (this.Input.hasAttribute("data-autocomplete-param-name")) {
  491. return this.Input.getAttribute("data-autocomplete-param-name");
  492. }
  493. return this.QueryArg;
  494. },
  495. /**
  496. * Returns the URL to use for AJAX request
  497. */
  498. _Url: function () {
  499. if (this.Input.hasAttribute("data-autocomplete")) {
  500. return this.Input.getAttribute("data-autocomplete");
  501. }
  502. return this.Url;
  503. },
  504. /**
  505. * Manage the close
  506. */
  507. _Blur: function (now) {
  508. if (now === true) {
  509. this.DOMResults.setAttribute("class", "autocomplete");
  510. this.Input.setAttribute("data-autocomplete-old-value", this.Input.value);
  511. }
  512. else {
  513. var params = this;
  514. setTimeout(function () {
  515. params._Blur(true);
  516. }, 150);
  517. }
  518. },
  519. /**
  520. * Manage the cache
  521. */
  522. _Cache: function (value) {
  523. return this.$Cache[value];
  524. },
  525. /**
  526. * Manage the open
  527. */
  528. _Focus: function () {
  529. var oldValue = this.Input.getAttribute("data-autocomplete-old-value");
  530. if ((!oldValue || this.Input.value != oldValue) && this._MinChars() <= this.Input.value.length) {
  531. this.DOMResults.setAttribute("class", "autocomplete open");
  532. }
  533. },
  534. /**
  535. * Bind all results item if one result is opened
  536. */
  537. _Open: function () {
  538. var params = this;
  539. Array.prototype.forEach.call(this.DOMResults.getElementsByTagName("li"), function (li) {
  540. if (li.getAttribute("class") != "locked") {
  541. li.onclick = function (event) {
  542. params._Select(li);
  543. };
  544. li.onmouseenter = function () {
  545. var active = params.DOMResults.querySelector("li.active");
  546. if (active !== li) {
  547. if (active !== null) {
  548. active.classList.remove("active");
  549. }
  550. li.classList.add("active");
  551. }
  552. };
  553. }
  554. });
  555. },
  556. /**
  557. * Position the results HTML element
  558. */
  559. _Position: function () {
  560. this.DOMResults.setAttribute("class", "autocomplete");
  561. this.DOMResults.setAttribute("style", "top:" + (this.Input.offsetTop + this.Input.offsetHeight) + "px;left:" + this.Input.offsetLeft + "px;width:" + this.Input.clientWidth + "px;");
  562. },
  563. /**
  564. * Execute the render of results DOM element
  565. */
  566. _Render: function (response) {
  567. var ul;
  568. if (typeof response == "string") {
  569. ul = this._RenderRaw(response);
  570. }
  571. else {
  572. ul = this._RenderResponseItems(response);
  573. }
  574. if (this.DOMResults.hasChildNodes()) {
  575. this.DOMResults.removeChild(this.DOMResults.childNodes[0]);
  576. }
  577. this.DOMResults.appendChild(ul);
  578. },
  579. /**
  580. * ResponseItems[] rendering
  581. */
  582. _RenderResponseItems: function (response) {
  583. var ul = document.createElement("ul"), li = document.createElement("li"), limit = this._Limit();
  584. // Order
  585. if (limit < 0) {
  586. response = response.reverse();
  587. }
  588. else if (limit === 0) {
  589. limit = response.length;
  590. }
  591. for (var item = 0; item < Math.min(Math.abs(limit), response.length); item++) {
  592. li.innerHTML = response[item].Label;
  593. li.setAttribute("data-autocomplete-value", response[item].Value);
  594. ul.appendChild(li);
  595. li = document.createElement("li");
  596. }
  597. return ul;
  598. },
  599. /**
  600. * string response rendering (RAW HTML)
  601. */
  602. _RenderRaw: function (response) {
  603. var ul = document.createElement("ul"), li = document.createElement("li");
  604. if (response.length > 0) {
  605. this.DOMResults.innerHTML = response;
  606. }
  607. else {
  608. var emptyMessage = this._EmptyMessage();
  609. if (emptyMessage !== "") {
  610. li.innerHTML = emptyMessage;
  611. li.setAttribute("class", "locked");
  612. ul.appendChild(li);
  613. }
  614. }
  615. return ul;
  616. },
  617. /**
  618. * Deal with request response
  619. */
  620. _Post: function (response) {
  621. try {
  622. var returnResponse = [];
  623. //JSON return
  624. var json = JSON.parse(response);
  625. if (Object.keys(json).length === 0) {
  626. return "";
  627. }
  628. if (Array.isArray(json)) {
  629. for (var i = 0; i < Object.keys(json).length; i++) {
  630. returnResponse[returnResponse.length] = { "Value": json[i], "Label": this._Highlight(json[i]) };
  631. }
  632. }
  633. else {
  634. for (var value in json) {
  635. returnResponse.push({
  636. "Value": value,
  637. "Label": this._Highlight(json[value])
  638. });
  639. }
  640. }
  641. return returnResponse;
  642. }
  643. catch (event) {
  644. //HTML return
  645. return response;
  646. }
  647. },
  648. /**
  649. * Return the autocomplete value to send (before request)
  650. */
  651. _Pre: function () {
  652. return this.Input.value;
  653. },
  654. /**
  655. * Choice one result item
  656. */
  657. _Select: function (item) {
  658. console.log('test test test');
  659. if (item.hasAttribute("data-autocomplete-value")) {
  660. this.Input.value = item.getAttribute("data-autocomplete-value");
  661. }
  662. else {
  663. this.Input.value = item.innerHTML;
  664. }
  665. this.Input.setAttribute("data-autocomplete-old-value", this.Input.value);
  666. },
  667. $AjaxTimer: null,
  668. $Cache: {},
  669. $Listeners: {}
  670. };
  671. module.exports = AutoComplete;
  672. },{}]},{},[1])(1)
  673. });
  674. ;/**
  675. *
  676. * Google Image Layout v0.0.1
  677. * Description, by Anh Trinh.
  678. * Heavily modified for searx
  679. * http://trinhtrunganh.com
  680. *
  681. * @license Free to use under the MIT License.
  682. *
  683. */
  684. (function(w, d) {
  685. 'use strict';
  686. function ImageLayout(container_selector, results_selector, img_selector, maxHeight) {
  687. this.container_selector = container_selector;
  688. this.results_selector = results_selector;
  689. this.img_selector = img_selector;
  690. this.margin = 10;
  691. this.maxHeight = maxHeight;
  692. this._alignAllDone = true;
  693. }
  694. /**
  695. * Get the height that make all images fit the container
  696. *
  697. * width = w1 + w2 + w3 + ... = r1*h + r2*h + r3*h + ...
  698. *
  699. * @param {[type]} images the images to be calculated
  700. * @param {[type]} width the container witdth
  701. * @param {[type]} margin the margin between each image
  702. *
  703. * @return {[type]} the height
  704. */
  705. ImageLayout.prototype._getHeigth = function(images, width) {
  706. var r = 0,
  707. img;
  708. width -= images.length * this.margin;
  709. for (var i = 0; i < images.length; i++) {
  710. img = images[i];
  711. if ((img.naturalWidth > 0) && (img.naturalHeight > 0)) {
  712. r += img.naturalWidth / img.naturalHeight;
  713. } else {
  714. // assume that not loaded images are square
  715. r += 1;
  716. }
  717. }
  718. return width / r; //have to round down because Firefox will automatically roundup value with number of decimals > 3
  719. };
  720. ImageLayout.prototype._setSize = function(images, height) {
  721. var img, imgWidth, imagesLength = images.length;
  722. for (var i = 0; i < imagesLength; i++) {
  723. img = images[i];
  724. if ((img.naturalWidth > 0) && (img.naturalHeight > 0)) {
  725. imgWidth = height * img.naturalWidth / img.naturalHeight;
  726. } else {
  727. // not loaded image : make it square as _getHeigth said it
  728. imgWidth = height;
  729. }
  730. img.style.width = imgWidth + 'px';
  731. img.style.height = height + 'px';
  732. img.style.marginLeft = '3px';
  733. img.style.marginTop = '3px';
  734. img.style.marginRight = this.margin - 7 + 'px'; // -4 is the negative margin of the inline element
  735. img.style.marginBottom = this.margin - 7 + 'px';
  736. }
  737. };
  738. ImageLayout.prototype._alignImgs = function(imgGroup) {
  739. var slice, h,
  740. containerWidth = d.querySelector(this.container_selector).clientWidth;
  741. w: while (imgGroup.length > 0) {
  742. for (var i = 1; i <= imgGroup.length; i++) {
  743. slice = imgGroup.slice(0, i);
  744. h = this._getHeigth(slice, containerWidth);
  745. if (h < this.maxHeight) {
  746. this._setSize(slice, h);
  747. imgGroup = imgGroup.slice(i);
  748. continue w;
  749. }
  750. }
  751. this._setSize(slice, Math.min(this.maxHeight, h));
  752. break;
  753. }
  754. };
  755. ImageLayout.prototype.align = function(results_selector) {
  756. var results_selectorNode = d.querySelectorAll(this.results_selector),
  757. results_length = results_selectorNode.length,
  758. previous = null,
  759. current = null,
  760. imgGroup = [];
  761. for (var i = 0; i < results_length; i++) {
  762. current = results_selectorNode[i];
  763. if (current.previousElementSibling !== previous && imgGroup.length > 0) {
  764. // the current image is not conected to previous one
  765. // so the current image is the start of a new group of images.
  766. // so call _alignImgs to align the current group
  767. this._alignImgs(imgGroup);
  768. // and start a new empty group of images
  769. imgGroup = [];
  770. }
  771. // add the current image to the group (only the img tag)
  772. imgGroup.push(current.querySelector(this.img_selector));
  773. // update the previous variable
  774. previous = current;
  775. }
  776. // align the remaining images
  777. if (imgGroup.length > 0) {
  778. this._alignImgs(imgGroup);
  779. }
  780. };
  781. ImageLayout.prototype.watch = function() {
  782. var i, img, imgGroup, imgNodeLength,
  783. obj = this,
  784. results_nodes = d.querySelectorAll(this.results_selector),
  785. results_length = results_nodes.length;
  786. function align(e) {
  787. obj.align();
  788. }
  789. function throttleAlign(e) {
  790. if (obj._alignAllDone) {
  791. obj._alignAllDone = false;
  792. setTimeout(function() {
  793. obj.align();
  794. obj._alignAllDone = true;
  795. }, 100);
  796. }
  797. }
  798. w.addEventListener('resize', throttleAlign);
  799. w.addEventListener('pageshow', align);
  800. for (i = 0; i < results_length; i++) {
  801. img = results_nodes[i].querySelector(this.img_selector);
  802. if (typeof img !== 'undefined') {
  803. img.addEventListener('load', throttleAlign);
  804. img.addEventListener('error', throttleAlign);
  805. }
  806. }
  807. };
  808. w.searx.ImageLayout = ImageLayout;
  809. })(window, document);
  810. ;searx.ready(function() {
  811. searx.on('.result', 'click', function() {
  812. highlightResult(this)(true);
  813. });
  814. searx.on('.result a', 'focus', function(e) {
  815. var el = e.target;
  816. while (el !== undefined) {
  817. if (el.classList.contains('result')) {
  818. if (el.getAttribute("data-vim-selected") === null) {
  819. highlightResult(el)(true);
  820. }
  821. break;
  822. }
  823. el = el.parentNode;
  824. }
  825. }, true);
  826. var vimKeys = {
  827. 27: {
  828. key: 'Escape',
  829. fun: removeFocus,
  830. des: 'remove focus from the focused input',
  831. cat: 'Control'
  832. },
  833. 73: {
  834. key: 'i',
  835. fun: searchInputFocus,
  836. des: 'focus on the search input',
  837. cat: 'Control'
  838. },
  839. 66: {
  840. key: 'b',
  841. fun: scrollPage(-window.innerHeight),
  842. des: 'scroll one page up',
  843. cat: 'Navigation'
  844. },
  845. 70: {
  846. key: 'f',
  847. fun: scrollPage(window.innerHeight),
  848. des: 'scroll one page down',
  849. cat: 'Navigation'
  850. },
  851. 85: {
  852. key: 'u',
  853. fun: scrollPage(-window.innerHeight / 2),
  854. des: 'scroll half a page up',
  855. cat: 'Navigation'
  856. },
  857. 68: {
  858. key: 'd',
  859. fun: scrollPage(window.innerHeight / 2),
  860. des: 'scroll half a page down',
  861. cat: 'Navigation'
  862. },
  863. 71: {
  864. key: 'g',
  865. fun: scrollPageTo(-document.body.scrollHeight, 'top'),
  866. des: 'scroll to the top of the page',
  867. cat: 'Navigation'
  868. },
  869. 86: {
  870. key: 'v',
  871. fun: scrollPageTo(document.body.scrollHeight, 'bottom'),
  872. des: 'scroll to the bottom of the page',
  873. cat: 'Navigation'
  874. },
  875. 75: {
  876. key: 'k',
  877. fun: highlightResult('up'),
  878. des: 'select previous search result',
  879. cat: 'Results'
  880. },
  881. 74: {
  882. key: 'j',
  883. fun: highlightResult('down'),
  884. des: 'select next search result',
  885. cat: 'Results'
  886. },
  887. 80: {
  888. key: 'p',
  889. fun: pageButtonClick(0),
  890. des: 'go to previous page',
  891. cat: 'Results'
  892. },
  893. 78: {
  894. key: 'n',
  895. fun: pageButtonClick(1),
  896. des: 'go to next page',
  897. cat: 'Results'
  898. },
  899. 79: {
  900. key: 'o',
  901. fun: openResult(false),
  902. des: 'open search result',
  903. cat: 'Results'
  904. },
  905. 84: {
  906. key: 't',
  907. fun: openResult(true),
  908. des: 'open the result in a new tab',
  909. cat: 'Results'
  910. },
  911. 82: {
  912. key: 'r',
  913. fun: reloadPage,
  914. des: 'reload page from the server',
  915. cat: 'Control'
  916. },
  917. 72: {
  918. key: 'h',
  919. fun: toggleHelp,
  920. des: 'toggle help window',
  921. cat: 'Other'
  922. }
  923. };
  924. searx.on(document, "keyup", function(e) {
  925. // check for modifiers so we don't break browser's hotkeys
  926. if (vimKeys.hasOwnProperty(e.keyCode) && !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey) {
  927. var tagName = e.target.tagName.toLowerCase();
  928. if (e.keyCode === 27) {
  929. if (tagName === 'input' || tagName === 'select' || tagName === 'textarea') {
  930. vimKeys[e.keyCode].fun();
  931. }
  932. } else {
  933. if (e.target === document.body || tagName === 'a' || tagName === 'button') {
  934. vimKeys[e.keyCode].fun();
  935. }
  936. }
  937. }
  938. });
  939. function highlightResult(which) {
  940. return function(noScroll) {
  941. var current = document.querySelector('.result[data-vim-selected]'),
  942. effectiveWhich = which;
  943. if (current === null) {
  944. // no selection : choose the first one
  945. current = document.querySelector('.result');
  946. if (current === null) {
  947. // no first one : there are no results
  948. return;
  949. }
  950. // replace up/down actions by selecting first one
  951. if (which === "down" || which === "up") {
  952. effectiveWhich = current;
  953. }
  954. }
  955. var next, results = document.querySelectorAll('.result');
  956. if (typeof effectiveWhich !== 'string') {
  957. next = effectiveWhich;
  958. } else {
  959. switch (effectiveWhich) {
  960. case 'visible':
  961. var top = document.documentElement.scrollTop || document.body.scrollTop;
  962. var bot = top + document.documentElement.clientHeight;
  963. for (var i = 0; i < results.length; i++) {
  964. next = results[i];
  965. var etop = next.offsetTop;
  966. var ebot = etop + next.clientHeight;
  967. if ((ebot <= bot) && (etop > top)) {
  968. break;
  969. }
  970. }
  971. break;
  972. case 'down':
  973. next = current.nextElementSibling;
  974. if (next === null) {
  975. next = results[0];
  976. }
  977. break;
  978. case 'up':
  979. next = current.previousElementSibling;
  980. if (next === null) {
  981. next = results[results.length - 1];
  982. }
  983. break;
  984. case 'bottom':
  985. next = results[results.length - 1];
  986. break;
  987. case 'top':
  988. /* falls through */
  989. default:
  990. next = results[0];
  991. }
  992. }
  993. if (next) {
  994. current.removeAttribute('data-vim-selected');
  995. next.setAttribute('data-vim-selected', 'true');
  996. var link = next.querySelector('h3 a') || next.querySelector('a');
  997. if (link !== null) {
  998. link.focus();
  999. }
  1000. if (!noScroll) {
  1001. scrollPageToSelected();
  1002. }
  1003. }
  1004. };
  1005. }
  1006. function reloadPage() {
  1007. document.location.reload(true);
  1008. }
  1009. function removeFocus() {
  1010. if (document.activeElement) {
  1011. document.activeElement.blur();
  1012. }
  1013. }
  1014. function pageButtonClick(num) {
  1015. return function() {
  1016. var buttons = $('div#pagination button[type="submit"]');
  1017. if (buttons.length !== 2) {
  1018. console.log('page navigation with this theme is not supported');
  1019. return;
  1020. }
  1021. if (num >= 0 && num < buttons.length) {
  1022. buttons[num].click();
  1023. } else {
  1024. console.log('pageButtonClick(): invalid argument');
  1025. }
  1026. };
  1027. }
  1028. function scrollPageToSelected() {
  1029. var sel = document.querySelector('.result[data-vim-selected]');
  1030. if (sel === null) {
  1031. return;
  1032. }
  1033. var wtop = document.documentElement.scrollTop || document.body.scrollTop,
  1034. wheight = document.documentElement.clientHeight,
  1035. etop = sel.offsetTop,
  1036. ebot = etop + sel.clientHeight,
  1037. offset = 120;
  1038. // first element ?
  1039. if ((sel.previousElementSibling === null) && (ebot < wheight)) {
  1040. // set to the top of page if the first element
  1041. // is fully included in the viewport
  1042. window.scroll(window.scrollX, 0);
  1043. return;
  1044. }
  1045. if (wtop > (etop - offset)) {
  1046. window.scroll(window.scrollX, etop - offset);
  1047. } else {
  1048. var wbot = wtop + wheight;
  1049. if (wbot < (ebot + offset)) {
  1050. window.scroll(window.scrollX, ebot - wheight + offset);
  1051. }
  1052. }
  1053. }
  1054. function scrollPage(amount) {
  1055. return function() {
  1056. window.scrollBy(0, amount);
  1057. highlightResult('visible')();
  1058. };
  1059. }
  1060. function scrollPageTo(position, nav) {
  1061. return function() {
  1062. window.scrollTo(0, position);
  1063. highlightResult(nav)();
  1064. };
  1065. }
  1066. function searchInputFocus() {
  1067. window.scrollTo(0, 0);
  1068. document.querySelector('#q').focus();
  1069. }
  1070. function openResult(newTab) {
  1071. return function() {
  1072. var link = document.querySelector('.result[data-vim-selected] h3 a');
  1073. if (link !== null) {
  1074. var url = link.getAttribute('href');
  1075. if (newTab) {
  1076. window.open(url);
  1077. } else {
  1078. window.location.href = url;
  1079. }
  1080. }
  1081. };
  1082. }
  1083. function toggleHelp() {
  1084. var helpPanel = document.querySelector('#vim-hotkeys-help');
  1085. if (helpPanel.length) {
  1086. helpPanel.classList.toggle('hidden');
  1087. return;
  1088. }
  1089. var categories = {};
  1090. for (var k in vimKeys) {
  1091. var key = vimKeys[k];
  1092. categories[key.cat] = categories[key.cat] || [];
  1093. categories[key.cat].push(key);
  1094. }
  1095. var sorted = Object.keys(categories).sort(function(a, b) {
  1096. return categories[b].length - categories[a].length;
  1097. });
  1098. if (sorted.length === 0) {
  1099. return;
  1100. }
  1101. var html = '<div id="vim-hotkeys-help" class="well vim-hotkeys-help">';
  1102. html += '<div class="container-fluid">';
  1103. html += '<div class="row">';
  1104. html += '<div class="col-sm-12">';
  1105. html += '<h3>How to navigate searx with Vim-like hotkeys</h3>';
  1106. html += '</div>'; // col-sm-12
  1107. html += '</div>'; // row
  1108. for (var i = 0; i < sorted.length; i++) {
  1109. var cat = categories[sorted[i]];
  1110. var lastCategory = i === (sorted.length - 1);
  1111. var first = i % 2 === 0;
  1112. if (first) {
  1113. html += '<div class="row dflex">';
  1114. }
  1115. html += '<div class="col-sm-' + (first && lastCategory ? 12 : 6) + ' dflex">';
  1116. html += '<div class="panel panel-default iflex">';
  1117. html += '<div class="panel-heading">' + cat[0].cat + '</div>';
  1118. html += '<div class="panel-body">';
  1119. html += '<ul class="list-unstyled">';
  1120. for (var cj in cat) {
  1121. html += '<li><kbd>' + cat[cj].key + '</kbd> ' + cat[cj].des + '</li>';
  1122. }
  1123. html += '</ul>';
  1124. html += '</div>'; // panel-body
  1125. html += '</div>'; // panel
  1126. html += '</div>'; // col-sm-*
  1127. if (!first || lastCategory) {
  1128. html += '</div>'; // row
  1129. }
  1130. }
  1131. html += '</div>'; // container-fluid
  1132. html += '</div>'; // vim-hotkeys-help
  1133. $('body').append(html);
  1134. }
  1135. });
  1136. ;/**
  1137. * searx is free software: you can redistribute it and/or modify
  1138. * it under the terms of the GNU Affero General Public License as published by
  1139. * the Free Software Foundation, either version 3 of the License, or
  1140. * (at your option) any later version.
  1141. *
  1142. * searx is distributed in the hope that it will be useful,
  1143. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  1144. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  1145. * GNU Affero General Public License for more details.
  1146. *
  1147. * You should have received a copy of the GNU Affero General Public License
  1148. * along with searx. If not, see < http://www.gnu.org/licenses/ >.
  1149. *
  1150. * (C) 2014 by Thomas Pointhuber, <thomas.pointhuber@gmx.at>
  1151. * (C) 2017 by Alexandre Flament, <alex@al-f.net>
  1152. */
  1153. (function (w, d, searx) {
  1154. 'use strict';
  1155. searx.ready(function () {
  1156. searx.on('.searx_overpass_request', 'click', function(event) {
  1157. // no more request
  1158. this.classList.remove("searx_overpass_request");
  1159. //
  1160. var overpass_url = "https://overpass-api.de/api/interpreter?data=";
  1161. var query_start = overpass_url + "[out:json][timeout:25];(";
  1162. var query_end = ");out meta;";
  1163. var osm_id = this.dataset.osmId;
  1164. var osm_type = this.dataset.osmType;
  1165. var result_table = d.querySelector("#" + this.dataset.resultTable);
  1166. var result_table_loadicon = d.querySelector("#" + this.dataset.resultTableLoadicon);
  1167. // tags which can be ignored
  1168. var osm_ignore_tags = [ "addr:city", "addr:country", "addr:housenumber", "addr:postcode", "addr:street" ];
  1169. if(osm_id && osm_type && result_table) {
  1170. var query = null;
  1171. switch(osm_type) {
  1172. case 'node':
  1173. query = query_start + "node(" + osm_id + ");" + query_end;
  1174. break;
  1175. case 'way':
  1176. query = query_start + "way(" + osm_id + ");" + query_end;
  1177. break;
  1178. case 'relation':
  1179. query = query_start + "relation(" + osm_id + ");" + query_end;
  1180. break;
  1181. default:
  1182. break;
  1183. }
  1184. if(query) {
  1185. // console.log(query);
  1186. searx.http( 'GET', query ).then(function(html, contentType) {
  1187. html = JSON.parse(html);
  1188. if(html && html.elements && html.elements[0]) {
  1189. var element = html.elements[0];
  1190. var newHtml = "";
  1191. for (var row in element.tags) {
  1192. if(element.tags.name === null || osm_ignore_tags.indexOf(row) == -1) {
  1193. newHtml += "<tr><td>" + row + "</td><td>";
  1194. switch(row) {
  1195. case "phone":
  1196. case "fax":
  1197. newHtml += "<a href=\"tel:" + element.tags[row].replace(/ /g,'') + "\">" + element.tags[row] + "</a>";
  1198. break;
  1199. case "email":
  1200. newHtml += "<a href=\"mailto:" + element.tags[row] + "\">" + element.tags[row] + "</a>";
  1201. break;
  1202. case "website":
  1203. case "url":
  1204. newHtml += "<a href=\"" + element.tags[row] + "\">" + element.tags[row] + "</a>";
  1205. break;
  1206. case "wikidata":
  1207. newHtml += "<a href=\"https://www.wikidata.org/wiki/" + element.tags[row] + "\">" + element.tags[row] + "</a>";
  1208. break;
  1209. case "wikipedia":
  1210. if(element.tags[row].indexOf(":") != -1) {
  1211. newHtml += "<a href=\"https://" + element.tags[row].substring(0,element.tags[row].indexOf(":")) + ".wikipedia.org/wiki/" + element.tags[row].substring(element.tags[row].indexOf(":")+1) + "\">" + element.tags[row] + "</a>";
  1212. break;
  1213. }
  1214. /* jshint ignore:start */
  1215. default:
  1216. /* jshint ignore:end */
  1217. newHtml += element.tags[row];
  1218. break;
  1219. }
  1220. newHtml += "</td></tr>";
  1221. }
  1222. }
  1223. result_table_loadicon.classList.add('invisible');
  1224. result_table.classList.remove('invisible');
  1225. result_table.querySelector("tbody").innerHTML = newHtml;
  1226. }
  1227. })
  1228. .catch(function() {
  1229. result_table_loadicon.innerHTML = result_table_loadicon.innerHTML + "<p class=\"text-muted\">could not load data!</p>";
  1230. });
  1231. }
  1232. }
  1233. // this event occour only once per element
  1234. event.preventDefault();
  1235. });
  1236. searx.on('.searx_init_map', 'click', function(event) {
  1237. // no more request
  1238. this.classList.remove("searx_init_map");
  1239. //
  1240. var leaflet_target = this.dataset.leafletTarget;
  1241. var map_lon = parseFloat(this.dataset.mapLon);
  1242. var map_lat = parseFloat(this.dataset.mapLat);
  1243. var map_zoom = parseFloat(this.dataset.mapZoom);
  1244. var map_boundingbox = JSON.parse(this.dataset.mapBoundingbox);
  1245. var map_geojson = JSON.parse(this.dataset.mapGeojson);
  1246. searx.loadStyle('leaflet/leaflet.css');
  1247. searx.loadScript('leaflet/leaflet.js', function() {
  1248. var map_bounds = null;
  1249. if(map_boundingbox) {
  1250. var southWest = L.latLng(map_boundingbox[0], map_boundingbox[2]);
  1251. var northEast = L.latLng(map_boundingbox[1], map_boundingbox[3]);
  1252. map_bounds = L.latLngBounds(southWest, northEast);
  1253. }
  1254. // init map
  1255. var map = L.map(leaflet_target);
  1256. // create the tile layer with correct attribution
  1257. var osmMapnikUrl='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
  1258. var osmMapnikAttrib='Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';
  1259. var osmMapnik = new L.TileLayer(osmMapnikUrl, {minZoom: 1, maxZoom: 19, attribution: osmMapnikAttrib});
  1260. var osmWikimediaUrl='https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png';
  1261. var osmWikimediaAttrib = 'Wikimedia maps beta | Maps data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';
  1262. var osmWikimedia = new L.TileLayer(osmWikimediaUrl, {minZoom: 1, maxZoom: 19, attribution: osmWikimediaAttrib});
  1263. // init map view
  1264. if(map_bounds) {
  1265. // TODO hack: https://github.com/Leaflet/Leaflet/issues/2021
  1266. // Still useful ?
  1267. setTimeout(function () {
  1268. map.fitBounds(map_bounds, {
  1269. maxZoom:17
  1270. });
  1271. }, 0);
  1272. } else if (map_lon && map_lat) {
  1273. if(map_zoom) {
  1274. map.setView(new L.latLng(map_lat, map_lon),map_zoom);
  1275. } else {
  1276. map.setView(new L.latLng(map_lat, map_lon),8);
  1277. }
  1278. }
  1279. map.addLayer(osmMapnik);
  1280. var baseLayers = {
  1281. "OSM Mapnik": osmMapnik/*,
  1282. "OSM Wikimedia": osmWikimedia*/
  1283. };
  1284. L.control.layers(baseLayers).addTo(map);
  1285. if(map_geojson) {
  1286. L.geoJson(map_geojson).addTo(map);
  1287. } /*else if(map_bounds) {
  1288. L.rectangle(map_bounds, {color: "#ff7800", weight: 3, fill:false}).addTo(map);
  1289. }*/
  1290. });
  1291. // this event occour only once per element
  1292. event.preventDefault();
  1293. });
  1294. });
  1295. })(window, document, window.searx);
  1296. ;/**
  1297. * searx is free software: you can redistribute it and/or modify
  1298. * it under the terms of the GNU Affero General Public License as published by
  1299. * the Free Software Foundation, either version 3 of the License, or
  1300. * (at your option) any later version.
  1301. *
  1302. * searx is distributed in the hope that it will be useful,
  1303. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  1304. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  1305. * GNU Affero General Public License for more details.
  1306. *
  1307. * You should have received a copy of the GNU Affero General Public License
  1308. * along with searx. If not, see < http://www.gnu.org/licenses/ >.
  1309. *
  1310. * (C) 2017 by Alexandre Flament, <alex@al-f.net>
  1311. */
  1312. (function(w, d, searx) {
  1313. 'use strict';
  1314. searx.ready(function() {
  1315. searx.image_thumbnail_layout = new searx.ImageLayout('#urls', '#urls .result-images', 'img.image_thumbnail', 200);
  1316. searx.image_thumbnail_layout.watch();
  1317. searx.on('.btn-collapse', 'click', function(event) {
  1318. var btnLabelCollapsed = this.getAttribute('data-btn-text-collapsed');
  1319. var btnLabelNotCollapsed = this.getAttribute('data-btn-text-not-collapsed');
  1320. var target = this.getAttribute('data-target');
  1321. var targetElement = d.querySelector(target);
  1322. var html = this.innerHTML;
  1323. if (this.classList.contains('collapsed')) {
  1324. html = html.replace(btnLabelCollapsed, btnLabelNotCollapsed);
  1325. } else {
  1326. html = html.replace(btnLabelNotCollapsed, btnLabelCollapsed);
  1327. }
  1328. this.innerHTML = html;
  1329. this.classList.toggle('collapsed');
  1330. targetElement.classList.toggle('invisible');
  1331. });
  1332. searx.on('.media-loader', 'click', function(event) {
  1333. var target = this.getAttribute('data-target');
  1334. var iframe_load = d.querySelector(target + ' > iframe');
  1335. var srctest = iframe_load.getAttribute('src');
  1336. if (srctest === null || srctest === undefined || srctest === false) {
  1337. iframe_load.setAttribute('src', iframe_load.getAttribute('data-src'));
  1338. }
  1339. });
  1340. w.addEventListener('scroll', function() {
  1341. var e = d.getElementById('backToTop'),
  1342. scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
  1343. if (e !== null) {
  1344. if (scrollTop >= 200) {
  1345. e.style.opacity = 1;
  1346. } else {
  1347. e.style.opacity = 0;
  1348. }
  1349. }
  1350. });
  1351. });
  1352. })(window, document, window.searx);
  1353. ;/**
  1354. * searx is free software: you can redistribute it and/or modify
  1355. * it under the terms of the GNU Affero General Public License as published by
  1356. * the Free Software Foundation, either version 3 of the License, or
  1357. * (at your option) any later version.
  1358. *
  1359. * searx is distributed in the hope that it will be useful,
  1360. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  1361. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  1362. * GNU Affero General Public License for more details.
  1363. *
  1364. * You should have received a copy of the GNU Affero General Public License
  1365. * along with searx. If not, see < http://www.gnu.org/licenses/ >.
  1366. *
  1367. * (C) 2017 by Alexandre Flament, <alex@al-f.net>
  1368. */
  1369. (function(w, d, searx) {
  1370. 'use strict';
  1371. var firstFocus = true, qinput_id = "q", qinput;
  1372. function placeCursorAtEnd(element) {
  1373. if (element.setSelectionRange) {
  1374. var len = element.value.length;
  1375. element.setSelectionRange(len, len);
  1376. }
  1377. }
  1378. function submitIfQuery() {
  1379. if (qinput.value.length > 0) {
  1380. var search = document.getElementById('search');
  1381. setTimeout(search.submit.bind(search), 0);
  1382. }
  1383. }
  1384. searx.ready(function() {
  1385. qinput = d.getElementById(qinput_id);
  1386. function placeCursorAtEndOnce(e) {
  1387. if (firstFocus) {
  1388. placeCursorAtEnd(qinput);
  1389. firstFocus = false;
  1390. } else {
  1391. // e.preventDefault();
  1392. }
  1393. }
  1394. if (qinput !== null) {
  1395. // autocompleter
  1396. if (searx.autocompleter) {
  1397. searx.autocomplete = AutoComplete.call(w, {
  1398. Url: "./autocompleter",
  1399. EmptyMessage: searx.noItemFound,
  1400. HttpMethod: searx.method,
  1401. MinChars: 4,
  1402. Delay: 300,
  1403. }, "#" + qinput_id);
  1404. // hack, see : https://github.com/autocompletejs/autocomplete.js/issues/37
  1405. w.addEventListener('resize', function() {
  1406. var event = new CustomEvent("position");
  1407. qinput.dispatchEvent(event);
  1408. });
  1409. }
  1410. qinput.addEventListener('focus', placeCursorAtEndOnce, false);
  1411. qinput.focus();
  1412. }
  1413. // vanilla js version of search_on_category_select.js
  1414. if (qinput !== null && searx.search_on_category_select) {
  1415. d.querySelector('.help').className='invisible';
  1416. searx.on('#categories input', 'change', function(e) {
  1417. var i, categories = d.querySelectorAll('#categories input[type="checkbox"]');
  1418. for(i=0; i<categories.length; i++) {
  1419. if (categories[i] !== this && categories[i].checked) {
  1420. categories[i].click();
  1421. }
  1422. }
  1423. if (! this.checked) {
  1424. this.click();
  1425. }
  1426. submitIfQuery();
  1427. return false;
  1428. });
  1429. searx.on(d.getElementById('time_range'), 'change', submitIfQuery);
  1430. searx.on(d.getElementById('language'), 'change', submitIfQuery);
  1431. }
  1432. });
  1433. })(window, document, window.searx);