From 1f21305b7c31326c5533ae235ed9f5e6225031f3 Mon Sep 17 00:00:00 2001
From: bastinc
Date: Thu, 20 Oct 2016 15:37:56 +0200
Subject: [PATCH] Update upstream sources to 0.10.0
---
sources/.gitignore | 1 +
sources/.travis.yml | 3 +-
sources/AUTHORS.rst | 13 +-
sources/CHANGELOG.rst | 36 ++
sources/README.rst | 2 +-
sources/examples/basic_engine.py | 4 +-
sources/manage.sh | 5 +-
sources/requirements-dev.txt | 6 +-
sources/requirements.txt | 16 +-
sources/searx/engines/__init__.py | 14 +-
sources/searx/engines/archlinux.py | 2 +
sources/searx/engines/btdigg.py | 16 +-
sources/searx/engines/currency_convert.py | 2 +-
sources/searx/engines/deviantart.py | 20 +-
sources/searx/engines/dictzone.py | 69 ++
sources/searx/engines/digbt.py | 58 ++
sources/searx/engines/duckduckgo.py | 37 +-
sources/searx/engines/filecrop.py | 1 +
sources/searx/engines/google.py | 12 +-
sources/searx/engines/google_images.py | 38 +-
sources/searx/engines/ina.py | 83 +++
sources/searx/engines/json_engine.py | 42 +-
sources/searx/engines/scanr_structures.py | 78 +++
sources/searx/engines/soundcloud.py | 1 +
sources/searx/engines/startpage.py | 10 +-
sources/searx/engines/swisscows.py | 8 +-
sources/searx/engines/tokyotoshokan.py | 2 +-
sources/searx/engines/translated.py | 65 ++
sources/searx/engines/wikidata.py | 592 +++++++++++-------
sources/searx/engines/wikipedia.py | 3 +-
sources/searx/engines/wolframalpha_noapi.py | 4 +-
sources/searx/engines/www500px.py | 2 +-
sources/searx/engines/xpath.py | 2 +-
sources/searx/engines/yahoo.py | 34 +-
sources/searx/engines/yahoo_news.py | 2 +-
sources/searx/plugins/__init__.py | 10 +-
sources/searx/plugins/doai_rewrite.py | 31 +
sources/searx/plugins/https_rewrite.py | 4 +-
sources/searx/plugins/infinite_scroll.py | 8 +
.../searx/plugins/open_results_on_new_tab.py | 2 +-
.../plugins/search_on_category_select.py | 2 +-
sources/searx/plugins/self_info.py | 4 +-
sources/searx/plugins/tracker_url_remover.py | 2 +-
sources/searx/plugins/vim_hotkeys.py | 2 +-
sources/searx/preferences.py | 5 +-
sources/searx/results.py | 74 ++-
sources/searx/search.py | 25 +-
sources/searx/settings.yml | 108 +++-
.../static/plugins/css/infinite_scroll.css | 16 +
.../static/plugins/js/infinite_scroll.js | 18 +
.../plugins/js/search_on_category_select.js | 9 +-
.../static/themes/oscar/css/logicodev.min.css | 2 +-
.../static/themes/oscar/css/pointhi.min.css | 2 +-
.../searx/static/themes/oscar/js/searx.min.js | 4 +-
.../themes/oscar/js/searx_src/leaflet_map.js | 25 +-
.../themes/oscar/less/logicodev/advanced.less | 72 +++
.../themes/oscar/less/logicodev/navbar.less | 2 +-
.../themes/oscar/less/logicodev/onoff.less | 57 ++
.../themes/oscar/less/logicodev/oscar.less | 4 +
.../themes/oscar/less/logicodev/results.less | 25 +-
.../themes/oscar/less/logicodev/search.less | 49 +-
.../oscar/less/logicodev/variables.less | 4 +-
.../themes/oscar/less/pointhi/advanced.less | 49 ++
.../themes/oscar/less/pointhi/onoff.less | 57 ++
.../themes/oscar/less/pointhi/oscar.less | 4 +
.../themes/oscar/less/pointhi/results.less | 22 +-
.../themes/oscar/less/pointhi/search.less | 34 +-
sources/searx/templates/courgette/404.html | 9 +
sources/searx/templates/courgette/about.html | 12 +-
sources/searx/templates/courgette/base.html | 2 +-
.../courgette/result_templates/code.html | 4 +-
.../courgette/result_templates/default.html | 2 +-
.../courgette/result_templates/images.html | 4 +-
.../courgette/result_templates/map.html | 2 +-
.../courgette/result_templates/torrent.html | 4 +-
.../courgette/result_templates/videos.html | 4 +-
sources/searx/templates/default/404.html | 9 +
sources/searx/templates/default/about.html | 12 +-
sources/searx/templates/default/base.html | 2 +-
sources/searx/templates/default/infobox.html | 14 +-
.../searx/templates/default/preferences.html | 9 +
.../default/result_templates/code.html | 6 +-
.../default/result_templates/default.html | 4 +-
.../default/result_templates/images.html | 4 +-
.../default/result_templates/map.html | 4 +-
.../default/result_templates/torrent.html | 4 +-
.../default/result_templates/videos.html | 4 +-
sources/searx/templates/oscar/404.html | 9 +
sources/searx/templates/oscar/about.html | 14 +-
sources/searx/templates/oscar/advanced.html | 9 +
sources/searx/templates/oscar/base.html | 5 +-
sources/searx/templates/oscar/categories.html | 48 +-
sources/searx/templates/oscar/infobox.html | 15 +-
sources/searx/templates/oscar/macros.html | 26 +-
.../searx/templates/oscar/preferences.html | 21 +-
.../oscar/result_templates/code.html | 2 +-
.../oscar/result_templates/default.html | 2 +-
.../oscar/result_templates/images.html | 14 +-
.../oscar/result_templates/videos.html | 2 +-
sources/searx/templates/oscar/results.html | 9 +-
sources/searx/templates/oscar/search.html | 6 +-
.../searx/templates/oscar/search_full.html | 9 +-
sources/searx/templates/oscar/time-range.html | 14 +
sources/searx/templates/pix-art/404.html | 9 +
sources/searx/templates/pix-art/about.html | 12 +-
sources/searx/templates/pix-art/base.html | 2 +-
sources/searx/templates/pix-art/index.html | 2 +-
sources/searx/templates/pix-art/results.html | 2 +-
.../translations/bg/LC_MESSAGES/messages.mo | Bin 9643 -> 11184 bytes
.../translations/bg/LC_MESSAGES/messages.po | 263 +++++---
.../translations/de/LC_MESSAGES/messages.mo | Bin 8110 -> 9872 bytes
.../translations/de/LC_MESSAGES/messages.po | 244 +++++---
.../el_GR/LC_MESSAGES/messages.mo | Bin 8297 -> 9826 bytes
.../el_GR/LC_MESSAGES/messages.po | 246 +++++---
.../translations/en/LC_MESSAGES/messages.mo | Bin 7565 -> 7565 bytes
.../translations/eo/LC_MESSAGES/messages.mo | Bin 7653 -> 9259 bytes
.../translations/eo/LC_MESSAGES/messages.po | 268 +++++---
.../translations/es/LC_MESSAGES/messages.mo | Bin 8193 -> 9916 bytes
.../translations/es/LC_MESSAGES/messages.po | 260 +++++---
.../translations/fr/LC_MESSAGES/messages.mo | Bin 8328 -> 10168 bytes
.../translations/fr/LC_MESSAGES/messages.po | 246 +++++---
.../translations/he/LC_MESSAGES/messages.mo | Bin 8728 -> 10743 bytes
.../translations/he/LC_MESSAGES/messages.po | 270 +++++---
.../translations/hu/LC_MESSAGES/messages.mo | Bin 7878 -> 9624 bytes
.../translations/hu/LC_MESSAGES/messages.po | 251 +++++---
.../translations/it/LC_MESSAGES/messages.mo | Bin 7751 -> 9344 bytes
.../translations/it/LC_MESSAGES/messages.po | 246 +++++---
.../translations/ja/LC_MESSAGES/messages.mo | Bin 9052 -> 10784 bytes
.../translations/ja/LC_MESSAGES/messages.po | 247 +++++---
.../translations/nl/LC_MESSAGES/messages.mo | Bin 7918 -> 9592 bytes
.../translations/nl/LC_MESSAGES/messages.po | 246 +++++---
.../translations/pt/LC_MESSAGES/messages.mo | Bin 7931 -> 7931 bytes
.../pt_BR/LC_MESSAGES/messages.mo | Bin 8064 -> 9647 bytes
.../pt_BR/LC_MESSAGES/messages.po | 246 +++++---
.../translations/ro/LC_MESSAGES/messages.mo | Bin 8156 -> 9734 bytes
.../translations/ro/LC_MESSAGES/messages.po | 246 +++++---
.../translations/ru/LC_MESSAGES/messages.mo | Bin 9537 -> 11450 bytes
.../translations/ru/LC_MESSAGES/messages.po | 299 ++++++---
.../translations/tr/LC_MESSAGES/messages.mo | Bin 8075 -> 9645 bytes
.../translations/tr/LC_MESSAGES/messages.po | 250 +++++---
.../zh_CN/LC_MESSAGES/messages.mo | Bin 7465 -> 8986 bytes
.../zh_CN/LC_MESSAGES/messages.po | 262 +++++---
sources/searx/utils.py | 60 +-
sources/searx/version.py | 2 +-
sources/searx/webapp.py | 52 +-
sources/tests/robot/test_basic.robot | 7 +-
.../tests/unit/engines/test_dailymotion.py | 2 +-
sources/tests/unit/engines/test_deezer.py | 4 +-
sources/tests/unit/engines/test_deviantart.py | 48 +-
sources/tests/unit/engines/test_digbt.py | 59 ++
sources/tests/unit/engines/test_duckduckgo.py | 9 +-
sources/tests/unit/engines/test_flickr.py | 10 +-
.../tests/unit/engines/test_flickr_noapi.py | 2 +-
sources/tests/unit/engines/test_google.py | 1 +
.../tests/unit/engines/test_google_images.py | 40 +-
sources/tests/unit/engines/test_ina.py | 64 ++
sources/tests/unit/engines/test_mediawiki.py | 2 +-
sources/tests/unit/engines/test_mixcloud.py | 2 +-
.../unit/engines/test_scanr_structures.py | 175 ++++++
.../unit/engines/test_searchcode_code.py | 2 +-
.../tests/unit/engines/test_searchcode_doc.py | 2 +-
sources/tests/unit/engines/test_wikidata.py | 504 +++++++++++++++
.../unit/engines/test_wolframalpha_noapi.py | 6 +-
sources/tests/unit/engines/test_yahoo.py | 1 +
sources/tests/unit/test_plugins.py | 24 +-
sources/tests/unit/test_preferences.py | 2 +
sources/tests/unit/test_webapp.py | 3 +-
sources/utils/fabfile.py | 2 +-
sources/utils/fetch_currencies.py | 76 +--
169 files changed, 5564 insertions(+), 2049 deletions(-)
create mode 100644 sources/searx/engines/dictzone.py
create mode 100644 sources/searx/engines/digbt.py
create mode 100644 sources/searx/engines/ina.py
create mode 100644 sources/searx/engines/scanr_structures.py
create mode 100644 sources/searx/engines/translated.py
create mode 100644 sources/searx/plugins/doai_rewrite.py
create mode 100644 sources/searx/plugins/infinite_scroll.py
create mode 100644 sources/searx/static/plugins/css/infinite_scroll.css
create mode 100644 sources/searx/static/plugins/js/infinite_scroll.js
create mode 100644 sources/searx/static/themes/oscar/less/logicodev/advanced.less
create mode 100644 sources/searx/static/themes/oscar/less/logicodev/onoff.less
create mode 100644 sources/searx/static/themes/oscar/less/pointhi/advanced.less
create mode 100644 sources/searx/static/themes/oscar/less/pointhi/onoff.less
create mode 100644 sources/searx/templates/courgette/404.html
create mode 100644 sources/searx/templates/default/404.html
create mode 100644 sources/searx/templates/oscar/404.html
create mode 100644 sources/searx/templates/oscar/advanced.html
create mode 100644 sources/searx/templates/oscar/time-range.html
create mode 100644 sources/searx/templates/pix-art/404.html
create mode 100644 sources/tests/unit/engines/test_digbt.py
create mode 100644 sources/tests/unit/engines/test_ina.py
create mode 100644 sources/tests/unit/engines/test_scanr_structures.py
create mode 100644 sources/tests/unit/engines/test_wikidata.py
diff --git a/sources/.gitignore b/sources/.gitignore
index 105f019..cd6d7e9 100644
--- a/sources/.gitignore
+++ b/sources/.gitignore
@@ -1,4 +1,5 @@
.coverage
+coverage/
.installed.cfg
engines.cfg
env
diff --git a/sources/.travis.yml b/sources/.travis.yml
index 3bef5e5..65f8ef2 100644
--- a/sources/.travis.yml
+++ b/sources/.travis.yml
@@ -16,11 +16,10 @@ install:
- ./manage.sh update_dev_packages
- pip install coveralls
script:
- - ./manage.sh pep8_check
- ./manage.sh styles
- ./manage.sh grunt_build
+ - ./manage.sh tests
- ./manage.sh py_test_coverage
- - ./manage.sh robot_tests
after_success:
coveralls
notifications:
diff --git a/sources/AUTHORS.rst b/sources/AUTHORS.rst
index 5bc6807..505b28e 100644
--- a/sources/AUTHORS.rst
+++ b/sources/AUTHORS.rst
@@ -1,4 +1,4 @@
-Searx was created by Adam Tauber and is maintained by Adam Tauber and Alexandre Flament.
+Searx was created by Adam Tauber and is maintained by Adam Tauber, Alexandre Flament and Noémi Ványi.
Major contributing authors:
@@ -7,6 +7,7 @@ Major contributing authors:
- Thomas Pointhuber
- Alexandre Flament `@dalf `_
- @Cqoicebordel
+- Noémi Ványi
People who have submitted patches/translates, reported bugs, consulted features or
generally made searx better:
@@ -39,15 +40,21 @@ generally made searx better:
- @underr
- Emmanuel Benazera
- @GreenLunar
-- Noemi Vanyi
- Kang-min Liu
- Kirill Isakov
- Guilhem Bonnefille
- Marc Abonce Seguin
-
- @jibe-b
- Christian Pietsch @pietsch
- @Maxqia
- Ashutosh Das @pyprism
- YuLun Shih @imZack
- Dmitry Mikhirev @mikhirev
+- David A Roberts `@davidar `_
+- Jan Verbeek @blyxxyz
+- Ammar Najjar @ammarnajjar
+- @stepshal
+- François Revol @mmuman
+- marc @a01200356
+- Harry Wood @harry-wood
+- Thomas Renard @threnard
diff --git a/sources/CHANGELOG.rst b/sources/CHANGELOG.rst
index 8907ab4..999570e 100644
--- a/sources/CHANGELOG.rst
+++ b/sources/CHANGELOG.rst
@@ -1,3 +1,38 @@
+0.10.0 2016.09.06
+=================
+
+- New engines
+
+ - Archive.is (general)
+ - INA (videos)
+ - Scanr (science)
+ - Google Scholar (science)
+ - Crossref (science)
+ - Openrepos (files)
+ - Microsoft Academic Search Engine (science)
+ - Hoogle (it)
+ - Diggbt (files)
+ - Dictzone (general - dictionary)
+ - Translated (general - translation)
+- New Plugins
+
+ - Infinite scroll on results page
+ - DOAI rewrite
+- Full theme redesign
+- Display the number of results
+- Filter searches by date range
+- Instance config API endpoint
+- Dependency version updates
+- Socks proxy support for outgoing requests
+- 404 page
+
+
+News
+~~~~
+
+@kvch joined the maintainer team
+
+
0.9.0 2016.05.24
================
@@ -36,6 +71,7 @@
- Multilingual autocompleter
- Qwant autocompleter backend
+
0.8.1 2015.12.22
================
diff --git a/sources/README.rst b/sources/README.rst
index 6563fe8..a0bb12f 100644
--- a/sources/README.rst
+++ b/sources/README.rst
@@ -15,7 +15,7 @@ Installation
~~~~~~~~~~~~
- clone source:
- ``git clone git@github.com:asciimoo/searx.git && cd searx``
+ ``git clone https://github.com/asciimoo/searx.git && cd searx``
- install dependencies: ``./manage.sh update_packages``
- edit your
`settings.yml `__
diff --git a/sources/examples/basic_engine.py b/sources/examples/basic_engine.py
index d786564..c7d02af 100644
--- a/sources/examples/basic_engine.py
+++ b/sources/examples/basic_engine.py
@@ -1,5 +1,6 @@
-categories = ['general'] # optional
+categories = ['general'] # optional
+
def request(query, params):
'''pre-request callback
@@ -22,4 +23,3 @@ def response(resp):
resp: requests response object
'''
return [{'url': '', 'title': '', 'content': ''}]
-
diff --git a/sources/manage.sh b/sources/manage.sh
index 0a21f0e..75ba320 100755
--- a/sources/manage.sh
+++ b/sources/manage.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-BASE_DIR=$(dirname `readlink -f $0`)
+BASE_DIR=$(dirname "`readlink -f "$0"`")
PYTHONPATH=$BASE_DIR
SEARX_DIR="$BASE_DIR/searx"
ACTION=$1
@@ -58,7 +58,8 @@ styles() {
build_style themes/courgette/less/style.less themes/courgette/css/style.css
build_style themes/courgette/less/style-rtl.less themes/courgette/css/style-rtl.css
build_style less/bootstrap/bootstrap.less css/bootstrap.min.css
- build_style themes/oscar/less/oscar/oscar.less themes/oscar/css/oscar.min.css
+ build_style themes/oscar/less/pointhi/oscar.less themes/oscar/css/pointhi.min.css
+ build_style themes/oscar/less/logicodev/oscar.less themes/oscar/css/logicodev.min.css
build_style themes/pix-art/less/style.less themes/pix-art/css/style.css
}
diff --git a/sources/requirements-dev.txt b/sources/requirements-dev.txt
index 38be888..580ef63 100644
--- a/sources/requirements-dev.txt
+++ b/sources/requirements-dev.txt
@@ -1,8 +1,8 @@
-babel==2.2.0
-mock==1.0.1
+babel==2.3.4
+mock==2.0.0
nose2[coverage-plugin]
pep8==1.7.0
-plone.testing==4.0.15
+plone.testing==5.0.0
robotframework-selenium2library==1.7.4
robotsuite==1.7.0
transifex-client==0.11
diff --git a/sources/requirements.txt b/sources/requirements.txt
index 80c08a4..029c0cf 100644
--- a/sources/requirements.txt
+++ b/sources/requirements.txt
@@ -1,12 +1,12 @@
-certifi==2015.11.20.1
-flask==0.10.1
-flask-babel==0.9
-lxml==3.5.0
-ndg-httpsclient==0.4.0
+certifi==2016.2.28
+flask==0.11.1
+flask-babel==0.11.1
+lxml==3.6.0
+ndg-httpsclient==0.4.1
pyasn1==0.1.9
pyasn1-modules==0.0.8
-pygments==2.0.2
+pygments==2.1.3
pyopenssl==0.15.1
-python-dateutil==2.4.2
+python-dateutil==2.5.3
pyyaml==3.11
-requests==2.9.1
+requests[socks]==2.10.0
diff --git a/sources/searx/engines/__init__.py b/sources/searx/engines/__init__.py
index 6d50667..782b622 100644
--- a/sources/searx/engines/__init__.py
+++ b/sources/searx/engines/__init__.py
@@ -19,7 +19,7 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
from os.path import realpath, dirname, splitext, join
import sys
from imp import load_source
-from flask.ext.babel import gettext
+from flask_babel import gettext
from operator import itemgetter
from searx import settings
from searx import logger
@@ -42,7 +42,8 @@ engine_default_args = {'paging': False,
'shortcut': '-',
'disabled': False,
'suspend_end_time': 0,
- 'continuous_errors': 0}
+ 'continuous_errors': 0,
+ 'time_range_support': False}
def load_module(filename):
@@ -57,7 +58,11 @@ def load_module(filename):
def load_engine(engine_data):
engine_name = engine_data['engine']
- engine = load_module(engine_name + '.py')
+ try:
+ engine = load_module(engine_name + '.py')
+ except:
+ logger.exception('Cannot load engine "{}"'.format(engine_name))
+ return None
for param_name in engine_data:
if param_name == 'engine':
@@ -199,4 +204,5 @@ if 'engines' not in settings or not settings['engines']:
for engine_data in settings['engines']:
engine = load_engine(engine_data)
- engines[engine.name] = engine
+ if engine is not None:
+ engines[engine.name] = engine
diff --git a/sources/searx/engines/archlinux.py b/sources/searx/engines/archlinux.py
index 84e0d0f..b846934 100644
--- a/sources/searx/engines/archlinux.py
+++ b/sources/searx/engines/archlinux.py
@@ -34,6 +34,7 @@ def locale_to_lang_code(locale):
locale = locale.split('_')[0]
return locale
+
# wikis for some languages were moved off from the main site, we need to make
# requests to correct URLs to be able to get results in those languages
lang_urls = {
@@ -70,6 +71,7 @@ def get_lang_urls(language):
return lang_urls[language]
return lang_urls['all']
+
# Language names to build search requests for
# those languages which are hosted on the main site.
main_langs = {
diff --git a/sources/searx/engines/btdigg.py b/sources/searx/engines/btdigg.py
index c2b22f0..ea6baf1 100644
--- a/sources/searx/engines/btdigg.py
+++ b/sources/searx/engines/btdigg.py
@@ -16,6 +16,7 @@ from urllib import quote
from lxml import html
from operator import itemgetter
from searx.engines.xpath import extract_text
+from searx.utils import get_torrent_size
# engine dependent config
categories = ['videos', 'music', 'files']
@@ -68,20 +69,7 @@ def response(resp):
leech = 0
# convert filesize to byte if possible
- try:
- filesize = float(filesize)
-
- # convert filesize to byte
- if filesize_multiplier == 'TB':
- filesize = int(filesize * 1024 * 1024 * 1024 * 1024)
- elif filesize_multiplier == 'GB':
- filesize = int(filesize * 1024 * 1024 * 1024)
- elif filesize_multiplier == 'MB':
- filesize = int(filesize * 1024 * 1024)
- elif filesize_multiplier == 'KB':
- filesize = int(filesize * 1024)
- except:
- filesize = None
+ filesize = get_torrent_size(filesize, filesize_multiplier)
# convert files to int if possible
if files.isdigit():
diff --git a/sources/searx/engines/currency_convert.py b/sources/searx/engines/currency_convert.py
index b0ffb49..bc839cf 100644
--- a/sources/searx/engines/currency_convert.py
+++ b/sources/searx/engines/currency_convert.py
@@ -9,7 +9,7 @@ categories = []
url = 'https://download.finance.yahoo.com/d/quotes.csv?e=.csv&f=sl1d1t1&s={query}=X'
weight = 100
-parser_re = re.compile(u'.*?(\d+(?:\.\d+)?) ([^.0-9]+) (?:in|to) ([^.0-9]+)', re.I) # noqa
+parser_re = re.compile(u'.*?(\\d+(?:\\.\\d+)?) ([^.0-9]+) (?:in|to) ([^.0-9]+)', re.I) # noqa
db = 1
diff --git a/sources/searx/engines/deviantart.py b/sources/searx/engines/deviantart.py
index 135aeb3..d893fc7 100644
--- a/sources/searx/engines/deviantart.py
+++ b/sources/searx/engines/deviantart.py
@@ -13,7 +13,6 @@
"""
from urllib import urlencode
-from urlparse import urljoin
from lxml import html
import re
from searx.engines.xpath import extract_text
@@ -21,10 +20,16 @@ from searx.engines.xpath import extract_text
# engine dependent config
categories = ['images']
paging = True
+time_range_support = True
# search-url
base_url = 'https://www.deviantart.com/'
search_url = base_url + 'browse/all/?offset={offset}&{query}'
+time_range_url = '&order={range}'
+
+time_range_dict = {'day': 11,
+ 'week': 14,
+ 'month': 15}
# do search-request
@@ -33,6 +38,8 @@ def request(query, params):
params['url'] = search_url.format(offset=offset,
query=urlencode({'q': query}))
+ if params['time_range'] in time_range_dict:
+ params['url'] += time_range_url.format(range=time_range_dict[params['time_range']])
return params
@@ -47,14 +54,13 @@ def response(resp):
dom = html.fromstring(resp.text)
- regex = re.compile('\/200H\/')
+ regex = re.compile(r'\/200H\/')
# parse results
- for result in dom.xpath('//div[contains(@class, "tt-a tt-fh")]'):
- link = result.xpath('.//a[contains(@class, "thumb")]')[0]
- url = urljoin(base_url, link.attrib.get('href'))
- title_links = result.xpath('.//span[@class="details"]//a[contains(@class, "t")]')
- title = extract_text(title_links[0])
+ for result in dom.xpath('.//span[@class="thumb wide"]'):
+ link = result.xpath('.//a[@class="torpedo-thumb-link"]')[0]
+ url = link.attrib.get('href')
+ title = extract_text(result.xpath('.//span[@class="title"]'))
thumbnail_src = link.xpath('.//img')[0].attrib.get('src')
img_src = regex.sub('/', thumbnail_src)
diff --git a/sources/searx/engines/dictzone.py b/sources/searx/engines/dictzone.py
new file mode 100644
index 0000000..9765d5f
--- /dev/null
+++ b/sources/searx/engines/dictzone.py
@@ -0,0 +1,69 @@
+"""
+ Dictzone
+
+ @website https://dictzone.com/
+ @provide-api no
+ @using-api no
+ @results HTML (using search portal)
+ @stable no (HTML can change)
+ @parse url, title, content
+"""
+
+import re
+from urlparse import urljoin
+from lxml import html
+from cgi import escape
+from searx.utils import is_valid_lang
+
+categories = ['general']
+url = u'http://dictzone.com/{from_lang}-{to_lang}-dictionary/{query}'
+weight = 100
+
+parser_re = re.compile(u'.*?([a-z]+)-([a-z]+) ([^ ]+)$', re.I)
+results_xpath = './/table[@id="r"]/tr'
+
+
+def request(query, params):
+ m = parser_re.match(unicode(query, 'utf8'))
+ if not m:
+ return params
+
+ from_lang, to_lang, query = m.groups()
+
+ from_lang = is_valid_lang(from_lang)
+ to_lang = is_valid_lang(to_lang)
+
+ if not from_lang or not to_lang:
+ return params
+
+ params['url'] = url.format(from_lang=from_lang[2],
+ to_lang=to_lang[2],
+ query=query)
+
+ return params
+
+
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ for k, result in enumerate(dom.xpath(results_xpath)[1:]):
+ try:
+ from_result, to_results_raw = result.xpath('./td')
+ except:
+ continue
+
+ to_results = []
+ for to_result in to_results_raw.xpath('./p/a'):
+ t = to_result.text_content()
+ if t.strip():
+ to_results.append(to_result.text_content())
+
+ results.append({
+ 'url': urljoin(resp.url, '?%d' % k),
+ 'title': escape(from_result.text_content()),
+ 'content': escape('; '.join(to_results))
+ })
+
+ return results
diff --git a/sources/searx/engines/digbt.py b/sources/searx/engines/digbt.py
new file mode 100644
index 0000000..c35327e
--- /dev/null
+++ b/sources/searx/engines/digbt.py
@@ -0,0 +1,58 @@
+"""
+ DigBT (Videos, Music, Files)
+
+ @website https://digbt.org
+ @provide-api no
+
+ @using-api no
+ @results HTML (using search portal)
+ @stable no (HTML can change)
+ @parse url, title, content, magnetlink
+"""
+
+from urlparse import urljoin
+from lxml import html
+from searx.engines.xpath import extract_text
+from searx.utils import get_torrent_size
+
+categories = ['videos', 'music', 'files']
+paging = True
+
+URL = 'https://digbt.org'
+SEARCH_URL = URL + '/search/{query}-time-{pageno}'
+FILESIZE = 3
+FILESIZE_MULTIPLIER = 4
+
+
+def request(query, params):
+ params['url'] = SEARCH_URL.format(query=query, pageno=params['pageno'])
+
+ return params
+
+
+def response(resp):
+ dom = html.fromstring(resp.content)
+ search_res = dom.xpath('.//td[@class="x-item"]')
+
+ if not search_res:
+ return list()
+
+ results = list()
+ for result in search_res:
+ url = urljoin(URL, result.xpath('.//a[@title]/@href')[0])
+ title = result.xpath('.//a[@title]/text()')[0]
+ content = extract_text(result.xpath('.//div[@class="files"]'))
+ files_data = extract_text(result.xpath('.//div[@class="tail"]')).split()
+ filesize = get_torrent_size(files_data[FILESIZE], files_data[FILESIZE_MULTIPLIER])
+ magnetlink = result.xpath('.//div[@class="tail"]//a[@class="title"]/@href')[0]
+
+ results.append({'url': url,
+ 'title': title,
+ 'content': content,
+ 'filesize': filesize,
+ 'magnetlink': magnetlink,
+ 'seed': 'N/A',
+ 'leech': 'N/A',
+ 'template': 'torrent.html'})
+
+ return results
diff --git a/sources/searx/engines/duckduckgo.py b/sources/searx/engines/duckduckgo.py
index 373ce1b..2153492 100644
--- a/sources/searx/engines/duckduckgo.py
+++ b/sources/searx/engines/duckduckgo.py
@@ -11,21 +11,26 @@
@parse url, title, content
@todo rewrite to api
- @todo language support
- (the current used site does not support language-change)
"""
from urllib import urlencode
from lxml.html import fromstring
from searx.engines.xpath import extract_text
+from searx.languages import language_codes
# engine dependent config
categories = ['general']
paging = True
language_support = True
+time_range_support = True
# search-url
url = 'https://duckduckgo.com/html?{query}&s={offset}'
+time_range_url = '&df={range}'
+
+time_range_dict = {'day': 'd',
+ 'week': 'w',
+ 'month': 'm'}
# specific xpath variables
result_xpath = '//div[@class="result results_links results_links_deep web-result "]' # noqa
@@ -39,13 +44,31 @@ def request(query, params):
offset = (params['pageno'] - 1) * 30
if params['language'] == 'all':
- locale = 'en-us'
+ locale = None
else:
- locale = params['language'].replace('_', '-').lower()
+ locale = params['language'].split('_')
+ if len(locale) == 2:
+ # country code goes first
+ locale = locale[1].lower() + '-' + locale[0].lower()
+ else:
+ # tries to get a country code from language
+ locale = locale[0].lower()
+ lang_codes = [x[0] for x in language_codes]
+ for lc in lang_codes:
+ lc = lc.split('_')
+ if locale == lc[0]:
+ locale = lc[1].lower() + '-' + lc[0].lower()
+ break
- params['url'] = url.format(
- query=urlencode({'q': query, 'kl': locale}),
- offset=offset)
+ if locale:
+ params['url'] = url.format(
+ query=urlencode({'q': query, 'kl': locale}), offset=offset)
+ else:
+ params['url'] = url.format(
+ query=urlencode({'q': query}), offset=offset)
+
+ if params['time_range'] in time_range_dict:
+ params['url'] += time_range_url.format(range=time_range_dict[params['time_range']])
return params
diff --git a/sources/searx/engines/filecrop.py b/sources/searx/engines/filecrop.py
index 89dc776..71665bd 100644
--- a/sources/searx/engines/filecrop.py
+++ b/sources/searx/engines/filecrop.py
@@ -8,6 +8,7 @@ paging = True
class FilecropResultParser(HTMLParser):
+
def __init__(self):
HTMLParser.__init__(self)
self.__start_processing = False
diff --git a/sources/searx/engines/google.py b/sources/searx/engines/google.py
index 6018ad1..ea93bc9 100644
--- a/sources/searx/engines/google.py
+++ b/sources/searx/engines/google.py
@@ -24,6 +24,7 @@ categories = ['general']
paging = True
language_support = True
use_locale_domain = True
+time_range_support = True
# based on https://en.wikipedia.org/wiki/List_of_Google_domains and tests
default_hostname = 'www.google.com'
@@ -92,6 +93,11 @@ search_url = ('https://{hostname}' +
search_path +
'?{query}&start={offset}&gws_rd=cr&gbv=1&lr={lang}&ei=x')
+time_range_search = "&tbs=qdr:{range}"
+time_range_dict = {'day': 'd',
+ 'week': 'w',
+ 'month': 'm'}
+
# other URLs
map_hostname_start = 'maps.google.'
maps_path = '/maps'
@@ -179,6 +185,8 @@ def request(query, params):
query=urlencode({'q': query}),
hostname=google_hostname,
lang=url_lang)
+ if params['time_range'] in time_range_dict:
+ params['url'] += time_range_search.format(range=time_range_dict[params['time_range']])
params['headers']['Accept-Language'] = language
params['headers']['Accept'] = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
@@ -300,9 +308,9 @@ def parse_map_detail(parsed_url, result, google_hostname):
results = []
# try to parse the geoloc
- m = re.search('@([0-9\.]+),([0-9\.]+),([0-9]+)', parsed_url.path)
+ m = re.search(r'@([0-9\.]+),([0-9\.]+),([0-9]+)', parsed_url.path)
if m is None:
- m = re.search('ll\=([0-9\.]+),([0-9\.]+)\&z\=([0-9]+)', parsed_url.query)
+ m = re.search(r'll\=([0-9\.]+),([0-9\.]+)\&z\=([0-9]+)', parsed_url.query)
if m is not None:
# geoloc found (ignored)
diff --git a/sources/searx/engines/google_images.py b/sources/searx/engines/google_images.py
index efe4681..77bdc13 100644
--- a/sources/searx/engines/google_images.py
+++ b/sources/searx/engines/google_images.py
@@ -11,7 +11,6 @@
"""
from urllib import urlencode
-from urlparse import parse_qs
from json import loads
from lxml import html
@@ -19,24 +18,38 @@ from lxml import html
categories = ['images']
paging = True
safesearch = True
+time_range_support = True
+number_of_results = 100
search_url = 'https://www.google.com/search'\
'?{query}'\
+ '&asearch=ichunk'\
+ '&async=_id:rg_s,_pms:s'\
'&tbm=isch'\
- '&ijn=1'\
- '&start={offset}'
+ '&yv=2'\
+ '&{search_options}'
+time_range_attr = "qdr:{range}"
+time_range_dict = {'day': 'd',
+ 'week': 'w',
+ 'month': 'm'}
# do search-request
def request(query, params):
- offset = (params['pageno'] - 1) * 100
- params['url'] = search_url.format(query=urlencode({'q': query}),
- offset=offset,
- safesearch=safesearch)
+ search_options = {
+ 'ijn': params['pageno'] - 1,
+ 'start': (params['pageno'] - 1) * number_of_results
+ }
+
+ if params['time_range'] in time_range_dict:
+ search_options['tbs'] = time_range_attr.format(range=time_range_dict[params['time_range']])
if safesearch and params['safesearch']:
- params['url'] += '&' + urlencode({'safe': 'active'})
+ search_options['safe'] = 'on'
+
+ params['url'] = search_url.format(query=urlencode({'q': query}),
+ search_options=urlencode(search_options))
return params
@@ -45,12 +58,17 @@ def request(query, params):
def response(resp):
results = []
- dom = html.fromstring(resp.text)
+ g_result = loads(resp.text)
+
+ dom = html.fromstring(g_result[1][1])
# parse results
for result in dom.xpath('//div[@data-ved]'):
- metadata = loads(result.xpath('./div[@class="rg_meta"]/text()')[0])
+ try:
+ metadata = loads(''.join(result.xpath('./div[@class="rg_meta"]/text()')))
+ except:
+ continue
thumbnail_src = metadata['tu']
diff --git a/sources/searx/engines/ina.py b/sources/searx/engines/ina.py
new file mode 100644
index 0000000..86a3978
--- /dev/null
+++ b/sources/searx/engines/ina.py
@@ -0,0 +1,83 @@
+# INA (Videos)
+#
+# @website https://www.ina.fr/
+# @provide-api no
+#
+# @using-api no
+# @results HTML (using search portal)
+# @stable no (HTML can change)
+# @parse url, title, content, publishedDate, thumbnail
+#
+# @todo set content-parameter with correct data
+# @todo embedded (needs some md5 from video page)
+
+from json import loads
+from urllib import urlencode
+from lxml import html
+from HTMLParser import HTMLParser
+from searx.engines.xpath import extract_text
+from dateutil import parser
+
+# engine dependent config
+categories = ['videos']
+paging = True
+page_size = 48
+
+# search-url
+base_url = 'https://www.ina.fr'
+search_url = base_url + '/layout/set/ajax/recherche/result?autopromote=&hf={ps}&b={start}&type=Video&r=&{query}'
+
+# specific xpath variables
+results_xpath = '//div[contains(@class,"search-results--list")]/div[@class="media"]'
+url_xpath = './/a/@href'
+title_xpath = './/h3[@class="h3--title media-heading"]'
+thumbnail_xpath = './/img/@src'
+publishedDate_xpath = './/span[@class="broadcast"]'
+content_xpath = './/p[@class="media-body__summary"]'
+
+
+# do search-request
+def request(query, params):
+ params['url'] = search_url.format(ps=page_size,
+ start=params['pageno'] * page_size,
+ query=urlencode({'q': query}))
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ # we get html in a JSON container...
+ response = loads(resp.text)
+ if "content" not in response:
+ return []
+ dom = html.fromstring(response["content"])
+ p = HTMLParser()
+
+ # parse results
+ for result in dom.xpath(results_xpath):
+ videoid = result.xpath(url_xpath)[0]
+ url = base_url + videoid
+ title = p.unescape(extract_text(result.xpath(title_xpath)))
+ thumbnail = extract_text(result.xpath(thumbnail_xpath)[0])
+ if thumbnail[0] == '/':
+ thumbnail = base_url + thumbnail
+ d = extract_text(result.xpath(publishedDate_xpath)[0])
+ d = d.split('/')
+ # force ISO date to avoid wrong parsing
+ d = "%s-%s-%s" % (d[2], d[1], d[0])
+ publishedDate = parser.parse(d)
+ content = extract_text(result.xpath(content_xpath))
+
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'content': content,
+ 'template': 'videos.html',
+ 'publishedDate': publishedDate,
+ 'thumbnail': thumbnail})
+
+ # return results
+ return results
diff --git a/sources/searx/engines/json_engine.py b/sources/searx/engines/json_engine.py
index 5525b7f..4604c3c 100644
--- a/sources/searx/engines/json_engine.py
+++ b/sources/searx/engines/json_engine.py
@@ -6,7 +6,16 @@ search_url = None
url_query = None
content_query = None
title_query = None
-# suggestion_xpath = ''
+suggestion_query = ''
+results_query = ''
+
+# parameters for engines with paging support
+#
+# number of results on each page
+# (only needed if the site requires not a page number, but an offset)
+page_size = 1
+# number of the first page (usually 0 or 1)
+first_page_num = 1
def iterate(iterable):
@@ -69,19 +78,36 @@ def query(data, query_string):
def request(query, params):
query = urlencode({'q': query})[2:]
- params['url'] = search_url.format(query=query)
+
+ fp = {'query': query}
+ if paging and search_url.find('{pageno}') >= 0:
+ fp['pageno'] = (params['pageno'] - 1) * page_size + first_page_num
+
+ params['url'] = search_url.format(**fp)
params['query'] = query
+
return params
def response(resp):
results = []
-
json = loads(resp.text)
+ if results_query:
+ for result in query(json, results_query)[0]:
+ url = query(result, url_query)[0]
+ title = query(result, title_query)[0]
+ content = query(result, content_query)[0]
+ results.append({'url': url, 'title': title, 'content': content})
+ else:
+ for url, title, content in zip(
+ query(json, url_query),
+ query(json, title_query),
+ query(json, content_query)
+ ):
+ results.append({'url': url, 'title': title, 'content': content})
- urls = query(json, url_query)
- contents = query(json, content_query)
- titles = query(json, title_query)
- for url, title, content in zip(urls, titles, contents):
- results.append({'url': url, 'title': title, 'content': content})
+ if not suggestion_query:
+ return results
+ for suggestion in query(json, suggestion_query):
+ results.append({'suggestion': suggestion})
return results
diff --git a/sources/searx/engines/scanr_structures.py b/sources/searx/engines/scanr_structures.py
new file mode 100644
index 0000000..ad78155
--- /dev/null
+++ b/sources/searx/engines/scanr_structures.py
@@ -0,0 +1,78 @@
+"""
+ ScanR Structures (Science)
+
+ @website https://scanr.enseignementsup-recherche.gouv.fr
+ @provide-api yes (https://scanr.enseignementsup-recherche.gouv.fr/api/swagger-ui.html)
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title, content, img_src
+"""
+
+from urllib import urlencode
+from json import loads, dumps
+from dateutil import parser
+from searx.utils import html_to_text
+
+# engine dependent config
+categories = ['science']
+paging = True
+page_size = 20
+
+# search-url
+url = 'https://scanr.enseignementsup-recherche.gouv.fr/'
+search_url = url + 'api/structures/search'
+
+
+# do search-request
+def request(query, params):
+
+ params['url'] = search_url
+ params['method'] = 'POST'
+ params['headers']['Content-type'] = "application/json"
+ params['data'] = dumps({"query": query,
+ "searchField": "ALL",
+ "sortDirection": "ASC",
+ "sortOrder": "RELEVANCY",
+ "page": params['pageno'],
+ "pageSize": page_size})
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ search_res = loads(resp.text)
+
+ # return empty array if there are no results
+ if search_res.get('total') < 1:
+ return []
+
+ # parse results
+ for result in search_res['results']:
+ if 'id' not in result:
+ continue
+
+ # is it thumbnail or img_src??
+ thumbnail = None
+ if 'logo' in result:
+ thumbnail = result['logo']
+ if thumbnail[0] == '/':
+ thumbnail = url + thumbnail
+
+ content = None
+ if 'highlights' in result:
+ content = result['highlights'][0]['value']
+
+ # append result
+ results.append({'url': url + 'structure/' + result['id'],
+ 'title': result['label'],
+ # 'thumbnail': thumbnail,
+ 'img_src': thumbnail,
+ 'content': html_to_text(content)})
+
+ # return results
+ return results
diff --git a/sources/searx/engines/soundcloud.py b/sources/searx/engines/soundcloud.py
index ac23c1e..62b03ac 100644
--- a/sources/searx/engines/soundcloud.py
+++ b/sources/searx/engines/soundcloud.py
@@ -57,6 +57,7 @@ def get_client_id():
logger.warning("Unable to fetch guest client_id from SoundCloud, check parser!")
return ""
+
# api-key
guest_client_id = get_client_id()
diff --git a/sources/searx/engines/startpage.py b/sources/searx/engines/startpage.py
index 52dd0b9..d8b702c 100644
--- a/sources/searx/engines/startpage.py
+++ b/sources/searx/engines/startpage.py
@@ -68,15 +68,15 @@ def response(resp):
url = link.attrib.get('href')
# block google-ad url's
- if re.match("^http(s|)://(www\.)?google\.[a-z]+/aclk.*$", url):
+ if re.match(r"^http(s|)://(www\.)?google\.[a-z]+/aclk.*$", url):
continue
# block startpage search url's
- if re.match("^http(s|)://(www\.)?startpage\.com/do/search\?.*$", url):
+ if re.match(r"^http(s|)://(www\.)?startpage\.com/do/search\?.*$", url):
continue
# block ixquick search url's
- if re.match("^http(s|)://(www\.)?ixquick\.com/do/search\?.*$", url):
+ if re.match(r"^http(s|)://(www\.)?ixquick\.com/do/search\?.*$", url):
continue
title = escape(extract_text(link))
@@ -89,7 +89,7 @@ def response(resp):
published_date = None
# check if search result starts with something like: "2 Sep 2014 ... "
- if re.match("^([1-9]|[1-2][0-9]|3[0-1]) [A-Z][a-z]{2} [0-9]{4} \.\.\. ", content):
+ if re.match(r"^([1-9]|[1-2][0-9]|3[0-1]) [A-Z][a-z]{2} [0-9]{4} \.\.\. ", content):
date_pos = content.find('...') + 4
date_string = content[0:date_pos - 5]
published_date = parser.parse(date_string, dayfirst=True)
@@ -98,7 +98,7 @@ def response(resp):
content = content[date_pos:]
# check if search result starts with something like: "5 days ago ... "
- elif re.match("^[0-9]+ days? ago \.\.\. ", content):
+ elif re.match(r"^[0-9]+ days? ago \.\.\. ", content):
date_pos = content.find('...') + 4
date_string = content[0:date_pos - 5]
diff --git a/sources/searx/engines/swisscows.py b/sources/searx/engines/swisscows.py
index 864436a..1a94ed6 100644
--- a/sources/searx/engines/swisscows.py
+++ b/sources/searx/engines/swisscows.py
@@ -25,10 +25,10 @@ base_url = 'https://swisscows.ch/'
search_string = '?{query}&page={page}'
# regex
-regex_json = re.compile('initialData: {"Request":(.|\n)*},\s*environment')
-regex_json_remove_start = re.compile('^initialData:\s*')
-regex_json_remove_end = re.compile(',\s*environment$')
-regex_img_url_remove_start = re.compile('^https?://i\.swisscows\.ch/\?link=')
+regex_json = re.compile(r'initialData: {"Request":(.|\n)*},\s*environment')
+regex_json_remove_start = re.compile(r'^initialData:\s*')
+regex_json_remove_end = re.compile(r',\s*environment$')
+regex_img_url_remove_start = re.compile(r'^https?://i\.swisscows\.ch/\?link=')
# do search-request
diff --git a/sources/searx/engines/tokyotoshokan.py b/sources/searx/engines/tokyotoshokan.py
index 17e8e21..e2990e1 100644
--- a/sources/searx/engines/tokyotoshokan.py
+++ b/sources/searx/engines/tokyotoshokan.py
@@ -48,7 +48,7 @@ def response(resp):
return []
# regular expression for parsing torrent size strings
- size_re = re.compile('Size:\s*([\d.]+)(TB|GB|MB|B)', re.IGNORECASE)
+ size_re = re.compile(r'Size:\s*([\d.]+)(TB|GB|MB|B)', re.IGNORECASE)
# processing the results, two rows at a time
for i in xrange(0, len(rows), 2):
diff --git a/sources/searx/engines/translated.py b/sources/searx/engines/translated.py
new file mode 100644
index 0000000..02047bc
--- /dev/null
+++ b/sources/searx/engines/translated.py
@@ -0,0 +1,65 @@
+"""
+ MyMemory Translated
+
+ @website https://mymemory.translated.net/
+ @provide-api yes (https://mymemory.translated.net/doc/spec.php)
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title, content
+"""
+import re
+from cgi import escape
+from searx.utils import is_valid_lang
+
+categories = ['general']
+url = u'http://api.mymemory.translated.net/get?q={query}&langpair={from_lang}|{to_lang}{key}'
+web_url = u'http://mymemory.translated.net/en/{from_lang}/{to_lang}/{query}'
+weight = 100
+
+parser_re = re.compile(u'.*?([a-z]+)-([a-z]+) (.{2,})$', re.I)
+api_key = ''
+
+
+def request(query, params):
+ m = parser_re.match(unicode(query, 'utf8'))
+ if not m:
+ return params
+
+ from_lang, to_lang, query = m.groups()
+
+ from_lang = is_valid_lang(from_lang)
+ to_lang = is_valid_lang(to_lang)
+
+ if not from_lang or not to_lang:
+ return params
+
+ if api_key:
+ key_form = '&key=' + api_key
+ else:
+ key_form = ''
+ params['url'] = url.format(from_lang=from_lang[1],
+ to_lang=to_lang[1],
+ query=query,
+ key=key_form)
+ params['query'] = query
+ params['from_lang'] = from_lang
+ params['to_lang'] = to_lang
+
+ return params
+
+
+def response(resp):
+ results = []
+ results.append({
+ 'url': escape(web_url.format(
+ from_lang=resp.search_params['from_lang'][2],
+ to_lang=resp.search_params['to_lang'][2],
+ query=resp.search_params['query'])),
+ 'title': escape('[{0}-{1}] {2}'.format(
+ resp.search_params['from_lang'][1],
+ resp.search_params['to_lang'][1],
+ resp.search_params['query'])),
+ 'content': escape(resp.json()['responseData']['translatedText'])
+ })
+ return results
diff --git a/sources/searx/engines/wikidata.py b/sources/searx/engines/wikidata.py
index 8aa2fcd..91040e2 100644
--- a/sources/searx/engines/wikidata.py
+++ b/sources/searx/engines/wikidata.py
@@ -1,56 +1,86 @@
-import json
+# -*- coding: utf-8 -*-
+"""
+ Wikidata
+
+ @website https://wikidata.org
+ @provide-api yes (https://wikidata.org/w/api.php)
+
+ @using-api partially (most things require scraping)
+ @results JSON, HTML
+ @stable no (html can change)
+ @parse url, infobox
+"""
from searx import logger
from searx.poolrequests import get
-from searx.utils import format_date_by_locale
+from searx.engines.xpath import extract_text
-from datetime import datetime
-from dateutil.parser import parse as dateutil_parse
+from json import loads
+from lxml.html import fromstring
from urllib import urlencode
-
logger = logger.getChild('wikidata')
result_count = 1
+
+# urls
wikidata_host = 'https://www.wikidata.org'
+url_search = wikidata_host \
+ + '/wiki/Special:ItemDisambiguation?{query}'
+
wikidata_api = wikidata_host + '/w/api.php'
-url_search = wikidata_api \
- + '?action=query&list=search&format=json'\
- + '&srnamespace=0&srprop=sectiontitle&{query}'
url_detail = wikidata_api\
- + '?action=wbgetentities&format=json'\
- + '&props=labels%7Cinfo%7Csitelinks'\
- + '%7Csitelinks%2Furls%7Cdescriptions%7Cclaims'\
- + '&{query}'
+ + '?action=parse&format=json&{query}'\
+ + '&redirects=1&prop=text%7Cdisplaytitle%7Clanglinks%7Crevid'\
+ + '&disableeditsection=1&disabletidy=1&preview=1§ionpreview=1&disabletoc=1&utf8=1&formatversion=2'
+
url_map = 'https://www.openstreetmap.org/'\
+ '?lat={latitude}&lon={longitude}&zoom={zoom}&layers=M'
+url_image = 'https://commons.wikimedia.org/wiki/Special:FilePath/{filename}?width=500&height=400'
+
+# xpaths
+wikidata_ids_xpath = '//div/ul[@class="wikibase-disambiguation"]/li/a/@title'
+title_xpath = '//*[contains(@class,"wikibase-title-label")]'
+description_xpath = '//div[contains(@class,"wikibase-entitytermsview-heading-description")]'
+property_xpath = '//div[@id="{propertyid}"]'
+label_xpath = './/div[contains(@class,"wikibase-statementgroupview-property-label")]/a'
+url_xpath = './/a[contains(@class,"external free") or contains(@class, "wb-external-id")]'
+wikilink_xpath = './/ul[contains(@class,"wikibase-sitelinklistview-listview")]'\
+ + '/li[contains(@data-wb-siteid,"{wikiid}")]//a/@href'
+property_row_xpath = './/div[contains(@class,"wikibase-statementview")]'
+preferred_rank_xpath = './/span[contains(@class,"wikibase-rankselector-preferred")]'
+value_xpath = './/div[contains(@class,"wikibase-statementview-mainsnak")]'\
+ + '/*/div[contains(@class,"wikibase-snakview-value")]'
+language_fallback_xpath = '//sup[contains(@class,"wb-language-fallback-indicator")]'
+calendar_name_xpath = './/sup[contains(@class,"wb-calendar-name")]'
def request(query, params):
+ language = params['language'].split('_')[0]
+ if language == 'all':
+ language = 'en'
+
params['url'] = url_search.format(
- query=urlencode({'srsearch': query,
- 'srlimit': result_count}))
+ query=urlencode({'label': query,
+ 'language': language}))
return params
def response(resp):
results = []
- search_res = json.loads(resp.text)
-
- wikidata_ids = set()
- for r in search_res.get('query', {}).get('search', {}):
- wikidata_ids.add(r.get('title', ''))
+ html = fromstring(resp.content)
+ wikidata_ids = html.xpath(wikidata_ids_xpath)
language = resp.search_params['language'].split('_')[0]
if language == 'all':
language = 'en'
- url = url_detail.format(query=urlencode({'ids': '|'.join(wikidata_ids),
- 'languages': language + '|en'}))
-
- htmlresponse = get(url)
- jsonresponse = json.loads(htmlresponse.content)
- for wikidata_id in wikidata_ids:
- results = results + getDetail(jsonresponse, wikidata_id, language, resp.search_params['language'])
+ # TODO: make requests asynchronous to avoid timeout when result_count > 1
+ for wikidata_id in wikidata_ids[:result_count]:
+ url = url_detail.format(query=urlencode({'page': wikidata_id,
+ 'uselang': language}))
+ htmlresponse = get(url)
+ jsonresponse = loads(htmlresponse.content)
+ results += getDetail(jsonresponse, wikidata_id, language, resp.search_params['language'])
return results
@@ -60,124 +90,206 @@ def getDetail(jsonresponse, wikidata_id, language, locale):
urls = []
attributes = []
- result = jsonresponse.get('entities', {}).get(wikidata_id, {})
+ title = jsonresponse.get('parse', {}).get('displaytitle', {})
+ result = jsonresponse.get('parse', {}).get('text', {})
- title = result.get('labels', {}).get(language, {}).get('value', None)
- if title is None:
- title = result.get('labels', {}).get('en', {}).get('value', None)
- if title is None:
+ if not title or not result:
return results
- description = result\
- .get('descriptions', {})\
- .get(language, {})\
- .get('value', None)
+ title = fromstring(title)
+ for elem in title.xpath(language_fallback_xpath):
+ elem.getparent().remove(elem)
+ title = extract_text(title.xpath(title_xpath))
- if description is None:
- description = result\
- .get('descriptions', {})\
- .get('en', {})\
- .get('value', '')
+ result = fromstring(result)
+ for elem in result.xpath(language_fallback_xpath):
+ elem.getparent().remove(elem)
- claims = result.get('claims', {})
- official_website = get_string(claims, 'P856', None)
- if official_website is not None:
- urls.append({'title': 'Official site', 'url': official_website})
- results.append({'title': title, 'url': official_website})
+ description = extract_text(result.xpath(description_xpath))
+ # URLS
+
+ # official website
+ add_url(urls, result, 'P856', results=results)
+
+ # wikipedia
wikipedia_link_count = 0
wikipedia_link = get_wikilink(result, language + 'wiki')
- wikipedia_link_count += add_url(urls,
- 'Wikipedia (' + language + ')',
- wikipedia_link)
+ if wikipedia_link:
+ wikipedia_link_count += 1
+ urls.append({'title': 'Wikipedia (' + language + ')',
+ 'url': wikipedia_link})
+
if language != 'en':
wikipedia_en_link = get_wikilink(result, 'enwiki')
- wikipedia_link_count += add_url(urls,
- 'Wikipedia (en)',
- wikipedia_en_link)
- if wikipedia_link_count == 0:
- misc_language = get_wiki_firstlanguage(result, 'wiki')
- if misc_language is not None:
- add_url(urls,
- 'Wikipedia (' + misc_language + ')',
- get_wikilink(result, misc_language + 'wiki'))
+ if wikipedia_en_link:
+ wikipedia_link_count += 1
+ urls.append({'title': 'Wikipedia (en)',
+ 'url': wikipedia_en_link})
- if language != 'en':
- add_url(urls,
- 'Wiki voyage (' + language + ')',
- get_wikilink(result, language + 'wikivoyage'))
+ # TODO: get_wiki_firstlanguage
+ # if wikipedia_link_count == 0:
- add_url(urls,
- 'Wiki voyage (en)',
- get_wikilink(result, 'enwikivoyage'))
+ # more wikis
+ add_url(urls, result, default_label='Wikivoyage (' + language + ')', link_type=language + 'wikivoyage')
+ add_url(urls, result, default_label='Wikiquote (' + language + ')', link_type=language + 'wikiquote')
+ add_url(urls, result, default_label='Wikimedia Commons', link_type='commonswiki')
- if language != 'en':
- add_url(urls,
- 'Wikiquote (' + language + ')',
- get_wikilink(result, language + 'wikiquote'))
+ add_url(urls, result, 'P625', 'OpenStreetMap', link_type='geo')
- add_url(urls,
- 'Wikiquote (en)',
- get_wikilink(result, 'enwikiquote'))
+ # musicbrainz
+ add_url(urls, result, 'P434', 'MusicBrainz', 'http://musicbrainz.org/artist/')
+ add_url(urls, result, 'P435', 'MusicBrainz', 'http://musicbrainz.org/work/')
+ add_url(urls, result, 'P436', 'MusicBrainz', 'http://musicbrainz.org/release-group/')
+ add_url(urls, result, 'P966', 'MusicBrainz', 'http://musicbrainz.org/label/')
- add_url(urls,
- 'Commons wiki',
- get_wikilink(result, 'commonswiki'))
+ # IMDb
+ add_url(urls, result, 'P345', 'IMDb', 'https://www.imdb.com/', link_type='imdb')
+ # source code repository
+ add_url(urls, result, 'P1324')
+ # blog
+ add_url(urls, result, 'P1581')
+ # social media links
+ add_url(urls, result, 'P2397', 'YouTube', 'https://www.youtube.com/channel/')
+ add_url(urls, result, 'P1651', 'YouTube', 'https://www.youtube.com/watch?v=')
+ add_url(urls, result, 'P2002', 'Twitter', 'https://twitter.com/')
+ add_url(urls, result, 'P2013', 'Facebook', 'https://facebook.com/')
+ add_url(urls, result, 'P2003', 'Instagram', 'https://instagram.com/')
- add_url(urls,
- 'Location',
- get_geolink(claims, 'P625', None))
+ urls.append({'title': 'Wikidata',
+ 'url': 'https://www.wikidata.org/wiki/'
+ + wikidata_id + '?uselang=' + language})
- add_url(urls,
- 'Wikidata',
- 'https://www.wikidata.org/wiki/'
- + wikidata_id + '?uselang=' + language)
+ # INFOBOX ATTRIBUTES (ROWS)
- musicbrainz_work_id = get_string(claims, 'P435')
- if musicbrainz_work_id is not None:
- add_url(urls,
- 'MusicBrainz',
- 'http://musicbrainz.org/work/'
- + musicbrainz_work_id)
+ # DATES
+ # inception date
+ add_attribute(attributes, result, 'P571', date=True)
+ # dissolution date
+ add_attribute(attributes, result, 'P576', date=True)
+ # start date
+ add_attribute(attributes, result, 'P580', date=True)
+ # end date
+ add_attribute(attributes, result, 'P582', date=True)
+ # date of birth
+ add_attribute(attributes, result, 'P569', date=True)
+ # date of death
+ add_attribute(attributes, result, 'P570', date=True)
+ # date of spacecraft launch
+ add_attribute(attributes, result, 'P619', date=True)
+ # date of spacecraft landing
+ add_attribute(attributes, result, 'P620', date=True)
- musicbrainz_artist_id = get_string(claims, 'P434')
- if musicbrainz_artist_id is not None:
- add_url(urls,
- 'MusicBrainz',
- 'http://musicbrainz.org/artist/'
- + musicbrainz_artist_id)
+ # nationality
+ add_attribute(attributes, result, 'P27')
+ # country of origin
+ add_attribute(attributes, result, 'P495')
+ # country
+ add_attribute(attributes, result, 'P17')
+ # headquarters
+ add_attribute(attributes, result, 'Q180')
- musicbrainz_release_group_id = get_string(claims, 'P436')
- if musicbrainz_release_group_id is not None:
- add_url(urls,
- 'MusicBrainz',
- 'http://musicbrainz.org/release-group/'
- + musicbrainz_release_group_id)
+ # PLACES
+ # capital
+ add_attribute(attributes, result, 'P36', trim=True)
+ # head of state
+ add_attribute(attributes, result, 'P35', trim=True)
+ # head of government
+ add_attribute(attributes, result, 'P6', trim=True)
+ # type of government
+ add_attribute(attributes, result, 'P122')
+ # official language
+ add_attribute(attributes, result, 'P37')
+ # population
+ add_attribute(attributes, result, 'P1082', trim=True)
+ # area
+ add_attribute(attributes, result, 'P2046')
+ # currency
+ add_attribute(attributes, result, 'P38', trim=True)
+ # heigth (building)
+ add_attribute(attributes, result, 'P2048')
- musicbrainz_label_id = get_string(claims, 'P966')
- if musicbrainz_label_id is not None:
- add_url(urls,
- 'MusicBrainz',
- 'http://musicbrainz.org/label/'
- + musicbrainz_label_id)
+ # MEDIA
+ # platform (videogames)
+ add_attribute(attributes, result, 'P400')
+ # author
+ add_attribute(attributes, result, 'P50')
+ # creator
+ add_attribute(attributes, result, 'P170')
+ # director
+ add_attribute(attributes, result, 'P57')
+ # performer
+ add_attribute(attributes, result, 'P175')
+ # developer
+ add_attribute(attributes, result, 'P178')
+ # producer
+ add_attribute(attributes, result, 'P162')
+ # manufacturer
+ add_attribute(attributes, result, 'P176')
+ # screenwriter
+ add_attribute(attributes, result, 'P58')
+ # production company
+ add_attribute(attributes, result, 'P272')
+ # record label
+ add_attribute(attributes, result, 'P264')
+ # publisher
+ add_attribute(attributes, result, 'P123')
+ # original network
+ add_attribute(attributes, result, 'P449')
+ # distributor
+ add_attribute(attributes, result, 'P750')
+ # composer
+ add_attribute(attributes, result, 'P86')
+ # publication date
+ add_attribute(attributes, result, 'P577', date=True)
+ # genre
+ add_attribute(attributes, result, 'P136')
+ # original language
+ add_attribute(attributes, result, 'P364')
+ # isbn
+ add_attribute(attributes, result, 'Q33057')
+ # software license
+ add_attribute(attributes, result, 'P275')
+ # programming language
+ add_attribute(attributes, result, 'P277')
+ # version
+ add_attribute(attributes, result, 'P348', trim=True)
+ # narrative location
+ add_attribute(attributes, result, 'P840')
- # musicbrainz_area_id = get_string(claims, 'P982')
- # P1407 MusicBrainz series ID
- # P1004 MusicBrainz place ID
- # P1330 MusicBrainz instrument ID
- # P1407 MusicBrainz series ID
+ # LANGUAGES
+ # number of speakers
+ add_attribute(attributes, result, 'P1098')
+ # writing system
+ add_attribute(attributes, result, 'P282')
+ # regulatory body
+ add_attribute(attributes, result, 'P1018')
+ # language code
+ add_attribute(attributes, result, 'P218')
- postal_code = get_string(claims, 'P281', None)
- if postal_code is not None:
- attributes.append({'label': 'Postal code(s)', 'value': postal_code})
+ # OTHER
+ # ceo
+ add_attribute(attributes, result, 'P169', trim=True)
+ # founder
+ add_attribute(attributes, result, 'P112')
+ # legal form (company/organization)
+ add_attribute(attributes, result, 'P1454')
+ # operator
+ add_attribute(attributes, result, 'P137')
+ # crew members (tripulation)
+ add_attribute(attributes, result, 'P1029')
+ # taxon
+ add_attribute(attributes, result, 'P225')
+ # chemical formula
+ add_attribute(attributes, result, 'P274')
+ # winner (sports/contests)
+ add_attribute(attributes, result, 'P1346')
+ # number of deaths
+ add_attribute(attributes, result, 'P1120')
+ # currency code
+ add_attribute(attributes, result, 'P498')
- date_of_birth = get_time(claims, 'P569', locale, None)
- if date_of_birth is not None:
- attributes.append({'label': 'Date of birth', 'value': date_of_birth})
-
- date_of_death = get_time(claims, 'P570', locale, None)
- if date_of_death is not None:
- attributes.append({'label': 'Date of death', 'value': date_of_death})
+ image = add_image(result)
if len(attributes) == 0 and len(urls) == 2 and len(description) == 0:
results.append({
@@ -190,6 +302,7 @@ def getDetail(jsonresponse, wikidata_id, language, locale):
'infobox': title,
'id': wikipedia_link,
'content': description,
+ 'img_src': image,
'attributes': attributes,
'urls': urls
})
@@ -197,92 +310,151 @@ def getDetail(jsonresponse, wikidata_id, language, locale):
return results
-def add_url(urls, title, url):
- if url is not None:
- urls.append({'title': title, 'url': url})
- return 1
+# only returns first match
+def add_image(result):
+ # P15: route map, P242: locator map, P154: logo, P18: image, P242: map, P41: flag, P2716: collage, P2910: icon
+ property_ids = ['P15', 'P242', 'P154', 'P18', 'P242', 'P41', 'P2716', 'P2910']
+
+ for property_id in property_ids:
+ image = result.xpath(property_xpath.replace('{propertyid}', property_id))
+ if image:
+ image_name = image[0].xpath(value_xpath)
+ image_src = url_image.replace('{filename}', extract_text(image_name[0]))
+ return image_src
+
+
+# setting trim will only returned high ranked rows OR the first row
+def add_attribute(attributes, result, property_id, default_label=None, date=False, trim=False):
+ attribute = result.xpath(property_xpath.replace('{propertyid}', property_id))
+ if attribute:
+
+ if default_label:
+ label = default_label
+ else:
+ label = extract_text(attribute[0].xpath(label_xpath))
+ label = label[0].upper() + label[1:]
+
+ if date:
+ trim = True
+ # remove calendar name
+ calendar_name = attribute[0].xpath(calendar_name_xpath)
+ for calendar in calendar_name:
+ calendar.getparent().remove(calendar)
+
+ concat_values = ""
+ values = []
+ first_value = None
+ for row in attribute[0].xpath(property_row_xpath):
+ if not first_value or not trim or row.xpath(preferred_rank_xpath):
+
+ value = row.xpath(value_xpath)
+ if not value:
+ continue
+ value = extract_text(value)
+
+ # save first value in case no ranked row is found
+ if trim and not first_value:
+ first_value = value
+ else:
+ # to avoid duplicate values
+ if value not in values:
+ concat_values += value + ", "
+ values.append(value)
+
+ if trim and not values:
+ attributes.append({'label': label,
+ 'value': first_value})
+ else:
+ attributes.append({'label': label,
+ 'value': concat_values[:-2]})
+
+
+# requires property_id unless it's a wiki link (defined in link_type)
+def add_url(urls, result, property_id=None, default_label=None, url_prefix=None, results=None, link_type=None):
+ links = []
+
+ # wiki links don't have property in wikidata page
+ if link_type and 'wiki' in link_type:
+ links.append(get_wikilink(result, link_type))
else:
- return 0
+ dom_element = result.xpath(property_xpath.replace('{propertyid}', property_id))
+ if dom_element:
+ dom_element = dom_element[0]
+ if not default_label:
+ label = extract_text(dom_element.xpath(label_xpath))
+ label = label[0].upper() + label[1:]
+
+ if link_type == 'geo':
+ links.append(get_geolink(dom_element))
+
+ elif link_type == 'imdb':
+ links.append(get_imdblink(dom_element, url_prefix))
+
+ else:
+ url_results = dom_element.xpath(url_xpath)
+ for link in url_results:
+ if link is not None:
+ if url_prefix:
+ link = url_prefix + extract_text(link)
+ else:
+ link = extract_text(link)
+ links.append(link)
+
+ # append urls
+ for url in links:
+ if url is not None:
+ urls.append({'title': default_label or label,
+ 'url': url})
+ if results is not None:
+ results.append({'title': default_label or label,
+ 'url': url})
-def get_mainsnak(claims, propertyName):
- propValue = claims.get(propertyName, {})
- if len(propValue) == 0:
+def get_imdblink(result, url_prefix):
+ imdb_id = result.xpath(value_xpath)
+ if imdb_id:
+ imdb_id = extract_text(imdb_id)
+ id_prefix = imdb_id[:2]
+ if id_prefix == 'tt':
+ url = url_prefix + 'title/' + imdb_id
+ elif id_prefix == 'nm':
+ url = url_prefix + 'name/' + imdb_id
+ elif id_prefix == 'ch':
+ url = url_prefix + 'character/' + imdb_id
+ elif id_prefix == 'co':
+ url = url_prefix + 'company/' + imdb_id
+ elif id_prefix == 'ev':
+ url = url_prefix + 'event/' + imdb_id
+ else:
+ url = None
+ return url
+
+
+def get_geolink(result):
+ coordinates = result.xpath(value_xpath)
+ if not coordinates:
return None
+ coordinates = extract_text(coordinates[0])
+ latitude, longitude = coordinates.split(',')
- propValue = propValue[0].get('mainsnak', None)
- return propValue
-
-
-def get_string(claims, propertyName, defaultValue=None):
- propValue = claims.get(propertyName, {})
- if len(propValue) == 0:
- return defaultValue
-
- result = []
- for e in propValue:
- mainsnak = e.get('mainsnak', {})
-
- datavalue = mainsnak.get('datavalue', {})
- if datavalue is not None:
- result.append(datavalue.get('value', ''))
-
- if len(result) == 0:
- return defaultValue
- else:
- # TODO handle multiple urls
- return result[0]
-
-
-def get_time(claims, propertyName, locale, defaultValue=None):
- propValue = claims.get(propertyName, {})
- if len(propValue) == 0:
- return defaultValue
-
- result = []
- for e in propValue:
- mainsnak = e.get('mainsnak', {})
-
- datavalue = mainsnak.get('datavalue', {})
- if datavalue is not None:
- value = datavalue.get('value', '')
- result.append(value.get('time', ''))
-
- if len(result) == 0:
- date_string = defaultValue
- else:
- date_string = ', '.join(result)
-
- try:
- parsed_date = datetime.strptime(date_string, "+%Y-%m-%dT%H:%M:%SZ")
- except:
- if date_string.startswith('-'):
- return date_string.split('T')[0]
- try:
- parsed_date = dateutil_parse(date_string, fuzzy=False, default=False)
- except:
- logger.debug('could not parse date %s', date_string)
- return date_string.split('T')[0]
-
- return format_date_by_locale(parsed_date, locale)
-
-
-def get_geolink(claims, propertyName, defaultValue=''):
- mainsnak = get_mainsnak(claims, propertyName)
-
- if mainsnak is None:
- return defaultValue
-
- datatype = mainsnak.get('datatype', '')
- datavalue = mainsnak.get('datavalue', {})
-
- if datatype != 'globe-coordinate':
- return defaultValue
-
- value = datavalue.get('value', {})
-
- precision = value.get('precision', 0.0002)
+ # convert to decimal
+ lat = int(latitude[:latitude.find(u'°')])
+ if latitude.find('\'') >= 0:
+ lat += int(latitude[latitude.find(u'°') + 1:latitude.find('\'')] or 0) / 60.0
+ if latitude.find('"') >= 0:
+ lat += float(latitude[latitude.find('\'') + 1:latitude.find('"')] or 0) / 3600.0
+ if latitude.find('S') >= 0:
+ lat *= -1
+ lon = int(longitude[:longitude.find(u'°')])
+ if longitude.find('\'') >= 0:
+ lon += int(longitude[longitude.find(u'°') + 1:longitude.find('\'')] or 0) / 60.0
+ if longitude.find('"') >= 0:
+ lon += float(longitude[longitude.find('\'') + 1:longitude.find('"')] or 0) / 3600.0
+ if longitude.find('W') >= 0:
+ lon *= -1
+ # TODO: get precision
+ precision = 0.0002
# there is no zoom information, deduce from precision (error prone)
# samples :
# 13 --> 5
@@ -298,26 +470,20 @@ def get_geolink(claims, propertyName, defaultValue=''):
zoom = int(15 - precision * 8.8322 + precision * precision * 0.625447)
url = url_map\
- .replace('{latitude}', str(value.get('latitude', 0)))\
- .replace('{longitude}', str(value.get('longitude', 0)))\
+ .replace('{latitude}', str(lat))\
+ .replace('{longitude}', str(lon))\
.replace('{zoom}', str(zoom))
return url
def get_wikilink(result, wikiid):
- url = result.get('sitelinks', {}).get(wikiid, {}).get('url', None)
- if url is None:
- return url
- elif url.startswith('http://'):
+ url = result.xpath(wikilink_xpath.replace('{wikiid}', wikiid))
+ if not url:
+ return None
+ url = url[0]
+ if url.startswith('http://'):
url = url.replace('http://', 'https://')
elif url.startswith('//'):
url = 'https:' + url
return url
-
-
-def get_wiki_firstlanguage(result, wikipatternid):
- for k in result.get('sitelinks', {}).keys():
- if k.endswith(wikipatternid) and len(k) == (2 + len(wikipatternid)):
- return k[0:2]
- return None
diff --git a/sources/searx/engines/wikipedia.py b/sources/searx/engines/wikipedia.py
index fed7b26..70191d2 100644
--- a/sources/searx/engines/wikipedia.py
+++ b/sources/searx/engines/wikipedia.py
@@ -99,9 +99,8 @@ def response(resp):
return []
# link to wikipedia article
- # parenthesis are not quoted to make infobox mergeable with wikidata's
wikipedia_link = url_lang(resp.search_params['language']) \
- + 'wiki/' + quote(title.replace(' ', '_').encode('utf8')).replace('%28', '(').replace('%29', ')')
+ + 'wiki/' + quote(title.replace(' ', '_').encode('utf8'))
results.append({'url': wikipedia_link, 'title': title})
diff --git a/sources/searx/engines/wolframalpha_noapi.py b/sources/searx/engines/wolframalpha_noapi.py
index 59629b8..3a8180f 100644
--- a/sources/searx/engines/wolframalpha_noapi.py
+++ b/sources/searx/engines/wolframalpha_noapi.py
@@ -8,11 +8,9 @@
# @stable no
# @parse url, infobox
-from cgi import escape
from json import loads
from time import time
from urllib import urlencode
-from lxml.etree import XML
from searx.poolrequests import get as http_get
@@ -36,7 +34,7 @@ search_url = url + 'input/json.jsp'\
referer_url = url + 'input/?{query}'
token = {'value': '',
- 'last_updated': None}
+ 'last_updated': 0}
# pods to display as image in infobox
# this pods do return a plaintext, but they look better and are more useful as images
diff --git a/sources/searx/engines/www500px.py b/sources/searx/engines/www500px.py
index c98e194..f1bc6c5 100644
--- a/sources/searx/engines/www500px.py
+++ b/sources/searx/engines/www500px.py
@@ -41,7 +41,7 @@ def response(resp):
results = []
dom = html.fromstring(resp.text)
- regex = re.compile('3\.jpg.*$')
+ regex = re.compile(r'3\.jpg.*$')
# parse results
for result in dom.xpath('//div[@class="photo"]'):
diff --git a/sources/searx/engines/xpath.py b/sources/searx/engines/xpath.py
index e701c02..e5c0c5b 100644
--- a/sources/searx/engines/xpath.py
+++ b/sources/searx/engines/xpath.py
@@ -87,7 +87,7 @@ def request(query, params):
fp = {'query': query}
if paging and search_url.find('{pageno}') >= 0:
- fp['pageno'] = (params['pageno'] + first_page_num - 1) * page_size
+ fp['pageno'] = (params['pageno'] - 1) * page_size + first_page_num
params['url'] = search_url.format(**fp)
params['query'] = query
diff --git a/sources/searx/engines/yahoo.py b/sources/searx/engines/yahoo.py
index b8b40e4..8e24a28 100644
--- a/sources/searx/engines/yahoo.py
+++ b/sources/searx/engines/yahoo.py
@@ -20,10 +20,12 @@ from searx.engines.xpath import extract_text, extract_url
categories = ['general']
paging = True
language_support = True
+time_range_support = True
# search-url
base_url = 'https://search.yahoo.com/'
search_url = 'search?{query}&b={offset}&fl=1&vl=lang_{lang}'
+search_url_with_time = 'search?{query}&b={offset}&fl=1&vl=lang_{lang}&age={age}&btf={btf}&fr2=time'
# specific xpath variables
results_xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' Sr ')]"
@@ -32,6 +34,10 @@ title_xpath = './/h3/a'
content_xpath = './/div[@class="compText aAbs"]'
suggestion_xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' AlsoTry ')]//a"
+time_range_dict = {'day': ['1d', 'd'],
+ 'week': ['1w', 'w'],
+ 'month': ['1m', 'm']}
+
# remove yahoo-specific tracking-url
def parse_url(url_string):
@@ -51,18 +57,30 @@ def parse_url(url_string):
return unquote(url_string[start:end])
+def _get_url(query, offset, language, time_range):
+ if time_range in time_range_dict:
+ return base_url + search_url_with_time.format(offset=offset,
+ query=urlencode({'p': query}),
+ lang=language,
+ age=time_range_dict[time_range][0],
+ btf=time_range_dict[time_range][1])
+ return base_url + search_url.format(offset=offset,
+ query=urlencode({'p': query}),
+ lang=language)
+
+
+def _get_language(params):
+ if params['language'] == 'all':
+ return 'en'
+ return params['language'].split('_')[0]
+
+
# do search-request
def request(query, params):
offset = (params['pageno'] - 1) * 10 + 1
+ language = _get_language(params)
- if params['language'] == 'all':
- language = 'en'
- else:
- language = params['language'].split('_')[0]
-
- params['url'] = base_url + search_url.format(offset=offset,
- query=urlencode({'p': query}),
- lang=language)
+ params['url'] = _get_url(query, offset, language, params['time_range'])
# TODO required?
params['cookies']['sB'] = 'fl=1&vl=lang_{lang}&sh=1&rw=new&v=1'\
diff --git a/sources/searx/engines/yahoo_news.py b/sources/searx/engines/yahoo_news.py
index d4cfbed..e91c1d3 100644
--- a/sources/searx/engines/yahoo_news.py
+++ b/sources/searx/engines/yahoo_news.py
@@ -55,7 +55,7 @@ def request(query, params):
def sanitize_url(url):
if ".yahoo.com/" in url:
- return re.sub(u"\;\_ylt\=.+$", "", url)
+ return re.sub(u"\\;\\_ylt\\=.+$", "", url)
else:
return url
diff --git a/sources/searx/plugins/__init__.py b/sources/searx/plugins/__init__.py
index efb9b06..768a510 100644
--- a/sources/searx/plugins/__init__.py
+++ b/sources/searx/plugins/__init__.py
@@ -19,15 +19,17 @@ from searx import logger
logger = logger.getChild('plugins')
-from searx.plugins import (https_rewrite,
+from searx.plugins import (doai_rewrite,
+ https_rewrite,
+ infinite_scroll,
open_results_on_new_tab,
self_info,
search_on_category_select,
tracker_url_remover,
vim_hotkeys)
-required_attrs = (('name', str),
- ('description', str),
+required_attrs = (('name', (str, unicode)),
+ ('description', (str, unicode)),
('default_on', bool))
optional_attrs = (('js_dependencies', tuple),
@@ -73,7 +75,9 @@ class PluginStore():
plugins = PluginStore()
+plugins.register(doai_rewrite)
plugins.register(https_rewrite)
+plugins.register(infinite_scroll)
plugins.register(open_results_on_new_tab)
plugins.register(self_info)
plugins.register(search_on_category_select)
diff --git a/sources/searx/plugins/doai_rewrite.py b/sources/searx/plugins/doai_rewrite.py
new file mode 100644
index 0000000..fc5998b
--- /dev/null
+++ b/sources/searx/plugins/doai_rewrite.py
@@ -0,0 +1,31 @@
+from flask_babel import gettext
+import re
+from urlparse import urlparse, parse_qsl
+
+regex = re.compile(r'10\.\d{4,9}/[^\s]+')
+
+name = gettext('DOAI rewrite')
+description = gettext('Avoid paywalls by redirecting to open-access versions of publications when available')
+default_on = False
+
+
+def extract_doi(url):
+ match = regex.search(url.path)
+ if match:
+ return match.group(0)
+ for _, v in parse_qsl(url.query):
+ match = regex.search(v)
+ if match:
+ return match.group(0)
+ return None
+
+
+def on_result(request, ctx):
+ doi = extract_doi(ctx['result']['parsed_url'])
+ if doi and len(doi) < 50:
+ for suffix in ('/', '.pdf', '/full', '/meta', '/abstract'):
+ if doi.endswith(suffix):
+ doi = doi[:-len(suffix)]
+ ctx['result']['url'] = 'http://doai.io/' + doi
+ ctx['result']['parsed_url'] = urlparse(ctx['result']['url'])
+ return True
diff --git a/sources/searx/plugins/https_rewrite.py b/sources/searx/plugins/https_rewrite.py
index 0a58cc8..8a9fcd4 100644
--- a/sources/searx/plugins/https_rewrite.py
+++ b/sources/searx/plugins/https_rewrite.py
@@ -21,7 +21,7 @@ from lxml import etree
from os import listdir, environ
from os.path import isfile, isdir, join
from searx.plugins import logger
-from flask.ext.babel import gettext
+from flask_babel import gettext
from searx import searx_dir
@@ -87,7 +87,7 @@ def load_single_https_ruleset(rules_path):
# convert host-rule to valid regex
host = ruleset.attrib.get('host')\
- .replace('.', '\.').replace('*', '.*')
+ .replace('.', r'\.').replace('*', '.*')
# append to host list
hosts.append(host)
diff --git a/sources/searx/plugins/infinite_scroll.py b/sources/searx/plugins/infinite_scroll.py
new file mode 100644
index 0000000..422a4be
--- /dev/null
+++ b/sources/searx/plugins/infinite_scroll.py
@@ -0,0 +1,8 @@
+from flask_babel import gettext
+
+name = gettext('Infinite scroll')
+description = gettext('Automatically load next page when scrolling to bottom of current page')
+default_on = False
+
+js_dependencies = ('plugins/js/infinite_scroll.js',)
+css_dependencies = ('plugins/css/infinite_scroll.css',)
diff --git a/sources/searx/plugins/open_results_on_new_tab.py b/sources/searx/plugins/open_results_on_new_tab.py
index 5ebece1..ae27ea2 100644
--- a/sources/searx/plugins/open_results_on_new_tab.py
+++ b/sources/searx/plugins/open_results_on_new_tab.py
@@ -14,7 +14,7 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
(C) 2016 by Adam Tauber,
'''
-from flask.ext.babel import gettext
+from flask_babel import gettext
name = gettext('Open result links on new browser tabs')
description = gettext('Results are opened in the same window by default. '
'This plugin overwrites the default behaviour to open links on new tabs/windows. '
diff --git a/sources/searx/plugins/search_on_category_select.py b/sources/searx/plugins/search_on_category_select.py
index 53585fa..f72c63d 100644
--- a/sources/searx/plugins/search_on_category_select.py
+++ b/sources/searx/plugins/search_on_category_select.py
@@ -14,7 +14,7 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
(C) 2015 by Adam Tauber,
'''
-from flask.ext.babel import gettext
+from flask_babel import gettext
name = gettext('Search on category select')
description = gettext('Perform search immediately if a category selected. '
'Disable to select multiple categories. (JavaScript required)')
diff --git a/sources/searx/plugins/self_info.py b/sources/searx/plugins/self_info.py
index dc6b7cd..438274c 100644
--- a/sources/searx/plugins/self_info.py
+++ b/sources/searx/plugins/self_info.py
@@ -14,7 +14,7 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
(C) 2015 by Adam Tauber,
'''
-from flask.ext.babel import gettext
+from flask_babel import gettext
import re
name = "Self Informations"
description = gettext('Displays your IP if the query is "ip" and your user agent if the query contains "user agent".')
@@ -29,6 +29,8 @@ p = re.compile('.*user[ -]agent.*', re.IGNORECASE)
# request: flask request object
# ctx: the whole local context of the pre search hook
def post_search(request, ctx):
+ if ctx['search'].pageno > 1:
+ return True
if ctx['search'].query == 'ip':
x_forwarded_for = request.headers.getlist("X-Forwarded-For")
if x_forwarded_for:
diff --git a/sources/searx/plugins/tracker_url_remover.py b/sources/searx/plugins/tracker_url_remover.py
index ed71c94..b909e3f 100644
--- a/sources/searx/plugins/tracker_url_remover.py
+++ b/sources/searx/plugins/tracker_url_remover.py
@@ -15,7 +15,7 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
(C) 2015 by Adam Tauber,
'''
-from flask.ext.babel import gettext
+from flask_babel import gettext
import re
from urlparse import urlunparse
diff --git a/sources/searx/plugins/vim_hotkeys.py b/sources/searx/plugins/vim_hotkeys.py
index e537a3a..8f06f13 100644
--- a/sources/searx/plugins/vim_hotkeys.py
+++ b/sources/searx/plugins/vim_hotkeys.py
@@ -1,4 +1,4 @@
-from flask.ext.babel import gettext
+from flask_babel import gettext
name = gettext('Vim-like hotkeys')
description = gettext('Navigate search results with Vim-like hotkeys '
diff --git a/sources/searx/preferences.py b/sources/searx/preferences.py
index dd9133d..ed7b6f6 100644
--- a/sources/searx/preferences.py
+++ b/sources/searx/preferences.py
@@ -166,6 +166,7 @@ class SwitchableSetting(Setting):
class EnginesSetting(SwitchableSetting):
+
def _post_init(self):
super(EnginesSetting, self)._post_init()
transformed_choices = []
@@ -191,6 +192,7 @@ class EnginesSetting(SwitchableSetting):
class PluginsSetting(SwitchableSetting):
+
def _post_init(self):
super(PluginsSetting, self)._post_init()
transformed_choices = []
@@ -225,7 +227,8 @@ class Preferences(object):
'safesearch': MapSetting(settings['search']['safe_search'], map={'0': 0,
'1': 1,
'2': 2}),
- 'theme': EnumStringSetting(settings['ui']['default_theme'], choices=themes)}
+ 'theme': EnumStringSetting(settings['ui']['default_theme'], choices=themes),
+ 'results_on_new_tab': MapSetting(False, map={'0': False, '1': True})}
self.engines = EnginesSetting('engines', choices=engines)
self.plugins = PluginsSetting('plugins', choices=plugins)
diff --git a/sources/searx/results.py b/sources/searx/results.py
index dcd966e..32832f1 100644
--- a/sources/searx/results.py
+++ b/sources/searx/results.py
@@ -5,7 +5,7 @@ from threading import RLock
from urlparse import urlparse, unquote
from searx.engines import engines
-CONTENT_LEN_IGNORED_CHARS_REGEX = re.compile('[,;:!?\./\\\\ ()-_]', re.M | re.U)
+CONTENT_LEN_IGNORED_CHARS_REGEX = re.compile(r'[,;:!?\./\\\\ ()-_]', re.M | re.U)
WHITESPACE_REGEX = re.compile('( |\t|\n)+', re.M | re.U)
@@ -18,7 +18,17 @@ def result_content_len(content):
def compare_urls(url_a, url_b):
- if url_a.netloc != url_b.netloc or url_a.query != url_b.query:
+ # ignore www. in comparison
+ if url_a.netloc.startswith('www.'):
+ host_a = url_a.netloc.replace('www.', '', 1)
+ else:
+ host_a = url_a.netloc
+ if url_b.netloc.startswith('www.'):
+ host_b = url_b.netloc.replace('www.', '', 1)
+ else:
+ host_b = url_b.netloc
+
+ if host_a != host_b or url_a.query != url_b.query or url_a.fragment != url_b.fragment:
return False
# remove / from the end of the url if required
@@ -33,25 +43,42 @@ def compare_urls(url_a, url_b):
def merge_two_infoboxes(infobox1, infobox2):
+ # get engines weights
+ if hasattr(engines[infobox1['engine']], 'weight'):
+ weight1 = engines[infobox1['engine']].weight
+ else:
+ weight1 = 1
+ if hasattr(engines[infobox2['engine']], 'weight'):
+ weight2 = engines[infobox2['engine']].weight
+ else:
+ weight2 = 1
+
+ if weight2 > weight1:
+ infobox1['engine'] = infobox2['engine']
+
if 'urls' in infobox2:
urls1 = infobox1.get('urls', None)
if urls1 is None:
urls1 = []
- infobox1['urls'] = urls1
- urlSet = set()
- for url in infobox1.get('urls', []):
- urlSet.add(url.get('url', None))
+ for url2 in infobox2.get('urls', []):
+ unique_url = True
+ for url1 in infobox1.get('urls', []):
+ if compare_urls(urlparse(url1.get('url', '')), urlparse(url2.get('url', ''))):
+ unique_url = False
+ break
+ if unique_url:
+ urls1.append(url2)
- for url in infobox2.get('urls', []):
- if url.get('url', None) not in urlSet:
- urls1.append(url)
+ infobox1['urls'] = urls1
if 'img_src' in infobox2:
img1 = infobox1.get('img_src', None)
img2 = infobox2.get('img_src')
if img1 is None:
infobox1['img_src'] = img2
+ elif weight2 > weight1:
+ infobox1['img_src'] = img2
if 'attributes' in infobox2:
attributes1 = infobox1.get('attributes', None)
@@ -65,7 +92,8 @@ def merge_two_infoboxes(infobox1, infobox2):
attributeSet.add(attribute.get('label', None))
for attribute in infobox2.get('attributes', []):
- attributes1.append(attribute)
+ if attribute.get('label', None) not in attributeSet:
+ attributes1.append(attribute)
if 'content' in infobox2:
content1 = infobox1.get('content', None)
@@ -91,15 +119,15 @@ def result_score(result):
class ResultContainer(object):
"""docstring for ResultContainer"""
+
def __init__(self):
super(ResultContainer, self).__init__()
self.results = defaultdict(list)
self._merged_results = []
self.infoboxes = []
- self._infobox_ids = {}
self.suggestions = set()
self.answers = set()
- self.number_of_results = 0
+ self._number_of_results = []
def extend(self, engine_name, results):
for result in list(results):
@@ -113,7 +141,7 @@ class ResultContainer(object):
self._merge_infobox(result)
results.remove(result)
elif 'number_of_results' in result:
- self.number_of_results = max(self.number_of_results, result['number_of_results'])
+ self._number_of_results.append(result['number_of_results'])
results.remove(result)
with RLock():
@@ -137,14 +165,13 @@ class ResultContainer(object):
add_infobox = True
infobox_id = infobox.get('id', None)
if infobox_id is not None:
- existingIndex = self._infobox_ids.get(infobox_id, None)
- if existingIndex is not None:
- merge_two_infoboxes(self.infoboxes[existingIndex], infobox)
- add_infobox = False
+ for existingIndex in self.infoboxes:
+ if compare_urls(urlparse(existingIndex.get('id', '')), urlparse(infobox_id)):
+ merge_two_infoboxes(existingIndex, infobox)
+ add_infobox = False
if add_infobox:
self.infoboxes.append(infobox)
- self._infobox_ids[infobox_id] = len(self.infoboxes) - 1
def _merge_result(self, result, position):
result['parsed_url'] = urlparse(result['url'])
@@ -154,11 +181,6 @@ class ResultContainer(object):
result['parsed_url'] = result['parsed_url']._replace(scheme="http")
result['url'] = result['parsed_url'].geturl()
- result['host'] = result['parsed_url'].netloc
-
- if result['host'].startswith('www.'):
- result['host'] = result['host'].replace('www.', '', 1)
-
result['engines'] = [result['engine']]
# strip multiple spaces and cariage returns from content
@@ -252,3 +274,9 @@ class ResultContainer(object):
def results_length(self):
return len(self._merged_results)
+
+ def results_number(self):
+ resultnum_sum = sum(self._number_of_results)
+ if not resultnum_sum or not self._number_of_results:
+ return 0
+ return resultnum_sum / len(self._number_of_results)
diff --git a/sources/searx/search.py b/sources/searx/search.py
index a408016..c6d17eb 100644
--- a/sources/searx/search.py
+++ b/sources/searx/search.py
@@ -15,14 +15,15 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
(C) 2013- by Adam Tauber,
'''
+import gc
import threading
-import searx.poolrequests as requests_lib
+from thread import start_new_thread
from time import time
-from searx import settings
+from uuid import uuid4
+import searx.poolrequests as requests_lib
from searx.engines import (
categories, engines
)
-from searx.languages import language_codes
from searx.utils import gen_useragent
from searx.query import Query
from searx.results import ResultContainer
@@ -56,19 +57,20 @@ def search_request_wrapper(fn, url, engine_name, **kwargs):
def threaded_requests(requests):
timeout_limit = max(r[2]['timeout'] for r in requests)
search_start = time()
+ search_id = uuid4().__str__()
for fn, url, request_args, engine_name in requests:
request_args['timeout'] = timeout_limit
th = threading.Thread(
target=search_request_wrapper,
args=(fn, url, engine_name),
kwargs=request_args,
- name='search_request',
+ name=search_id,
)
th._engine_name = engine_name
th.start()
for th in threading.enumerate():
- if th.name == 'search_request':
+ if th.name == search_id:
remaining_time = max(0.0, timeout_limit - (time() - search_start))
th.join(remaining_time)
if th.isAlive():
@@ -138,6 +140,8 @@ class Search(object):
self.paging = False
self.pageno = 1
self.lang = 'all'
+ self.time_range = None
+ self.is_advanced = None
# set blocked engines
self.disabled_engines = request.preferences.engines.get_disabled()
@@ -178,9 +182,10 @@ class Search(object):
if len(query_obj.languages):
self.lang = query_obj.languages[-1]
- self.engines = query_obj.engines
+ self.time_range = self.request_data.get('time_range')
+ self.is_advanced = self.request_data.get('advanced_search')
- self.categories = []
+ self.engines = query_obj.engines
# if engines are calculated from query,
# set categories by using that informations
@@ -279,6 +284,9 @@ class Search(object):
if self.lang != 'all' and not engine.language_support:
continue
+ if self.time_range and not engine.time_range_support:
+ continue
+
# set default request parameters
request_params = default_request_params()
request_params['headers']['User-Agent'] = user_agent
@@ -293,6 +301,8 @@ class Search(object):
# 0 = None, 1 = Moderate, 2 = Strict
request_params['safesearch'] = request.preferences.get_value('safesearch')
+ request_params['time_range'] = self.time_range
+ request_params['advanced_search'] = self.is_advanced
# update request parameters dependent on
# search-engine (contained in engines folder)
@@ -339,6 +349,7 @@ class Search(object):
return self
# send all search-request
threaded_requests(requests)
+ start_new_thread(gc.collect, tuple())
# return results, suggestions, answers and infoboxes
return self
diff --git a/sources/searx/settings.yml b/sources/searx/settings.yml
index 9308756..4969c4a 100644
--- a/sources/searx/settings.yml
+++ b/sources/searx/settings.yml
@@ -25,7 +25,7 @@ outgoing: # communication with search engines
pool_maxsize : 10 # Number of simultaneous requests by host
# uncomment below section if you want to use a proxy
# see http://docs.python-requests.org/en/latest/user/advanced/#proxies
-# SOCKS proxies are not supported : see https://github.com/kennethreitz/requests/pull/478
+# SOCKS proxies are also supported: see http://docs.python-requests.org/en/master/user/advanced/#socks
# proxies :
# http : http://127.0.0.1:8080
# https: http://127.0.0.1:8080
@@ -84,9 +84,15 @@ engines:
disabled : True
shortcut : bb
- - name : btdigg
- engine : btdigg
- shortcut : bt
+ - name : crossref
+ engine : json_engine
+ paging : True
+ search_url : http://search.crossref.org/dois?q={query}&page={pageno}
+ url_query : doi
+ title_query : title
+ content_query : fullCitation
+ categories : science
+ shortcut : cr
- name : currency
engine : currency_convert
@@ -105,6 +111,13 @@ engines:
- name : ddg definitions
engine : duckduckgo_definitions
shortcut : ddd
+ weight : 2
+ disabled : True
+
+ - name : digbt
+ engine : digbt
+ shortcut : dbt
+ timeout : 6.0
disabled : True
- name : digg
@@ -127,10 +140,12 @@ engines:
- name : wikidata
engine : wikidata
shortcut : wd
+ weight : 2
- name : duckduckgo
engine : duckduckgo
shortcut : ddg
+ disabled : True
# api-key required: http://www.faroo.com/hp/api/api.html#key
# - name : faroo
@@ -200,6 +215,20 @@ engines:
engine : google_news
shortcut : gon
+ - name : google scholar
+ engine : xpath
+ paging : True
+ search_url : https://scholar.google.com/scholar?start={pageno}&q={query}&hl=en&as_sdt=0,5&as_vis=1
+ results_xpath : //div[@class="gs_r"]/div[@class="gs_ri"]
+ url_xpath : .//h3/a/@href
+ title_xpath : .//h3/a
+ content_xpath : .//div[@class="gs_rs"]
+ suggestion_xpath : //div[@id="gs_qsuggest"]/ul/li
+ page_size : 10
+ first_page_num : 0
+ categories : science
+ shortcut : gos
+
- name : google play apps
engine : xpath
search_url : https://play.google.com/store/search?q={query}&c=apps
@@ -254,6 +283,37 @@ engines:
disabled : True
shortcut : habr
+ - name : hoogle
+ engine : json_engine
+ paging : True
+ search_url : https://www.haskell.org/hoogle/?mode=json&hoogle={query}&start={pageno}
+ results_query : results
+ url_query : location
+ title_query : self
+ content_query : docs
+ page_size : 20
+ categories : it
+ shortcut : ho
+
+ - name : ina
+ engine : ina
+ shortcut : in
+ timeout : 6.0
+ disabled : True
+
+ - name : microsoft academic
+ engine : json_engine
+ paging : True
+ search_url : https://academic.microsoft.com/api/search/GetEntityResults?query=%40{query}%40&filters=&offset={pageno}&limit=8&correlationId=undefined
+ results_query : results
+ url_query : u
+ title_query : dn
+ content_query : d
+ page_size : 8
+ first_page_num : 0
+ categories : science
+ shortcut : ma
+
- name : mixcloud
engine : mixcloud
shortcut : mc
@@ -267,6 +327,18 @@ engines:
engine : openstreetmap
shortcut : osm
+ - name : openrepos
+ engine : xpath
+ paging : True
+ search_url : https://openrepos.net/search/node/{query}?page={pageno}
+ url_xpath : //li[@class="search-result"]//h3[@class="title"]/a/@href
+ title_xpath : //li[@class="search-result"]//h3[@class="title"]/a
+ content_xpath : //li[@class="search-result"]//div[@class="search-snippet-info"]//p[@class="search-snippet"]
+ categories : files
+ timeout : 4.0
+ disabled : True
+ shortcut : or
+
- name : photon
engine : photon
shortcut : ph
@@ -274,7 +346,8 @@ engines:
- name : piratebay
engine : piratebay
shortcut : tpb
- disabled : True
+ url: https://pirateproxy.red/
+ timeout : 3.0
- name : qwant
engine : qwant
@@ -304,9 +377,10 @@ engines:
timeout : 10.0
disabled : True
- - name : kickass
- engine : kickass
- shortcut : ka
+ - name : scanr_structures
+ shortcut: scs
+ engine : scanr_structures
+ disabled : True
- name : soundcloud
engine : soundcloud
@@ -361,11 +435,6 @@ engines:
timeout : 6.0
disabled : True
- - name : torrentz
- engine : torrentz
- timeout : 5.0
- shortcut : to
-
- name : twitter
engine : twitter
shortcut : tw
@@ -426,6 +495,19 @@ engines:
timeout: 6.0
categories : science
+ - name : dictzone
+ engine : dictzone
+ shortcut : dc
+
+ - name : mymemory translated
+ engine : translated
+ shortcut : tl
+ timeout : 5.0
+ disabled : True
+ # You can use without an API key, but you are limited to 1000 words/day
+ # See : http://mymemory.translated.net/doc/usagelimits.php
+ # api_key : ''
+
#The blekko technology and team have joined IBM Watson! -> https://blekko.com/
# - name : blekko images
# engine : blekko_images
diff --git a/sources/searx/static/plugins/css/infinite_scroll.css b/sources/searx/static/plugins/css/infinite_scroll.css
new file mode 100644
index 0000000..7e0ee20
--- /dev/null
+++ b/sources/searx/static/plugins/css/infinite_scroll.css
@@ -0,0 +1,16 @@
+@keyframes rotate-forever {
+ 0% { transform: rotate(0deg) }
+ 100% { transform: rotate(360deg) }
+}
+.loading-spinner {
+ animation-duration: 0.75s;
+ animation-iteration-count: infinite;
+ animation-name: rotate-forever;
+ animation-timing-function: linear;
+ height: 30px;
+ width: 30px;
+ border: 8px solid #666;
+ border-right-color: transparent;
+ border-radius: 50% !important;
+ margin: 0 auto;
+}
diff --git a/sources/searx/static/plugins/js/infinite_scroll.js b/sources/searx/static/plugins/js/infinite_scroll.js
new file mode 100644
index 0000000..213f74b
--- /dev/null
+++ b/sources/searx/static/plugins/js/infinite_scroll.js
@@ -0,0 +1,18 @@
+$(document).ready(function() {
+ var win = $(window);
+ win.scroll(function() {
+ if ($(document).height() - win.height() == win.scrollTop()) {
+ var formData = $('#pagination form:last').serialize();
+ if (formData) {
+ $('#pagination').html('');
+ $.post('/', formData, function (data) {
+ var body = $(data);
+ $('#pagination').remove();
+ $('#main_results').append('
');
+ $('#main_results').append(body.find('.result'));
+ $('#main_results').append(body.find('#pagination'));
+ });
+ }
+ }
+ });
+});
diff --git a/sources/searx/static/plugins/js/search_on_category_select.js b/sources/searx/static/plugins/js/search_on_category_select.js
index 5ecc2cd..19aeef9 100644
--- a/sources/searx/static/plugins/js/search_on_category_select.js
+++ b/sources/searx/static/plugins/js/search_on_category_select.js
@@ -4,13 +4,16 @@ $(document).ready(function() {
$('#categories input[type="checkbox"]').each(function(i, checkbox) {
$(checkbox).prop('checked', false);
});
- $('#categories label').removeClass('btn-primary').removeClass('active').addClass('btn-default');
- $(this).removeClass('btn-default').addClass('btn-primary').addClass('active');
- $($(this).children()[0]).prop('checked', 'checked');
+ $(document.getElementById($(this).attr("for"))).prop('checked', true);
if($('#q').val()) {
$('#search_form').submit();
}
return false;
});
+ $('#time-range > option').click(function(e) {
+ if($('#q').val()) {
+ $('#search_form').submit();
+ }
+ });
}
});
diff --git a/sources/searx/static/themes/oscar/css/logicodev.min.css b/sources/searx/static/themes/oscar/css/logicodev.min.css
index 4f9fffb..bd58aa5 100644
--- a/sources/searx/static/themes/oscar/css/logicodev.min.css
+++ b/sources/searx/static/themes/oscar/css/logicodev.min.css
@@ -1 +1 @@
-.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{background:#29314d;color:#01d7d4}.navbar>li>a{padding:0;margin:0}.navbar-nav>li>a{background:#29314d;padding:0 8px;margin:0;line-height:30px}.navbar,.navbar-default{background-color:#29314d;border:none;border-top:4px solid #01d7d4;padding-top:5px;color:#f6f9fa!important;font-weight:700;font-size:1.1em;text-transform:lowercase;margin-bottom:24px;height:30px;line-height:30px;z-index:1}.navbar .navbar-nav>li>a,.navbar-default .navbar-nav>li>a{color:#f6f9fa}.navbar .navbar-brand,.navbar-default .navbar-brand{font-weight:700;color:#01d7d4;line-height:30px;padding:0 30px;margin:0}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#01d7d4;background:#29314d}.navbar-toggle{margin-top:0}*{border-radius:0!important}html{position:relative;min-height:100%;color:#29314d}body{font-family:Roboto,Helvetica,Arial,sans-serif;margin-bottom:80px;background-color:#fff}body a{color:#08c}.footer{position:absolute;bottom:0;width:100%;height:60px;text-align:center;color:#999}input[type=checkbox]:checked+.label_hide_if_checked,input[type=checkbox]:checked+.label_hide_if_not_checked+.label_hide_if_checked{display:none}input[type=checkbox]:not(:checked)+.label_hide_if_not_checked,input[type=checkbox]:not(:checked)+.label_hide_if_checked+.label_hide_if_not_checked{display:none}.result_header{margin-top:6px;margin-bottom:4px;font-size:16px}.result_header .favicon{margin-bottom:-3px}.result_header a{color:#29314d;text-decoration:none}.result_header a:hover{color:#08c}.result_header a:visited{color:#684898}.result_header a .highlight{background-color:#f6f9fa}.result-content{margin-top:2px;margin-bottom:0;word-wrap:break-word;color:#666;font-size:13px}.result-content .highlight{font-weight:700}.external-link,.external-link a{color:#2ecc71}.external-link a,.external-link a a{margin-right:3px}.result-default,.result-code,.result-torrent,.result-videos,.result-map{clear:both;padding:2px 4px}.result-default:hover,.result-code:hover,.result-torrent:hover,.result-videos:hover,.result-map:hover{background-color:#f6f9fa}.result-images{float:left!important;width:24%;margin:.5%}.result-images a{display:block;width:100%;height:170px;background-size:cover}.img-thumbnail{margin:5px;max-height:128px;min-height:128px}.result-videos{clear:both}.result-videos hr{margin:5px 0 15px 0}.result-videos .collapse{width:100%}.result-videos .in{margin-bottom:8px}.result-torrent{clear:both}.result-torrent b{margin-right:5px;margin-left:5px}.result-torrent .seeders{color:#2ecc71}.result-torrent .leechers{color:#f35e77}.result-map{clear:both}.result-code{clear:both}.result-code .code-fork,.result-code .code-fork a{color:#666}.suggestion_item{margin:2px 5px}.result_download{margin-right:5px}#pagination{margin-top:30px;padding-bottom:60px}.label-default{color:#a4a4a4;background:0 0}.infobox .panel-heading{background-color:#f6f9fa}.infobox .panel-heading .panel-title{font-weight:700}.infobox p{font-family:"DejaVu Serif",Georgia,Cambria,"Times New Roman",Times,serif!important;font-style:italic}.infobox .btn{background-color:#2ecc71;border:none}.infobox .btn a{color:#fff;margin:5px}.infobox .infobox_part{margin-bottom:20px;word-wrap:break-word;table-layout:fixed}.infobox .infobox_part:last-child{margin-bottom:0}.search_categories,#categories{margin:10px 0 4px 0;text-transform:capitalize}.search_categories label,#categories label{border:none;box-shadow:none;font-size:13px;padding-bottom:2px;color:#a4a4a4;margin-bottom:5px}.search_categories label:hover,#categories label:hover{color:#29314d;background-color:transparent}.search_categories label:active,#categories label:active{box-shadow:none}.search_categories .active,#categories .active,.search_categories .btn-primary,#categories .btn-primary{color:#29314d;font-weight:700;border-bottom:5px solid #01d7d4;background-color:transparent}#categories{margin:0}#main-logo{margin-top:10vh;margin-bottom:25px}#main-logo>img{max-width:350px;width:80%}#q{box-shadow:none;border-right:none;border-color:#a4a4a4}#search_form .input-group-btn .btn{border-color:#a4a4a4}#search_form .input-group-btn .btn:hover{background-color:#2ecc71;color:#fff}.cursor-text{cursor:text!important}.cursor-pointer{cursor:pointer!important}pre,code{font-family:'Ubuntu Mono','Courier New','Lucida Console',monospace!important}.lineno{margin-right:5px}.highlight .hll{background-color:#ffc}.highlight{background:#f8f8f8}.highlight .c{color:#556366;font-style:italic}.highlight .err{border:1px solid #ffa92f}.highlight .k{color:#BE74D5;font-weight:700}.highlight .o{color:#d19a66}.highlight .cm{color:#556366;font-style:italic}.highlight .cp{color:#bc7a00}.highlight .c1{color:#556366;font-style:italic}.highlight .cs{color:#556366;font-style:italic}.highlight .gd{color:#a00000}.highlight .ge{font-style:italic}.highlight .gr{color:red}.highlight .gh{color:navy;font-weight:700}.highlight .gi{color:#00a000}.highlight .go{color:#888}.highlight .gp{color:navy;font-weight:700}.highlight .gs{font-weight:700}.highlight .gu{color:purple;font-weight:700}.highlight .gt{color:#04d}.highlight .kc{color:#BE74D5;font-weight:700}.highlight .kd{color:#BE74D5;font-weight:700}.highlight .kn{color:#BE74D5;font-weight:700}.highlight .kp{color:#be74d5}.highlight .kr{color:#BE74D5;font-weight:700}.highlight .kt{color:#d46c72}.highlight .m{color:#d19a66}.highlight .s{color:#86c372}.highlight .na{color:#7d9029}.highlight .nb{color:#be74d5}.highlight .nc{color:#61AFEF;font-weight:700}.highlight .no{color:#d19a66}.highlight .nd{color:#a2f}.highlight .ni{color:#999;font-weight:700}.highlight .ne{color:#D2413A;font-weight:700}.highlight .nf{color:#61afef}.highlight .nl{color:#a0a000}.highlight .nn{color:#61AFEF;font-weight:700}.highlight .nt{color:#BE74D5;font-weight:700}.highlight .nv{color:#dfc06f}.highlight .ow{color:#A2F;font-weight:700}.highlight .w{color:#d7dae0}.highlight .mf{color:#d19a66}.highlight .mh{color:#d19a66}.highlight .mi{color:#d19a66}.highlight .mo{color:#d19a66}.highlight .sb{color:#86c372}.highlight .sc{color:#86c372}.highlight .sd{color:#86C372;font-style:italic}.highlight .s2{color:#86c372}.highlight .se{color:#B62;font-weight:700}.highlight .sh{color:#86c372}.highlight .si{color:#B68;font-weight:700}.highlight .sx{color:#be74d5}.highlight .sr{color:#b68}.highlight .s1{color:#86c372}.highlight .ss{color:#dfc06f}.highlight .bp{color:#be74d5}.highlight .vc{color:#dfc06f}.highlight .vg{color:#dfc06f}.highlight .vi{color:#dfc06f}.highlight .il{color:#d19a66}.highlight .lineno{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default;color:#556366}.highlight .lineno::selection{background:0 0}.highlight .lineno::-moz-selection{background:0 0}.highlight pre{background-color:#282C34;color:#D7DAE0;border:none;margin-bottom:25px;font-size:15px;padding:20px 10px}.highlight{font-weight:700}
\ No newline at end of file
+.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{background:#29314d;color:#01d7d4}.navbar>li>a{padding:0;margin:0}.navbar-nav>li>a{background:#29314d;padding:0 8px;margin:0;line-height:30px}.navbar,.navbar-default{background-color:#29314d;border:none;border-top:4px solid #01d7d4;padding-top:5px;color:#f6f9fa!important;font-weight:700;font-size:1.1em;text-transform:lowercase;margin-bottom:24px;height:30px;line-height:30px;z-index:10}.navbar .navbar-nav>li>a,.navbar-default .navbar-nav>li>a{color:#f6f9fa}.navbar .navbar-brand,.navbar-default .navbar-brand{font-weight:700;color:#01d7d4;line-height:30px;padding:0 30px;margin:0}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#01d7d4;background:#29314d}.navbar-toggle{margin-top:0}*{border-radius:0!important}html{position:relative;min-height:100%;color:#29314d}body{font-family:Roboto,Helvetica,Arial,sans-serif;margin-bottom:80px;background-color:#fff}body a{color:#08c}.footer{position:absolute;bottom:0;width:100%;height:60px;text-align:center;color:#999}input[type=checkbox]:checked+.label_hide_if_checked,input[type=checkbox]:checked+.label_hide_if_not_checked+.label_hide_if_checked{display:none}input[type=checkbox]:not(:checked)+.label_hide_if_not_checked,input[type=checkbox]:not(:checked)+.label_hide_if_checked+.label_hide_if_not_checked{display:none}.onoff-checkbox{width:15%}.onoffswitch{position:relative;width:110px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.onoffswitch-checkbox{display:none}.onoffswitch-label{display:block;overflow:hidden;cursor:pointer;border:2px solid #FFF!important;border-radius:50px!important}.onoffswitch-inner{display:block;transition:margin .3s ease-in 0s}.onoffswitch-inner:before,.onoffswitch-inner:after{display:block;float:left;width:50%;height:30px;padding:0;line-height:40px;font-size:20px;box-sizing:border-box;content:"";background-color:#EEE}.onoffswitch-switch{display:block;width:37px;background-color:#01d7d4;position:absolute;top:0;bottom:0;right:0;border:2px solid #FFF!important;border-radius:50px!important;transition:all .3s ease-in 0s}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-inner{margin-right:0}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-switch{right:71px;background-color:#A1A1A1}.result_header{margin-top:6px;margin-bottom:4px;font-size:16px}.result_header .favicon{margin-bottom:-3px}.result_header a{color:#29314d;text-decoration:none}.result_header a:hover{color:#08c}.result_header a:visited{color:#684898}.result_header a .highlight{background-color:#f6f9fa}.result-content{margin-top:2px;margin-bottom:0;word-wrap:break-word;color:#666;font-size:13px}.result-content .highlight{font-weight:700}.external-link,.external-link a{color:#2ecc71}.external-link a,.external-link a a{margin-right:3px}.result-default,.result-code,.result-torrent,.result-videos,.result-map{clear:both;padding:2px 4px}.result-default:hover,.result-code:hover,.result-torrent:hover,.result-videos:hover,.result-map:hover{background-color:#f6f9fa}.result-images{float:left!important;width:24%;margin:.5%}.result-images a{display:block;width:100%;background-size:cover}.img-thumbnail{margin:5px;max-height:128px;min-height:128px}.result-videos{clear:both}.result-videos hr{margin:5px 0 15px 0}.result-videos .collapse{width:100%}.result-videos .in{margin-bottom:8px}.result-torrent{clear:both}.result-torrent b{margin-right:5px;margin-left:5px}.result-torrent .seeders{color:#2ecc71}.result-torrent .leechers{color:#f35e77}.result-map{clear:both}.result-code{clear:both}.result-code .code-fork,.result-code .code-fork a{color:#666}.suggestion_item{margin:2px 5px}.result_download{margin-right:5px}#pagination{margin-top:30px;padding-bottom:60px}.label-default{color:#a4a4a4;background:0 0}.result .text-muted small{word-wrap:break-word}.modal-wrapper{box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-wrapper{background-clip:padding-box;background-color:#fff;border:1px solid rgba(0,0,0,.2);border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,.5);outline:0 none;position:relative}.infobox .panel-heading{background-color:#f6f9fa}.infobox .panel-heading .panel-title{font-weight:700}.infobox p{font-family:"DejaVu Serif",Georgia,Cambria,"Times New Roman",Times,serif!important;font-style:italic}.infobox .btn{background-color:#2ecc71;border:none}.infobox .btn a{color:#fff;margin:5px}.infobox .infobox_part{margin-bottom:20px;word-wrap:break-word;table-layout:fixed}.infobox .infobox_part:last-child{margin-bottom:0}.search_categories,#categories{text-transform:capitalize;margin-bottom:.5rem;display:flex;flex-wrap:wrap;flex-flow:row wrap;align-content:stretch}.search_categories label,#categories label,.search_categories .input-group-addon,#categories .input-group-addon{flex-grow:1;flex-basis:auto;font-size:1.2rem;font-weight:400;background-color:#fff;border:#ddd 1px solid;border-right:none;color:#666;padding-bottom:.4rem;padding-top:.4rem;text-align:center}.search_categories label:last-child,#categories label:last-child,.search_categories .input-group-addon:last-child,#categories .input-group-addon:last-child{border-right:#ddd 1px solid}.search_categories input[type=checkbox]:checked+label,#categories input[type=checkbox]:checked+label{color:#29314d;font-weight:700;border-bottom:#01d7d4 5px solid}#main-logo{margin-top:10vh;margin-bottom:25px}#main-logo>img{max-width:350px;width:80%}#q{box-shadow:none;border-right:none;border-color:#a4a4a4}#search_form .input-group-btn .btn{border-color:#a4a4a4}#search_form .input-group-btn .btn:hover{background-color:#2ecc71;color:#fff}#advanced-search-container{display:none;text-align:left;margin-bottom:1rem;clear:both}#advanced-search-container label,#advanced-search-container .input-group-addon{font-size:1.2rem;font-weight:400;background-color:#fff;border:#ddd 1px solid;border-right:none;color:#666;padding-bottom:.4rem;padding-right:.7rem;padding-left:.7rem}#advanced-search-container label:last-child,#advanced-search-container .input-group-addon:last-child{border-right:#ddd 1px solid}#advanced-search-container input[type=radio]{display:none}#advanced-search-container input[type=radio]:checked+label{color:#29314d;font-weight:700;border-bottom:#01d7d4 5px solid}#advanced-search-container select{appearance:none;-webkit-appearance:none;-moz-appearance:none;font-size:1.2rem;font-weight:400;background-color:#fff;border:#ddd 1px solid;color:#666;padding-bottom:.4rem;padding-top:.4rem;padding-left:1rem;padding-right:5rem;margin-right:.5rem;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAQAAACR313BAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAABFkAAARZAVnbJUkAAAAHdElNRQfgBxgLDwB20OFsAAAAbElEQVQY073OsQ3CMAAEwJMYwJGnsAehpoXJItltBkmcdZBYgIIiQoLglnz3ui+eP+bk5uneteTMZJa6OJuIqvYzSJoqwqBq8gdmTTW86/dghxAUq4xsVYT9laBYXCw93Aajh7GPEF23t4fkBYevGFTANkPRAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE2LTA3LTI0VDExOjU1OjU4KzAyOjAwRFqFOQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNi0wNy0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAAElFTkSuQmCC) 96% no-repeat}#check-advanced{display:none}#check-advanced:checked~#advanced-search-container{display:block}.advanced{padding:0;margin-top:.3rem;text-align:right}.advanced label,.advanced select{cursor:pointer}.cursor-text{cursor:text!important}.cursor-pointer{cursor:pointer!important}pre,code{font-family:'Ubuntu Mono','Courier New','Lucida Console',monospace!important}.lineno{margin-right:5px}.highlight .hll{background-color:#ffc}.highlight{background:#f8f8f8}.highlight .c{color:#556366;font-style:italic}.highlight .err{border:1px solid #ffa92f}.highlight .k{color:#BE74D5;font-weight:700}.highlight .o{color:#d19a66}.highlight .cm{color:#556366;font-style:italic}.highlight .cp{color:#bc7a00}.highlight .c1{color:#556366;font-style:italic}.highlight .cs{color:#556366;font-style:italic}.highlight .gd{color:#a00000}.highlight .ge{font-style:italic}.highlight .gr{color:red}.highlight .gh{color:navy;font-weight:700}.highlight .gi{color:#00a000}.highlight .go{color:#888}.highlight .gp{color:navy;font-weight:700}.highlight .gs{font-weight:700}.highlight .gu{color:purple;font-weight:700}.highlight .gt{color:#04d}.highlight .kc{color:#BE74D5;font-weight:700}.highlight .kd{color:#BE74D5;font-weight:700}.highlight .kn{color:#BE74D5;font-weight:700}.highlight .kp{color:#be74d5}.highlight .kr{color:#BE74D5;font-weight:700}.highlight .kt{color:#d46c72}.highlight .m{color:#d19a66}.highlight .s{color:#86c372}.highlight .na{color:#7d9029}.highlight .nb{color:#be74d5}.highlight .nc{color:#61AFEF;font-weight:700}.highlight .no{color:#d19a66}.highlight .nd{color:#a2f}.highlight .ni{color:#999;font-weight:700}.highlight .ne{color:#D2413A;font-weight:700}.highlight .nf{color:#61afef}.highlight .nl{color:#a0a000}.highlight .nn{color:#61AFEF;font-weight:700}.highlight .nt{color:#BE74D5;font-weight:700}.highlight .nv{color:#dfc06f}.highlight .ow{color:#A2F;font-weight:700}.highlight .w{color:#d7dae0}.highlight .mf{color:#d19a66}.highlight .mh{color:#d19a66}.highlight .mi{color:#d19a66}.highlight .mo{color:#d19a66}.highlight .sb{color:#86c372}.highlight .sc{color:#86c372}.highlight .sd{color:#86C372;font-style:italic}.highlight .s2{color:#86c372}.highlight .se{color:#B62;font-weight:700}.highlight .sh{color:#86c372}.highlight .si{color:#B68;font-weight:700}.highlight .sx{color:#be74d5}.highlight .sr{color:#b68}.highlight .s1{color:#86c372}.highlight .ss{color:#dfc06f}.highlight .bp{color:#be74d5}.highlight .vc{color:#dfc06f}.highlight .vg{color:#dfc06f}.highlight .vi{color:#dfc06f}.highlight .il{color:#d19a66}.highlight .lineno{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default;color:#556366}.highlight .lineno::selection{background:0 0}.highlight .lineno::-moz-selection{background:0 0}.highlight pre{background-color:#282C34;color:#D7DAE0;border:none;margin-bottom:25px;font-size:15px;padding:20px 10px}.highlight{font-weight:700}
\ No newline at end of file
diff --git a/sources/searx/static/themes/oscar/css/pointhi.min.css b/sources/searx/static/themes/oscar/css/pointhi.min.css
index 29f7497..0c2472c 100644
--- a/sources/searx/static/themes/oscar/css/pointhi.min.css
+++ b/sources/searx/static/themes/oscar/css/pointhi.min.css
@@ -1 +1 @@
-html{position:relative;min-height:100%}body{margin-bottom:80px}.footer{position:absolute;bottom:0;width:100%;height:60px}input[type=checkbox]:checked+.label_hide_if_checked,input[type=checkbox]:checked+.label_hide_if_not_checked+.label_hide_if_checked{display:none}input[type=checkbox]:not(:checked)+.label_hide_if_not_checked,input[type=checkbox]:not(:checked)+.label_hide_if_checked+.label_hide_if_not_checked{display:none}.result_header{margin-bottom:5px;margin-top:20px}.result_header .favicon{margin-bottom:-3px}.result_header a{vertical-align:bottom}.result_header a .highlight{font-weight:700}.result-content{margin-top:5px;word-wrap:break-word}.result-content .highlight{font-weight:700}.result-default{clear:both}.result-images{float:left!important}.img-thumbnail{margin:5px;max-height:128px;min-height:128px}.result-videos{clear:both}.result-torrents{clear:both}.result-map{clear:both}.result-code{clear:both}.suggestion_item{margin:2px 5px}.result_download{margin-right:5px}#pagination{margin-top:30px;padding-bottom:50px}.label-default{color:#AAA;background:#FFF}.infobox .infobox_part{margin-bottom:20px;word-wrap:break-word;table-layout:fixed}.infobox .infobox_part:last-child{margin-bottom:0}.search_categories{margin:10px 0;text-transform:capitalize}.cursor-text{cursor:text!important}.cursor-pointer{cursor:pointer!important}.highlight .hll{background-color:#ffc}.highlight{background:#f8f8f8}.highlight .c{color:#408080;font-style:italic}.highlight .err{border:1px solid red}.highlight .k{color:green;font-weight:700}.highlight .o{color:#666}.highlight .cm{color:#408080;font-style:italic}.highlight .cp{color:#bc7a00}.highlight .c1{color:#408080;font-style:italic}.highlight .cs{color:#408080;font-style:italic}.highlight .gd{color:#a00000}.highlight .ge{font-style:italic}.highlight .gr{color:red}.highlight .gh{color:navy;font-weight:700}.highlight .gi{color:#00a000}.highlight .go{color:#888}.highlight .gp{color:navy;font-weight:700}.highlight .gs{font-weight:700}.highlight .gu{color:purple;font-weight:700}.highlight .gt{color:#04d}.highlight .kc{color:green;font-weight:700}.highlight .kd{color:green;font-weight:700}.highlight .kn{color:green;font-weight:700}.highlight .kp{color:green}.highlight .kr{color:green;font-weight:700}.highlight .kt{color:#b00040}.highlight .m{color:#666}.highlight .s{color:#ba2121}.highlight .na{color:#7d9029}.highlight .nb{color:green}.highlight .nc{color:#00F;font-weight:700}.highlight .no{color:#800}.highlight .nd{color:#a2f}.highlight .ni{color:#999;font-weight:700}.highlight .ne{color:#D2413A;font-weight:700}.highlight .nf{color:#00f}.highlight .nl{color:#a0a000}.highlight .nn{color:#00F;font-weight:700}.highlight .nt{color:green;font-weight:700}.highlight .nv{color:#19177c}.highlight .ow{color:#A2F;font-weight:700}.highlight .w{color:#bbb}.highlight .mf{color:#666}.highlight .mh{color:#666}.highlight .mi{color:#666}.highlight .mo{color:#666}.highlight .sb{color:#ba2121}.highlight .sc{color:#ba2121}.highlight .sd{color:#BA2121;font-style:italic}.highlight .s2{color:#ba2121}.highlight .se{color:#B62;font-weight:700}.highlight .sh{color:#ba2121}.highlight .si{color:#B68;font-weight:700}.highlight .sx{color:green}.highlight .sr{color:#b68}.highlight .s1{color:#ba2121}.highlight .ss{color:#19177c}.highlight .bp{color:green}.highlight .vc{color:#19177c}.highlight .vg{color:#19177c}.highlight .vi{color:#19177c}.highlight .il{color:#666}.highlight .lineno{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default}.highlight .lineno::selection{background:0 0}.highlight .lineno::-moz-selection{background:0 0}
\ No newline at end of file
+html{position:relative;min-height:100%}body{margin-bottom:80px}.footer{position:absolute;bottom:0;width:100%;height:60px}input[type=checkbox]:checked+.label_hide_if_checked,input[type=checkbox]:checked+.label_hide_if_not_checked+.label_hide_if_checked{display:none}input[type=checkbox]:not(:checked)+.label_hide_if_not_checked,input[type=checkbox]:not(:checked)+.label_hide_if_checked+.label_hide_if_not_checked{display:none}.onoff-checkbox{width:15%}.onoffswitch{position:relative;width:110px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.onoffswitch-checkbox{display:none}.onoffswitch-label{display:block;overflow:hidden;cursor:pointer;border:2px solid #FFF!important;border-radius:50px!important}.onoffswitch-inner{display:block;transition:margin .3s ease-in 0s}.onoffswitch-inner:before,.onoffswitch-inner:after{display:block;float:left;width:50%;height:30px;padding:0;line-height:40px;font-size:20px;box-sizing:border-box;content:"";background-color:#EEE}.onoffswitch-switch{display:block;width:37px;background-color:#0C0;position:absolute;top:0;bottom:0;right:0;border:2px solid #FFF!important;border-radius:50px!important;transition:all .3s ease-in 0s}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-inner{margin-right:0}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-switch{right:71px;background-color:#A1A1A1}.result_header{margin-bottom:5px;margin-top:20px}.result_header .favicon{margin-bottom:-3px}.result_header a{vertical-align:bottom}.result_header a .highlight{font-weight:700}.result-content{margin-top:5px;word-wrap:break-word}.result-content .highlight{font-weight:700}.result-default{clear:both}.result-images{float:left!important}.img-thumbnail{margin:5px;max-height:128px;min-height:128px}.result-videos{clear:both}.result-torrents{clear:both}.result-map{clear:both}.result-code{clear:both}.suggestion_item{margin:2px 5px}.result_download{margin-right:5px}#pagination{margin-top:30px;padding-bottom:50px}.label-default{color:#AAA;background:#FFF}.result .text-muted small{word-wrap:break-word}.modal-wrapper{box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-wrapper{background-clip:padding-box;background-color:#fff;border:1px solid rgba(0,0,0,.2);border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,.5);outline:0 none;position:relative}.infobox .infobox_part{margin-bottom:20px;word-wrap:break-word;table-layout:fixed}.infobox .infobox_part:last-child{margin-bottom:0}.search_categories,#categories{text-transform:capitalize;margin-bottom:1.5rem;margin-top:1.5rem;display:flex;flex-wrap:wrap;align-content:stretch}.search_categories label,#categories label,.search_categories .input-group-addon,#categories .input-group-addon{flex-grow:1;flex-basis:auto;font-size:1.3rem;font-weight:400;background-color:#fff;border:#DDD 1px solid;border-right:none;color:#333;padding-bottom:.8rem;padding-top:.8rem;text-align:center}.search_categories label:last-child,#categories label:last-child,.search_categories .input-group-addon:last-child,#categories .input-group-addon:last-child{border-right:#DDD 1px solid}.search_categories input[type=checkbox]:checked+label,#categories input[type=checkbox]:checked+label{color:#000;font-weight:700;background-color:#EEE}#advanced-search-container{display:none;text-align:center;margin-bottom:1rem;clear:both}#advanced-search-container label,#advanced-search-container .input-group-addon{font-size:1.3rem;font-weight:400;background-color:#fff;border:#DDD 1px solid;border-right:none;color:#333;padding-bottom:.8rem;padding-left:1.2rem;padding-right:1.2rem}#advanced-search-container label:last-child,#advanced-search-container .input-group-addon:last-child{border-right:#DDD 1px solid}#advanced-search-container input[type=radio]{display:none}#advanced-search-container input[type=radio]:checked+label{color:#000;font-weight:700;background-color:#EEE}#check-advanced{display:none}#check-advanced:checked~#advanced-search-container{display:block}.advanced{padding:0;margin-top:.3rem;text-align:right}.advanced label,.advanced select{cursor:pointer}.cursor-text{cursor:text!important}.cursor-pointer{cursor:pointer!important}.highlight .hll{background-color:#ffc}.highlight{background:#f8f8f8}.highlight .c{color:#408080;font-style:italic}.highlight .err{border:1px solid red}.highlight .k{color:green;font-weight:700}.highlight .o{color:#666}.highlight .cm{color:#408080;font-style:italic}.highlight .cp{color:#bc7a00}.highlight .c1{color:#408080;font-style:italic}.highlight .cs{color:#408080;font-style:italic}.highlight .gd{color:#a00000}.highlight .ge{font-style:italic}.highlight .gr{color:red}.highlight .gh{color:navy;font-weight:700}.highlight .gi{color:#00a000}.highlight .go{color:#888}.highlight .gp{color:navy;font-weight:700}.highlight .gs{font-weight:700}.highlight .gu{color:purple;font-weight:700}.highlight .gt{color:#04d}.highlight .kc{color:green;font-weight:700}.highlight .kd{color:green;font-weight:700}.highlight .kn{color:green;font-weight:700}.highlight .kp{color:green}.highlight .kr{color:green;font-weight:700}.highlight .kt{color:#b00040}.highlight .m{color:#666}.highlight .s{color:#ba2121}.highlight .na{color:#7d9029}.highlight .nb{color:green}.highlight .nc{color:#00F;font-weight:700}.highlight .no{color:#800}.highlight .nd{color:#a2f}.highlight .ni{color:#999;font-weight:700}.highlight .ne{color:#D2413A;font-weight:700}.highlight .nf{color:#00f}.highlight .nl{color:#a0a000}.highlight .nn{color:#00F;font-weight:700}.highlight .nt{color:green;font-weight:700}.highlight .nv{color:#19177c}.highlight .ow{color:#A2F;font-weight:700}.highlight .w{color:#bbb}.highlight .mf{color:#666}.highlight .mh{color:#666}.highlight .mi{color:#666}.highlight .mo{color:#666}.highlight .sb{color:#ba2121}.highlight .sc{color:#ba2121}.highlight .sd{color:#BA2121;font-style:italic}.highlight .s2{color:#ba2121}.highlight .se{color:#B62;font-weight:700}.highlight .sh{color:#ba2121}.highlight .si{color:#B68;font-weight:700}.highlight .sx{color:green}.highlight .sr{color:#b68}.highlight .s1{color:#ba2121}.highlight .ss{color:#19177c}.highlight .bp{color:green}.highlight .vc{color:#19177c}.highlight .vg{color:#19177c}.highlight .vi{color:#19177c}.highlight .il{color:#666}.highlight .lineno{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default}.highlight .lineno::selection{background:0 0}.highlight .lineno::-moz-selection{background:0 0}
\ No newline at end of file
diff --git a/sources/searx/static/themes/oscar/js/searx.min.js b/sources/searx/static/themes/oscar/js/searx.min.js
index 8a1055d..9bc4aae 100644
--- a/sources/searx/static/themes/oscar/js/searx.min.js
+++ b/sources/searx/static/themes/oscar/js/searx.min.js
@@ -1,2 +1,2 @@
-/*! oscar/searx.min.js | 09-01-2015 | https://github.com/asciimoo/searx */
-requirejs.config({baseUrl:"./static/themes/oscar/js",paths:{app:"../app"}}),searx.autocompleter&&(searx.searchResults=new Bloodhound({datumTokenizer:Bloodhound.tokenizers.obj.whitespace("value"),queryTokenizer:Bloodhound.tokenizers.whitespace,remote:"./autocompleter?q=%QUERY"}),searx.searchResults.initialize()),$(document).ready(function(){searx.autocompleter&&$("#q").typeahead(null,{name:"search-results",displayKey:function(a){return a},source:searx.searchResults.ttAdapter()})}),$(document).ready(function(){$("#q.autofocus").focus(),$(".select-all-on-click").click(function(){$(this).select()}),$(".btn-collapse").click(function(){var a=$(this).data("btn-text-collapsed"),b=$(this).data("btn-text-not-collapsed");""!==a&&""!==b&&(new_html=$(this).hasClass("collapsed")?$(this).html().replace(a,b):$(this).html().replace(b,a),$(this).html(new_html))}),$(".btn-toggle .btn").click(function(){var a="btn-"+$(this).data("btn-class"),b=$(this).data("btn-label-default"),c=$(this).data("btn-label-toggled");""!==c&&(new_html=$(this).hasClass("btn-default")?$(this).html().replace(b,c):$(this).html().replace(c,b),$(this).html(new_html)),$(this).toggleClass(a),$(this).toggleClass("btn-default")}),$(".media-loader").click(function(){var a=$(this).data("target"),b=$(a+" > iframe"),c=b.attr("src");(void 0===c||c===!1)&&b.attr("src",b.data("src"))}),$(".btn-sm").dblclick(function(){var a="btn-"+$(this).data("btn-class");$(this).hasClass("btn-default")?($(".btn-sm > input").attr("checked","checked"),$(".btn-sm > input").prop("checked",!0),$(".btn-sm").addClass(a),$(".btn-sm").addClass("active"),$(".btn-sm").removeClass("btn-default")):($(".btn-sm > input").attr("checked",""),$(".btn-sm > input").removeAttr("checked"),$(".btn-sm > input").checked=!1,$(".btn-sm").removeClass(a),$(".btn-sm").removeClass("active"),$(".btn-sm").addClass("btn-default"))})}),$(document).ready(function(){$(".searx_overpass_request").on("click",function(a){var b="https://overpass-api.de/api/interpreter?data=",c=b+"[out:json][timeout:25];(",d=");out meta;",e=$(this).data("osm-id"),f=$(this).data("osm-type"),g=$(this).data("result-table"),h="#"+$(this).data("result-table-loadicon"),i=["addr:city","addr:country","addr:housenumber","addr:postcode","addr:street"];if(e&&f&&g){g="#"+g;var j=null;switch(f){case"node":j=c+"node("+e+");"+d;break;case"way":j=c+"way("+e+");"+d;break;case"relation":j=c+"relation("+e+");"+d}if(j){$.ajax(j).done(function(a){if(a&&a.elements&&a.elements[0]){var b=a.elements[0],c=$(g).html();for(var d in b.tags)if(null===b.tags.name||-1==i.indexOf(d)){switch(c+=""+d+" | ",d){case"phone":case"fax":c+=''+b.tags[d]+"";break;case"email":c+=''+b.tags[d]+"";break;case"website":case"url":c+=''+b.tags[d]+"";break;case"wikidata":c+=''+b.tags[d]+"";break;case"wikipedia":if(-1!=b.tags[d].indexOf(":")){c+=''+b.tags[d]+"";break}default:c+=b.tags[d]}c+=" |
"}$(g).html(c),$(g).removeClass("hidden"),$(h).addClass("hidden")}}).fail(function(){$(h).html($(h).html()+'could not load data!
')})}}$(this).off(a)}),$(".searx_init_map").on("click",function(a){var b=$(this).data("leaflet-target"),c=$(this).data("map-lon"),d=$(this).data("map-lat"),e=$(this).data("map-zoom"),f=$(this).data("map-boundingbox"),g=$(this).data("map-geojson");require(["leaflet-0.7.3.min"],function(){f&&(southWest=L.latLng(f[0],f[2]),northEast=L.latLng(f[1],f[3]),map_bounds=L.latLngBounds(southWest,northEast)),L.Icon.Default.imagePath="./static/themes/oscar/img/map";{var a=L.map(b),h="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",i='Map data © OpenStreetMap contributors',j=new L.TileLayer(h,{minZoom:1,maxZoom:19,attribution:i}),k="http://otile{s}.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.jpg",l='Map data © OpenStreetMap contributors | Tiles Courtesy of MapQuest ',m=new L.TileLayer(k,{minZoom:1,maxZoom:18,subdomains:"1234",attribution:l}),n="http://otile{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg",o='Map data © OpenStreetMap contributors | Tiles Courtesy of MapQuest | Portions Courtesy NASA/JPL-Caltech and U.S. Depart. of Agriculture, Farm Service Agency';new L.TileLayer(n,{minZoom:1,maxZoom:11,subdomains:"1234",attribution:o})}map_bounds?setTimeout(function(){a.fitBounds(map_bounds,{maxZoom:17})},0):c&&d&&(e?a.setView(new L.LatLng(d,c),e):a.setView(new L.LatLng(d,c),8)),a.addLayer(m);var p={"OSM Mapnik":j,MapQuest:m};L.control.layers(p).addTo(a),g&&L.geoJson(g).addTo(a)}),$(this).off(a)})});
\ No newline at end of file
+/*! oscar/searx.min.js | 25-07-2016 | https://github.com/asciimoo/searx */
+requirejs.config({baseUrl:"./static/themes/oscar/js",paths:{app:"../app"}}),searx.autocompleter&&(searx.searchResults=new Bloodhound({datumTokenizer:Bloodhound.tokenizers.obj.whitespace("value"),queryTokenizer:Bloodhound.tokenizers.whitespace,remote:"./autocompleter?q=%QUERY"}),searx.searchResults.initialize()),$(document).ready(function(){searx.autocompleter&&$("#q").typeahead(null,{name:"search-results",displayKey:function(a){return a},source:searx.searchResults.ttAdapter()})}),$(document).ready(function(){$("#q.autofocus").focus(),$(".select-all-on-click").click(function(){$(this).select()}),$(".btn-collapse").click(function(){var a=$(this).data("btn-text-collapsed"),b=$(this).data("btn-text-not-collapsed");""!==a&&""!==b&&($(this).hasClass("collapsed")?new_html=$(this).html().replace(a,b):new_html=$(this).html().replace(b,a),$(this).html(new_html))}),$(".btn-toggle .btn").click(function(){var a="btn-"+$(this).data("btn-class"),b=$(this).data("btn-label-default"),c=$(this).data("btn-label-toggled");""!==c&&($(this).hasClass("btn-default")?new_html=$(this).html().replace(b,c):new_html=$(this).html().replace(c,b),$(this).html(new_html)),$(this).toggleClass(a),$(this).toggleClass("btn-default")}),$(".media-loader").click(function(){var a=$(this).data("target"),b=$(a+" > iframe"),c=b.attr("src");void 0!==c&&c!==!1||b.attr("src",b.data("src"))}),$(".btn-sm").dblclick(function(){var a="btn-"+$(this).data("btn-class");$(this).hasClass("btn-default")?($(".btn-sm > input").attr("checked","checked"),$(".btn-sm > input").prop("checked",!0),$(".btn-sm").addClass(a),$(".btn-sm").addClass("active"),$(".btn-sm").removeClass("btn-default")):($(".btn-sm > input").attr("checked",""),$(".btn-sm > input").removeAttr("checked"),$(".btn-sm > input").checked=!1,$(".btn-sm").removeClass(a),$(".btn-sm").removeClass("active"),$(".btn-sm").addClass("btn-default"))})}),$(document).ready(function(){$(".searx_overpass_request").on("click",function(a){var b="https://overpass-api.de/api/interpreter?data=",c=b+"[out:json][timeout:25];(",d=");out meta;",e=$(this).data("osm-id"),f=$(this).data("osm-type"),g=$(this).data("result-table"),h="#"+$(this).data("result-table-loadicon"),i=["addr:city","addr:country","addr:housenumber","addr:postcode","addr:street"];if(e&&f&&g){g="#"+g;var j=null;switch(f){case"node":j=c+"node("+e+");"+d;break;case"way":j=c+"way("+e+");"+d;break;case"relation":j=c+"relation("+e+");"+d}if(j){$.ajax(j).done(function(a){if(a&&a.elements&&a.elements[0]){var b=a.elements[0],c=$(g).html();for(var d in b.tags)if(null===b.tags.name||i.indexOf(d)==-1){switch(c+=""+d+" | ",d){case"phone":case"fax":c+=''+b.tags[d]+"";break;case"email":c+=''+b.tags[d]+"";break;case"website":case"url":c+=''+b.tags[d]+"";break;case"wikidata":c+=''+b.tags[d]+"";break;case"wikipedia":if(b.tags[d].indexOf(":")!=-1){c+=''+b.tags[d]+"";break}default:c+=b.tags[d]}c+=" |
"}$(g).html(c),$(g).removeClass("hidden"),$(h).addClass("hidden")}}).fail(function(){$(h).html($(h).html()+'could not load data!
')})}}$(this).off(a)}),$(".searx_init_map").on("click",function(a){var b=$(this).data("leaflet-target"),c=$(this).data("map-lon"),d=$(this).data("map-lat"),e=$(this).data("map-zoom"),f=$(this).data("map-boundingbox"),g=$(this).data("map-geojson");require(["leaflet-0.7.3.min"],function(a){f&&(southWest=L.latLng(f[0],f[2]),northEast=L.latLng(f[1],f[3]),map_bounds=L.latLngBounds(southWest,northEast)),L.Icon.Default.imagePath="./static/themes/oscar/img/map";var h=L.map(b),i="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",j='Map data © OpenStreetMap contributors',k=new L.TileLayer(i,{minZoom:1,maxZoom:19,attribution:j}),l="https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png",m='Wikimedia maps beta | Maps data © OpenStreetMap contributors';new L.TileLayer(l,{minZoom:1,maxZoom:19,attribution:m});map_bounds?setTimeout(function(){h.fitBounds(map_bounds,{maxZoom:17})},0):c&&d&&(e?h.setView(new L.LatLng(d,c),e):h.setView(new L.LatLng(d,c),8)),h.addLayer(k);var n={"OSM Mapnik":k};L.control.layers(n).addTo(h),g&&L.geoJson(g).addTo(h)}),$(this).off(a)})});
\ No newline at end of file
diff --git a/sources/searx/static/themes/oscar/js/searx_src/leaflet_map.js b/sources/searx/static/themes/oscar/js/searx_src/leaflet_map.js
index cbcbe15..4be46ac 100644
--- a/sources/searx/static/themes/oscar/js/searx_src/leaflet_map.js
+++ b/sources/searx/static/themes/oscar/js/searx_src/leaflet_map.js
@@ -122,17 +122,13 @@ $(document).ready(function(){
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 © OpenStreetMap contributors';
- var osmMapnik = new L.TileLayer(osmMapnikUrl, {minZoom: 1, maxZoom: 19, attribution: osmMapnikAttrib});
-
- var osmMapquestUrl='http://otile{s}.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.jpg';
- var osmMapquestAttrib='Map data © OpenStreetMap contributors | Tiles Courtesy of MapQuest ';
- var osmMapquest = new L.TileLayer(osmMapquestUrl, {minZoom: 1, maxZoom: 18, subdomains: '1234', attribution: osmMapquestAttrib});
-
- var osmMapquestOpenAerialUrl='http://otile{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg';
- var osmMapquestOpenAerialAttrib='Map data © OpenStreetMap contributors | Tiles Courtesy of MapQuest | Portions Courtesy NASA/JPL-Caltech and U.S. Depart. of Agriculture, Farm Service Agency';
- var osmMapquestOpenAerial = new L.TileLayer(osmMapquestOpenAerialUrl, {minZoom: 1, maxZoom: 11, subdomains: '1234', attribution: osmMapquestOpenAerialAttrib});
+ var osmMapnikUrl='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
+ var osmMapnikAttrib='Map data © OpenStreetMap 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 © OpenStreetMap contributors';
+ var osmWikimedia = new L.TileLayer(osmWikimediaUrl, {minZoom: 1, maxZoom: 19, attribution: osmWikimediaAttrib});
// init map view
if(map_bounds) {
@@ -149,12 +145,11 @@ $(document).ready(function(){
map.setView(new L.LatLng(map_lat, map_lon),8);
}
- map.addLayer(osmMapquest);
+ map.addLayer(osmMapnik);
var baseLayers = {
- "OSM Mapnik": osmMapnik,
- "MapQuest": osmMapquest/*,
- "MapQuest Open Aerial": osmMapquestOpenAerial*/
+ "OSM Mapnik": osmMapnik/*,
+ "OSM Wikimedia": osmWikimedia*/
};
L.control.layers(baseLayers).addTo(map);
diff --git a/sources/searx/static/themes/oscar/less/logicodev/advanced.less b/sources/searx/static/themes/oscar/less/logicodev/advanced.less
new file mode 100644
index 0000000..3b2cd3c
--- /dev/null
+++ b/sources/searx/static/themes/oscar/less/logicodev/advanced.less
@@ -0,0 +1,72 @@
+#advanced-search-container {
+ display: none;
+ text-align: left;
+ margin-bottom: 1rem;
+ clear: both;
+
+ label, .input-group-addon {
+ font-size: 1.2rem;
+ font-weight:normal;
+ background-color: white;
+ border: @mild-gray 1px solid;
+ border-right: none;
+ color: @dark-gray;
+ padding-bottom: 0.4rem;
+ padding-right: 0.7rem;
+ padding-left: 0.7rem;
+ }
+
+ label:last-child, .input-group-addon:last-child {
+ border-right: @mild-gray 1px solid;
+ }
+
+ input[type="radio"] {
+ display: none;
+ }
+
+ input[type="radio"]:checked + label{
+ color: @black;
+ font-weight: bold;
+ border-bottom: @light-green 5px solid;
+ }
+ select {
+ appearance: none;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ font-size: 1.2rem;
+ font-weight:normal;
+ background-color: white;
+ border: @mild-gray 1px solid;
+ color: @dark-gray;
+ padding-bottom: 0.4rem;
+ padding-top: 0.4rem;
+ padding-left: 1rem;
+ padding-right: 5rem;
+ margin-right: 0.5rem;
+ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAQAAACR313BAAAABGdBTUEAALGPC/xhBQAAACBjSFJN
+AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAJcEhZ
+cwAABFkAAARZAVnbJUkAAAAHdElNRQfgBxgLDwB20OFsAAAAbElEQVQY073OsQ3CMAAEwJMYwJGn
+sAehpoXJItltBkmcdZBYgIIiQoLglnz3ui+eP+bk5uneteTMZJa6OJuIqvYzSJoqwqBq8gdmTTW8
+6/dghxAUq4xsVYT9laBYXCw93Aajh7GPEF23t4fkBYevGFTANkPRAAAAJXRFWHRkYXRlOmNyZWF0
+ZQAyMDE2LTA3LTI0VDExOjU1OjU4KzAyOjAwRFqFOQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNi0w
+Ny0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb
+7jwaAAAAAElFTkSuQmCC) 96% no-repeat;
+ }
+}
+
+#check-advanced {
+ display: none;
+}
+
+#check-advanced:checked ~ #advanced-search-container {
+ display: block;
+}
+
+.advanced {
+ padding: 0;
+ margin-top: 0.3rem;
+ text-align: right;
+ label, select {
+ cursor: pointer;
+ }
+}
diff --git a/sources/searx/static/themes/oscar/less/logicodev/navbar.less b/sources/searx/static/themes/oscar/less/logicodev/navbar.less
index 493c9dc..2426210 100644
--- a/sources/searx/static/themes/oscar/less/logicodev/navbar.less
+++ b/sources/searx/static/themes/oscar/less/logicodev/navbar.less
@@ -39,7 +39,7 @@
padding: 0 30px;
margin: 0;
}
- z-index: 1;
+ z-index: 10;
}
// Hover color
diff --git a/sources/searx/static/themes/oscar/less/logicodev/onoff.less b/sources/searx/static/themes/oscar/less/logicodev/onoff.less
new file mode 100644
index 0000000..f471892
--- /dev/null
+++ b/sources/searx/static/themes/oscar/less/logicodev/onoff.less
@@ -0,0 +1,57 @@
+.onoff-checkbox {
+ width:15%;
+}
+.onoffswitch {
+ position: relative;
+ width: 110px;
+ -webkit-user-select:none;
+ -moz-user-select:none;
+ -ms-user-select: none;
+}
+.onoffswitch-checkbox {
+ display: none;
+}
+.onoffswitch-label {
+ display: block;
+ overflow: hidden;
+ cursor: pointer;
+ border: 2px solid #FFFFFF !important;
+ border-radius: 50px !important;
+}
+.onoffswitch-inner {
+ display: block;
+ transition: margin 0.3s ease-in 0s;
+}
+
+.onoffswitch-inner:before, .onoffswitch-inner:after {
+ display: block;
+ float: left;
+ width: 50%;
+ height: 30px;
+ padding: 0;
+ line-height: 40px;
+ font-size: 20px;
+ box-sizing: border-box;
+ content: "";
+ background-color: #EEEEEE;
+}
+
+.onoffswitch-switch {
+ display: block;
+ width: 37px;
+ background-color: @light-green;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ right: 0px;
+ border: 2px solid #FFFFFF !important;
+ border-radius: 50px !important;
+ transition: all 0.3s ease-in 0s;
+}
+.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner {
+ margin-right: 0;
+}
+.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch {
+ right: 71px;
+ background-color: #A1A1A1;
+}
diff --git a/sources/searx/static/themes/oscar/less/logicodev/oscar.less b/sources/searx/static/themes/oscar/less/logicodev/oscar.less
index fba596c..55181cb 100644
--- a/sources/searx/static/themes/oscar/less/logicodev/oscar.less
+++ b/sources/searx/static/themes/oscar/less/logicodev/oscar.less
@@ -6,12 +6,16 @@
@import "checkbox.less";
+@import "onoff.less";
+
@import "results.less";
@import "infobox.less";
@import "search.less";
+@import "advanced.less";
+
@import "cursor.less";
@import "code.less";
diff --git a/sources/searx/static/themes/oscar/less/logicodev/results.less b/sources/searx/static/themes/oscar/less/logicodev/results.less
index 37a5a76..9e57da7 100644
--- a/sources/searx/static/themes/oscar/less/logicodev/results.less
+++ b/sources/searx/static/themes/oscar/less/logicodev/results.less
@@ -6,7 +6,7 @@
.favicon {
margin-bottom:-3px;
}
-
+
a {
color: @black;
text-decoration: none;
@@ -18,7 +18,7 @@
&:visited{
color: @violet;
}
-
+
.highlight {
background-color: @dim-gray;
// Chrome hack: bold is different size than normal
@@ -64,10 +64,9 @@
float: left !important;
width: 24%;
margin: .5%;
- a{
+ a {
display: block;
width: 100%;
- height: 170px;
background-size: cover;
}
}
@@ -148,3 +147,21 @@
color: @gray;
background: transparent;
}
+
+.result .text-muted small {
+ word-wrap: break-word;
+}
+
+.modal-wrapper {
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
+}
+
+.modal-wrapper {
+ background-clip: padding-box;
+ background-color: #fff;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 6px;
+ box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
+ outline: 0 none;
+ position: relative;
+}
diff --git a/sources/searx/static/themes/oscar/less/logicodev/search.less b/sources/searx/static/themes/oscar/less/logicodev/search.less
index 1bb71a7..4ebfe88 100644
--- a/sources/searx/static/themes/oscar/less/logicodev/search.less
+++ b/sources/searx/static/themes/oscar/less/logicodev/search.less
@@ -1,36 +1,33 @@
.search_categories, #categories {
- margin: 10px 0 4px 0;
text-transform: capitalize;
-
- label{
- border: none;
- box-shadow: none;
- font-size: 13px;
- padding-bottom: 2px;
- color: @gray;
- margin-bottom: 5px;
+ margin-bottom: 0.5rem;
+ display: flex;
+ flex-wrap: wrap;
+ flex-flow: row wrap;
+ align-content: stretch;
- &:hover{
- color: @black;
- background-color: transparent;
- }
-
- &:active{
- box-shadow: none;
- }
+ label, .input-group-addon {
+ flex-grow: 1;
+ flex-basis: auto;
+ font-size: 1.2rem;
+ font-weight: normal;
+ background-color: white;
+ border: @mild-gray 1px solid;
+ border-right: none;
+ color: @dark-gray;
+ padding-bottom: 0.4rem;
+ padding-top: 0.4rem;
+ text-align: center;
+ }
+ label:last-child, .input-group-addon:last-child {
+ border-right: @mild-gray 1px solid;
}
- .active, .btn-primary{
+ input[type="checkbox"]:checked + label {
color: @black;
- font-weight: 700;
- border-bottom: 5px solid @light-green;
- background-color: transparent;
+ font-weight: bold;
+ border-bottom: @light-green 5px solid;
}
-
-}
-
-#categories{
- margin: 0;
}
#main-logo{
diff --git a/sources/searx/static/themes/oscar/less/logicodev/variables.less b/sources/searx/static/themes/oscar/less/logicodev/variables.less
index 3ca05e7..32d5bb9 100644
--- a/sources/searx/static/themes/oscar/less/logicodev/variables.less
+++ b/sources/searx/static/themes/oscar/less/logicodev/variables.less
@@ -2,7 +2,9 @@
@gray: #A4A4A4;
@dim-gray: #F6F9FA;
@dark-gray: #666;
-@blue: #0088CC;
+@middle-gray: #F5F5F5;
+@mild-gray: #DDD;
+@blue: #0088CC;
@red: #F35E77;
@violet: #684898;
@green: #2ecc71;
diff --git a/sources/searx/static/themes/oscar/less/pointhi/advanced.less b/sources/searx/static/themes/oscar/less/pointhi/advanced.less
new file mode 100644
index 0000000..23bfdb0
--- /dev/null
+++ b/sources/searx/static/themes/oscar/less/pointhi/advanced.less
@@ -0,0 +1,49 @@
+#advanced-search-container {
+ display: none;
+ text-align: center;
+ margin-bottom: 1rem;
+ clear: both;
+
+ label, .input-group-addon {
+ font-size: 1.3rem;
+ font-weight:normal;
+ background-color: white;
+ border: #DDD 1px solid;
+ border-right: none;
+ color: #333;
+ padding-bottom: 0.8rem;
+ padding-left: 1.2rem;
+ padding-right: 1.2rem;
+ }
+
+ label:last-child, .input-group-addon:last-child {
+ border-right: #DDD 1px solid;
+ }
+
+ input[type="radio"] {
+ display: none;
+ }
+
+ input[type="radio"]:checked + label {
+ color: black;
+ font-weight: bold;
+ background-color: #EEE;
+ }
+}
+
+#check-advanced {
+ display: none;
+}
+
+#check-advanced:checked ~ #advanced-search-container {
+ display: block;
+}
+
+.advanced {
+ padding: 0;
+ margin-top: 0.3rem;
+ text-align: right;
+ label, select {
+ cursor: pointer;
+ }
+}
diff --git a/sources/searx/static/themes/oscar/less/pointhi/onoff.less b/sources/searx/static/themes/oscar/less/pointhi/onoff.less
new file mode 100644
index 0000000..72b289a
--- /dev/null
+++ b/sources/searx/static/themes/oscar/less/pointhi/onoff.less
@@ -0,0 +1,57 @@
+.onoff-checkbox {
+ width:15%;
+}
+.onoffswitch {
+ position: relative;
+ width: 110px;
+ -webkit-user-select:none;
+ -moz-user-select:none;
+ -ms-user-select: none;
+}
+.onoffswitch-checkbox {
+ display: none;
+}
+.onoffswitch-label {
+ display: block;
+ overflow: hidden;
+ cursor: pointer;
+ border: 2px solid #FFFFFF !important;
+ border-radius: 50px !important;
+}
+.onoffswitch-inner {
+ display: block;
+ transition: margin 0.3s ease-in 0s;
+}
+
+.onoffswitch-inner:before, .onoffswitch-inner:after {
+ display: block;
+ float: left;
+ width: 50%;
+ height: 30px;
+ padding: 0;
+ line-height: 40px;
+ font-size: 20px;
+ box-sizing: border-box;
+ content: "";
+ background-color: #EEEEEE;
+}
+
+.onoffswitch-switch {
+ display: block;
+ width: 37px;
+ background-color: #00CC00;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ right: 0px;
+ border: 2px solid #FFFFFF !important;
+ border-radius: 50px !important;
+ transition: all 0.3s ease-in 0s;
+}
+.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner {
+ margin-right: 0;
+}
+.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch {
+ right: 71px;
+ background-color: #A1A1A1;
+}
diff --git a/sources/searx/static/themes/oscar/less/pointhi/oscar.less b/sources/searx/static/themes/oscar/less/pointhi/oscar.less
index 6a84785..ef69ace 100644
--- a/sources/searx/static/themes/oscar/less/pointhi/oscar.less
+++ b/sources/searx/static/themes/oscar/less/pointhi/oscar.less
@@ -2,12 +2,16 @@
@import "checkbox.less";
+@import "onoff.less";
+
@import "results.less";
@import "infobox.less";
@import "search.less";
+@import "advanced.less";
+
@import "cursor.less";
@import "code.less";
diff --git a/sources/searx/static/themes/oscar/less/pointhi/results.less b/sources/searx/static/themes/oscar/less/pointhi/results.less
index b3d8700..beea353 100644
--- a/sources/searx/static/themes/oscar/less/pointhi/results.less
+++ b/sources/searx/static/themes/oscar/less/pointhi/results.less
@@ -6,10 +6,10 @@
.favicon {
margin-bottom:-3px;
}
-
+
a {
vertical-align: bottom;
-
+
.highlight {
font-weight:bold;
}
@@ -81,3 +81,21 @@
color: #AAA;
background: #FFF;
}
+
+.result .text-muted small {
+ word-wrap: break-word;
+}
+
+.modal-wrapper {
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
+}
+
+.modal-wrapper {
+ background-clip: padding-box;
+ background-color: #fff;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 6px;
+ box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
+ outline: 0 none;
+ position: relative;
+}
diff --git a/sources/searx/static/themes/oscar/less/pointhi/search.less b/sources/searx/static/themes/oscar/less/pointhi/search.less
index f95ab50..cea6799 100644
--- a/sources/searx/static/themes/oscar/less/pointhi/search.less
+++ b/sources/searx/static/themes/oscar/less/pointhi/search.less
@@ -1,4 +1,32 @@
-.search_categories {
- margin:10px 0;
- text-transform: capitalize;
+.search_categories, #categories {
+ text-transform: capitalize;
+ margin-bottom: 1.5rem;
+ margin-top: 1.5rem;
+ display: flex;
+ flex-wrap: wrap;
+ align-content: stretch;
+
+ label, .input-group-addon {
+ flex-grow: 1;
+ flex-basis: auto;
+ font-size: 1.3rem;
+ font-weight: normal;
+ background-color: white;
+ border: #DDD 1px solid;
+ border-right: none;
+ color: #333;
+ padding-bottom: 0.8rem;
+ padding-top: 0.8rem;
+ text-align: center;
+ }
+
+ label:last-child, .input-group-addon:last-child {
+ border-right: #DDD 1px solid;
+ }
+
+ input[type="checkbox"]:checked + label{
+ color: black;
+ font-weight: bold;
+ background-color: #EEE;
+ }
}
diff --git a/sources/searx/templates/courgette/404.html b/sources/searx/templates/courgette/404.html
new file mode 100644
index 0000000..77f1287
--- /dev/null
+++ b/sources/searx/templates/courgette/404.html
@@ -0,0 +1,9 @@
+{% extends "courgette/base.html" %}
+{% block content %}
+
+
{{ _('Page not found') }}
+ {% autoescape false %}
+
{{ _('Go to %(search_page)s.', search_page='{}'.decode('utf-8').format(url_for('index'), _('search page'))) }}
+ {% endautoescape %}
+
+{% endblock %}
diff --git a/sources/searx/templates/courgette/about.html b/sources/searx/templates/courgette/about.html
index 2945e1f..3855d46 100644
--- a/sources/searx/templates/courgette/about.html
+++ b/sources/searx/templates/courgette/about.html
@@ -6,20 +6,20 @@
Searx is a metasearch engine, aggregating the results of other search engines while not storing information about its users.
- Why use Searx?
+ Why use searx?
- - Searx may not offer you as personalised results as Google, but it doesn't generate a profile about you
- - Searx doesn't care about what you search for, never shares anything with a third party, and it can't be used to compromise you
- - Searx is free software, the code is 100% open and you can help to make it better. See more on github
+ - searx may not offer you as personalised results as Google, but it doesn't generate a profile about you
+ - searx doesn't care about what you search for, never shares anything with a third party, and it can't be used to compromise you
+ - searx is free software, the code is 100% open and you can help to make it better. See more on github
If you do care about privacy, want to be a conscious user, or otherwise believe
- in digital freedom, make Searx your default search engine or run it on your own server
+ in digital freedom, make searx your default search engine or run it on your own server
Technical details - How does it work?
Searx is a metasearch engine,
inspired by the seeks project.
-It provides basic privacy by mixing your queries with searches on other platforms without storing search data. Queries are made using a POST request on every browser (except chrome*). Therefore they show up in neither our logs, nor your url history. In case of Chrome* users there is an exception, Searx uses the search bar to perform GET requests.
+It provides basic privacy by mixing your queries with searches on other platforms without storing search data. Queries are made using a POST request on every browser (except chrome*). Therefore they show up in neither our logs, nor your url history. In case of Chrome* users there is an exception, searx uses the search bar to perform GET requests.
Searx can be added to your browser's search bar; moreover, it can be set as the default search engine.
diff --git a/sources/searx/templates/courgette/base.html b/sources/searx/templates/courgette/base.html
index 276fae8..b2c70a3 100644
--- a/sources/searx/templates/courgette/base.html
+++ b/sources/searx/templates/courgette/base.html
@@ -2,7 +2,7 @@
-
+
diff --git a/sources/searx/templates/courgette/result_templates/code.html b/sources/searx/templates/courgette/result_templates/code.html
index 726f305..953617e 100644
--- a/sources/searx/templates/courgette/result_templates/code.html
+++ b/sources/searx/templates/courgette/result_templates/code.html
@@ -1,8 +1,8 @@
-
+
{% if result.publishedDate %}
{{ result.publishedDate }}{% endif %}
{% if result.img_src %}{% endif %}{% if result.content %}{{ result.content|safe }}
{% endif %}
- {% if result.repository %}
{{ result.repository }}
{% endif %}
+ {% if result.repository %}
{{ result.repository }}
{% endif %}
{{ result.codelines|code_highlighter(result.code_language)|safe }}
diff --git a/sources/searx/templates/courgette/result_templates/default.html b/sources/searx/templates/courgette/result_templates/default.html
index 585ecf3..5f2ead6 100644
--- a/sources/searx/templates/courgette/result_templates/default.html
+++ b/sources/searx/templates/courgette/result_templates/default.html
@@ -5,7 +5,7 @@
{% endif %}
-
+
{% if result.publishedDate %}
{{ result.publishedDate }}{% endif %}
{% if result.content %}{{ result.content|safe }}
{% endif %}
{{ result.pretty_url }}
diff --git a/sources/searx/templates/courgette/result_templates/images.html b/sources/searx/templates/courgette/result_templates/images.html
index 87fc774..49acb3b 100644
--- a/sources/searx/templates/courgette/result_templates/images.html
+++ b/sources/searx/templates/courgette/result_templates/images.html
@@ -1,6 +1,6 @@
diff --git a/sources/searx/templates/courgette/result_templates/map.html b/sources/searx/templates/courgette/result_templates/map.html
index 585ecf3..5f2ead6 100644
--- a/sources/searx/templates/courgette/result_templates/map.html
+++ b/sources/searx/templates/courgette/result_templates/map.html
@@ -5,7 +5,7 @@
{% endif %}
-
+
{% if result.publishedDate %}
{{ result.publishedDate }}{% endif %}
{% if result.content %}{{ result.content|safe }}
{% endif %}
{{ result.pretty_url }}
diff --git a/sources/searx/templates/courgette/result_templates/torrent.html b/sources/searx/templates/courgette/result_templates/torrent.html
index 33b5742..2fd8395 100644
--- a/sources/searx/templates/courgette/result_templates/torrent.html
+++ b/sources/searx/templates/courgette/result_templates/torrent.html
@@ -2,12 +2,12 @@
{% if "icon_"~result.engine~".ico" in favicons %}
{% endif %}
-
+
{% if result.content %}
{{ result.content|safe }}{% endif %}
{{ _('Seeder') }} : {{ result.seed }}, {{ _('Leecher') }} : {{ result.leech }}
{% if result.magnetlink %}{{ _('magnet link') }}{% endif %}
- {% if result.torrentfile %}{{ _('torrent file') }}{% endif %}
+ {% if result.torrentfile %}{{ _('torrent file') }}{% endif %}
{{ result.pretty_url }}
diff --git a/sources/searx/templates/courgette/result_templates/videos.html b/sources/searx/templates/courgette/result_templates/videos.html
index ceed8b2..b3e19e0 100644
--- a/sources/searx/templates/courgette/result_templates/videos.html
+++ b/sources/searx/templates/courgette/result_templates/videos.html
@@ -3,8 +3,8 @@
{% endif %}
-
+
{% if result.publishedDate %}
{{ result.publishedDate }}{% endif %}
-
+
{{ result.pretty_url }}
diff --git a/sources/searx/templates/default/404.html b/sources/searx/templates/default/404.html
new file mode 100644
index 0000000..1d88f86
--- /dev/null
+++ b/sources/searx/templates/default/404.html
@@ -0,0 +1,9 @@
+{% extends "default/base.html" %}
+{% block content %}
+
+
{{ _('Page not found') }}
+ {% autoescape false %}
+
{{ _('Go to %(search_page)s.', search_page='{}'.decode('utf-8').format(url_for('index'), _('search page'))) }}
+ {% endautoescape %}
+
+{% endblock %}
diff --git a/sources/searx/templates/default/about.html b/sources/searx/templates/default/about.html
index 1b5fc34..f21a6f2 100644
--- a/sources/searx/templates/default/about.html
+++ b/sources/searx/templates/default/about.html
@@ -6,20 +6,20 @@
Searx is a metasearch engine, aggregating the results of other search engines while not storing information about its users.
-
Why use Searx?
+
Why use searx?
- - Searx may not offer you as personalised results as Google, but it doesn't generate a profile about you
- - Searx doesn't care about what you search for, never shares anything with a third party, and it can't be used to compromise you
- - Searx is free software, the code is 100% open and you can help to make it better. See more on github
+ - searx may not offer you as personalised results as Google, but it doesn't generate a profile about you
+ - searx doesn't care about what you search for, never shares anything with a third party, and it can't be used to compromise you
+ - searx is free software, the code is 100% open and you can help to make it better. See more on github
If you do care about privacy, want to be a conscious user, or otherwise believe
- in digital freedom, make Searx your default search engine or run it on your own server
+ in digital freedom, make searx your default search engine or run it on your own server
Technical details - How does it work?
Searx is a metasearch engine,
inspired by the seeks project.
-It provides basic privacy by mixing your queries with searches on other platforms without storing search data. Queries are made using a POST request on every browser (except chrome*). Therefore they show up in neither our logs, nor your url history. In case of Chrome* users there is an exception, if Searx used from the search bar it performs GET requests.
+It provides basic privacy by mixing your queries with searches on other platforms without storing search data. Queries are made using a POST request on every browser (except chrome*). Therefore they show up in neither our logs, nor your url history. In case of Chrome* users there is an exception, if searx used from the search bar it performs GET requests.
Searx can be added to your browser's search bar; moreover, it can be set as the default search engine.
diff --git a/sources/searx/templates/default/base.html b/sources/searx/templates/default/base.html
index 143bdb8..a2c38fe 100644
--- a/sources/searx/templates/default/base.html
+++ b/sources/searx/templates/default/base.html
@@ -2,7 +2,7 @@
-
+
diff --git a/sources/searx/templates/default/infobox.html b/sources/searx/templates/default/infobox.html
index 178a27e..4dd25fa 100644
--- a/sources/searx/templates/default/infobox.html
+++ b/sources/searx/templates/default/infobox.html
@@ -1,18 +1,18 @@
-
{{ infobox.infobox }}
+
{{ infobox.infobox }}
{% if infobox.img_src %}
{% endif %}
-
{{ infobox.entity }}
-
{{ infobox.content | safe }}
+
{{ infobox.entity }}
+
{{ infobox.content | safe }}
{% if infobox.attributes %}
{% for attribute in infobox.attributes %}
- {{ attribute.label }} |
+ {{ attribute.label }} |
{% if attribute.image %}
|
{% else %}
- {{ attribute.value }} |
+ {{ attribute.value }} |
{% endif %}
{% endfor %}
@@ -24,7 +24,7 @@
@@ -34,7 +34,7 @@