Merge branch 'master' into fix_startpage_ValueError_on_spanish_datetime

This commit is contained in:
Noémi Ványi 2020-04-15 23:33:16 +02:00 committed by GitHub
commit fcb44c6542
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 29000 additions and 27824 deletions

5
searx/brand.py Normal file
View file

@ -0,0 +1,5 @@
GIT_URL = 'https://github.com/asciimoo/searx'
ISSUE_URL = 'https://github.com/asciimoo/searx/issues'
SEARX_URL = 'https://searx.me'
DOCS_URL = 'https://asciimoo.github.io/searx'
PUBLIC_INSTANCES = 'https://searx.space'

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,8 @@
{
"versions": [
"70.0.1",
"70.0",
"69.0.3",
"69.0.2",
"69.0.1",
"69.0"
"74.0",
"73.0.1",
"73.0"
],
"os": [
"Windows NT 10; WOW64",

View file

@ -110,13 +110,18 @@ def response(resp):
# get supported languages from their site
def _fetch_supported_languages(resp):
supported_languages = []
dom = html.fromstring(resp.text)
options = eval_xpath(dom, '//div[@id="limit-languages"]//input')
for option in options:
code = eval_xpath(option, './@id')[0].replace('_', '-')
if code == 'nb':
code = 'no'
supported_languages.append(code)
lang_tags = set()
return supported_languages
setmkt = re.compile('setmkt=([^&]*)')
dom = html.fromstring(resp.text)
lang_links = eval_xpath(dom, "//li/a[contains(@href, 'setmkt')]")
for a in lang_links:
href = eval_xpath(a, './@href')[0]
match = setmkt.search(href)
l_tag = match.groups()[0]
_lang, _nation = l_tag.split('-', 1)
l_tag = _lang.lower() + '-' + _nation.upper()
lang_tags.add(l_tag)
return list(lang_tags)

View file

@ -18,6 +18,8 @@ import re
from searx.url_utils import urlencode
from searx.utils import match_language
from searx.engines.bing import _fetch_supported_languages, supported_languages_url, language_aliases
# engine dependent config
categories = ['images']
paging = True
@ -103,22 +105,3 @@ def response(resp):
continue
return results
# get supported languages from their site
def _fetch_supported_languages(resp):
supported_languages = []
dom = html.fromstring(resp.text)
regions_xpath = '//div[@id="region-section-content"]' \
+ '//ul[@class="b_vList"]/li/a/@href'
regions = dom.xpath(regions_xpath)
for region in regions:
code = re.search('setmkt=[^\&]+', region).group()[7:]
if code == 'nb-NO':
code = 'no-NO'
supported_languages.append(code)
return supported_languages

View file

@ -15,9 +15,10 @@ from datetime import datetime
from dateutil import parser
from lxml import etree
from searx.utils import list_get, match_language
from searx.engines.bing import _fetch_supported_languages, supported_languages_url, language_aliases
from searx.url_utils import urlencode, urlparse, parse_qsl
from searx.engines.bing import _fetch_supported_languages, supported_languages_url, language_aliases
# engine dependent config
categories = ['news']
paging = True
@ -58,6 +59,7 @@ def _get_url(query, language, offset, time_range):
offset=offset,
interval=time_range_dict[time_range])
else:
# e.g. setmkt=de-de&setlang=de
search_path = search_string.format(
query=urlencode({'q': query, 'setmkt': language}),
offset=offset)

View file

@ -12,10 +12,10 @@
from json import loads
from lxml import html
from searx.engines.bing_images import _fetch_supported_languages, supported_languages_url
from searx.url_utils import urlencode
from searx.utils import match_language
from searx.engines.bing import _fetch_supported_languages, supported_languages_url, language_aliases
categories = ['videos']
paging = True
@ -67,6 +67,10 @@ def request(query, params):
if params['time_range'] in time_range_dict:
params['url'] += time_range_string.format(interval=time_range_dict[params['time_range']])
# bing videos did not like "older" versions < 70.0.1 when selectin other
# languages then 'en' .. very strange ?!?!
params['headers']['User-Agent'] = 'Mozilla/5.0 (X11; Linux x86_64; rv:73.0.1) Gecko/20100101 Firefox/73.0.1'
return params

View file

@ -54,7 +54,7 @@ def request(query, params):
if params['language'] != 'all':
language = match_language(params['language'], supported_languages, language_aliases).split('-')[0]
if language:
params['url'] += '&lr=lang_' + language
params['url'] += '&hl=' + language
return params

View file

@ -3,9 +3,11 @@
# this file is generated automatically by utils/update_search_languages.py
language_codes = (
(u"af-NA", u"Afrikaans", u"", u"Afrikaans"),
(u"ar-SA", u"العربية", u"", u"Arabic"),
(u"be-BY", u"Беларуская", u"", u"Belarusian"),
(u"bg-BG", u"Български", u"", u"Bulgarian"),
(u"ca-ES", u"Català", u"", u"Catalan"),
(u"ca-AD", u"Català", u"", u"Catalan"),
(u"cs-CZ", u"Čeština", u"", u"Czech"),
(u"da-DK", u"Dansk", u"", u"Danish"),
(u"de", u"Deutsch", u"", u"German"),
@ -17,11 +19,15 @@ language_codes = (
(u"en-AU", u"English", u"Australia", u"English"),
(u"en-CA", u"English", u"Canada", u"English"),
(u"en-GB", u"English", u"United Kingdom", u"English"),
(u"en-IE", u"English", u"Ireland", u"English"),
(u"en-IN", u"English", u"India", u"English"),
(u"en-MY", u"English", u"Malaysia", u"English"),
(u"en-NZ", u"English", u"New Zealand", u"English"),
(u"en-PH", u"English", u"Philippines", u"English"),
(u"en-SG", u"English", u"Singapore", u"English"),
(u"en-US", u"English", u"United States", u"English"),
(u"es", u"Español", u"", u"Spanish"),
(u"es-AR", u"Español", u"Argentina", u"Spanish"),
(u"es-CL", u"Español", u"Chile", u"Spanish"),
(u"es-ES", u"Español", u"España", u"Spanish"),
(u"es-MX", u"Español", u"México", u"Spanish"),
(u"et-EE", u"Eesti", u"", u"Estonian"),
@ -35,6 +41,7 @@ language_codes = (
(u"he-IL", u"עברית", u"", u"Hebrew"),
(u"hr-HR", u"Hrvatski", u"", u"Croatian"),
(u"hu-HU", u"Magyar", u"", u"Hungarian"),
(u"hy-AM", u"Հայերեն", u"", u"Armenian"),
(u"id-ID", u"Indonesia", u"", u"Indonesian"),
(u"is-IS", u"Íslenska", u"", u"Icelandic"),
(u"it-IT", u"Italiano", u"", u"Italian"),
@ -42,7 +49,7 @@ language_codes = (
(u"ko-KR", u"한국어", u"", u"Korean"),
(u"lt-LT", u"Lietuvių", u"", u"Lithuanian"),
(u"lv-LV", u"Latviešu", u"", u"Latvian"),
(u"ms-MY", u"Bahasa Melayu", u"", u"Malay"),
(u"ms-MY", u"Melayu", u"", u"Malay"),
(u"nb-NO", u"Norsk Bokmål", u"", u"Norwegian Bokmål"),
(u"nl", u"Nederlands", u"", u"Dutch"),
(u"nl-BE", u"Nederlands", u"België", u"Dutch"),
@ -55,8 +62,9 @@ language_codes = (
(u"ru-RU", u"Русский", u"", u"Russian"),
(u"sk-SK", u"Slovenčina", u"", u"Slovak"),
(u"sl-SI", u"Slovenščina", u"", u"Slovenian"),
(u"sr-RS", u"Српски", u"", u"Serbian"),
(u"sr-RS", u"Srpski", u"", u"Serbian"),
(u"sv-SE", u"Svenska", u"", u"Swedish"),
(u"sw-KE", u"Kiswahili", u"", u"Swahili"),
(u"th-TH", u"ไทย", u"", u"Thai"),
(u"tr-TR", u"Türkçe", u"", u"Turkish"),
(u"uk-UA", u"Українська", u"", u"Ukrainian"),

View file

@ -686,6 +686,69 @@ engines:
engine : vimeo
shortcut : vm
- name : wikibooks
engine : mediawiki
shortcut : wb
categories : general
base_url : "https://{language}.wikibooks.org/"
number_of_results : 5
search_type : text
disabled : True
- name : wikinews
engine : mediawiki
shortcut : wn
categories : news
base_url : "https://{language}.wikinews.org/"
number_of_results : 5
search_type : text
disabled : True
- name : wikiquote
engine : mediawiki
shortcut : wq
categories : general
base_url : "https://{language}.wikiquote.org/"
number_of_results : 5
search_type : text
disabled : True
- name : wikisource
engine : mediawiki
shortcut : ws
categories : general
base_url : "https://{language}.wikisource.org/"
number_of_results : 5
search_type : text
disabled : True
- name : wiktionary
engine : mediawiki
shortcut : wt
categories : general
base_url : "https://{language}.wiktionary.org/"
number_of_results : 5
search_type : text
disabled : True
- name : wikiversity
engine : mediawiki
shortcut : wv
categories : general
base_url : "https://{language}.wikiversity.org/"
number_of_results : 5
search_type : text
disabled : True
- name : wikivoyage
engine : mediawiki
shortcut : wy
categories : general
base_url : "https://{language}.wikivoyage.org/"
number_of_results : 5
search_type : text
disabled : True
- name : wolframalpha
shortcut : wa
# You can use the engine using the official stable API, but you need an API key
@ -763,6 +826,20 @@ engines:
engine : seedpeer
categories: files, music, videos
- name : rubygems
shortcut: rbg
engine: xpath
paging : True
search_url : https://rubygems.org/search?page={pageno}&query={query}
results_xpath: /html/body/main/div/a[@class="gems__gem"]
url_xpath : ./@href
title_xpath : ./span/h2
content_xpath : ./span/p
suggestion_xpath : /html/body/main/div/div[@class="search__suggestions"]/p/a
first_page_num : 1
categories: it
disabled : True
# - name : yacy
# engine : yacy
# shortcut : ya

View file

@ -1,24 +1,40 @@
function hasScrollbar() {
var root = document.compatMode=='BackCompat'? document.body : document.documentElement;
return root.scrollHeight>root.clientHeight;
}
function loadNextPage() {
var formData = $('#pagination form:last').serialize();
if (formData) {
$('#pagination').html('<div class="loading-spinner"></div>');
$.ajax({
type: "POST",
url: './',
data: formData,
dataType: 'html',
success: function(data) {
var body = $(data);
$('#pagination').remove();
$('#main_results').append('<hr/>');
$('#main_results').append(body.find('.result'));
$('#main_results').append(body.find('#pagination'));
if(!hasScrollbar()) {
loadNextPage();
}
}
});
}
}
$(document).ready(function() {
var win = $(window);
if(!hasScrollbar()) {
loadNextPage();
}
win.scroll(function() {
$("#pagination button").css("visibility", "hidden");
if ($(document).height() - win.height() - win.scrollTop() < 150) {
var formData = $('#pagination form:last').serialize();
if (formData) {
$('#pagination').html('<div class="loading-spinner"></div>');
$.ajax({
type: "POST",
url: './',
data: formData,
dataType: 'html',
success: function(data) {
var body = $(data);
$('#pagination').remove();
$('#main_results').append('<hr/>');
$('#main_results').append(body.find('.result'));
$('#main_results').append(body.find('#pagination'));
}
});
}
loadNextPage();
}
});
});

View file

@ -1 +1 @@
node_modules/
/node_modules

View file

@ -13,7 +13,7 @@ module.exports = function(grunt) {
},
uglify: {
options: {
banner: '/*! oscar/searx.min.js | <%= grunt.template.today("dd-mm-yyyy") %> | https://github.com/asciimoo/searx */\n'
banner: '/*! oscar/searx.min.js | <%= grunt.template.today("dd-mm-yyyy") %> | <%= process.env.GIT_URL %> */\n'
},
dist: {
files: {
@ -38,7 +38,6 @@ module.exports = function(grunt) {
development: {
options: {
paths: ["less/pointhi", "less/logicodev", "less/logicodev-dark"]
//banner: '/*! less/oscar/oscar.css | <%= grunt.template.today("dd-mm-yyyy") %> | https://github.com/asciimoo/searx */\n'
},
files: {"css/pointhi.css": "less/pointhi/oscar.less",
"css/logicodev.css": "less/logicodev-dark/oscar.less",
@ -47,7 +46,6 @@ module.exports = function(grunt) {
production: {
options: {
paths: ["less/pointhi", "less/logicodev", "less/logicodev-dark"],
//banner: '/*! less/oscar/oscar.css | <%= grunt.template.today("dd-mm-yyyy") %> | https://github.com/asciimoo/searx */\n',
cleancss: true
},
files: {"css/pointhi.min.css": "less/pointhi/oscar.less",

View file

@ -86,6 +86,9 @@ $(document).ready(function(){
},
source: searx.searchResults.ttAdapter()
});
$('#q').bind('typeahead:selected', function(ev, suggestion) {
$("#search_form").submit();
});
}
});
;/**

View file

@ -1,2 +1,2 @@
/*! oscar/searx.min.js | 06-08-2019 | https://github.com/asciimoo/searx */
requirejs.config({baseUrl:"./static/themes/oscar/js",paths:{app:"../app"}}),window.searx=function(a){"use strict";var b=a.currentScript||function(){var b=a.getElementsByTagName("script");return b[b.length-1]}();return{autocompleter:"true"===b.getAttribute("data-autocompleter"),method:b.getAttribute("data-method")}}(document),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&&!1!==c||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+="<tr><td>"+d+"</td><td>",d){case"phone":case"fax":c+='<a href="tel:'+b.tags[d].replace(/ /g,"")+'">'+b.tags[d]+"</a>";break;case"email":c+='<a href="mailto:'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"website":case"url":c+='<a href="'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"wikidata":c+='<a href="https://www.wikidata.org/wiki/'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"wikipedia":if(-1!=b.tags[d].indexOf(":")){c+='<a href="https://'+b.tags[d].substring(0,b.tags[d].indexOf(":"))+".wikipedia.org/wiki/"+b.tags[d].substring(b.tags[d].indexOf(":")+1)+'">'+b.tags[d]+"</a>";break}default:c+=b.tags[d]}c+="</td></tr>"}$(g).html(c),$(g).removeClass("hidden"),$(h).addClass("hidden")}}).fail(function(){$(h).html($(h).html()+'<p class="text-muted">could not load data!</p>')})}}$(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 © <a href="https://openstreetmap.org">OpenStreetMap</a> 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 © <a href="https://openstreetmap.org">OpenStreetMap</a> 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)})});
/*! oscar/searx.min.js | 23-03-2020 | https://github.com/asciimoo/searx */
requirejs.config({baseUrl:"./static/themes/oscar/js",paths:{app:"../app"}}),window.searx=function(a){"use strict";var b=a.currentScript||function(){var b=a.getElementsByTagName("script");return b[b.length-1]}();return{autocompleter:"true"===b.getAttribute("data-autocompleter"),method:b.getAttribute("data-method")}}(document),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()}),$("#q").bind("typeahead:selected",function(a,b){$("#search_form").submit()}))}),$(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+="<tr><td>"+d+"</td><td>",d){case"phone":case"fax":c+='<a href="tel:'+b.tags[d].replace(/ /g,"")+'">'+b.tags[d]+"</a>";break;case"email":c+='<a href="mailto:'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"website":case"url":c+='<a href="'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"wikidata":c+='<a href="https://www.wikidata.org/wiki/'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"wikipedia":if(b.tags[d].indexOf(":")!=-1){c+='<a href="https://'+b.tags[d].substring(0,b.tags[d].indexOf(":"))+".wikipedia.org/wiki/"+b.tags[d].substring(b.tags[d].indexOf(":")+1)+'">'+b.tags[d]+"</a>";break}default:c+=b.tags[d]}c+="</td></tr>"}$(g).html(c),$(g).removeClass("hidden"),$(h).addClass("hidden")}}).fail(function(){$(h).html($(h).html()+'<p class="text-muted">could not load data!</p>')})}}$(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 © <a href="https://openstreetmap.org">OpenStreetMap</a> 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 © <a href="https://openstreetmap.org">OpenStreetMap</a> 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)})});

View file

@ -33,5 +33,8 @@ $(document).ready(function(){
},
source: searx.searchResults.ttAdapter()
});
$('#q').bind('typeahead:selected', function(ev, suggestion) {
$("#search_form").submit();
});
}
});

1
searx/static/themes/simple/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/node_modules

View file

@ -36,7 +36,7 @@ module.exports = function(grunt) {
},
uglify: {
options: {
banner: '/*! simple/searx.min.js | <%= grunt.template.today("dd-mm-yyyy") %> | https://github.com/asciimoo/searx */\n',
banner: '/*! simple/searx.min.js | <%= grunt.template.today("dd-mm-yyyy") %> | <%= process.env.GIT_URL %> */\n',
output: {
comments: 'some'
},
@ -57,7 +57,7 @@ module.exports = function(grunt) {
development: {
options: {
paths: ["less"],
banner: '/*! searx | <%= grunt.template.today("dd-mm-yyyy") %> | https://github.com/asciimoo/searx */\n'
banner: '/*! searx | <%= grunt.template.today("dd-mm-yyyy") %> | <%= process.env.GIT_URL %> */\n'
},
files: {
"css/searx.css": "less/style.less",
@ -73,7 +73,7 @@ module.exports = function(grunt) {
compatibility: '*'
})
],
banner: '/*! searx | <%= grunt.template.today("dd-mm-yyyy") %> | https://github.com/asciimoo/searx */\n'
banner: '/*! searx | <%= grunt.template.today("dd-mm-yyyy") %> | <%= process.env.GIT_URL %> */\n'
},
files: {
"css/searx.min.css": "less/style.less",

View file

@ -1,63 +1,97 @@
<div{% if rtl %} dir="ltr"{% endif %}>
<h1>About <a href="{{ url_for('index') }}">searx</a></h1>
<h1>About <a href="{{ url_for('index') }}">searx</a></h1>
<p>Searx is a <a href="https://en.wikipedia.org/wiki/Metasearch_engine">metasearch engine</a>, aggregating the results of other <a href="{{ url_for('preferences') }}">search engines</a> while not storing information about its users.
</p>
<h2>Why use searx?</h2>
<ul>
<li>searx may not offer you as personalised results as Google, but it doesn't generate a profile about you</li>
<li>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</li>
<li>searx is free software, the code is 100% open and you can help to make it better. See more on <a href="https://github.com/asciimoo/searx">github</a></li>
</ul>
<p>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</p>
<p>
Searx is a <a href="https://en.wikipedia.org/wiki/Metasearch_engine">metasearch engine</a>,
aggregating the results of other <a href="{{ url_for('preferences') }}">search engines</a>
while not storing information about its users.
</p>
<h2>Technical details - How does it work?</h2>
<p>More about searx ...</p>
<p>Searx is a <a href="https://en.wikipedia.org/wiki/Metasearch_engine">metasearch engine</a>,
inspired by the <a href="https://beniz.github.io/seeks/">seeks project</a>.<br />
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.<br />
Searx can be added to your browser's search bar; moreover, it can be set as the default search engine.
</p>
<h2>How can I make it my own?</h2>
<p>Searx appreciates your concern regarding logs, so take the <a href="https://github.com/asciimoo/searx">code</a> and run it yourself! <br />Add your Searx to this <a href="https://searx.space/">list</a> to help other people reclaim their privacy and make the Internet freer!
<br />The more decentralized the Internet is, the more freedom we have!</p>
<h2>More about searx</h2>
<ul>
<ul>
<li><a href="https://github.com/asciimoo/searx">github</a></li>
<li><a href="https://www.ohloh.net/p/searx/">ohloh</a></li>
<li><a href="https://twitter.com/Searx_engine">twitter</a></li>
<li>IRC: #searx @ freenode (<a href="https://kiwiirc.com/client/irc.freenode.com/searx">webclient</a>)</li>
<li><a href="https://www.transifex.com/projects/p/searx/">transifex</a></li>
</ul>
</ul>
<hr />
<hr />
<h2>Why use searx?</h2>
<h2 id="faq">FAQ</h2>
<ul>
<li>
Searx may not offer you as personalised results as Google, but it doesn't
generate a profile about you.
</li>
<li>
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.
</li>
<li>
Searx is free software, the code is 100% open and you can help to make it
better. See more on <a href="https://github.com/asciimoo/searx">github</a>.
</li>
</ul>
<h3>How to add to firefox?</h3>
<p><a href="#" onclick="window.external.AddSearchProvider(window.location.protocol + '//' + window.location.host + '{{ url_for('opensearch') }}');">Install</a> searx as a search engine on any version of Firefox! (javascript required)</p>
<p>
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
</p>
<h2 id="dev_faq">Developer FAQ</h2>
<h2>Technical details - How does it work?</h2>
<h3>New engines?</h3>
<ul>
<li>Edit your <a href="https://raw.github.com/asciimoo/searx/master/searx/settings.yml">settings.yml</a></li>
<li>Create your custom engine module, check the <a href="https://github.com/asciimoo/searx/blob/master/examples/basic_engine.py">example engine</a></li>
</ul>
<p>Don't forget to restart searx after config edit!</p>
<p>
Searx is a <a href="https://en.wikipedia.org/wiki/Metasearch_engine">metasearch engine</a>,
inspired by the <a href="https://beniz.github.io/seeks/">seeks project</a>.
<h3>Installation/WSGI support?</h3>
<p>See the <a href="https://github.com/asciimoo/searx/wiki/Installation">installation and setup</a> wiki page</p>
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.
<h3>How to debug engines?</h3>
<p><a href="{{ url_for('stats') }}">Stats page</a> contains some useful data about the engines used.</p>
Searx can be added to your browser's search bar; moreover, it can be set as
the default search engine.
</p>
<h2 id='add to browser'>How to set as the default search engine?</h2>
<dt>Firefox</dt>
<dd>
<a href="#" onclick="window.external.AddSearchProvider(window.location.protocol + '//' + window.location.host + '{{ url_for('opensearch') }}');">Install</a>
searx as a search engine on any version of Firefox! (javascript required)
</dd>
<h2>Where to find anonymous usage statistics of this instance ?</h2>
<p>
<a href="{{ url_for('stats') }}">Stats page</a> contains some useful data about the engines used.
</p>
<h2>How can I make it my own?</h2>
<p>
Searx appreciates your concern regarding logs, so take the
code from the <a href="https://github.com/asciimoo/searx">original searx project</a> and
run it yourself!
</p>
<p>
Add your searx instance to this <a href="{{ brand.PUBLIC_INSTANCES }}"> list
of public searx instances</a> to help other people reclaim their privacy and
make the Internet freer! The more decentralized the Internet is, the more
freedom we have!
</p>
<h2>Where are the docs & code of this instance?</h2>
<p>
See the <a href="{{ brand.DOCS_URL }}">{{ brand.DOCS_URL }}</a>
and <a href="{{ brand.GIT_URL }}">{{ brand.GIT_URL }}</a>
</p>
</div>
{% include "__common__/aboutextend.html" ignore missing %}

View file

@ -25,5 +25,29 @@
{% if r.pubdate %}<pubDate>{{ r.pubdate }}</pubDate>{% endif %}
</item>
{% endfor %}
{% if answers %}
{% for a in answers %}
<item>
<title>{{ a }}</title>
<type>answer</type>
</item>
{% endfor %}
{% endif %}
{% if corrections %}
{% for a in corrections %}
<item>
<title>{{ a }}</title>
<type>correction</type>
</item>
{% endfor %}
{% endif %}
{% if suggestions %}
{% for a in suggestions %}
<item>
<title>{{ a }}</title>
<type>suggestion</type>
</item>
{% endfor %}
{% endif %}
</channel>
</rss>

View file

@ -1,3 +1,3 @@
<a href="https://github.com/asciimoo/searx" class="github">
<img style="position: absolute; top: 0; right: 0; border: 0;" src="{{ url_for('static', filename='img/github_ribbon.png') }}" alt="Fork me on GitHub" class="github"/>
</a>
</a>

View file

@ -85,10 +85,10 @@
{% endblock %}
<p class="text-muted">
<small>
{{ _('Powered by') }} <a href="https://asciimoo.github.io/searx/">searx</a> - {{ searx_version }} - {{ _('a privacy-respecting, hackable metasearch engine') }}<br/>
<a href="https://github.com/asciimoo/searx">{{ _('Source code') }}</a> |
<a href="https://github.com/asciimoo/searx/issues">{{ _('Issue tracker') }}</a> |
<a href="https://searx.space/">{{ _('Public instances') }}</a>
{{ _('Powered by') }} <a href="{{ brand.DOCS_URL }}">searx</a> - {{ searx_version }} - {{ _('a privacy-respecting, hackable metasearch engine') }}<br/>
<a href="{{ brand.GIT_URL }}">{{ _('Source code') }}</a> |
<a href="{{ brand.ISSUE_URL }}">{{ _('Issue tracker') }}</a> |
<a href="{{ brand.PUBLIC_INSTANCES }}">{{ _('Public instances') }}</a>
</small>
</p>
</div>

View file

@ -6,6 +6,7 @@
<input type="search" name="q" class="form-control" id="q" placeholder="{{ _('Search for...') }}" aria-label="{{ _('Search for...') }}" autocomplete="off" value="{{ q }}" accesskey="s">
<span class="input-group-btn">
<button type="submit" class="btn btn-default" aria-label="{{ _('Start search') }}"><span class="hide_if_nojs">{{ icon('search') }}</span><span class="hidden active_if_nojs">{{ _('Start search') }}</span></button>
<button type="reset" class="btn btn-default" aria-label="{{ _('Clear search') }}"><span class="hide_if_nojs">{{ icon('remove') }}</span><span class="hidden active_if_nojs">{{ _('Clear') }}</span></button>
</span>
</div>
</div>

View file

@ -9,6 +9,7 @@
<input type="search" name="q" class="form-control input-lg autofocus" id="q" placeholder="{{ _('Search for...') }}" aria-label="{{ _('Search for...') }}" autocomplete="off" value="{{ q }}" accesskey="s">
<span class="input-group-btn">
<button type="submit" class="btn btn-default input-lg" aria-label="{{ _('Start search') }}"><span class="hide_if_nojs">{{ icon('search') }}</span><span class="hidden active_if_nojs">{{ _('Start search') }}</span></button>
<button type="reset" class="btn btn-default input-lg" aria-label="{{ _('Clear search') }}"><span class="hide_if_nojs">{{ icon('remove') }}</span><span class="hidden active_if_nojs">{{ _('Clear') }}</span></button>
</span>
</div>
<div class="col-md-8 col-md-offset-2 advanced">

View file

@ -51,9 +51,9 @@
<footer>
<p>
{{ _('Powered by') }} <a href="{{ url_for('about') }}">searx</a> - {{ searx_version }} - {{ _('a privacy-respecting, hackable metasearch engine') }}<br/>
<a href="https://github.com/asciimoo/searx">{{ _('Source code') }}</a> |
<a href="https://github.com/asciimoo/searx/issues">{{ _('Issue tracker') }}</a> |
<a href="https://searx.space/">{{ _('Public instances') }}</a>
<a href="{{ brand.GIT_URL }}">{{ _('Source code') }}</a> |
<a href="{{ brand.ISSUE_URL }}">{{ _('Issue tracker') }}</a> |
<a href="{{ brand.PUBLIC_INSTANCES }}">{{ _('Public instances') }}</a>
</p>
</footer>
<!--[if gte IE 9]>-->

96
searx/webapp.py Normal file → Executable file
View file

@ -57,6 +57,7 @@ from babel.support import Translations
import flask_babel
from flask_babel import Babel, gettext, format_date, format_decimal
from flask.json import jsonify
from searx import brand
from searx import settings, searx_dir, searx_debug
from searx.exceptions import SearxParameterException
from searx.engines import (
@ -178,9 +179,12 @@ flask_babel.get_translations = _get_translations
def _get_browser_language(request, lang_list):
for lang in request.headers.get("Accept-Language", "en").split(","):
if ';' in lang:
lang = lang.split(';')[0]
locale = match_language(lang, lang_list, fallback=None)
if locale is not None:
return locale
return settings['search']['default_lang'] or 'en'
@babel.localeselector
@ -424,6 +428,8 @@ def render(template_name, override_theme=None, **kwargs):
kwargs['preferences'] = request.preferences
kwargs['brand'] = brand
kwargs['scripts'] = set()
for plugin in request.user_plugins:
for script in plugin.js_dependencies:
@ -626,20 +632,33 @@ def index():
mimetype='application/json')
elif output_format == 'csv':
csv = UnicodeWriter(StringIO())
keys = ('title', 'url', 'content', 'host', 'engine', 'score')
keys = ('title', 'url', 'content', 'host', 'engine', 'score', 'type')
csv.writerow(keys)
for row in results:
row['host'] = row['parsed_url'].netloc
row['type'] = 'result'
csv.writerow([row.get(key, '') for key in keys])
for a in result_container.answers:
row = {'title': a, 'type': 'answer'}
csv.writerow([row.get(key, '') for key in keys])
for a in result_container.suggestions:
row = {'title': a, 'type': 'suggestion'}
csv.writerow([row.get(key, '') for key in keys])
for a in result_container.corrections:
row = {'title': a, 'type': 'correction'}
csv.writerow([row.get(key, '') for key in keys])
csv.stream.seek(0)
response = Response(csv.stream.read(), mimetype='application/csv')
cont_disp = 'attachment;Filename=searx_-_{0}.csv'.format(search_query.query)
cont_disp = 'attachment;Filename=searx_-_{0}.csv'.format(search_query.query.decode('utf-8'))
response.headers.add('Content-Disposition', cont_disp)
return response
elif output_format == 'rss':
response_rss = render(
'opensearch_response_rss.xml',
results=results,
answers=result_container.answers,
corrections=result_container.corrections,
suggestions=result_container.suggestions,
q=request.form['q'],
number_of_results=number_of_results,
base_url=get_base_url(),
@ -939,34 +958,51 @@ def clear_cookies():
@app.route('/config')
def config():
return jsonify({'categories': list(categories.keys()),
'engines': [{'name': name,
'categories': engine.categories,
'shortcut': engine.shortcut,
'enabled': not engine.disabled,
'paging': engine.paging,
'language_support': engine.language_support,
'supported_languages':
list(engine.supported_languages.keys())
if isinstance(engine.supported_languages, dict)
else engine.supported_languages,
'safesearch': engine.safesearch,
'time_range_support': engine.time_range_support,
'timeout': engine.timeout}
for name, engine in engines.items() if request.preferences.validate_token(engine)],
'plugins': [{'name': plugin.name,
'enabled': plugin.default_on}
for plugin in plugins],
'instance_name': settings['general']['instance_name'],
'locales': settings['locales'],
'default_locale': settings['ui']['default_locale'],
'autocomplete': settings['search']['autocomplete'],
'safe_search': settings['search']['safe_search'],
'default_theme': settings['ui']['default_theme'],
'version': VERSION_STRING,
'doi_resolvers': [r for r in settings['doi_resolvers']],
'default_doi_resolver': settings['default_doi_resolver'],
})
"""Return configuration in JSON format."""
_engines = []
for name, engine in engines.items():
if not request.preferences.validate_token(engine):
continue
supported_languages = engine.supported_languages
if isinstance(engine.supported_languages, dict):
supported_languages = list(engine.supported_languages.keys())
_engines.append({
'name': name,
'categories': engine.categories,
'shortcut': engine.shortcut,
'enabled': not engine.disabled,
'paging': engine.paging,
'language_support': engine.language_support,
'supported_languages': supported_languages,
'safesearch': engine.safesearch,
'time_range_support': engine.time_range_support,
'timeout': engine.timeout
})
_plugins = []
for _ in plugins:
_plugins.append({'name': _.name, 'enabled': _.default_on})
return jsonify({
'categories': list(categories.keys()),
'engines': _engines,
'plugins': _plugins,
'instance_name': settings['general']['instance_name'],
'locales': settings['locales'],
'default_locale': settings['ui']['default_locale'],
'autocomplete': settings['search']['autocomplete'],
'safe_search': settings['search']['safe_search'],
'default_theme': settings['ui']['default_theme'],
'version': VERSION_STRING,
'brand': {
'GIT_URL': brand.GIT_URL,
'DOCS_URL': brand.DOCS_URL
},
'doi_resolvers': [r for r in settings['doi_resolvers']],
'default_doi_resolver': settings['default_doi_resolver'],
})
@app.errorhandler(404)