summaryrefslogtreecommitdiff
path: root/searx/static/themes/simple/js
diff options
context:
space:
mode:
authorAlexandre Flament <alex@al-f.net>2017-02-12 15:06:01 +0100
committerAlexandre Flament <alex@al-f.net>2017-08-06 16:04:21 +0200
commit10a24bdc2c3870f07ec62dd710841628d325aaf6 (patch)
tree400b579adb6268092f21ec21621a16c730cfef41 /searx/static/themes/simple/js
parent4f6586d8085460c368ad16904685199de630e1c8 (diff)
downloadsearxng-10a24bdc2c3870f07ec62dd710841628d325aaf6.tar.gz
searxng-10a24bdc2c3870f07ec62dd710841628d325aaf6.zip
[enh] add simple theme (WIP)
Diffstat (limited to 'searx/static/themes/simple/js')
-rw-r--r--searx/static/themes/simple/js/searx.js1534
-rw-r--r--searx/static/themes/simple/js/searx.min.js4
-rw-r--r--searx/static/themes/simple/js/searx.min.js.map1
-rw-r--r--searx/static/themes/simple/js/searx_src/00_searx_toolkit.js155
-rw-r--r--searx/static/themes/simple/js/searx_src/autocomplete.js536
-rw-r--r--searx/static/themes/simple/js/searx_src/searx_imageresult.js151
-rw-r--r--searx/static/themes/simple/js/searx_src/searx_keyboard.js360
-rw-r--r--searx/static/themes/simple/js/searx_src/searx_mapresult.js175
-rw-r--r--searx/static/themes/simple/js/searx_src/searx_results.js63
-rw-r--r--searx/static/themes/simple/js/searx_src/searx_search.js94
10 files changed, 3073 insertions, 0 deletions
diff --git a/searx/static/themes/simple/js/searx.js b/searx/static/themes/simple/js/searx.js
new file mode 100644
index 000000000..64329fde1
--- /dev/null
+++ b/searx/static/themes/simple/js/searx.js
@@ -0,0 +1,1534 @@
+/**
+* searx is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* searx is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with searx. If not, see < http://www.gnu.org/licenses/ >.
+*
+* (C) 2017 by Alexandre Flament, <alex@al-f.net>
+*
+*/
+(function(w, d, searx) {
+
+ 'use strict';
+
+ // not invented here tookit with bugs fixed elsewhere
+ // purposes : be just good enough and as small as possible
+
+ // from https://plainjs.com/javascript/events/live-binding-event-handlers-14/
+ if (w.Element) {
+ (function(ElementPrototype) {
+ ElementPrototype.matches = ElementPrototype.matches ||
+ ElementPrototype.matchesSelector ||
+ ElementPrototype.webkitMatchesSelector ||
+ ElementPrototype.msMatchesSelector ||
+ function(selector) {
+ var node = this, nodes = (node.parentNode || node.document).querySelectorAll(selector), i = -1;
+ while (nodes[++i] && nodes[i] != node);
+ return !!nodes[i];
+ };
+ })(Element.prototype);
+ }
+
+ function callbackSafe(callback, el, e) {
+ try {
+ callback.call(el, e);
+ } catch (exception) {
+ console.log(exception);
+ }
+ }
+
+ searx = searx || {};
+
+ searx.on = function(obj, eventType, callback, useCapture) {
+ useCapture = useCapture || false;
+ if (typeof obj !== 'string') {
+ // obj HTMLElement, HTMLDocument
+ obj.addEventListener(eventType, callback, useCapture);
+ } else {
+ // obj is a selector
+ d.addEventListener(eventType, function(e) {
+ var el = e.target || e.srcElement, found = false;
+ while (el && el.matches && el !== d && !(found = el.matches(obj))) el = el.parentElement;
+ if (found) callbackSafe(callback, el, e);
+ }, useCapture);
+ }
+ };
+
+ searx.ready = function(callback) {
+ if (document.readyState != 'loading') {
+ callback.call(w);
+ } else {
+ w.addEventListener('DOMContentLoaded', callback.bind(w));
+ }
+ };
+
+ searx.http = function(method, url, callback) {
+ var req = new XMLHttpRequest(),
+ resolve = function() {},
+ reject = function() {},
+ promise = {
+ then: function(callback) { resolve = callback; return promise; },
+ catch: function(callback) { reject = callback; return promise; }
+ };
+
+ try {
+ req.open(method, url, true);
+
+ // On load
+ req.onload = function() {
+ if (req.status == 200) {
+ resolve(req.response, req.responseType);
+ } else {
+ reject(Error(req.statusText));
+ }
+ };
+
+ // Handle network errors
+ req.onerror = function() {
+ reject(Error("Network Error"));
+ };
+
+ req.onabort = function() {
+ reject(Error("Transaction is aborted"));
+ };
+
+ // Make the request
+ req.send();
+ } catch (ex) {
+ reject(ex);
+ }
+
+ return promise;
+ };
+
+ searx.loadStyle = function(src) {
+ var path = searx.staticPath + src,
+ id = "style_" + src.replace('.', '_'),
+ s = d.getElementById(id);
+ if (s === null) {
+ s = d.createElement('link');
+ s.setAttribute('id', id);
+ s.setAttribute('rel', 'stylesheet');
+ s.setAttribute('type', 'text/css');
+ s.setAttribute('href', path);
+ d.body.appendChild(s);
+ }
+ };
+
+ searx.loadScript = function(src, callback) {
+ var path = searx.staticPath + src,
+ id = "script_" + src.replace('.', '_'),
+ s = d.getElementById(id);
+ if (s === null) {
+ s = d.createElement('script');
+ s.setAttribute('id', id);
+ s.setAttribute('src', path);
+ s.onload = callback;
+ s.onerror = function() {
+ s.setAttribute('error', '1');
+ };
+ d.body.appendChild(s);
+ } else if (!s.hasAttribute('error')) {
+ try {
+ callback.apply(s, []);
+ } catch (exception) {
+ console.log(exception);
+ }
+ } else {
+ console.log("callback not executed : script '" + path + "' not loaded.");
+ }
+ };
+
+ searx.on('.close', 'click', function(e) {
+ var el = e.target || e.srcElement;
+ this.parentNode.style.display="None";
+ });
+ return searx;
+})(window, document, window.searx);
+;(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){
+/*
+ * @license MIT
+ *
+ * Autocomplete.js v2.6.3
+ * Developed by Baptiste Donaux
+ * http://autocomplete-js.com
+ *
+ * (c) 2017, Baptiste Donaux
+ */
+"use strict";
+var ConditionOperator;
+(function (ConditionOperator) {
+ ConditionOperator[ConditionOperator["AND"] = 0] = "AND";
+ ConditionOperator[ConditionOperator["OR"] = 1] = "OR";
+})(ConditionOperator || (ConditionOperator = {}));
+var EventType;
+(function (EventType) {
+ EventType[EventType["KEYDOWN"] = 0] = "KEYDOWN";
+ EventType[EventType["KEYUP"] = 1] = "KEYUP";
+})(EventType || (EventType = {}));
+/**
+ * Core
+ *
+ * @class
+ * @author Baptiste Donaux <baptiste.donaux@gmail.com> @baptistedonaux
+ */
+var AutoComplete = (function () {
+ // Constructor
+ function AutoComplete(params, selector) {
+ if (params === void 0) { params = {}; }
+ if (selector === void 0) { selector = "[data-autocomplete]"; }
+ if (Array.isArray(selector)) {
+ selector.forEach(function (s) {
+ new AutoComplete(params, s);
+ });
+ }
+ else if (typeof selector == "string") {
+ var elements = document.querySelectorAll(selector);
+ Array.prototype.forEach.call(elements, function (input) {
+ new AutoComplete(params, input);
+ });
+ }
+ else {
+ var specificParams = AutoComplete.merge(AutoComplete.defaults, params, {
+ DOMResults: document.createElement("div")
+ });
+ AutoComplete.prototype.create(specificParams, selector);
+ return specificParams;
+ }
+ }
+ AutoComplete.prototype.create = function (params, element) {
+ params.Input = element;
+ if (params.Input.nodeName.match(/^INPUT$/i) && (params.Input.hasAttribute("type") === false || params.Input.getAttribute("type").match(/^TEXT|SEARCH$/i))) {
+ params.Input.setAttribute("autocomplete", "off");
+ params._Position(params);
+ params.Input.parentNode.appendChild(params.DOMResults);
+ params.$Listeners = {
+ blur: params._Blur.bind(params),
+ destroy: AutoComplete.prototype.destroy.bind(null, params),
+ focus: params._Focus.bind(params),
+ keyup: AutoComplete.prototype.event.bind(null, params, EventType.KEYUP),
+ keydown: AutoComplete.prototype.event.bind(null, params, EventType.KEYDOWN),
+ position: params._Position.bind(params)
+ };
+ for (var event in params.$Listeners) {
+ params.Input.addEventListener(event, params.$Listeners[event]);
+ }
+ }
+ };
+ AutoComplete.prototype.getEventsByType = function (params, type) {
+ var mappings = {};
+ for (var key in params.KeyboardMappings) {
+ var event = EventType.KEYUP;
+ if (params.KeyboardMappings[key].Event !== undefined) {
+ event = params.KeyboardMappings[key].Event;
+ }
+ if (event == type) {
+ mappings[key] = params.KeyboardMappings[key];
+ }
+ }
+ return mappings;
+ };
+ AutoComplete.prototype.event = function (params, type, event) {
+ var eventIdentifier = function (condition) {
+ if ((match === true && mapping.Operator == ConditionOperator.AND) || (match === false && mapping.Operator == ConditionOperator.OR)) {
+ condition = AutoComplete.merge({
+ Not: false
+ }, condition);
+ if (condition.hasOwnProperty("Is")) {
+ if (condition.Is == event.keyCode) {
+ match = !condition.Not;
+ }
+ else {
+ match = condition.Not;
+ }
+ }
+ else if (condition.hasOwnProperty("From") && condition.hasOwnProperty("To")) {
+ if (event.keyCode >= condition.From && event.keyCode <= condition.To) {
+ match = !condition.Not;
+ }
+ else {
+ match = condition.Not;
+ }
+ }
+ }
+ };
+ for (var name in AutoComplete.prototype.getEventsByType(params, type)) {
+ var mapping = AutoComplete.merge({
+ Operator: ConditionOperator.AND
+ }, params.KeyboardMappings[name]), match = ConditionOperator.AND == mapping.Operator;
+ mapping.Conditions.forEach(eventIdentifier);
+ if (match === true) {
+ mapping.Callback.call(params, event);
+ }
+ }
+ };
+ AutoComplete.prototype.makeRequest = function (params, callback) {
+ var propertyHttpHeaders = Object.getOwnPropertyNames(params.HttpHeaders), request = new XMLHttpRequest(), method = params._HttpMethod(), url = params._Url(), queryParams = params._Pre(), queryParamsStringify = encodeURIComponent(params._QueryArg()) + "=" + encodeURIComponent(queryParams);
+ if (method.match(/^GET$/i)) {
+ if (url.indexOf("?") !== -1) {
+ url += "&" + queryParamsStringify;
+ }
+ else {
+ url += "?" + queryParamsStringify;
+ }
+ }
+ request.open(method, url, true);
+ for (var i = propertyHttpHeaders.length - 1; i >= 0; i--) {
+ request.setRequestHeader(propertyHttpHeaders[i], params.HttpHeaders[propertyHttpHeaders[i]]);
+ }
+ request.onreadystatechange = function () {
+ if (request.readyState == 4 && request.status == 200) {
+ params.$Cache[queryParams] = request.response;
+ callback(request.response);
+ }
+ };
+ return request;
+ };
+ AutoComplete.prototype.ajax = function (params, request, timeout) {
+ if (timeout === void 0) { timeout = true; }
+ if (params.$AjaxTimer) {
+ window.clearTimeout(params.$AjaxTimer);
+ }
+ if (timeout === true) {
+ params.$AjaxTimer = window.setTimeout(AutoComplete.prototype.ajax.bind(null, params, request, false), params.Delay);
+ }
+ else {
+ if (params.Request) {
+ params.Request.abort();
+ }
+ params.Request = request;
+ params.Request.send(params._QueryArg() + "=" + params._Pre());
+ }
+ };
+ AutoComplete.prototype.cache = function (params, callback) {
+ var response = params._Cache(params._Pre());
+ if (response === undefined) {
+ var request = AutoComplete.prototype.makeRequest(params, callback);
+ AutoComplete.prototype.ajax(params, request);
+ }
+ else {
+ callback(response);
+ }
+ };
+ AutoComplete.prototype.destroy = function (params) {
+ for (var event in params.$Listeners) {
+ params.Input.removeEventListener(event, params.$Listeners[event]);
+ }
+ params.DOMResults.parentNode.removeChild(params.DOMResults);
+ };
+ return AutoComplete;
+}());
+AutoComplete.merge = function () {
+ var merge = {}, tmp;
+ for (var i = 0; i < arguments.length; i++) {
+ for (tmp in arguments[i]) {
+ merge[tmp] = arguments[i][tmp];
+ }
+ }
+ return merge;
+};
+AutoComplete.defaults = {
+ Delay: 150,
+ EmptyMessage: "No result here",
+ Highlight: {
+ getRegex: function (value) {
+ return new RegExp(value, "ig");
+ },
+ transform: function (value) {
+ return "<strong>" + value + "</strong>";
+ }
+ },
+ HttpHeaders: {
+ "Content-type": "application/x-www-form-urlencoded"
+ },
+ Limit: 0,
+ MinChars: 0,
+ HttpMethod: "GET",
+ QueryArg: "q",
+ Url: null,
+ KeyboardMappings: {
+ "Enter": {
+ Conditions: [{
+ Is: 13,
+ Not: false
+ }],
+ Callback: function (event) {
+ if (this.DOMResults.getAttribute("class").indexOf("open") != -1) {
+ var liActive = this.DOMResults.querySelector("li.active");
+ if (liActive !== null) {
+ event.preventDefault();
+ this._Select(liActive);
+ this.DOMResults.setAttribute("class", "autocomplete");
+ }
+ }
+ },
+ Operator: ConditionOperator.AND,
+ Event: EventType.KEYDOWN
+ },
+ "KeyUpAndDown_down": {
+ Conditions: [{
+ Is: 38,
+ Not: false
+ },
+ {
+ Is: 40,
+ Not: false
+ }],
+ Callback: function (event) {
+ event.preventDefault();
+ },
+ Operator: ConditionOperator.OR,
+ Event: EventType.KEYDOWN
+ },
+ "KeyUpAndDown_up": {
+ Conditions: [{
+ Is: 38,
+ Not: false
+ },
+ {
+ Is: 40,
+ Not: false
+ }],
+ Callback: function (event) {
+ event.preventDefault();
+ 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");
+ if (active) {
+ var currentIndex = Array.prototype.indexOf.call(active.parentNode.children, active), position = currentIndex + (event.keyCode - 39), lisCount = this.DOMResults.getElementsByTagName("li").length;
+ if (position < 0) {
+ position = lisCount - 1;
+ }
+ else if (position >= lisCount) {
+ position = 0;
+ }
+ active.classList.remove("active");
+ active.parentElement.children.item(position).classList.add("active");
+ }
+ else if (last && event.keyCode == 38) {
+ last.classList.add("active");
+ }
+ else if (first) {
+ first.classList.add("active");
+ }
+ },
+ Operator: ConditionOperator.OR,
+ Event: EventType.KEYUP
+ },
+ "AlphaNum": {
+ Conditions: [{
+ Is: 13,
+ Not: true
+ }, {
+ From: 35,
+ To: 40,
+ Not: true
+ }],
+ Callback: function () {
+ var oldValue = this.Input.getAttribute("data-autocomplete-old-value"), currentValue = this._Pre();
+ if (currentValue !== "" && currentValue.length >= this._MinChars()) {
+ if (!oldValue || currentValue != oldValue) {
+ this.DOMResults.setAttribute("class", "autocomplete open");
+ }
+ AutoComplete.prototype.cache(this, function (response) {
+ this._Render(this._Post(response));
+ this._Open();
+ }.bind(this));
+ }
+ },
+ Operator: ConditionOperator.AND,
+ Event: EventType.KEYUP
+ }
+ },
+ DOMResults: null,
+ Request: null,
+ Input: null,
+ /**
+ * Return the message when no result returns
+ */
+ _EmptyMessage: function () {
+ var emptyMessage = "";
+ if (this.Input.hasAttribute("data-autocomplete-empty-message")) {
+ emptyMessage = this.Input.getAttribute("data-autocomplete-empty-message");
+ }
+ else if (this.EmptyMessage !== false) {
+ emptyMessage = this.EmptyMessage;
+ }
+ else {
+ emptyMessage = "";
+ }
+ return emptyMessage;
+ },
+ /**
+ * Returns the maximum number of results
+ */
+ _Limit: function () {
+ var limit = this.Input.getAttribute("data-autocomplete-limit");
+ if (isNaN(limit) || limit === null) {
+ return this.Limit;
+ }
+ return parseInt(limit, 10);
+ },
+ /**
+ * Returns the minimum number of characters entered before firing ajax
+ */
+ _MinChars: function () {
+ var minchars = this.Input.getAttribute("data-autocomplete-minchars");
+ if (isNaN(minchars) || minchars === null) {
+ return this.MinChars;
+ }
+ return parseInt(minchars, 10);
+ },
+ /**
+ * Apply transformation on labels response
+ */
+ _Highlight: function (label) {
+ return label.replace(this.Highlight.getRegex(this._Pre()), this.Highlight.transform);
+ },
+ /**
+ * Returns the HHTP method to use
+ */
+ _HttpMethod: function () {
+ if (this.Input.hasAttribute("data-autocomplete-method")) {
+ return this.Input.getAttribute("data-autocomplete-method");
+ }
+ return this.HttpMethod;
+ },
+ /**
+ * Returns the query param to use
+ */
+ _QueryArg: function () {
+ if (this.Input.hasAttribute("data-autocomplete-param-name")) {
+ return this.Input.getAttribute("data-autocomplete-param-name");
+ }
+ return this.QueryArg;
+ },
+ /**
+ * Returns the URL to use for AJAX request
+ */
+ _Url: function () {
+ if (this.Input.hasAttribute("data-autocomplete")) {
+ return this.Input.getAttribute("data-autocomplete");
+ }
+ return this.Url;
+ },
+ /**
+ * Manage the close
+ */
+ _Blur: function (now) {
+ if (now === true) {
+ this.DOMResults.setAttribute("class", "autocomplete");
+ this.Input.setAttribute("data-autocomplete-old-value", this.Input.value);
+ }
+ else {
+ var params = this;
+ setTimeout(function () {
+ params._Blur(true);
+ }, 150);
+ }
+ },
+ /**
+ * Manage the cache
+ */
+ _Cache: function (value) {
+ return this.$Cache[value];
+ },
+ /**
+ * Manage the open
+ */
+ _Focus: function () {
+ var oldValue = this.Input.getAttribute("data-autocomplete-old-value");
+ if ((!oldValue || this.Input.value != oldValue) && this._MinChars() <= this.Input.value.length) {
+ this.DOMResults.setAttribute("class", "autocomplete open");
+ }
+ },
+ /**
+ * Bind all results item if one result is opened
+ */
+ _Open: function () {
+ var params = this;
+ Array.prototype.forEach.call(this.DOMResults.getElementsByTagName("li"), function (li) {
+ if (li.getAttribute("class") != "locked") {
+ li.onclick = function (event) {
+ params._Select(li);
+ };
+ li.onmouseenter = function () {
+ var active = params.DOMResults.querySelector("li.active");
+ if (active !== li) {
+ if (active !== null) {
+ active.classList.remove("active");
+ }
+ li.classList.add("active");
+ }
+ };
+ }
+ });
+ },
+ /**
+ * Position the results HTML element
+ */
+ _Position: function () {
+ this.DOMResults.setAttribute("class", "autocomplete");
+ this.DOMResults.setAttribute("style", "top:" + (this.Input.offsetTop + this.Input.offsetHeight) + "px;left:" + this.Input.offsetLeft + "px;width:" + this.Input.clientWidth + "px;");
+ },
+ /**
+ * Execute the render of results DOM element
+ */
+ _Render: function (response) {
+ var ul;
+ if (typeof response == "string") {
+ ul = this._RenderRaw(response);
+ }
+ else {
+ ul = this._RenderResponseItems(response);
+ }
+ if (this.DOMResults.hasChildNodes()) {
+ this.DOMResults.removeChild(this.DOMResults.childNodes[0]);
+ }
+ this.DOMResults.appendChild(ul);
+ },
+ /**
+ * ResponseItems[] rendering
+ */
+ _RenderResponseItems: function (response) {
+ var ul = document.createElement("ul"), li = document.createElement("li"), limit = this._Limit();
+ // Order
+ if (limit < 0) {
+ response = response.reverse();
+ }
+ else if (limit === 0) {
+ limit = response.length;
+ }
+ for (var item = 0; item < Math.min(Math.abs(limit), response.length); item++) {
+ li.innerHTML = response[item].Label;
+ li.setAttribute("data-autocomplete-value", response[item].Value);
+ ul.appendChild(li);
+ li = document.createElement("li");
+ }
+ return ul;
+ },
+ /**
+ * string response rendering (RAW HTML)
+ */
+ _RenderRaw: function (response) {
+ var ul = document.createElement("ul"), li = document.createElement("li");
+ if (response.length > 0) {
+ this.DOMResults.innerHTML = response;
+ }
+ else {
+ var emptyMessage = this._EmptyMessage();
+ if (emptyMessage !== "") {
+ li.innerHTML = emptyMessage;
+ li.setAttribute("class", "locked");
+ ul.appendChild(li);
+ }
+ }
+ return ul;
+ },
+ /**
+ * Deal with request response
+ */
+ _Post: function (response) {
+ try {
+ var returnResponse = [];
+ //JSON return
+ var json = JSON.parse(response);
+ if (Object.keys(json).length === 0) {
+ return "";
+ }
+ if (Array.isArray(json)) {
+ for (var i = 0; i < Object.keys(json).length; i++) {
+ returnResponse[returnResponse.length] = { "Value": json[i], "Label": this._Highlight(json[i]) };
+ }
+ }
+ else {
+ for (var value in json) {
+ returnResponse.push({
+ "Value": value,
+ "Label": this._Highlight(json[value])
+ });
+ }
+ }
+ return returnResponse;
+ }
+ catch (event) {
+ //HTML return
+ return response;
+ }
+ },
+ /**
+ * Return the autocomplete value to send (before request)
+ */
+ _Pre: function () {
+ return this.Input.value;
+ },
+ /**
+ * Choice one result item
+ */
+ _Select: function (item) {
+ console.log('test test test');
+ if (item.hasAttribute("data-autocomplete-value")) {
+ this.Input.value = item.getAttribute("data-autocomplete-value");
+ }
+ else {
+ this.Input.value = item.innerHTML;
+ }
+ this.Input.setAttribute("data-autocomplete-old-value", this.Input.value);
+ },
+ $AjaxTimer: null,
+ $Cache: {},
+ $Listeners: {}
+};
+module.exports = AutoComplete;
+
+},{}]},{},[1])(1)
+});
+;/**
+*
+* Google Image Layout v0.0.1
+* Description, by Anh Trinh.
+* Heavily modified for searx
+* http://trinhtrunganh.com
+*
+* @license Free to use under the MIT License.
+*
+*/
+(function(w, d) {
+ 'use strict';
+
+ function ImageLayout(container_selector, results_selector, img_selector, maxHeight) {
+ this.container_selector = container_selector;
+ this.results_selector = results_selector;
+ this.img_selector = img_selector;
+ this.margin = 10;
+ this.maxHeight = maxHeight;
+ this._alignAllDone = true;
+ }
+
+ /**
+ * Get the height that make all images fit the container
+ *
+ * width = w1 + w2 + w3 + ... = r1*h + r2*h + r3*h + ...
+ *
+ * @param {[type]} images the images to be calculated
+ * @param {[type]} width the container witdth
+ * @param {[type]} margin the margin between each image
+ *
+ * @return {[type]} the height
+ */
+ ImageLayout.prototype._getHeigth = function(images, width) {
+ var r = 0,
+ img;
+
+ width -= images.length * this.margin;
+ for (var i = 0; i < images.length; i++) {
+ img = images[i];
+ if ((img.naturalWidth > 0) && (img.naturalHeight > 0)) {
+ r += img.naturalWidth / img.naturalHeight;
+ } else {
+ // assume that not loaded images are square
+ r += 1;
+ }
+ }
+
+ return width / r; //have to round down because Firefox will automatically roundup value with number of decimals > 3
+ };
+
+ ImageLayout.prototype._setSize = function(images, height) {
+ var img, imgWidth, imagesLength = images.length;
+ for (var i = 0; i < imagesLength; i++) {
+ img = images[i];
+ if ((img.naturalWidth > 0) && (img.naturalHeight > 0)) {
+ imgWidth = height * img.naturalWidth / img.naturalHeight;
+ } else {
+ // not loaded image : make it square as _getHeigth said it
+ imgWidth = height;
+ }
+ img.style.width = imgWidth + 'px';
+ img.style.height = height + 'px';
+ img.style.marginLeft = '3px';
+ img.style.marginTop = '3px';
+ img.style.marginRight = this.margin - 7 + 'px'; // -4 is the negative margin of the inline element
+ img.style.marginBottom = this.margin - 7 + 'px';
+ }
+ };
+
+ ImageLayout.prototype._alignImgs = function(imgGroup) {
+ var slice, h,
+ containerWidth = d.querySelector(this.container_selector).clientWidth;
+
+ w: while (imgGroup.length > 0) {
+ for (var i = 1; i <= imgGroup.length; i++) {
+ slice = imgGroup.slice(0, i);
+ h = this._getHeigth(slice, containerWidth);
+ if (h < this.maxHeight) {
+ this._setSize(slice, h);
+ imgGroup = imgGroup.slice(i);
+ continue w;
+ }
+ }
+ this._setSize(slice, Math.min(this.maxHeight, h));
+ break;
+ }
+ };
+
+ ImageLayout.prototype.align = function(results_selector) {
+ var results_selectorNode = d.querySelectorAll(this.results_selector),
+ results_length = results_selectorNode.length,
+ previous = null,
+ current = null,
+ imgGroup = [];
+ for (var i = 0; i < results_length; i++) {
+ current = results_selectorNode[i];
+ if (current.previousElementSibling !== previous && imgGroup.length > 0) {
+ // the current image is not conected to previous one
+ // so the current image is the start of a new group of images.
+ // so call _alignImgs to align the current group
+ this._alignImgs(imgGroup);
+ // and start a new empty group of images
+ imgGroup = [];
+ }
+ // add the current image to the group (only the img tag)
+ imgGroup.push(current.querySelector(this.img_selector));
+ // update the previous variable
+ previous = current;
+ }
+ // align the remaining images
+ if (imgGroup.length > 0) {
+ this._alignImgs(imgGroup);
+ }
+ };
+
+ ImageLayout.prototype.watch = function() {
+ var i, img, imgGroup, imgNodeLength,
+ obj = this,
+ results_nodes = d.querySelectorAll(this.results_selector),
+ results_length = results_nodes.length;
+
+ function align(e) {
+ obj.align();
+ }
+
+ function throttleAlign(e) {
+ if (obj._alignAllDone) {
+ obj._alignAllDone = false;
+ setTimeout(function() {
+ obj.align();
+ obj._alignAllDone = true;
+ }, 100);
+ }
+ }
+
+ w.addEventListener('resize', throttleAlign);
+ w.addEventListener('pageshow', align);
+
+ for (i = 0; i < results_length; i++) {
+ img = results_nodes[i].querySelector(this.img_selector);
+ if (typeof img !== 'undefined') {
+ img.addEventListener('load', throttleAlign);
+ img.addEventListener('error', throttleAlign);
+ }
+ }
+ };
+
+ w.searx.ImageLayout = ImageLayout;
+
+})(window, document);
+;searx.ready(function() {
+
+ searx.on('.result', 'click', function() {
+ highlightResult(this)(true);
+ });
+
+ searx.on('.result a', 'focus', function(e) {
+ var el = e.target;
+ while (el !== undefined) {
+ if (el.classList.contains('result')) {
+ if (el.getAttribute("data-vim-selected") === null) {
+ highlightResult(el)(true);
+ }
+ break;
+ }
+ el = el.parentNode;
+ }
+ }, true);
+
+ var vimKeys = {
+ 27: {
+ key: 'Escape',
+ fun: removeFocus,
+ des: 'remove focus from the focused input',
+ cat: 'Control'
+ },
+ 73: {
+ key: 'i',
+ fun: searchInputFocus,
+ des: 'focus on the search input',
+ cat: 'Control'
+ },
+ 66: {
+ key: 'b',
+ fun: scrollPage(-window.innerHeight),
+ des: 'scroll one page up',
+ cat: 'Navigation'
+ },
+ 70: {
+ key: 'f',
+ fun: scrollPage(window.innerHeight),
+ des: 'scroll one page down',
+ cat: 'Navigation'
+ },
+ 85: {
+ key: 'u',
+ fun: scrollPage(-window.innerHeight / 2),
+ des: 'scroll half a page up',
+ cat: 'Navigation'
+ },
+ 68: {
+ key: 'd',
+ fun: scrollPage(window.innerHeight / 2),
+ des: 'scroll half a page down',
+ cat: 'Navigation'
+ },
+ 71: {
+ key: 'g',
+ fun: scrollPageTo(-document.body.scrollHeight, 'top'),
+ des: 'scroll to the top of the page',
+ cat: 'Navigation'
+ },
+ 86: {
+ key: 'v',
+ fun: scrollPageTo(document.body.scrollHeight, 'bottom'),
+ des: 'scroll to the bottom of the page',
+ cat: 'Navigation'
+ },
+ 75: {
+ key: 'k',
+ fun: highlightResult('up'),
+ des: 'select previous search result',
+ cat: 'Results'
+ },
+ 74: {
+ key: 'j',
+ fun: highlightResult('down'),
+ des: 'select next search result',
+ cat: 'Results'
+ },
+ 80: {
+ key: 'p',
+ fun: pageButtonClick(0),
+ des: 'go to previous page',
+ cat: 'Results'
+ },
+ 78: {
+ key: 'n',
+ fun: pageButtonClick(1),
+ des: 'go to next page',
+ cat: 'Results'
+ },
+ 79: {
+ key: 'o',
+ fun: openResult(false),
+ des: 'open search result',
+ cat: 'Results'
+ },
+ 84: {
+ key: 't',
+ fun: openResult(true),
+ des: 'open the result in a new tab',
+ cat: 'Results'
+ },
+ 82: {
+ key: 'r',
+ fun: reloadPage,
+ des: 'reload page from the server',
+ cat: 'Control'
+ },
+ 72: {
+ key: 'h',
+ fun: toggleHelp,
+ des: 'toggle help window',
+ cat: 'Other'
+ }
+ };
+
+ searx.on(document, "keyup", function(e) {
+ // check for modifiers so we don't break browser's hotkeys
+ if (vimKeys.hasOwnProperty(e.keyCode) && !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey) {
+ var tagName = e.target.tagName.toLowerCase();
+ if (e.keyCode === 27) {
+ if (tagName === 'input' || tagName === 'select' || tagName === 'textarea') {
+ vimKeys[e.keyCode].fun();
+ }
+ } else {
+ if (e.target === document.body || tagName === 'a' || tagName === 'button') {
+ vimKeys[e.keyCode].fun();
+ }
+ }
+ }
+ });
+
+ function highlightResult(which) {
+ return function(noScroll) {
+ var current = document.querySelector('.result[data-vim-selected]'),
+ effectiveWhich = which;
+ if (current === null) {
+ // no selection : choose the first one
+ current = document.querySelector('.result');
+ if (current === null) {
+ // no first one : there are no results
+ return;
+ }
+ // replace up/down actions by selecting first one
+ if (which === "down" || which === "up") {
+ effectiveWhich = current;
+ }
+ }
+
+ var next, results = document.querySelectorAll('.result');
+
+ if (typeof effectiveWhich !== 'string') {
+ next = effectiveWhich;
+ } else {
+ switch (effectiveWhich) {
+ case 'visible':
+ var top = document.documentElement.scrollTop || document.body.scrollTop;
+ var bot = top + document.documentElement.clientHeight;
+
+ for (var i = 0; i < results.length; i++) {
+ next = results[i];
+ var etop = next.offsetTop;
+ var ebot = etop + next.clientHeight;
+
+ if ((ebot <= bot) && (etop > top)) {
+ break;
+ }
+ }
+ break;
+ case 'down':
+ next = current.nextElementSibling;
+ if (next === null) {
+ next = results[0];
+ }
+ break;
+ case 'up':
+ next = current.previousElementSibling;
+ if (next === null) {
+ next = results[results.length - 1];
+ }
+ break;
+ case 'bottom':
+ next = results[results.length - 1];
+ break;
+ case 'top':
+ /* falls through */
+ default:
+ next = results[0];
+ }
+ }
+
+ if (next) {
+ current.removeAttribute('data-vim-selected');
+ next.setAttribute('data-vim-selected', 'true');
+ var link = next.querySelector('h3 a') || next.querySelector('a');
+ if (link !== null) {
+ link.focus();
+ }
+ if (!noScroll) {
+ scrollPageToSelected();
+ }
+ }
+ };
+ }
+
+ function reloadPage() {
+ document.location.reload(true);
+ }
+
+ function removeFocus() {
+ if (document.activeElement) {
+ document.activeElement.blur();
+ }
+ }
+
+ function pageButtonClick(num) {
+ return function() {
+ var buttons = $('div#pagination button[type="submit"]');
+ if (buttons.length !== 2) {
+ console.log('page navigation with this theme is not supported');
+ return;
+ }
+ if (num >= 0 && num < buttons.length) {
+ buttons[num].click();
+ } else {
+ console.log('pageButtonClick(): invalid argument');
+ }
+ };
+ }
+
+ function scrollPageToSelected() {
+ var sel = document.querySelector('.result[data-vim-selected]');
+ if (sel === null) {
+ return;
+ }
+ var wtop = document.documentElement.scrollTop || document.body.scrollTop,
+ wheight = document.documentElement.clientHeight,
+ etop = sel.offsetTop,
+ ebot = etop + sel.clientHeight,
+ offset = 120;
+ // first element ?
+ if ((sel.previousElementSibling === null) && (ebot < wheight)) {
+ // set to the top of page if the first element
+ // is fully included in the viewport
+ window.scroll(window.scrollX, 0);
+ return;
+ }
+ if (wtop > (etop - offset)) {
+ window.scroll(window.scrollX, etop - offset);
+ } else {
+ var wbot = wtop + wheight;
+ if (wbot < (ebot + offset)) {
+ window.scroll(window.scrollX, ebot - wheight + offset);
+ }
+ }
+ }
+
+ function scrollPage(amount) {
+ return function() {
+ window.scrollBy(0, amount);
+ highlightResult('visible')();
+ };
+ }
+
+ function scrollPageTo(position, nav) {
+ return function() {
+ window.scrollTo(0, position);
+ highlightResult(nav)();
+ };
+ }
+
+ function searchInputFocus() {
+ window.scrollTo(0, 0);
+ document.querySelector('#q').focus();
+ }
+
+ function openResult(newTab) {
+ return function() {
+ var link = document.querySelector('.result[data-vim-selected] h3 a');
+ if (link !== null) {
+ var url = link.getAttribute('href');
+ if (newTab) {
+ window.open(url);
+ } else {
+ window.location.href = url;
+ }
+ }
+ };
+ }
+
+ function toggleHelp() {
+ var helpPanel = document.querySelector('#vim-hotkeys-help');
+ if (helpPanel.length) {
+ helpPanel.classList.toggle('hidden');
+ return;
+ }
+
+ var categories = {};
+
+ for (var k in vimKeys) {
+ var key = vimKeys[k];
+ categories[key.cat] = categories[key.cat] || [];
+ categories[key.cat].push(key);
+ }
+
+ var sorted = Object.keys(categories).sort(function(a, b) {
+ return categories[b].length - categories[a].length;
+ });
+
+ if (sorted.length === 0) {
+ return;
+ }
+
+ var html = '<div id="vim-hotkeys-help" class="well vim-hotkeys-help">';
+ html += '<div class="container-fluid">';
+
+ html += '<div class="row">';
+ html += '<div class="col-sm-12">';
+ html += '<h3>How to navigate searx with Vim-like hotkeys</h3>';
+ html += '</div>'; // col-sm-12
+ html += '</div>'; // row
+
+ for (var i = 0; i < sorted.length; i++) {
+ var cat = categories[sorted[i]];
+
+ var lastCategory = i === (sorted.length - 1);
+ var first = i % 2 === 0;
+
+ if (first) {
+ html += '<div class="row dflex">';
+ }
+ html += '<div class="col-sm-' + (first && lastCategory ? 12 : 6) + ' dflex">';
+
+ html += '<div class="panel panel-default iflex">';
+ html += '<div class="panel-heading">' + cat[0].cat + '</div>';
+ html += '<div class="panel-body">';
+ html += '<ul class="list-unstyled">';
+
+ for (var cj in cat) {
+ html += '<li><kbd>' + cat[cj].key + '</kbd> ' + cat[cj].des + '</li>';
+ }
+
+ html += '</ul>';
+ html += '</div>'; // panel-body
+ html += '</div>'; // panel
+ html += '</div>'; // col-sm-*
+
+ if (!first || lastCategory) {
+ html += '</div>'; // row
+ }
+ }
+
+ html += '</div>'; // container-fluid
+ html += '</div>'; // vim-hotkeys-help
+
+ $('body').append(html);
+ }
+});
+;/**
+* searx is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* searx is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with searx. If not, see < http://www.gnu.org/licenses/ >.
+*
+* (C) 2014 by Thomas Pointhuber, <thomas.pointhuber@gmx.at>
+* (C) 2017 by Alexandre Flament, <alex@al-f.net>
+*/
+(function (w, d, searx) {
+ 'use strict';
+
+ searx.ready(function () {
+ searx.on('.searx_overpass_request', 'click', function(event) {
+ // no more request
+ this.classList.remove("searx_overpass_request");
+
+ //
+ var overpass_url = "https://overpass-api.de/api/interpreter?data=";
+ var query_start = overpass_url + "[out:json][timeout:25];(";
+ var query_end = ");out meta;";
+
+ var osm_id = this.dataset.osmId;
+ var osm_type = this.dataset.osmType;
+ var result_table = d.querySelector("#" + this.dataset.resultTable);
+ var result_table_loadicon = d.querySelector("#" + this.dataset.resultTableLoadicon);
+
+ // tags which can be ignored
+ var osm_ignore_tags = [ "addr:city", "addr:country", "addr:housenumber", "addr:postcode", "addr:street" ];
+
+ if(osm_id && osm_type && result_table) {
+ var query = null;
+ switch(osm_type) {
+ case 'node':
+ query = query_start + "node(" + osm_id + ");" + query_end;
+ break;
+ case 'way':
+ query = query_start + "way(" + osm_id + ");" + query_end;
+ break;
+ case 'relation':
+ query = query_start + "relation(" + osm_id + ");" + query_end;
+ break;
+ default:
+ break;
+ }
+ if(query) {
+ // console.log(query);
+ searx.http( 'GET', query ).then(function(html, contentType) {
+ html = JSON.parse(html);
+ if(html && html.elements && html.elements[0]) {
+ var element = html.elements[0];
+ var newHtml = "";
+ for (var row in element.tags) {
+ if(element.tags.name === null || osm_ignore_tags.indexOf(row) == -1) {
+ newHtml += "<tr><td>" + row + "</td><td>";
+ switch(row) {
+ case "phone":
+ case "fax":
+ newHtml += "<a href=\"tel:" + element.tags[row].replace(/ /g,'') + "\">" + element.tags[row] + "</a>";
+ break;
+ case "email":
+ newHtml += "<a href=\"mailto:" + element.tags[row] + "\">" + element.tags[row] + "</a>";
+ break;
+ case "website":
+ case "url":
+ newHtml += "<a href=\"" + element.tags[row] + "\">" + element.tags[row] + "</a>";
+ break;
+ case "wikidata":
+ newHtml += "<a href=\"https://www.wikidata.org/wiki/" + element.tags[row] + "\">" + element.tags[row] + "</a>";
+ break;
+ case "wikipedia":
+ if(element.tags[row].indexOf(":") != -1) {
+ 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>";
+ break;
+ }
+ /* jshint ignore:start */
+ default:
+ /* jshint ignore:end */
+ newHtml += element.tags[row];
+ break;
+ }
+ newHtml += "</td></tr>";
+ }
+ }
+ result_table_loadicon.classList.add('invisible');
+ result_table.classList.remove('invisible');
+ result_table.querySelector("tbody").innerHTML = newHtml;
+ }
+ })
+ .catch(function() {
+ result_table_loadicon.innerHTML = result_table_loadicon.innerHTML + "<p class=\"text-muted\">could not load data!</p>";
+ });
+ }
+ }
+
+ // this event occour only once per element
+ event.preventDefault();
+ });
+
+ searx.on('.searx_init_map', 'click', function(event) {
+ // no more request
+ this.classList.remove("searx_init_map");
+
+ //
+ var leaflet_target = this.dataset.leafletTarget;
+ var map_lon = parseFloat(this.dataset.mapLon);
+ var map_lat = parseFloat(this.dataset.mapLat);
+ var map_zoom = parseFloat(this.dataset.mapZoom);
+ var map_boundingbox = JSON.parse(this.dataset.mapBoundingbox);
+ var map_geojson = JSON.parse(this.dataset.mapGeojson);
+
+ searx.loadStyle('leaflet/leaflet.css');
+ searx.loadScript('leaflet/leaflet.js', function() {
+ var map_bounds = null;
+ if(map_boundingbox) {
+ var southWest = L.latLng(map_boundingbox[0], map_boundingbox[2]);
+ var northEast = L.latLng(map_boundingbox[1], map_boundingbox[3]);
+ map_bounds = L.latLngBounds(southWest, northEast);
+ }
+
+ // init map
+ var map = L.map(leaflet_target);
+ // create the tile layer with correct attribution
+ var osmMapnikUrl='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
+ var osmMapnikAttrib='Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';
+ var osmMapnik = new L.TileLayer(osmMapnikUrl, {minZoom: 1, maxZoom: 19, attribution: osmMapnikAttrib});
+ var osmWikimediaUrl='https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png';
+ var osmWikimediaAttrib = 'Wikimedia maps beta | Maps data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';
+ var osmWikimedia = new L.TileLayer(osmWikimediaUrl, {minZoom: 1, maxZoom: 19, attribution: osmWikimediaAttrib});
+ // init map view
+ if(map_bounds) {
+ // TODO hack: https://github.com/Leaflet/Leaflet/issues/2021
+ // Still useful ?
+ setTimeout(function () {
+ map.fitBounds(map_bounds, {
+ maxZoom:17
+ });
+ }, 0);
+ } else if (map_lon && map_lat) {
+ if(map_zoom) {
+ map.setView(new L.latLng(map_lat, map_lon),map_zoom);
+ } else {
+ map.setView(new L.latLng(map_lat, map_lon),8);
+ }
+ }
+
+ map.addLayer(osmMapnik);
+
+ var baseLayers = {
+ "OSM Mapnik": osmMapnik/*,
+ "OSM Wikimedia": osmWikimedia*/
+ };
+
+ L.control.layers(baseLayers).addTo(map);
+
+ if(map_geojson) {
+ L.geoJson(map_geojson).addTo(map);
+ } /*else if(map_bounds) {
+ L.rectangle(map_bounds, {color: "#ff7800", weight: 3, fill:false}).addTo(map);
+ }*/
+ });
+
+ // this event occour only once per element
+ event.preventDefault();
+ });
+ });
+})(window, document, window.searx);
+;/**
+* searx is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* searx is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with searx. If not, see < http://www.gnu.org/licenses/ >.
+*
+* (C) 2017 by Alexandre Flament, <alex@al-f.net>
+*/
+(function(w, d, searx) {
+ 'use strict';
+
+ searx.ready(function() {
+ searx.image_thumbnail_layout = new searx.ImageLayout('#urls', '#urls .result-images', 'img.image_thumbnail', 200);
+ searx.image_thumbnail_layout.watch();
+
+ searx.on('.btn-collapse', 'click', function(event) {
+ var btnLabelCollapsed = this.getAttribute('data-btn-text-collapsed');
+ var btnLabelNotCollapsed = this.getAttribute('data-btn-text-not-collapsed');
+ var target = this.getAttribute('data-target');
+ var targetElement = d.querySelector(target);
+ var html = this.innerHTML;
+ if (this.classList.contains('collapsed')) {
+ html = html.replace(btnLabelCollapsed, btnLabelNotCollapsed);
+ } else {
+ html = html.replace(btnLabelNotCollapsed, btnLabelCollapsed);
+ }
+ this.innerHTML = html;
+ this.classList.toggle('collapsed');
+ targetElement.classList.toggle('invisible');
+ });
+
+ searx.on('.media-loader', 'click', function(event) {
+ var target = this.getAttribute('data-target');
+ var iframe_load = d.querySelector(target + ' > iframe');
+ var srctest = iframe_load.getAttribute('src');
+ if (srctest === null || srctest === undefined || srctest === false) {
+ iframe_load.setAttribute('src', iframe_load.getAttribute('data-src'));
+ }
+ });
+
+ w.addEventListener('scroll', function() {
+ var e = d.getElementById('backToTop'),
+ scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
+ if (e !== null) {
+ if (scrollTop >= 200) {
+ e.style.opacity = 1;
+ } else {
+ e.style.opacity = 0;
+ }
+ }
+ });
+
+ });
+
+})(window, document, window.searx);
+;/**
+* searx is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* searx is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with searx. If not, see < http://www.gnu.org/licenses/ >.
+*
+* (C) 2017 by Alexandre Flament, <alex@al-f.net>
+*/
+(function(w, d, searx) {
+ 'use strict';
+
+ var firstFocus = true, qinput_id = "q", qinput;
+
+ function placeCursorAtEnd(element) {
+ if (element.setSelectionRange) {
+ var len = element.value.length;
+ element.setSelectionRange(len, len);
+ }
+ }
+
+ function submitIfQuery() {
+ if (qinput.value.length > 0) {
+ var search = document.getElementById('search');
+ setTimeout(search.submit.bind(search), 0);
+ }
+ }
+
+ searx.ready(function() {
+ qinput = d.getElementById(qinput_id);
+
+ function placeCursorAtEndOnce(e) {
+ if (firstFocus) {
+ placeCursorAtEnd(qinput);
+ firstFocus = false;
+ } else {
+ // e.preventDefault();
+ }
+ }
+
+ if (qinput !== null) {
+ // autocompleter
+ if (searx.autocompleter) {
+ searx.autocomplete = AutoComplete.call(w, {
+ Url: "./autocompleter",
+ EmptyMessage: searx.noItemFound,
+ HttpMethod: searx.method,
+ MinChars: 4,
+ Delay: 300,
+ }, "#" + qinput_id);
+
+ // hack, see : https://github.com/autocompletejs/autocomplete.js/issues/37
+ w.addEventListener('resize', function() {
+ var event = new CustomEvent("position");
+ qinput.dispatchEvent(event);
+ });
+ }
+
+ qinput.addEventListener('focus', placeCursorAtEndOnce, false);
+ qinput.focus();
+ }
+
+ // vanilla js version of search_on_category_select.js
+ if (qinput !== null && searx.search_on_category_select) {
+ d.querySelector('.help').className='invisible';
+
+ searx.on('#categories input', 'change', function(e) {
+ var i, categories = d.querySelectorAll('#categories input[type="checkbox"]');
+ for(i=0; i<categories.length; i++) {
+ if (categories[i] !== this && categories[i].checked) {
+ categories[i].click();
+ }
+ }
+ if (! this.checked) {
+ this.click();
+ }
+ submitIfQuery();
+ return false;
+ });
+
+ searx.on(d.getElementById('time_range'), 'change', submitIfQuery);
+ searx.on(d.getElementById('language'), 'change', submitIfQuery);
+ }
+
+ });
+
+})(window, document, window.searx);
diff --git a/searx/static/themes/simple/js/searx.min.js b/searx/static/themes/simple/js/searx.min.js
new file mode 100644
index 000000000..3299d8b9d
--- /dev/null
+++ b/searx/static/themes/simple/js/searx.min.js
@@ -0,0 +1,4 @@
+/*! simple/searx.min.js | 06-08-2017 | https://github.com/asciimoo/searx */
+
+!function(t,e,n){"use strict";function o(t,e,n){try{t.call(e,n)}catch(t){console.log(t)}}t.Element&&function(t){t.matches=t.matches||t.matchesSelector||t.webkitMatchesSelector||t.msMatchesSelector||function(t){for(var e=this,n=(e.parentNode||e.document).querySelectorAll(t),o=-1;n[++o]&&n[o]!=e;);return!!n[o]}}(Element.prototype),n=n||{},n.on=function(t,n,a,i){i=i||!1,"string"!=typeof t?t.addEventListener(n,a,i):e.addEventListener(n,function(n){for(var i=n.target||n.srcElement,r=!1;i&&i.matches&&i!==e&&!(r=i.matches(t));)i=i.parentElement;r&&o(a,i,n)},i)},n.ready=function(e){"loading"!=document.readyState?e.call(t):t.addEventListener("DOMContentLoaded",e.bind(t))},n.http=function(t,e,n){var o=new XMLHttpRequest,a=function(){},i=function(){},r={then:function(t){return a=t,r},catch:function(t){return i=t,r}};try{o.open(t,e,!0),o.onload=function(){200==o.status?a(o.response,o.responseType):i(Error(o.statusText))},o.onerror=function(){i(Error("Network Error"))},o.onabort=function(){i(Error("Transaction is aborted"))},o.send()}catch(t){i(t)}return r},n.loadStyle=function(t){var o=n.staticPath+t,a="style_"+t.replace(".","_"),i=e.getElementById(a);null===i&&((i=e.createElement("link")).setAttribute("id",a),i.setAttribute("rel","stylesheet"),i.setAttribute("type","text/css"),i.setAttribute("href",o),e.body.appendChild(i))},n.loadScript=function(t,o){var a=n.staticPath+t,i="script_"+t.replace(".","_"),r=e.getElementById(i);if(null===r)(r=e.createElement("script")).setAttribute("id",i),r.setAttribute("src",a),r.onload=o,r.onerror=function(){r.setAttribute("error","1")},e.body.appendChild(r);else if(r.hasAttribute("error"))console.log("callback not executed : script '"+a+"' not loaded.");else try{o.apply(r,[])}catch(t){console.log(t)}},n.on(".close","click",function(t){t.target||t.srcElement;this.parentNode.style.display="None"})}(window,document,window.searx),function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).AutoComplete=t()}}(function(){return function t(e,n,o){function a(r,s){if(!n[r]){if(!e[r]){var l="function"==typeof require&&require;if(!s&&l)return l(r,!0);if(i)return i(r,!0);var u=new Error("Cannot find module '"+r+"'");throw u.code="MODULE_NOT_FOUND",u}var c=n[r]={exports:{}};e[r][0].call(c.exports,function(t){var n=e[r][1][t];return a(n||t)},c,c.exports,t,e,n,o)}return n[r].exports}for(var i="function"==typeof require&&require,r=0;r<o.length;r++)a(o[r]);return a}({1:[function(t,e,n){"use strict";var o;!function(t){t[t.AND=0]="AND",t[t.OR=1]="OR"}(o||(o={}));var a;!function(t){t[t.KEYDOWN=0]="KEYDOWN",t[t.KEYUP=1]="KEYUP"}(a||(a={}));var i=function(){function t(e,n){if(void 0===e&&(e={}),void 0===n&&(n="[data-autocomplete]"),Array.isArray(n))n.forEach(function(n){new t(e,n)});else{if("string"!=typeof n){var o=t.merge(t.defaults,e,{DOMResults:document.createElement("div")});return t.prototype.create(o,n),o}var a=document.querySelectorAll(n);Array.prototype.forEach.call(a,function(n){new t(e,n)})}}return t.prototype.create=function(e,n){if(e.Input=n,e.Input.nodeName.match(/^INPUT$/i)&&(!1===e.Input.hasAttribute("type")||e.Input.getAttribute("type").match(/^TEXT|SEARCH$/i))){e.Input.setAttribute("autocomplete","off"),e._Position(e),e.Input.parentNode.appendChild(e.DOMResults),e.$Listeners={blur:e._Blur.bind(e),destroy:t.prototype.destroy.bind(null,e),focus:e._Focus.bind(e),keyup:t.prototype.event.bind(null,e,a.KEYUP),keydown:t.prototype.event.bind(null,e,a.KEYDOWN),position:e._Position.bind(e)};for(var o in e.$Listeners)e.Input.addEventListener(o,e.$Listeners[o])}},t.prototype.getEventsByType=function(t,e){var n={};for(var o in t.KeyboardMappings){var i=a.KEYUP;void 0!==t.KeyboardMappings[o].Event&&(i=t.KeyboardMappings[o].Event),i==e&&(n[o]=t.KeyboardMappings[o])}return n},t.prototype.event=function(e,n,a){for(var i in t.prototype.getEventsByType(e,n)){var r=t.merge({Operator:o.AND},e.KeyboardMappings[i]),s=o.AND==r.Operator;r.Conditions.forEach(function(e){(!0===s&&r.Operator==o.AND||!1===s&&r.Operator==o.OR)&&((e=t.merge({Not:!1},e)).hasOwnProperty("Is")?s=e.Is==a.keyCode?!e.Not:e.Not:e.hasOwnProperty("From")&&e.hasOwnProperty("To")&&(s=a.keyCode>=e.From&&a.keyCode<=e.To?!e.Not:e.Not))}),!0===s&&r.Callback.call(e,a)}},t.prototype.makeRequest=function(t,e){var n=Object.getOwnPropertyNames(t.HttpHeaders),o=new XMLHttpRequest,a=t._HttpMethod(),i=t._Url(),r=t._Pre(),s=encodeURIComponent(t._QueryArg())+"="+encodeURIComponent(r);a.match(/^GET$/i)&&(-1!==i.indexOf("?")?i+="&"+s:i+="?"+s),o.open(a,i,!0);for(var l=n.length-1;l>=0;l--)o.setRequestHeader(n[l],t.HttpHeaders[n[l]]);return o.onreadystatechange=function(){4==o.readyState&&200==o.status&&(t.$Cache[r]=o.response,e(o.response))},o},t.prototype.ajax=function(e,n,o){void 0===o&&(o=!0),e.$AjaxTimer&&window.clearTimeout(e.$AjaxTimer),!0===o?e.$AjaxTimer=window.setTimeout(t.prototype.ajax.bind(null,e,n,!1),e.Delay):(e.Request&&e.Request.abort(),e.Request=n,e.Request.send(e._QueryArg()+"="+e._Pre()))},t.prototype.cache=function(e,n){var o=e._Cache(e._Pre());if(void 0===o){var a=t.prototype.makeRequest(e,n);t.prototype.ajax(e,a)}else n(o)},t.prototype.destroy=function(t){for(var e in t.$Listeners)t.Input.removeEventListener(e,t.$Listeners[e]);t.DOMResults.parentNode.removeChild(t.DOMResults)},t}();i.merge=function(){for(var t,e={},n=0;n<arguments.length;n++)for(t in arguments[n])e[t]=arguments[n][t];return e},i.defaults={Delay:150,EmptyMessage:"No result here",Highlight:{getRegex:function(t){return new RegExp(t,"ig")},transform:function(t){return"<strong>"+t+"</strong>"}},HttpHeaders:{"Content-type":"application/x-www-form-urlencoded"},Limit:0,MinChars:0,HttpMethod:"GET",QueryArg:"q",Url:null,KeyboardMappings:{Enter:{Conditions:[{Is:13,Not:!1}],Callback:function(t){if(-1!=this.DOMResults.getAttribute("class").indexOf("open")){var e=this.DOMResults.querySelector("li.active");null!==e&&(t.preventDefault(),this._Select(e),this.DOMResults.setAttribute("class","autocomplete"))}},Operator:o.AND,Event:a.KEYDOWN},KeyUpAndDown_down:{Conditions:[{Is:38,Not:!1},{Is:40,Not:!1}],Callback:function(t){t.preventDefault()},Operator:o.OR,Event:a.KEYDOWN},KeyUpAndDown_up:{Conditions:[{Is:38,Not:!1},{Is:40,Not:!1}],Callback:function(t){t.preventDefault();var e=this.DOMResults.querySelector("li:first-child:not(.locked)"),n=this.DOMResults.querySelector("li:last-child:not(.locked)"),o=this.DOMResults.querySelector("li.active");if(o){var a=Array.prototype.indexOf.call(o.parentNode.children,o)+(t.keyCode-39),i=this.DOMResults.getElementsByTagName("li").length;a<0?a=i-1:a>=i&&(a=0),o.classList.remove("active"),o.parentElement.children.item(a).classList.add("active")}else n&&38==t.keyCode?n.classList.add("active"):e&&e.classList.add("active")},Operator:o.OR,Event:a.KEYUP},AlphaNum:{Conditions:[{Is:13,Not:!0},{From:35,To:40,Not:!0}],Callback:function(){var t=this.Input.getAttribute("data-autocomplete-old-value"),e=this._Pre();""!==e&&e.length>=this._MinChars()&&(t&&e==t||this.DOMResults.setAttribute("class","autocomplete open"),i.prototype.cache(this,function(t){this._Render(this._Post(t)),this._Open()}.bind(this)))},Operator:o.AND,Event:a.KEYUP}},DOMResults:null,Request:null,Input:null,_EmptyMessage:function(){return this.Input.hasAttribute("data-autocomplete-empty-message")?this.Input.getAttribute("data-autocomplete-empty-message"):!1!==this.EmptyMessage?this.EmptyMessage:""},_Limit:function(){var t=this.Input.getAttribute("data-autocomplete-limit");return isNaN(t)||null===t?this.Limit:parseInt(t,10)},_MinChars:function(){var t=this.Input.getAttribute("data-autocomplete-minchars");return isNaN(t)||null===t?this.MinChars:parseInt(t,10)},_Highlight:function(t){return t.replace(this.Highlight.getRegex(this._Pre()),this.Highlight.transform)},_HttpMethod:function(){return this.Input.hasAttribute("data-autocomplete-method")?this.Input.getAttribute("data-autocomplete-method"):this.HttpMethod},_QueryArg:function(){return this.Input.hasAttribute("data-autocomplete-param-name")?this.Input.getAttribute("data-autocomplete-param-name"):this.QueryArg},_Url:function(){return this.Input.hasAttribute("data-autocomplete")?this.Input.getAttribute("data-autocomplete"):this.Url},_Blur:function(t){if(!0===t)this.DOMResults.setAttribute("class","autocomplete"),this.Input.setAttribute("data-autocomplete-old-value",this.Input.value);else{var e=this;setTimeout(function(){e._Blur(!0)},150)}},_Cache:function(t){return this.$Cache[t]},_Focus:function(){var t=this.Input.getAttribute("data-autocomplete-old-value");(!t||this.Input.value!=t)&&this._MinChars()<=this.Input.value.length&&this.DOMResults.setAttribute("class","autocomplete open")},_Open:function(){var t=this;Array.prototype.forEach.call(this.DOMResults.getElementsByTagName("li"),function(e){"locked"!=e.getAttribute("class")&&(e.onclick=function(n){t._Select(e)},e.onmouseenter=function(){var n=t.DOMResults.querySelector("li.active");n!==e&&(null!==n&&n.classList.remove("active"),e.classList.add("active"))})})},_Position:function(){this.DOMResults.setAttribute("class","autocomplete"),this.DOMResults.setAttribute("style","top:"+(this.Input.offsetTop+this.Input.offsetHeight)+"px;left:"+this.Input.offsetLeft+"px;width:"+this.Input.clientWidth+"px;")},_Render:function(t){var e;e="string"==typeof t?this._RenderRaw(t):this._RenderResponseItems(t),this.DOMResults.hasChildNodes()&&this.DOMResults.removeChild(this.DOMResults.childNodes[0]),this.DOMResults.appendChild(e)},_RenderResponseItems:function(t){var e=document.createElement("ul"),n=document.createElement("li"),o=this._Limit();o<0?t=t.reverse():0===o&&(o=t.length);for(var a=0;a<Math.min(Math.abs(o),t.length);a++)n.innerHTML=t[a].Label,n.setAttribute("data-autocomplete-value",t[a].Value),e.appendChild(n),n=document.createElement("li");return e},_RenderRaw:function(t){var e=document.createElement("ul"),n=document.createElement("li");if(t.length>0)this.DOMResults.innerHTML=t;else{var o=this._EmptyMessage();""!==o&&(n.innerHTML=o,n.setAttribute("class","locked"),e.appendChild(n))}return e},_Post:function(t){try{var e=[],n=JSON.parse(t);if(0===Object.keys(n).length)return"";if(Array.isArray(n))for(var o=0;o<Object.keys(n).length;o++)e[e.length]={Value:n[o],Label:this._Highlight(n[o])};else for(var a in n)e.push({Value:a,Label:this._Highlight(n[a])});return e}catch(e){return t}},_Pre:function(){return this.Input.value},_Select:function(t){console.log("test test test"),t.hasAttribute("data-autocomplete-value")?this.Input.value=t.getAttribute("data-autocomplete-value"):this.Input.value=t.innerHTML,this.Input.setAttribute("data-autocomplete-old-value",this.Input.value)},$AjaxTimer:null,$Cache:{},$Listeners:{}},e.exports=i},{}]},{},[1])(1)}),function(t,e){"use strict";function n(t,e,n,o){this.container_selector=t,this.results_selector=e,this.img_selector=n,this.margin=10,this.maxHeight=o,this._alignAllDone=!0}n.prototype._getHeigth=function(t,e){var n,o=0;e-=t.length*this.margin;for(var a=0;a<t.length;a++)(n=t[a]).naturalWidth>0&&n.naturalHeight>0?o+=n.naturalWidth/n.naturalHeight:o+=1;return e/o},n.prototype._setSize=function(t,e){for(var n,o,a=t.length,i=0;i<a;i++)o=(n=t[i]).naturalWidth>0&&n.naturalHeight>0?e*n.naturalWidth/n.naturalHeight:e,n.style.width=o+"px",n.style.height=e+"px",n.style.marginLeft="3px",n.style.marginTop="3px",n.style.marginRight=this.margin-7+"px",n.style.marginBottom=this.margin-7+"px"},n.prototype._alignImgs=function(t){var n,o,a=e.querySelector(this.container_selector).clientWidth;t:for(;t.length>0;){for(var i=1;i<=t.length;i++)if(n=t.slice(0,i),(o=this._getHeigth(n,a))<this.maxHeight){this._setSize(n,o),t=t.slice(i);continue t}this._setSize(n,Math.min(this.maxHeight,o));break}},n.prototype.align=function(t){for(var n=e.querySelectorAll(this.results_selector),o=n.length,a=null,i=null,r=[],s=0;s<o;s++)(i=n[s]).previousElementSibling!==a&&r.length>0&&(this._alignImgs(r),r=[]),r.push(i.querySelector(this.img_selector)),a=i;r.length>0&&this._alignImgs(r)},n.prototype.watch=function(){function n(t){i._alignAllDone&&(i._alignAllDone=!1,setTimeout(function(){i.align(),i._alignAllDone=!0},100))}var o,a,i=this,r=e.querySelectorAll(this.results_selector),s=r.length;for(t.addEventListener("resize",n),t.addEventListener("pageshow",function(t){i.align()}),o=0;o<s;o++)void 0!==(a=r[o].querySelector(this.img_selector))&&(a.addEventListener("load",n),a.addEventListener("error",n))},t.searx.ImageLayout=n}(window,document),searx.ready(function(){function t(t){return function(e){var o=document.querySelector(".result[data-vim-selected]"),a=t;if(null===o){if(null===(o=document.querySelector(".result")))return;"down"!==t&&"up"!==t||(a=o)}var i,r=document.querySelectorAll(".result");if("string"!=typeof a)i=a;else switch(a){case"visible":for(var s=document.documentElement.scrollTop||document.body.scrollTop,l=s+document.documentElement.clientHeight,u=0;u<r.length;u++){var c=(i=r[u]).offsetTop;if(c+i.clientHeight<=l&&c>s)break}break;case"down":null===(i=o.nextElementSibling)&&(i=r[0]);break;case"up":null===(i=o.previousElementSibling)&&(i=r[r.length-1]);break;case"bottom":i=r[r.length-1];break;case"top":default:i=r[0]}if(i){o.removeAttribute("data-vim-selected"),i.setAttribute("data-vim-selected","true");var d=i.querySelector("h3 a")||i.querySelector("a");null!==d&&d.focus(),e||n()}}}function e(t){return function(){var e=$('div#pagination button[type="submit"]');2===e.length?t>=0&&t<e.length?e[t].click():console.log("pageButtonClick(): invalid argument"):console.log("page navigation with this theme is not supported")}}function n(){var t=document.querySelector(".result[data-vim-selected]");if(null!==t){var e=document.documentElement.scrollTop||document.body.scrollTop,n=document.documentElement.clientHeight,o=t.offsetTop,a=o+t.clientHeight;null===t.previousElementSibling&&a<n?window.scroll(window.scrollX,0):e>o-120?window.scroll(window.scrollX,o-120):e+n<a+120&&window.scroll(window.scrollX,a-n+120)}}function o(e){return function(){window.scrollBy(0,e),t("visible")()}}function a(e,n){return function(){window.scrollTo(0,e),t(n)()}}function i(t){return function(){var e=document.querySelector(".result[data-vim-selected] h3 a");if(null!==e){var n=e.getAttribute("href");t?window.open(n):window.location.href=n}}}searx.on(".result","click",function(){t(this)(!0)}),searx.on(".result a","focus",function(e){for(var n=e.target;void 0!==n;){if(n.classList.contains("result")){null===n.getAttribute("data-vim-selected")&&t(n)(!0);break}n=n.parentNode}},!0);var r={27:{key:"Escape",fun:function(){document.activeElement&&document.activeElement.blur()},des:"remove focus from the focused input",cat:"Control"},73:{key:"i",fun:function(){window.scrollTo(0,0),document.querySelector("#q").focus()},des:"focus on the search input",cat:"Control"},66:{key:"b",fun:o(-window.innerHeight),des:"scroll one page up",cat:"Navigation"},70:{key:"f",fun:o(window.innerHeight),des:"scroll one page down",cat:"Navigation"},85:{key:"u",fun:o(-window.innerHeight/2),des:"scroll half a page up",cat:"Navigation"},68:{key:"d",fun:o(window.innerHeight/2),des:"scroll half a page down",cat:"Navigation"},71:{key:"g",fun:a(-document.body.scrollHeight,"top"),des:"scroll to the top of the page",cat:"Navigation"},86:{key:"v",fun:a(document.body.scrollHeight,"bottom"),des:"scroll to the bottom of the page",cat:"Navigation"},75:{key:"k",fun:t("up"),des:"select previous search result",cat:"Results"},74:{key:"j",fun:t("down"),des:"select next search result",cat:"Results"},80:{key:"p",fun:e(0),des:"go to previous page",cat:"Results"},78:{key:"n",fun:e(1),des:"go to next page",cat:"Results"},79:{key:"o",fun:i(!1),des:"open search result",cat:"Results"},84:{key:"t",fun:i(!0),des:"open the result in a new tab",cat:"Results"},82:{key:"r",fun:function(){document.location.reload(!0)},des:"reload page from the server",cat:"Control"},72:{key:"h",fun:function(){var t=document.querySelector("#vim-hotkeys-help");if(t.length)t.classList.toggle("hidden");else{var e={};for(var n in r){var o=r[n];e[o.cat]=e[o.cat]||[],e[o.cat].push(o)}var a=Object.keys(e).sort(function(t,n){return e[n].length-e[t].length});if(0!==a.length){var i='<div id="vim-hotkeys-help" class="well vim-hotkeys-help">';i+='<div class="container-fluid">',i+='<div class="row">',i+='<div class="col-sm-12">',i+="<h3>How to navigate searx with Vim-like hotkeys</h3>",i+="</div>",i+="</div>";for(var s=0;s<a.length;s++){var l=e[a[s]],u=s===a.length-1,c=s%2==0;c&&(i+='<div class="row dflex">'),i+='<div class="col-sm-'+(c&&u?12:6)+' dflex">',i+='<div class="panel panel-default iflex">',i+='<div class="panel-heading">'+l[0].cat+"</div>",i+='<div class="panel-body">',i+='<ul class="list-unstyled">';for(var d in l)i+="<li><kbd>"+l[d].key+"</kbd> "+l[d].des+"</li>";i+="</ul>",i+="</div>",i+="</div>",i+="</div>",c&&!u||(i+="</div>")}i+="</div>",i+="</div>",$("body").append(i)}}},des:"toggle help window",cat:"Other"}};searx.on(document,"keyup",function(t){if(r.hasOwnProperty(t.keyCode)&&!t.ctrlKey&&!t.altKey&&!t.shiftKey&&!t.metaKey){var e=t.target.tagName.toLowerCase();27===t.keyCode?"input"!==e&&"select"!==e&&"textarea"!==e||r[t.keyCode].fun():t.target!==document.body&&"a"!==e&&"button"!==e||r[t.keyCode].fun()}})}),function(t,e,n){"use strict";n.ready(function(){n.on(".searx_overpass_request","click",function(t){this.classList.remove("searx_overpass_request");var o="https://overpass-api.de/api/interpreter?data=[out:json][timeout:25];(",a=this.dataset.osmId,i=this.dataset.osmType,r=e.querySelector("#"+this.dataset.resultTable),s=e.querySelector("#"+this.dataset.resultTableLoadicon),l=["addr:city","addr:country","addr:housenumber","addr:postcode","addr:street"];if(a&&i&&r){var u=null;switch(i){case"node":u=o+"node("+a+"););out meta;";break;case"way":u=o+"way("+a+"););out meta;";break;case"relation":u=o+"relation("+a+"););out meta;"}u&&n.http("GET",u).then(function(t,e){if((t=JSON.parse(t))&&t.elements&&t.elements[0]){var n=t.elements[0],o="";for(var a in n.tags)if(null===n.tags.name||-1==l.indexOf(a)){switch(o+="<tr><td>"+a+"</td><td>",a){case"phone":case"fax":o+='<a href="tel:'+n.tags[a].replace(/ /g,"")+'">'+n.tags[a]+"</a>";break;case"email":o+='<a href="mailto:'+n.tags[a]+'">'+n.tags[a]+"</a>";break;case"website":case"url":o+='<a href="'+n.tags[a]+'">'+n.tags[a]+"</a>";break;case"wikidata":o+='<a href="https://www.wikidata.org/wiki/'+n.tags[a]+'">'+n.tags[a]+"</a>";break;case"wikipedia":if(-1!=n.tags[a].indexOf(":")){o+='<a href="https://'+n.tags[a].substring(0,n.tags[a].indexOf(":"))+".wikipedia.org/wiki/"+n.tags[a].substring(n.tags[a].indexOf(":")+1)+'">'+n.tags[a]+"</a>";break}default:o+=n.tags[a]}o+="</td></tr>"}s.classList.add("invisible"),r.classList.remove("invisible"),r.querySelector("tbody").innerHTML=o}}).catch(function(){s.innerHTML=s.innerHTML+'<p class="text-muted">could not load data!</p>'})}t.preventDefault()}),n.on(".searx_init_map","click",function(t){this.classList.remove("searx_init_map");var e=this.dataset.leafletTarget,o=parseFloat(this.dataset.mapLon),a=parseFloat(this.dataset.mapLat),i=parseFloat(this.dataset.mapZoom),r=JSON.parse(this.dataset.mapBoundingbox),s=JSON.parse(this.dataset.mapGeojson);n.loadStyle("leaflet/leaflet.css"),n.loadScript("leaflet/leaflet.js",function(){var t=null;if(r){var n=L.latLng(r[0],r[2]),l=L.latLng(r[1],r[3]);t=L.latLngBounds(n,l)}var u=L.map(e),c=new L.TileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",{minZoom:1,maxZoom:19,attribution:'Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors'});new L.TileLayer("https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png",{minZoom:1,maxZoom:19,attribution:'Wikimedia maps beta | Maps data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors'});t?setTimeout(function(){u.fitBounds(t,{maxZoom:17})},0):o&&a&&(i?u.setView(new L.latLng(a,o),i):u.setView(new L.latLng(a,o),8)),u.addLayer(c);var d={"OSM Mapnik":c};L.control.layers(d).addTo(u),s&&L.geoJson(s).addTo(u)}),t.preventDefault()})})}(window,document,window.searx),function(t,e,n){"use strict";n.ready(function(){n.image_thumbnail_layout=new n.ImageLayout("#urls","#urls .result-images","img.image_thumbnail",200),n.image_thumbnail_layout.watch(),n.on(".btn-collapse","click",function(t){var n=this.getAttribute("data-btn-text-collapsed"),o=this.getAttribute("data-btn-text-not-collapsed"),a=this.getAttribute("data-target"),i=e.querySelector(a),r=this.innerHTML;r=this.classList.contains("collapsed")?r.replace(n,o):r.replace(o,n),this.innerHTML=r,this.classList.toggle("collapsed"),i.classList.toggle("invisible")}),n.on(".media-loader","click",function(t){var n=this.getAttribute("data-target"),o=e.querySelector(n+" > iframe"),a=o.getAttribute("src");null!==a&&void 0!==a&&!1!==a||o.setAttribute("src",o.getAttribute("data-src"))}),t.addEventListener("scroll",function(){var t=e.getElementById("backToTop"),n=document.documentElement.scrollTop||document.body.scrollTop;null!==t&&(t.style.opacity=n>=200?1:0)})})}(window,document,window.searx),function(t,e,n){"use strict";function o(t){if(t.setSelectionRange){var e=t.value.length;t.setSelectionRange(e,e)}}function a(){if(i.value.length>0){var t=document.getElementById("search");setTimeout(t.submit.bind(t),0)}}var i,r=!0;n.ready(function(){null!==(i=e.getElementById("q"))&&(n.autocompleter&&(n.autocomplete=AutoComplete.call(t,{Url:"./autocompleter",EmptyMessage:n.noItemFound,HttpMethod:n.method,MinChars:4,Delay:300},"#q"),t.addEventListener("resize",function(){var t=new CustomEvent("position");i.dispatchEvent(t)})),i.addEventListener("focus",function(t){r&&(o(i),r=!1)},!1),i.focus()),null!==i&&n.search_on_category_select&&(e.querySelector(".help").className="invisible",n.on("#categories input","change",function(t){var n,o=e.querySelectorAll('#categories input[type="checkbox"]');for(n=0;n<o.length;n++)o[n]!==this&&o[n].checked&&o[n].click();return this.checked||this.click(),a(),!1}),n.on(e.getElementById("time_range"),"change",a),n.on(e.getElementById("language"),"change",a))})}(window,document,window.searx);
+//# sourceMappingURL=searx.min.js.map \ No newline at end of file
diff --git a/searx/static/themes/simple/js/searx.min.js.map b/searx/static/themes/simple/js/searx.min.js.map
new file mode 100644
index 000000000..a9ddb23bb
--- /dev/null
+++ b/searx/static/themes/simple/js/searx.min.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["searx.js"],"names":["w","d","searx","callbackSafe","callback","el","e","call","exception","console","log","Element","ElementPrototype","matches","matchesSelector","webkitMatchesSelector","msMatchesSelector","selector","node","this","nodes","parentNode","document","querySelectorAll","i","prototype","on","obj","eventType","useCapture","addEventListener","target","srcElement","found","parentElement","ready","readyState","bind","http","method","url","req","XMLHttpRequest","resolve","reject","promise","then","catch","open","onload","status","response","responseType","Error","statusText","onerror","onabort","send","ex","loadStyle","src","path","staticPath","id","replace","s","getElementById","createElement","setAttribute","body","appendChild","loadScript","hasAttribute","apply","style","display","window","f","exports","module","define","amd","global","self","AutoComplete","t","n","r","o","u","a","require","code","l","length","1","ConditionOperator","EventType","params","Array","isArray","forEach","specificParams","merge","defaults","DOMResults","create","elements","input","element","Input","nodeName","match","getAttribute","_Position","$Listeners","blur","_Blur","destroy","focus","_Focus","keyup","event","KEYUP","keydown","KEYDOWN","position","getEventsByType","type","mappings","key","KeyboardMappings","undefined","Event","name","mapping","Operator","AND","Conditions","condition","OR","Not","hasOwnProperty","Is","keyCode","From","To","Callback","makeRequest","propertyHttpHeaders","Object","getOwnPropertyNames","HttpHeaders","request","_HttpMethod","_Url","queryParams","_Pre","queryParamsStringify","encodeURIComponent","_QueryArg","indexOf","setRequestHeader","onreadystatechange","$Cache","ajax","timeout","$AjaxTimer","clearTimeout","setTimeout","Delay","Request","abort","cache","_Cache","removeEventListener","removeChild","tmp","arguments","EmptyMessage","Highlight","getRegex","value","RegExp","transform","Content-type","Limit","MinChars","HttpMethod","QueryArg","Url","Enter","liActive","querySelector","preventDefault","_Select","KeyUpAndDown_down","KeyUpAndDown_up","first","last","active","children","lisCount","getElementsByTagName","classList","remove","item","add","AlphaNum","oldValue","currentValue","_MinChars","_Render","_Post","_Open","_EmptyMessage","_Limit","limit","isNaN","parseInt","minchars","_Highlight","label","now","li","onclick","onmouseenter","offsetTop","offsetHeight","offsetLeft","clientWidth","ul","_RenderRaw","_RenderResponseItems","hasChildNodes","childNodes","reverse","Math","min","abs","innerHTML","Label","Value","emptyMessage","returnResponse","json","JSON","parse","keys","push","ImageLayout","container_selector","results_selector","img_selector","maxHeight","margin","_alignAllDone","_getHeigth","images","width","img","naturalWidth","naturalHeight","_setSize","height","imgWidth","imagesLength","marginLeft","marginTop","marginRight","marginBottom","_alignImgs","imgGroup","slice","h","containerWidth","align","results_selectorNode","results_length","previous","current","previousElementSibling","watch","throttleAlign","results_nodes","highlightResult","which","noScroll","effectiveWhich","next","results","top","documentElement","scrollTop","bot","clientHeight","etop","nextElementSibling","removeAttribute","link","scrollPageToSelected","pageButtonClick","num","buttons","$","click","sel","wtop","wheight","ebot","scroll","scrollX","scrollPage","amount","scrollBy","scrollPageTo","nav","scrollTo","openResult","newTab","location","href","contains","vimKeys","27","fun","activeElement","des","cat","73","66","innerHeight","70","85","68","71","scrollHeight","86","75","74","80","78","79","84","82","reload","72","helpPanel","toggle","categories","k","sorted","sort","b","html","lastCategory","cj","append","ctrlKey","altKey","shiftKey","metaKey","tagName","toLowerCase","query_start","overpass_url","osm_id","dataset","osmId","osm_type","osmType","result_table","resultTable","result_table_loadicon","resultTableLoadicon","osm_ignore_tags","query","contentType","newHtml","row","tags","substring","leaflet_target","leafletTarget","map_lon","parseFloat","mapLon","map_lat","mapLat","map_zoom","mapZoom","map_boundingbox","mapBoundingbox","map_geojson","mapGeojson","map_bounds","southWest","L","latLng","northEast","latLngBounds","map","osmMapnik","TileLayer","minZoom","maxZoom","attribution","fitBounds","setView","addLayer","baseLayers","OSM Mapnik","control","layers","addTo","geoJson","image_thumbnail_layout","btnLabelCollapsed","btnLabelNotCollapsed","targetElement","iframe_load","srctest","opacity","placeCursorAtEnd","setSelectionRange","len","submitIfQuery","qinput","search","submit","firstFocus","autocompleter","autocomplete","noItemFound","CustomEvent","dispatchEvent","search_on_category_select","className","checked"],"mappings":"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","file":"searx.min.js"} \ No newline at end of file
diff --git a/searx/static/themes/simple/js/searx_src/00_searx_toolkit.js b/searx/static/themes/simple/js/searx_src/00_searx_toolkit.js
new file mode 100644
index 000000000..fca7e1669
--- /dev/null
+++ b/searx/static/themes/simple/js/searx_src/00_searx_toolkit.js
@@ -0,0 +1,155 @@
+/**
+* searx is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* searx is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with searx. If not, see < http://www.gnu.org/licenses/ >.
+*
+* (C) 2017 by Alexandre Flament, <alex@al-f.net>
+*
+*/
+(function(w, d, searx) {
+
+ 'use strict';
+
+ // not invented here tookit with bugs fixed elsewhere
+ // purposes : be just good enough and as small as possible
+
+ // from https://plainjs.com/javascript/events/live-binding-event-handlers-14/
+ if (w.Element) {
+ (function(ElementPrototype) {
+ ElementPrototype.matches = ElementPrototype.matches ||
+ ElementPrototype.matchesSelector ||
+ ElementPrototype.webkitMatchesSelector ||
+ ElementPrototype.msMatchesSelector ||
+ function(selector) {
+ var node = this, nodes = (node.parentNode || node.document).querySelectorAll(selector), i = -1;
+ while (nodes[++i] && nodes[i] != node);
+ return !!nodes[i];
+ };
+ })(Element.prototype);
+ }
+
+ function callbackSafe(callback, el, e) {
+ try {
+ callback.call(el, e);
+ } catch (exception) {
+ console.log(exception);
+ }
+ }
+
+ searx = searx || {};
+
+ searx.on = function(obj, eventType, callback, useCapture) {
+ useCapture = useCapture || false;
+ if (typeof obj !== 'string') {
+ // obj HTMLElement, HTMLDocument
+ obj.addEventListener(eventType, callback, useCapture);
+ } else {
+ // obj is a selector
+ d.addEventListener(eventType, function(e) {
+ var el = e.target || e.srcElement, found = false;
+ while (el && el.matches && el !== d && !(found = el.matches(obj))) el = el.parentElement;
+ if (found) callbackSafe(callback, el, e);
+ }, useCapture);
+ }
+ };
+
+ searx.ready = function(callback) {
+ if (document.readyState != 'loading') {
+ callback.call(w);
+ } else {
+ w.addEventListener('DOMContentLoaded', callback.bind(w));
+ }
+ };
+
+ searx.http = function(method, url, callback) {
+ var req = new XMLHttpRequest(),
+ resolve = function() {},
+ reject = function() {},
+ promise = {
+ then: function(callback) { resolve = callback; return promise; },
+ catch: function(callback) { reject = callback; return promise; }
+ };
+
+ try {
+ req.open(method, url, true);
+
+ // On load
+ req.onload = function() {
+ if (req.status == 200) {
+ resolve(req.response, req.responseType);
+ } else {
+ reject(Error(req.statusText));
+ }
+ };
+
+ // Handle network errors
+ req.onerror = function() {
+ reject(Error("Network Error"));
+ };
+
+ req.onabort = function() {
+ reject(Error("Transaction is aborted"));
+ };
+
+ // Make the request
+ req.send();
+ } catch (ex) {
+ reject(ex);
+ }
+
+ return promise;
+ };
+
+ searx.loadStyle = function(src) {
+ var path = searx.staticPath + src,
+ id = "style_" + src.replace('.', '_'),
+ s = d.getElementById(id);
+ if (s === null) {
+ s = d.createElement('link');
+ s.setAttribute('id', id);
+ s.setAttribute('rel', 'stylesheet');
+ s.setAttribute('type', 'text/css');
+ s.setAttribute('href', path);
+ d.body.appendChild(s);
+ }
+ };
+
+ searx.loadScript = function(src, callback) {
+ var path = searx.staticPath + src,
+ id = "script_" + src.replace('.', '_'),
+ s = d.getElementById(id);
+ if (s === null) {
+ s = d.createElement('script');
+ s.setAttribute('id', id);
+ s.setAttribute('src', path);
+ s.onload = callback;
+ s.onerror = function() {
+ s.setAttribute('error', '1');
+ };
+ d.body.appendChild(s);
+ } else if (!s.hasAttribute('error')) {
+ try {
+ callback.apply(s, []);
+ } catch (exception) {
+ console.log(exception);
+ }
+ } else {
+ console.log("callback not executed : script '" + path + "' not loaded.");
+ }
+ };
+
+ searx.on('.close', 'click', function(e) {
+ var el = e.target || e.srcElement;
+ this.parentNode.style.display="None";
+ });
+ return searx;
+})(window, document, window.searx);
diff --git a/searx/static/themes/simple/js/searx_src/autocomplete.js b/searx/static/themes/simple/js/searx_src/autocomplete.js
new file mode 100644
index 000000000..b95fbcfb2
--- /dev/null
+++ b/searx/static/themes/simple/js/searx_src/autocomplete.js
@@ -0,0 +1,536 @@
+(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){
+/*
+ * @license MIT
+ *
+ * Autocomplete.js v2.6.3
+ * Developed by Baptiste Donaux
+ * http://autocomplete-js.com
+ *
+ * (c) 2017, Baptiste Donaux
+ */
+"use strict";
+var ConditionOperator;
+(function (ConditionOperator) {
+ ConditionOperator[ConditionOperator["AND"] = 0] = "AND";
+ ConditionOperator[ConditionOperator["OR"] = 1] = "OR";
+})(ConditionOperator || (ConditionOperator = {}));
+var EventType;
+(function (EventType) {
+ EventType[EventType["KEYDOWN"] = 0] = "KEYDOWN";
+ EventType[EventType["KEYUP"] = 1] = "KEYUP";
+})(EventType || (EventType = {}));
+/**
+ * Core
+ *
+ * @class
+ * @author Baptiste Donaux <baptiste.donaux@gmail.com> @baptistedonaux
+ */
+var AutoComplete = (function () {
+ // Constructor
+ function AutoComplete(params, selector) {
+ if (params === void 0) { params = {}; }
+ if (selector === void 0) { selector = "[data-autocomplete]"; }
+ if (Array.isArray(selector)) {
+ selector.forEach(function (s) {
+ new AutoComplete(params, s);
+ });
+ }
+ else if (typeof selector == "string") {
+ var elements = document.querySelectorAll(selector);
+ Array.prototype.forEach.call(elements, function (input) {
+ new AutoComplete(params, input);
+ });
+ }
+ else {
+ var specificParams = AutoComplete.merge(AutoComplete.defaults, params, {
+ DOMResults: document.createElement("div")
+ });
+ AutoComplete.prototype.create(specificParams, selector);
+ return specificParams;
+ }
+ }
+ AutoComplete.prototype.create = function (params, element) {
+ params.Input = element;
+ if (params.Input.nodeName.match(/^INPUT$/i) && (params.Input.hasAttribute("type") === false || params.Input.getAttribute("type").match(/^TEXT|SEARCH$/i))) {
+ params.Input.setAttribute("autocomplete", "off");
+ params._Position(params);
+ params.Input.parentNode.appendChild(params.DOMResults);
+ params.$Listeners = {
+ blur: params._Blur.bind(params),
+ destroy: AutoComplete.prototype.destroy.bind(null, params),
+ focus: params._Focus.bind(params),
+ keyup: AutoComplete.prototype.event.bind(null, params, EventType.KEYUP),
+ keydown: AutoComplete.prototype.event.bind(null, params, EventType.KEYDOWN),
+ position: params._Position.bind(params)
+ };
+ for (var event in params.$Listeners) {
+ params.Input.addEventListener(event, params.$Listeners[event]);
+ }
+ }
+ };
+ AutoComplete.prototype.getEventsByType = function (params, type) {
+ var mappings = {};
+ for (var key in params.KeyboardMappings) {
+ var event = EventType.KEYUP;
+ if (params.KeyboardMappings[key].Event !== undefined) {
+ event = params.KeyboardMappings[key].Event;
+ }
+ if (event == type) {
+ mappings[key] = params.KeyboardMappings[key];
+ }
+ }
+ return mappings;
+ };
+ AutoComplete.prototype.event = function (params, type, event) {
+ var eventIdentifier = function (condition) {
+ if ((match === true && mapping.Operator == ConditionOperator.AND) || (match === false && mapping.Operator == ConditionOperator.OR)) {
+ condition = AutoComplete.merge({
+ Not: false
+ }, condition);
+ if (condition.hasOwnProperty("Is")) {
+ if (condition.Is == event.keyCode) {
+ match = !condition.Not;
+ }
+ else {
+ match = condition.Not;
+ }
+ }
+ else if (condition.hasOwnProperty("From") && condition.hasOwnProperty("To")) {
+ if (event.keyCode >= condition.From && event.keyCode <= condition.To) {
+ match = !condition.Not;
+ }
+ else {
+ match = condition.Not;
+ }
+ }
+ }
+ };
+ for (var name in AutoComplete.prototype.getEventsByType(params, type)) {
+ var mapping = AutoComplete.merge({
+ Operator: ConditionOperator.AND
+ }, params.KeyboardMappings[name]), match = ConditionOperator.AND == mapping.Operator;
+ mapping.Conditions.forEach(eventIdentifier);
+ if (match === true) {
+ mapping.Callback.call(params, event);
+ }
+ }
+ };
+ AutoComplete.prototype.makeRequest = function (params, callback) {
+ var propertyHttpHeaders = Object.getOwnPropertyNames(params.HttpHeaders), request = new XMLHttpRequest(), method = params._HttpMethod(), url = params._Url(), queryParams = params._Pre(), queryParamsStringify = encodeURIComponent(params._QueryArg()) + "=" + encodeURIComponent(queryParams);
+ if (method.match(/^GET$/i)) {
+ if (url.indexOf("?") !== -1) {
+ url += "&" + queryParamsStringify;
+ }
+ else {
+ url += "?" + queryParamsStringify;
+ }
+ }
+ request.open(method, url, true);
+ for (var i = propertyHttpHeaders.length - 1; i >= 0; i--) {
+ request.setRequestHeader(propertyHttpHeaders[i], params.HttpHeaders[propertyHttpHeaders[i]]);
+ }
+ request.onreadystatechange = function () {
+ if (request.readyState == 4 && request.status == 200) {
+ params.$Cache[queryParams] = request.response;
+ callback(request.response);
+ }
+ };
+ return request;
+ };
+ AutoComplete.prototype.ajax = function (params, request, timeout) {
+ if (timeout === void 0) { timeout = true; }
+ if (params.$AjaxTimer) {
+ window.clearTimeout(params.$AjaxTimer);
+ }
+ if (timeout === true) {
+ params.$AjaxTimer = window.setTimeout(AutoComplete.prototype.ajax.bind(null, params, request, false), params.Delay);
+ }
+ else {
+ if (params.Request) {
+ params.Request.abort();
+ }
+ params.Request = request;
+ params.Request.send(params._QueryArg() + "=" + params._Pre());
+ }
+ };
+ AutoComplete.prototype.cache = function (params, callback) {
+ var response = params._Cache(params._Pre());
+ if (response === undefined) {
+ var request = AutoComplete.prototype.makeRequest(params, callback);
+ AutoComplete.prototype.ajax(params, request);
+ }
+ else {
+ callback(response);
+ }
+ };
+ AutoComplete.prototype.destroy = function (params) {
+ for (var event in params.$Listeners) {
+ params.Input.removeEventListener(event, params.$Listeners[event]);
+ }
+ params.DOMResults.parentNode.removeChild(params.DOMResults);
+ };
+ return AutoComplete;
+}());
+AutoComplete.merge = function () {
+ var merge = {}, tmp;
+ for (var i = 0; i < arguments.length; i++) {
+ for (tmp in arguments[i]) {
+ merge[tmp] = arguments[i][tmp];
+ }
+ }
+ return merge;
+};
+AutoComplete.defaults = {
+ Delay: 150,
+ EmptyMessage: "No result here",
+ Highlight: {
+ getRegex: function (value) {
+ return new RegExp(value, "ig");
+ },
+ transform: function (value) {
+ return "<strong>" + value + "</strong>";
+ }
+ },
+ HttpHeaders: {
+ "Content-type": "application/x-www-form-urlencoded"
+ },
+ Limit: 0,
+ MinChars: 0,
+ HttpMethod: "GET",
+ QueryArg: "q",
+ Url: null,
+ KeyboardMappings: {
+ "Enter": {
+ Conditions: [{
+ Is: 13,
+ Not: false
+ }],
+ Callback: function (event) {
+ if (this.DOMResults.getAttribute("class").indexOf("open") != -1) {
+ var liActive = this.DOMResults.querySelector("li.active");
+ if (liActive !== null) {
+ event.preventDefault();
+ this._Select(liActive);
+ this.DOMResults.setAttribute("class", "autocomplete");
+ }
+ }
+ },
+ Operator: ConditionOperator.AND,
+ Event: EventType.KEYDOWN
+ },
+ "KeyUpAndDown_down": {
+ Conditions: [{
+ Is: 38,
+ Not: false
+ },
+ {
+ Is: 40,
+ Not: false
+ }],
+ Callback: function (event) {
+ event.preventDefault();
+ },
+ Operator: ConditionOperator.OR,
+ Event: EventType.KEYDOWN
+ },
+ "KeyUpAndDown_up": {
+ Conditions: [{
+ Is: 38,
+ Not: false
+ },
+ {
+ Is: 40,
+ Not: false
+ }],
+ Callback: function (event) {
+ event.preventDefault();
+ 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");
+ if (active) {
+ var currentIndex = Array.prototype.indexOf.call(active.parentNode.children, active), position = currentIndex + (event.keyCode - 39), lisCount = this.DOMResults.getElementsByTagName("li").length;
+ if (position < 0) {
+ position = lisCount - 1;
+ }
+ else if (position >= lisCount) {
+ position = 0;
+ }
+ active.classList.remove("active");
+ active.parentElement.children.item(position).classList.add("active");
+ }
+ else if (last && event.keyCode == 38) {
+ last.classList.add("active");
+ }
+ else if (first) {
+ first.classList.add("active");
+ }
+ },
+ Operator: ConditionOperator.OR,
+ Event: EventType.KEYUP
+ },
+ "AlphaNum": {
+ Conditions: [{
+ Is: 13,
+ Not: true
+ }, {
+ From: 35,
+ To: 40,
+ Not: true
+ }],
+ Callback: function () {
+ var oldValue = this.Input.getAttribute("data-autocomplete-old-value"), currentValue = this._Pre();
+ if (currentValue !== "" && currentValue.length >= this._MinChars()) {
+ if (!oldValue || currentValue != oldValue) {
+ this.DOMResults.setAttribute("class", "autocomplete open");
+ }
+ AutoComplete.prototype.cache(this, function (response) {
+ this._Render(this._Post(response));
+ this._Open();
+ }.bind(this));
+ }
+ },
+ Operator: ConditionOperator.AND,
+ Event: EventType.KEYUP
+ }
+ },
+ DOMResults: null,
+ Request: null,
+ Input: null,
+ /**
+ * Return the message when no result returns
+ */
+ _EmptyMessage: function () {
+ var emptyMessage = "";
+ if (this.Input.hasAttribute("data-autocomplete-empty-message")) {
+ emptyMessage = this.Input.getAttribute("data-autocomplete-empty-message");
+ }
+ else if (this.EmptyMessage !== false) {
+ emptyMessage = this.EmptyMessage;
+ }
+ else {
+ emptyMessage = "";
+ }
+ return emptyMessage;
+ },
+ /**
+ * Returns the maximum number of results
+ */
+ _Limit: function () {
+ var limit = this.Input.getAttribute("data-autocomplete-limit");
+ if (isNaN(limit) || limit === null) {
+ return this.Limit;
+ }
+ return parseInt(limit, 10);
+ },
+ /**
+ * Returns the minimum number of characters entered before firing ajax
+ */
+ _MinChars: function () {
+ var minchars = this.Input.getAttribute("data-autocomplete-minchars");
+ if (isNaN(minchars) || minchars === null) {
+ return this.MinChars;
+ }
+ return parseInt(minchars, 10);
+ },
+ /**
+ * Apply transformation on labels response
+ */
+ _Highlight: function (label) {
+ return label.replace(this.Highlight.getRegex(this._Pre()), this.Highlight.transform);
+ },
+ /**
+ * Returns the HHTP method to use
+ */
+ _HttpMethod: function () {
+ if (this.Input.hasAttribute("data-autocomplete-method")) {
+ return this.Input.getAttribute("data-autocomplete-method");
+ }
+ return this.HttpMethod;
+ },
+ /**
+ * Returns the query param to use
+ */
+ _QueryArg: function () {
+ if (this.Input.hasAttribute("data-autocomplete-param-name")) {
+ return this.Input.getAttribute("data-autocomplete-param-name");
+ }
+ return this.QueryArg;
+ },
+ /**
+ * Returns the URL to use for AJAX request
+ */
+ _Url: function () {
+ if (this.Input.hasAttribute("data-autocomplete")) {
+ return this.Input.getAttribute("data-autocomplete");
+ }
+ return this.Url;
+ },
+ /**
+ * Manage the close
+ */
+ _Blur: function (now) {
+ if (now === true) {
+ this.DOMResults.setAttribute("class", "autocomplete");
+ this.Input.setAttribute("data-autocomplete-old-value", this.Input.value);
+ }
+ else {
+ var params = this;
+ setTimeout(function () {
+ params._Blur(true);
+ }, 150);
+ }
+ },
+ /**
+ * Manage the cache
+ */
+ _Cache: function (value) {
+ return this.$Cache[value];
+ },
+ /**
+ * Manage the open
+ */
+ _Focus: function () {
+ var oldValue = this.Input.getAttribute("data-autocomplete-old-value");
+ if ((!oldValue || this.Input.value != oldValue) && this._MinChars() <= this.Input.value.length) {
+ this.DOMResults.setAttribute("class", "autocomplete open");
+ }
+ },
+ /**
+ * Bind all results item if one result is opened
+ */
+ _Open: function () {
+ var params = this;
+ Array.prototype.forEach.call(this.DOMResults.getElementsByTagName("li"), function (li) {
+ if (li.getAttribute("class") != "locked") {
+ li.onclick = function (event) {
+ params._Select(li);
+ };
+ li.onmouseenter = function () {
+ var active = params.DOMResults.querySelector("li.active");
+ if (active !== li) {
+ if (active !== null) {
+ active.classList.remove("active");
+ }
+ li.classList.add("active");
+ }
+ };
+ }
+ });
+ },
+ /**
+ * Position the results HTML element
+ */
+ _Position: function () {
+ this.DOMResults.setAttribute("class", "autocomplete");
+ this.DOMResults.setAttribute("style", "top:" + (this.Input.offsetTop + this.Input.offsetHeight) + "px;left:" + this.Input.offsetLeft + "px;width:" + this.Input.clientWidth + "px;");
+ },
+ /**
+ * Execute the render of results DOM element
+ */
+ _Render: function (response) {
+ var ul;
+ if (typeof response == "string") {
+ ul = this._RenderRaw(response);
+ }
+ else {
+ ul = this._RenderResponseItems(response);
+ }
+ if (this.DOMResults.hasChildNodes()) {
+ this.DOMResults.removeChild(this.DOMResults.childNodes[0]);
+ }
+ this.DOMResults.appendChild(ul);
+ },
+ /**
+ * ResponseItems[] rendering
+ */
+ _RenderResponseItems: function (response) {
+ var ul = document.createElement("ul"), li = document.createElement("li"), limit = this._Limit();
+ // Order
+ if (limit < 0) {
+ response = response.reverse();
+ }
+ else if (limit === 0) {
+ limit = response.length;
+ }
+ for (var item = 0; item < Math.min(Math.abs(limit), response.length); item++) {
+ li.innerHTML = response[item].Label;
+ li.setAttribute("data-autocomplete-value", response[item].Value);
+ ul.appendChild(li);
+ li = document.createElement("li");
+ }
+ return ul;
+ },
+ /**
+ * string response rendering (RAW HTML)
+ */
+ _RenderRaw: function (response) {
+ var ul = document.createElement("ul"), li = document.createElement("li");
+ if (response.length > 0) {
+ this.DOMResults.innerHTML = response;
+ }
+ else {
+ var emptyMessage = this._EmptyMessage();
+ if (emptyMessage !== "") {
+ li.innerHTML = emptyMessage;
+ li.setAttribute("class", "locked");
+ ul.appendChild(li);
+ }
+ }
+ return ul;
+ },
+ /**
+ * Deal with request response
+ */
+ _Post: function (response) {
+ try {
+ var returnResponse = [];
+ //JSON return
+ var json = JSON.parse(response);
+ if (Object.keys(json).length === 0) {
+ return "";
+ }
+ if (Array.isArray(json)) {
+ for (var i = 0; i < Object.keys(json).length; i++) {
+ returnResponse[returnResponse.length] = { "Value": json[i], "Label": this._Highlight(json[i]) };
+ }
+ }
+ else {
+ for (var value in json) {
+ returnResponse.push({
+ "Value": value,
+ "Label": this._Highlight(json[value])
+ });
+ }
+ }
+ return returnResponse;
+ }
+ catch (event) {
+ //HTML return
+ return response;
+ }
+ },
+ /**
+ * Return the autocomplete value to send (before request)
+ */
+ _Pre: function () {
+ return this.Input.value;
+ },
+ /**
+ * Choice one result item
+ */
+ _Select: function (item) {
+ console.log('test test test');
+ if (item.hasAttribute("data-autocomplete-value")) {
+ this.Input.value = item.getAttribute("data-autocomplete-value");
+ }
+ else {
+ this.Input.value = item.innerHTML;
+ }
+ this.Input.setAttribute("data-autocomplete-old-value", this.Input.value);
+ },
+ $AjaxTimer: null,
+ $Cache: {},
+ $Listeners: {}
+};
+module.exports = AutoComplete;
+
+},{}]},{},[1])(1)
+});
diff --git a/searx/static/themes/simple/js/searx_src/searx_imageresult.js b/searx/static/themes/simple/js/searx_src/searx_imageresult.js
new file mode 100644
index 000000000..7bbfc1454
--- /dev/null
+++ b/searx/static/themes/simple/js/searx_src/searx_imageresult.js
@@ -0,0 +1,151 @@
+/**
+*
+* Google Image Layout v0.0.1
+* Description, by Anh Trinh.
+* Heavily modified for searx
+* http://trinhtrunganh.com
+*
+* @license Free to use under the MIT License.
+*
+*/
+(function(w, d) {
+ 'use strict';
+
+ function ImageLayout(container_selector, results_selector, img_selector, maxHeight) {
+ this.container_selector = container_selector;
+ this.results_selector = results_selector;
+ this.img_selector = img_selector;
+ this.margin = 10;
+ this.maxHeight = maxHeight;
+ this._alignAllDone = true;
+ }
+
+ /**
+ * Get the height that make all images fit the container
+ *
+ * width = w1 + w2 + w3 + ... = r1*h + r2*h + r3*h + ...
+ *
+ * @param {[type]} images the images to be calculated
+ * @param {[type]} width the container witdth
+ * @param {[type]} margin the margin between each image
+ *
+ * @return {[type]} the height
+ */
+ ImageLayout.prototype._getHeigth = function(images, width) {
+ var r = 0,
+ img;
+
+ width -= images.length * this.margin;
+ for (var i = 0; i < images.length; i++) {
+ img = images[i];
+ if ((img.naturalWidth > 0) && (img.naturalHeight > 0)) {
+ r += img.naturalWidth / img.naturalHeight;
+ } else {
+ // assume that not loaded images are square
+ r += 1;
+ }
+ }
+
+ return width / r; //have to round down because Firefox will automatically roundup value with number of decimals > 3
+ };
+
+ ImageLayout.prototype._setSize = function(images, height) {
+ var img, imgWidth, imagesLength = images.length;
+ for (var i = 0; i < imagesLength; i++) {
+ img = images[i];
+ if ((img.naturalWidth > 0) && (img.naturalHeight > 0)) {
+ imgWidth = height * img.naturalWidth / img.naturalHeight;
+ } else {
+ // not loaded image : make it square as _getHeigth said it
+ imgWidth = height;
+ }
+ img.style.width = imgWidth + 'px';
+ img.style.height = height + 'px';
+ img.style.marginLeft = '3px';
+ img.style.marginTop = '3px';
+ img.style.marginRight = this.margin - 7 + 'px'; // -4 is the negative margin of the inline element
+ img.style.marginBottom = this.margin - 7 + 'px';
+ }
+ };
+
+ ImageLayout.prototype._alignImgs = function(imgGroup) {
+ var slice, h,
+ containerWidth = d.querySelector(this.container_selector).clientWidth;
+
+ w: while (imgGroup.length > 0) {
+ for (var i = 1; i <= imgGroup.length; i++) {
+ slice = imgGroup.slice(0, i);
+ h = this._getHeigth(slice, containerWidth);
+ if (h < this.maxHeight) {
+ this._setSize(slice, h);
+ imgGroup = imgGroup.slice(i);
+ continue w;
+ }
+ }
+ this._setSize(slice, Math.min(this.maxHeight, h));
+ break;
+ }
+ };
+
+ ImageLayout.prototype.align = function(results_selector) {
+ var results_selectorNode = d.querySelectorAll(this.results_selector),
+ results_length = results_selectorNode.length,
+ previous = null,
+ current = null,
+ imgGroup = [];
+ for (var i = 0; i < results_length; i++) {
+ current = results_selectorNode[i];
+ if (current.previousElementSibling !== previous && imgGroup.length > 0) {
+ // the current image is not conected to previous one
+ // so the current image is the start of a new group of images.
+ // so call _alignImgs to align the current group
+ this._alignImgs(imgGroup);
+ // and start a new empty group of images
+ imgGroup = [];
+ }
+ // add the current image to the group (only the img tag)
+ imgGroup.push(current.querySelector(this.img_selector));
+ // update the previous variable
+ previous = current;
+ }
+ // align the remaining images
+ if (imgGroup.length > 0) {
+ this._alignImgs(imgGroup);
+ }
+ };
+
+ ImageLayout.prototype.watch = function() {
+ var i, img, imgGroup, imgNodeLength,
+ obj = this,
+ results_nodes = d.querySelectorAll(this.results_selector),
+ results_length = results_nodes.length;
+
+ function align(e) {
+ obj.align();
+ }
+
+ function throttleAlign(e) {
+ if (obj._alignAllDone) {
+ obj._alignAllDone = false;
+ setTimeout(function() {
+ obj.align();
+ obj._alignAllDone = true;
+ }, 100);
+ }
+ }
+
+ w.addEventListener('resize', throttleAlign);
+ w.addEventListener('pageshow', align);
+
+ for (i = 0; i < results_length; i++) {
+ img = results_nodes[i].querySelector(this.img_selector);
+ if (typeof img !== 'undefined') {
+ img.addEventListener('load', throttleAlign);
+ img.addEventListener('error', throttleAlign);
+ }
+ }
+ };
+
+ w.searx.ImageLayout = ImageLayout;
+
+})(window, document);
diff --git a/searx/static/themes/simple/js/searx_src/searx_keyboard.js b/searx/static/themes/simple/js/searx_src/searx_keyboard.js
new file mode 100644
index 000000000..6365b5243
--- /dev/null
+++ b/searx/static/themes/simple/js/searx_src/searx_keyboard.js
@@ -0,0 +1,360 @@
+searx.ready(function() {
+
+ searx.on('.result', 'click', function() {
+ highlightResult(this)(true);
+ });
+
+ searx.on('.result a', 'focus', function(e) {
+ var el = e.target;
+ while (el !== undefined) {
+ if (el.classList.contains('result')) {
+ if (el.getAttribute("data-vim-selected") === null) {
+ highlightResult(el)(true);
+ }
+ break;
+ }
+ el = el.parentNode;
+ }
+ }, true);
+
+ var vimKeys = {
+ 27: {
+ key: 'Escape',
+ fun: removeFocus,
+ des: 'remove focus from the focused input',
+ cat: 'Control'
+ },
+ 73: {
+ key: 'i',
+ fun: searchInputFocus,
+ des: 'focus on the search input',
+ cat: 'Control'
+ },
+ 66: {
+ key: 'b',
+ fun: scrollPage(-window.innerHeight),
+ des: 'scroll one page up',
+ cat: 'Navigation'
+ },
+ 70: {
+ key: 'f',
+ fun: scrollPage(window.innerHeight),
+ des: 'scroll one page down',
+ cat: 'Navigation'
+ },
+ 85: {
+ key: 'u',
+ fun: scrollPage(-window.innerHeight / 2),
+ des: 'scroll half a page up',
+ cat: 'Navigation'
+ },
+ 68: {
+ key: 'd',
+ fun: scrollPage(window.innerHeight / 2),
+ des: 'scroll half a page down',
+ cat: 'Navigation'
+ },
+ 71: {
+ key: 'g',
+ fun: scrollPageTo(-document.body.scrollHeight, 'top'),
+ des: 'scroll to the top of the page',
+ cat: 'Navigation'
+ },
+ 86: {
+ key: 'v',
+ fun: scrollPageTo(document.body.scrollHeight, 'bottom'),
+ des: 'scroll to the bottom of the page',
+ cat: 'Navigation'
+ },
+ 75: {
+ key: 'k',
+ fun: highlightResult('up'),
+ des: 'select previous search result',
+ cat: 'Results'
+ },
+ 74: {
+ key: 'j',
+ fun: highlightResult('down'),
+ des: 'select next search result',
+ cat: 'Results'
+ },
+ 80: {
+ key: 'p',
+ fun: pageButtonClick(0),
+ des: 'go to previous page',
+ cat: 'Results'
+ },
+ 78: {
+ key: 'n',
+ fun: pageButtonClick(1),
+ des: 'go to next page',
+ cat: 'Results'
+ },
+ 79: {
+ key: 'o',
+ fun: openResult(false),
+ des: 'open search result',
+ cat: 'Results'
+ },
+ 84: {
+ key: 't',
+ fun: openResult(true),
+ des: 'open the result in a new tab',
+ cat: 'Results'
+ },
+ 82: {
+ key: 'r',
+ fun: reloadPage,
+ des: 'reload page from the server',
+ cat: 'Control'
+ },
+ 72: {
+ key: 'h',
+ fun: toggleHelp,
+ des: 'toggle help window',
+ cat: 'Other'
+ }
+ };
+
+ searx.on(document, "keyup", function(e) {
+ // check for modifiers so we don't break browser's hotkeys
+ if (vimKeys.hasOwnProperty(e.keyCode) && !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey) {
+ var tagName = e.target.tagName.toLowerCase();
+ if (e.keyCode === 27) {
+ if (tagName === 'input' || tagName === 'select' || tagName === 'textarea') {
+ vimKeys[e.keyCode].fun();
+ }
+ } else {
+ if (e.target === document.body || tagName === 'a' || tagName === 'button') {
+ vimKeys[e.keyCode].fun();
+ }
+ }
+ }
+ });
+
+ function highlightResult(which) {
+ return function(noScroll) {
+ var current = document.querySelector('.result[data-vim-selected]'),
+ effectiveWhich = which;
+ if (current === null) {
+ // no selection : choose the first one
+ current = document.querySelector('.result');
+ if (current === null) {
+ // no first one : there are no results
+ return;
+ }
+ // replace up/down actions by selecting first one
+ if (which === "down" || which === "up") {
+ effectiveWhich = current;
+ }
+ }
+
+ var next, results = document.querySelectorAll('.result');
+
+ if (typeof effectiveWhich !== 'string') {
+ next = effectiveWhich;
+ } else {
+ switch (effectiveWhich) {
+ case 'visible':
+ var top = document.documentElement.scrollTop || document.body.scrollTop;
+ var bot = top + document.documentElement.clientHeight;
+
+ for (var i = 0; i < results.length; i++) {
+ next = results[i];
+ var etop = next.offsetTop;
+ var ebot = etop + next.clientHeight;
+
+ if ((ebot <= bot) && (etop > top)) {
+ break;
+ }
+ }
+ break;
+ case 'down':
+ next = current.nextElementSibling;
+ if (next === null) {
+ next = results[0];
+ }
+ break;
+ case 'up':
+ next = current.previousElementSibling;
+ if (next === null) {
+ next = results[results.length - 1];
+ }
+ break;
+ case 'bottom':
+ next = results[results.length - 1];
+ break;
+ case 'top':
+ /* falls through */
+ default:
+ next = results[0];
+ }
+ }
+
+ if (next) {
+ current.removeAttribute('data-vim-selected');
+ next.setAttribute('data-vim-selected', 'true');
+ var link = next.querySelector('h3 a') || next.querySelector('a');
+ if (link !== null) {
+ link.focus();
+ }
+ if (!noScroll) {
+ scrollPageToSelected();
+ }
+ }
+ };
+ }
+
+ function reloadPage() {
+ document.location.reload(true);
+ }
+
+ function removeFocus() {
+ if (document.activeElement) {
+ document.activeElement.blur();
+ }
+ }
+
+ function pageButtonClick(num) {
+ return function() {
+ var buttons = $('div#pagination button[type="submit"]');
+ if (buttons.length !== 2) {
+ console.log('page navigation with this theme is not supported');
+ return;
+ }
+ if (num >= 0 && num < buttons.length) {
+ buttons[num].click();
+ } else {
+ console.log('pageButtonClick(): invalid argument');
+ }
+ };
+ }
+
+ function scrollPageToSelected() {
+ var sel = document.querySelector('.result[data-vim-selected]');
+ if (sel === null) {
+ return;
+ }
+ var wtop = document.documentElement.scrollTop || document.body.scrollTop,
+ wheight = document.documentElement.clientHeight,
+ etop = sel.offsetTop,
+ ebot = etop + sel.clientHeight,
+ offset = 120;
+ // first element ?
+ if ((sel.previousElementSibling === null) && (ebot < wheight)) {
+ // set to the top of page if the first element
+ // is fully included in the viewport
+ window.scroll(window.scrollX, 0);
+ return;
+ }
+ if (wtop > (etop - offset)) {
+ window.scroll(window.scrollX, etop - offset);
+ } else {
+ var wbot = wtop + wheight;
+ if (wbot < (ebot + offset)) {
+ window.scroll(window.scrollX, ebot - wheight + offset);
+ }
+ }
+ }
+
+ function scrollPage(amount) {
+ return function() {
+ window.scrollBy(0, amount);
+ highlightResult('visible')();
+ };
+ }
+
+ function scrollPageTo(position, nav) {
+ return function() {
+ window.scrollTo(0, position);
+ highlightResult(nav)();
+ };
+ }
+
+ function searchInputFocus() {
+ window.scrollTo(0, 0);
+ document.querySelector('#q').focus();
+ }
+
+ function openResult(newTab) {
+ return function() {
+ var link = document.querySelector('.result[data-vim-selected] h3 a');
+ if (link !== null) {
+ var url = link.getAttribute('href');
+ if (newTab) {
+ window.open(url);
+ } else {
+ window.location.href = url;
+ }
+ }
+ };
+ }
+
+ function toggleHelp() {
+ var helpPanel = document.querySelector('#vim-hotkeys-help');
+ if (helpPanel.length) {
+ helpPanel.classList.toggle('hidden');
+ return;
+ }
+
+ var categories = {};
+
+ for (var k in vimKeys) {
+ var key = vimKeys[k];
+ categories[key.cat] = categories[key.cat] || [];
+ categories[key.cat].push(key);
+ }
+
+ var sorted = Object.keys(categories).sort(function(a, b) {
+ return categories[b].length - categories[a].length;
+ });
+
+ if (sorted.length === 0) {
+ return;
+ }
+
+ var html = '<div id="vim-hotkeys-help" class="well vim-hotkeys-help">';
+ html += '<div class="container-fluid">';
+
+ html += '<div class="row">';
+ html += '<div class="col-sm-12">';
+ html += '<h3>How to navigate searx with Vim-like hotkeys</h3>';
+ html += '</div>'; // col-sm-12
+ html += '</div>'; // row
+
+ for (var i = 0; i < sorted.length; i++) {
+ var cat = categories[sorted[i]];
+
+ var lastCategory = i === (sorted.length - 1);
+ var first = i % 2 === 0;
+
+ if (first) {
+ html += '<div class="row dflex">';
+ }
+ html += '<div class="col-sm-' + (first && lastCategory ? 12 : 6) + ' dflex">';
+
+ html += '<div class="panel panel-default iflex">';
+ html += '<div class="panel-heading">' + cat[0].cat + '</div>';
+ html += '<div class="panel-body">';
+ html += '<ul class="list-unstyled">';
+
+ for (var cj in cat) {
+ html += '<li><kbd>' + cat[cj].key + '</kbd> ' + cat[cj].des + '</li>';
+ }
+
+ html += '</ul>';
+ html += '</div>'; // panel-body
+ html += '</div>'; // panel
+ html += '</div>'; // col-sm-*
+
+ if (!first || lastCategory) {
+ html += '</div>'; // row
+ }
+ }
+
+ html += '</div>'; // container-fluid
+ html += '</div>'; // vim-hotkeys-help
+
+ $('body').append(html);
+ }
+});
diff --git a/searx/static/themes/simple/js/searx_src/searx_mapresult.js b/searx/static/themes/simple/js/searx_src/searx_mapresult.js
new file mode 100644
index 000000000..823f64815
--- /dev/null
+++ b/searx/static/themes/simple/js/searx_src/searx_mapresult.js
@@ -0,0 +1,175 @@
+/**
+* searx is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* searx is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with searx. If not, see < http://www.gnu.org/licenses/ >.
+*
+* (C) 2014 by Thomas Pointhuber, <thomas.pointhuber@gmx.at>
+* (C) 2017 by Alexandre Flament, <alex@al-f.net>
+*/
+(function (w, d, searx) {
+ 'use strict';
+
+ searx.ready(function () {
+ searx.on('.searx_overpass_request', 'click', function(event) {
+ // no more request
+ this.classList.remove("searx_overpass_request");
+
+ //
+ var overpass_url = "https://overpass-api.de/api/interpreter?data=";
+ var query_start = overpass_url + "[out:json][timeout:25];(";
+ var query_end = ");out meta;";
+
+ var osm_id = this.dataset.osmId;
+ var osm_type = this.dataset.osmType;
+ var result_table = d.querySelector("#" + this.dataset.resultTable);
+ var result_table_loadicon = d.querySelector("#" + this.dataset.resultTableLoadicon);
+
+ // tags which can be ignored
+ var osm_ignore_tags = [ "addr:city", "addr:country", "addr:housenumber", "addr:postcode", "addr:street" ];
+
+ if(osm_id && osm_type && result_table) {
+ var query = null;
+ switch(osm_type) {
+ case 'node':
+ query = query_start + "node(" + osm_id + ");" + query_end;
+ break;
+ case 'way':
+ query = query_start + "way(" + osm_id + ");" + query_end;
+ break;
+ case 'relation':
+ query = query_start + "relation(" + osm_id + ");" + query_end;
+ break;
+ default:
+ break;
+ }
+ if(query) {
+ // console.log(query);
+ searx.http( 'GET', query ).then(function(html, contentType) {
+ html = JSON.parse(html);
+ if(html && html.elements && html.elements[0]) {
+ var element = html.elements[0];
+ var newHtml = "";
+ for (var row in element.tags) {
+ if(element.tags.name === null || osm_ignore_tags.indexOf(row) == -1) {
+ newHtml += "<tr><td>" + row + "</td><td>";
+ switch(row) {
+ case "phone":
+ case "fax":
+ newHtml += "<a href=\"tel:" + element.tags[row].replace(/ /g,'') + "\">" + element.tags[row] + "</a>";
+ break;
+ case "email":
+ newHtml += "<a href=\"mailto:" + element.tags[row] + "\">" + element.tags[row] + "</a>";
+ break;
+ case "website":
+ case "url":
+ newHtml += "<a href=\"" + element.tags[row] + "\">" + element.tags[row] + "</a>";
+ break;
+ case "wikidata":
+ newHtml += "<a href=\"https://www.wikidata.org/wiki/" + element.tags[row] + "\">" + element.tags[row] + "</a>";
+ break;
+ case "wikipedia":
+ if(element.tags[row].indexOf(":") != -1) {
+ 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>";
+ break;
+ }
+ /* jshint ignore:start */
+ default:
+ /* jshint ignore:end */
+ newHtml += element.tags[row];
+ break;
+ }
+ newHtml += "</td></tr>";
+ }
+ }
+ result_table_loadicon.classList.add('invisible');
+ result_table.classList.remove('invisible');
+ result_table.querySelector("tbody").innerHTML = newHtml;
+ }
+ })
+ .catch(function() {
+ result_table_loadicon.innerHTML = result_table_loadicon.innerHTML + "<p class=\"text-muted\">could not load data!</p>";
+ });
+ }
+ }
+
+ // this event occour only once per element
+ event.preventDefault();
+ });
+
+ searx.on('.searx_init_map', 'click', function(event) {
+ // no more request
+ this.classList.remove("searx_init_map");
+
+ //
+ var leaflet_target = this.dataset.leafletTarget;
+ var map_lon = parseFloat(this.dataset.mapLon);
+ var map_lat = parseFloat(this.dataset.mapLat);
+ var map_zoom = parseFloat(this.dataset.mapZoom);
+ var map_boundingbox = JSON.parse(this.dataset.mapBoundingbox);
+ var map_geojson = JSON.parse(this.dataset.mapGeojson);
+
+ searx.loadStyle('leaflet/leaflet.css');
+ searx.loadScript('leaflet/leaflet.js', function() {
+ var map_bounds = null;
+ if(map_boundingbox) {
+ var southWest = L.latLng(map_boundingbox[0], map_boundingbox[2]);
+ var northEast = L.latLng(map_boundingbox[1], map_boundingbox[3]);
+ map_bounds = L.latLngBounds(southWest, northEast);
+ }
+
+ // init map
+ var map = L.map(leaflet_target);
+ // create the tile layer with correct attribution
+ var osmMapnikUrl='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
+ var osmMapnikAttrib='Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';
+ var osmMapnik = new L.TileLayer(osmMapnikUrl, {minZoom: 1, maxZoom: 19, attribution: osmMapnikAttrib});
+ var osmWikimediaUrl='https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png';
+ var osmWikimediaAttrib = 'Wikimedia maps beta | Maps data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';
+ var osmWikimedia = new L.TileLayer(osmWikimediaUrl, {minZoom: 1, maxZoom: 19, attribution: osmWikimediaAttrib});
+ // init map view
+ if(map_bounds) {
+ // TODO hack: https://github.com/Leaflet/Leaflet/issues/2021
+ // Still useful ?
+ setTimeout(function () {
+ map.fitBounds(map_bounds, {
+ maxZoom:17
+ });
+ }, 0);
+ } else if (map_lon && map_lat) {
+ if(map_zoom) {
+ map.setView(new L.latLng(map_lat, map_lon),map_zoom);
+ } else {
+ map.setView(new L.latLng(map_lat, map_lon),8);
+ }
+ }
+
+ map.addLayer(osmMapnik);
+
+ var baseLayers = {
+ "OSM Mapnik": osmMapnik/*,
+ "OSM Wikimedia": osmWikimedia*/
+ };
+
+ L.control.layers(baseLayers).addTo(map);
+
+ if(map_geojson) {
+ L.geoJson(map_geojson).addTo(map);
+ } /*else if(map_bounds) {
+ L.rectangle(map_bounds, {color: "#ff7800", weight: 3, fill:false}).addTo(map);
+ }*/
+ });
+
+ // this event occour only once per element
+ event.preventDefault();
+ });
+ });
+})(window, document, window.searx);
diff --git a/searx/static/themes/simple/js/searx_src/searx_results.js b/searx/static/themes/simple/js/searx_src/searx_results.js
new file mode 100644
index 000000000..b13da8391
--- /dev/null
+++ b/searx/static/themes/simple/js/searx_src/searx_results.js
@@ -0,0 +1,63 @@
+/**
+* searx is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* searx is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with searx. If not, see < http://www.gnu.org/licenses/ >.
+*
+* (C) 2017 by Alexandre Flament, <alex@al-f.net>
+*/
+(function(w, d, searx) {
+ 'use strict';
+
+ searx.ready(function() {
+ searx.image_thumbnail_layout = new searx.ImageLayout('#urls', '#urls .result-images', 'img.image_thumbnail', 200);
+ searx.image_thumbnail_layout.watch();
+
+ searx.on('.btn-collapse', 'click', function(event) {
+ var btnLabelCollapsed = this.getAttribute('data-btn-text-collapsed');
+ var btnLabelNotCollapsed = this.getAttribute('data-btn-text-not-collapsed');
+ var target = this.getAttribute('data-target');
+ var targetElement = d.querySelector(target);
+ var html = this.innerHTML;
+ if (this.classList.contains('collapsed')) {
+ html = html.replace(btnLabelCollapsed, btnLabelNotCollapsed);
+ } else {
+ html = html.replace(btnLabelNotCollapsed, btnLabelCollapsed);
+ }
+ this.innerHTML = html;
+ this.classList.toggle('collapsed');
+ targetElement.classList.toggle('invisible');
+ });
+
+ searx.on('.media-loader', 'click', function(event) {
+ var target = this.getAttribute('data-target');
+ var iframe_load = d.querySelector(target + ' > iframe');
+ var srctest = iframe_load.getAttribute('src');
+ if (srctest === null || srctest === undefined || srctest === false) {
+ iframe_load.setAttribute('src', iframe_load.getAttribute('data-src'));
+ }
+ });
+
+ w.addEventListener('scroll', function() {
+ var e = d.getElementById('backToTop'),
+ scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
+ if (e !== null) {
+ if (scrollTop >= 200) {
+ e.style.opacity = 1;
+ } else {
+ e.style.opacity = 0;
+ }
+ }
+ });
+
+ });
+
+})(window, document, window.searx);
diff --git a/searx/static/themes/simple/js/searx_src/searx_search.js b/searx/static/themes/simple/js/searx_src/searx_search.js
new file mode 100644
index 000000000..1b93f9039
--- /dev/null
+++ b/searx/static/themes/simple/js/searx_src/searx_search.js
@@ -0,0 +1,94 @@
+/**
+* searx is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* searx is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with searx. If not, see < http://www.gnu.org/licenses/ >.
+*
+* (C) 2017 by Alexandre Flament, <alex@al-f.net>
+*/
+(function(w, d, searx) {
+ 'use strict';
+
+ var firstFocus = true, qinput_id = "q", qinput;
+
+ function placeCursorAtEnd(element) {
+ if (element.setSelectionRange) {
+ var len = element.value.length;
+ element.setSelectionRange(len, len);
+ }
+ }
+
+ function submitIfQuery() {
+ if (qinput.value.length > 0) {
+ var search = document.getElementById('search');
+ setTimeout(search.submit.bind(search), 0);
+ }
+ }
+
+ searx.ready(function() {
+ qinput = d.getElementById(qinput_id);
+
+ function placeCursorAtEndOnce(e) {
+ if (firstFocus) {
+ placeCursorAtEnd(qinput);
+ firstFocus = false;
+ } else {
+ // e.preventDefault();
+ }
+ }
+
+ if (qinput !== null) {
+ // autocompleter
+ if (searx.autocompleter) {
+ searx.autocomplete = AutoComplete.call(w, {
+ Url: "./autocompleter",
+ EmptyMessage: searx.noItemFound,
+ HttpMethod: searx.method,
+ MinChars: 4,
+ Delay: 300,
+ }, "#" + qinput_id);
+
+ // hack, see : https://github.com/autocompletejs/autocomplete.js/issues/37
+ w.addEventListener('resize', function() {
+ var event = new CustomEvent("position");
+ qinput.dispatchEvent(event);
+ });
+ }
+
+ qinput.addEventListener('focus', placeCursorAtEndOnce, false);
+ qinput.focus();
+ }
+
+ // vanilla js version of search_on_category_select.js
+ if (qinput !== null && searx.search_on_category_select) {
+ d.querySelector('.help').className='invisible';
+
+ searx.on('#categories input', 'change', function(e) {
+ var i, categories = d.querySelectorAll('#categories input[type="checkbox"]');
+ for(i=0; i<categories.length; i++) {
+ if (categories[i] !== this && categories[i].checked) {
+ categories[i].click();
+ }
+ }
+ if (! this.checked) {
+ this.click();
+ }
+ submitIfQuery();
+ return false;
+ });
+
+ searx.on(d.getElementById('time_range'), 'change', submitIfQuery);
+ searx.on(d.getElementById('language'), 'change', submitIfQuery);
+ }
+
+ });
+
+})(window, document, window.searx);