diff options
Diffstat (limited to 'searx')
53 files changed, 1100 insertions, 436 deletions
diff --git a/searx/engines/__init__.py b/searx/engines/__init__.py index 099baa587..782b622b0 100644 --- a/searx/engines/__init__.py +++ b/searx/engines/__init__.py @@ -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/searx/engines/currency_convert.py b/searx/engines/currency_convert.py index b0ffb490a..bc839cfb5 100644 --- a/searx/engines/currency_convert.py +++ b/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/searx/engines/deviantart.py b/searx/engines/deviantart.py index 135aeb324..d893fc7fe 100644 --- a/searx/engines/deviantart.py +++ b/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/searx/engines/duckduckgo.py b/searx/engines/duckduckgo.py index d29e4416a..2153492e9 100644 --- a/searx/engines/duckduckgo.py +++ b/searx/engines/duckduckgo.py @@ -22,9 +22,15 @@ from searx.languages import language_codes 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 @@ -61,6 +67,9 @@ def request(query, params): 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/searx/engines/google.py b/searx/engines/google.py index 6018ad1b2..ea93bc94f 100644 --- a/searx/engines/google.py +++ b/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/searx/engines/google_images.py b/searx/engines/google_images.py index efe46812a..090d44704 100644 --- a/searx/engines/google_images.py +++ b/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,12 +18,17 @@ from lxml import html categories = ['images'] paging = True safesearch = True +time_range_support = True search_url = 'https://www.google.com/search'\ '?{query}'\ '&tbm=isch'\ '&ijn=1'\ '&start={offset}' +time_range_search = "&tbs=qdr:{range}" +time_range_dict = {'day': 'd', + 'week': 'w', + 'month': 'm'} # do search-request @@ -34,6 +38,8 @@ def request(query, params): params['url'] = search_url.format(query=urlencode({'q': query}), offset=offset, safesearch=safesearch) + if params['time_range'] in time_range_dict: + params['url'] += time_range_search.format(range=time_range_dict[params['time_range']]) if safesearch and params['safesearch']: params['url'] += '&' + urlencode({'safe': 'active'}) diff --git a/searx/engines/json_engine.py b/searx/engines/json_engine.py index 5525b7f7e..cd5e3a7e1 100644 --- a/searx/engines/json_engine.py +++ b/searx/engines/json_engine.py @@ -8,6 +8,14 @@ content_query = None title_query = None # suggestion_xpath = '' +# 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): if type(iterable) == dict: @@ -69,8 +77,14 @@ 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'] + first_page_num - 1) * page_size + + params['url'] = search_url.format(**fp) params['query'] = query + return params diff --git a/searx/engines/startpage.py b/searx/engines/startpage.py index 52dd0b92f..d8b702c4d 100644 --- a/searx/engines/startpage.py +++ b/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/searx/engines/swisscows.py b/searx/engines/swisscows.py index 864436a52..1a94ed64e 100644 --- a/searx/engines/swisscows.py +++ b/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/searx/engines/tokyotoshokan.py b/searx/engines/tokyotoshokan.py index 17e8e2191..e2990e153 100644 --- a/searx/engines/tokyotoshokan.py +++ b/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/searx/engines/wikidata.py b/searx/engines/wikidata.py index 8aa2fcd5c..91040e218 100644 --- a/searx/engines/wikidata.py +++ b/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)) - wikipedia_link_count = 0 - wikipedia_link = get_wikilink(result, language + 'wiki') - wikipedia_link_count += add_url(urls, - 'Wikipedia (' + language + ')', - 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')) + # URLS - if language != 'en': - add_url(urls, - 'Wiki voyage (' + language + ')', - get_wikilink(result, language + 'wikivoyage')) + # official website + add_url(urls, result, 'P856', results=results) - add_url(urls, - 'Wiki voyage (en)', - get_wikilink(result, 'enwikivoyage')) + # wikipedia + wikipedia_link_count = 0 + wikipedia_link = get_wikilink(result, language + 'wiki') + if wikipedia_link: + wikipedia_link_count += 1 + urls.append({'title': 'Wikipedia (' + language + ')', + 'url': wikipedia_link}) if language != 'en': - add_url(urls, - 'Wikiquote (' + language + ')', - get_wikilink(result, language + 'wikiquote')) - - add_url(urls, - 'Wikiquote (en)', - get_wikilink(result, 'enwikiquote')) - - add_url(urls, - 'Commons wiki', - get_wikilink(result, 'commonswiki')) - - add_url(urls, - 'Location', - get_geolink(claims, 'P625', None)) - - add_url(urls, - 'Wikidata', - 'https://www.wikidata.org/wiki/' - + wikidata_id + '?uselang=' + language) - - 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) - - 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) - - 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) - - 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) - - # musicbrainz_area_id = get_string(claims, 'P982') - # P1407 MusicBrainz series ID - # P1004 MusicBrainz place ID - # P1330 MusicBrainz instrument ID - # P1407 MusicBrainz series ID - - postal_code = get_string(claims, 'P281', None) - if postal_code is not None: - attributes.append({'label': 'Postal code(s)', 'value': postal_code}) - - 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}) + wikipedia_en_link = get_wikilink(result, 'enwiki') + if wikipedia_en_link: + wikipedia_link_count += 1 + urls.append({'title': 'Wikipedia (en)', + 'url': wikipedia_en_link}) + + # TODO: get_wiki_firstlanguage + # if wikipedia_link_count == 0: + + # 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') + + add_url(urls, result, 'P625', 'OpenStreetMap', link_type='geo') + + # 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/') + + # 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/') + + urls.append({'title': 'Wikidata', + 'url': 'https://www.wikidata.org/wiki/' + + wikidata_id + '?uselang=' + language}) + + # INFOBOX ATTRIBUTES (ROWS) + + # 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) + + # 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') + + # 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') + + # 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') + + # 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') + + # 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') + + 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_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_mainsnak(claims, propertyName): - propValue = claims.get(propertyName, {}) - if len(propValue) == 0: +def get_geolink(result): + coordinates = result.xpath(value_xpath) + if not coordinates: return None - - 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) - + coordinates = extract_text(coordinates[0]) + latitude, longitude = coordinates.split(',') + + # 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/searx/engines/wikipedia.py b/searx/engines/wikipedia.py index fed7b263f..70191d22b 100644 --- a/searx/engines/wikipedia.py +++ b/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/searx/engines/www500px.py b/searx/engines/www500px.py index c98e19443..f1bc6c583 100644 --- a/searx/engines/www500px.py +++ b/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/searx/engines/yahoo.py b/searx/engines/yahoo.py index b8b40e4aa..8e24a283e 100644 --- a/searx/engines/yahoo.py +++ b/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/searx/engines/yahoo_news.py b/searx/engines/yahoo_news.py index d4cfbeda2..e91c1d34e 100644 --- a/searx/engines/yahoo_news.py +++ b/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/searx/plugins/__init__.py b/searx/plugins/__init__.py index b9b9ca0d0..768a510af 100644 --- a/searx/plugins/__init__.py +++ b/searx/plugins/__init__.py @@ -19,7 +19,9 @@ 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, @@ -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/searx/plugins/doai_rewrite.py b/searx/plugins/doai_rewrite.py new file mode 100644 index 000000000..fc5998b14 --- /dev/null +++ b/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/searx/plugins/https_rewrite.py b/searx/plugins/https_rewrite.py index 8c29520d2..8a9fcd4ad 100644 --- a/searx/plugins/https_rewrite.py +++ b/searx/plugins/https_rewrite.py @@ -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/searx/plugins/infinite_scroll.py b/searx/plugins/infinite_scroll.py new file mode 100644 index 000000000..422a4befb --- /dev/null +++ b/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/searx/results.py b/searx/results.py index 4bb0de0d8..9a4ec0b28 100644 --- a/searx/results.py +++ b/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: 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) @@ -97,10 +125,9 @@ class ResultContainer(object): 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): @@ -114,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(): @@ -138,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']) @@ -155,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 @@ -253,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/searx/search.py b/searx/search.py index a40801640..6de07d2b5 100644 --- a/searx/search.py +++ b/searx/search.py @@ -15,14 +15,14 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >. (C) 2013- by Adam Tauber, <asciimoo@gmail.com> ''' +import gc import threading -import searx.poolrequests as requests_lib +from thread import start_new_thread from time import time -from searx import settings +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 @@ -138,6 +138,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 +180,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 +282,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 +299,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 +347,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/searx/settings.yml b/searx/settings.yml index d64b73a17..4feebfcd0 100644 --- a/searx/settings.yml +++ b/searx/settings.yml @@ -87,6 +87,16 @@ engines: - 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 +115,7 @@ engines: - name : ddg definitions engine : duckduckgo_definitions shortcut : ddd + weight : 2 disabled : True - name : digg @@ -127,10 +138,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 +213,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 @@ -273,6 +300,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 @@ -310,10 +349,6 @@ engines: timeout : 10.0 disabled : True - - name : kickass - engine : kickass - shortcut : ka - - name : scanr_structures shortcut: scs engine : scanr_structures diff --git a/searx/static/plugins/css/infinite_scroll.css b/searx/static/plugins/css/infinite_scroll.css new file mode 100644 index 000000000..7e0ee20f5 --- /dev/null +++ b/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/searx/static/plugins/js/infinite_scroll.js b/searx/static/plugins/js/infinite_scroll.js new file mode 100644 index 000000000..213f74b15 --- /dev/null +++ b/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('<div class="loading-spinner"></div>'); + $.post('/', formData, function (data) { + var body = $(data); + $('#pagination').remove(); + $('#main_results').append('<hr/>'); + $('#main_results').append(body.find('.result')); + $('#main_results').append(body.find('#pagination')); + }); + } + } + }); +}); diff --git a/searx/static/plugins/js/search_on_category_select.js b/searx/static/plugins/js/search_on_category_select.js index 5ecc2cdb9..19aeef944 100644 --- a/searx/static/plugins/js/search_on_category_select.js +++ b/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/searx/static/themes/oscar/css/logicodev.min.css b/searx/static/themes/oscar/css/logicodev.min.css index 4f9fffba7..195385bba 100644 --- a/searx/static/themes/oscar/css/logicodev.min.css +++ b/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%;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{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/searx/static/themes/oscar/css/pointhi.min.css b/searx/static/themes/oscar/css/pointhi.min.css index 29f749757..389add2b9 100644 --- a/searx/static/themes/oscar/css/pointhi.min.css +++ b/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}.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/searx/static/themes/oscar/js/searx.min.js b/searx/static/themes/oscar/js/searx.min.js index 8a1055da3..9bc4aae87 100644 --- a/searx/static/themes/oscar/js/searx.min.js +++ b/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+="<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(){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 © <a href="https://openstreetmap.org">OpenStreetMap</a> 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 © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors | Tiles Courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> <img src="http://developer.mapquest.com/content/osm/mq_logo.png">',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 © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors | Tiles Courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> <img src="https://developer.mapquest.com/content/osm/mq_logo.png"> | 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+="<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)})});
\ No newline at end of file diff --git a/searx/static/themes/oscar/js/searx_src/leaflet_map.js b/searx/static/themes/oscar/js/searx_src/leaflet_map.js index cbcbe157e..4be46acb5 100644 --- a/searx/static/themes/oscar/js/searx_src/leaflet_map.js +++ b/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 © <a href="https://openstreetmap.org">OpenStreetMap</a> 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 © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors | Tiles Courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> <img src="http://developer.mapquest.com/content/osm/mq_logo.png">';
- 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 © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors | Tiles Courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> <img src="https://developer.mapquest.com/content/osm/mq_logo.png"> | 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 © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';
+ var osmMapnik = new L.TileLayer(osmMapnikUrl, {minZoom: 1, maxZoom: 19, attribution: osmMapnikAttrib});
+
+ var osmWikimediaUrl='https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png';
+ var osmWikimediaAttrib = 'Wikimedia maps beta | Maps data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';
+ var osmWikimedia = new L.TileLayer(osmWikimediaUrl, {minZoom: 1, maxZoom: 19, attribution: osmWikimediaAttrib});
// init map view
if(map_bounds) {
@@ -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/searx/static/themes/oscar/less/logicodev/advanced.less b/searx/static/themes/oscar/less/logicodev/advanced.less new file mode 100644 index 000000000..3b2cd3cbc --- /dev/null +++ b/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/searx/static/themes/oscar/less/logicodev/navbar.less b/searx/static/themes/oscar/less/logicodev/navbar.less index 493c9dc53..2426210ca 100644 --- a/searx/static/themes/oscar/less/logicodev/navbar.less +++ b/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/searx/static/themes/oscar/less/logicodev/onoff.less b/searx/static/themes/oscar/less/logicodev/onoff.less new file mode 100644 index 000000000..f47189216 --- /dev/null +++ b/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/searx/static/themes/oscar/less/logicodev/oscar.less b/searx/static/themes/oscar/less/logicodev/oscar.less index fba596c07..55181cb9c 100644 --- a/searx/static/themes/oscar/less/logicodev/oscar.less +++ b/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/searx/static/themes/oscar/less/logicodev/search.less b/searx/static/themes/oscar/less/logicodev/search.less index 1bb71a702..4ebfe8899 100644 --- a/searx/static/themes/oscar/less/logicodev/search.less +++ b/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; - - &:hover{ - color: @black; - background-color: transparent; - } - - &:active{ - box-shadow: none; - } + margin-bottom: 0.5rem; + display: flex; + flex-wrap: wrap; + flex-flow: row wrap; + align-content: stretch; + + 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/searx/static/themes/oscar/less/logicodev/variables.less b/searx/static/themes/oscar/less/logicodev/variables.less index 3ca05e76e..2b0e1a39b 100644 --- a/searx/static/themes/oscar/less/logicodev/variables.less +++ b/searx/static/themes/oscar/less/logicodev/variables.less @@ -2,6 +2,8 @@ @gray: #A4A4A4; @dim-gray: #F6F9FA; @dark-gray: #666; +@middle-gray: #F5F5F5; +@mild-gray: #DDD; @blue: #0088CC; @red: #F35E77; @violet: #684898; diff --git a/searx/static/themes/oscar/less/pointhi/advanced.less b/searx/static/themes/oscar/less/pointhi/advanced.less new file mode 100644 index 000000000..23bfdb0d2 --- /dev/null +++ b/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/searx/static/themes/oscar/less/pointhi/onoff.less b/searx/static/themes/oscar/less/pointhi/onoff.less new file mode 100644 index 000000000..72b289a23 --- /dev/null +++ b/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/searx/static/themes/oscar/less/pointhi/oscar.less b/searx/static/themes/oscar/less/pointhi/oscar.less index 6a8478530..ef69acee3 100644 --- a/searx/static/themes/oscar/less/pointhi/oscar.less +++ b/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/searx/static/themes/oscar/less/pointhi/search.less b/searx/static/themes/oscar/less/pointhi/search.less index f95ab50d4..cea67998f 100644 --- a/searx/static/themes/oscar/less/pointhi/search.less +++ b/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/searx/templates/default/infobox.html b/searx/templates/default/infobox.html index 178a27e6d..ce26ac578 100644 --- a/searx/templates/default/infobox.html +++ b/searx/templates/default/infobox.html @@ -1,18 +1,18 @@ <div class="infobox"> - <h2>{{ infobox.infobox }}</h2> +<h2><bdi>{{ infobox.infobox }}</bdi></h2> {% if infobox.img_src %}<img src="{{ image_proxify(infobox.img_src) }}" title="{{ infobox.infobox|striptags }}" alt="{{ infobox.infobox|striptags }}" />{% endif %} - <p>{{ infobox.entity }}</p> - <p>{{ infobox.content | safe }}</p> + <p><bdi>{{ infobox.entity }}</bdi></p> + <p><bdi>{{ infobox.content | safe }}</bdi></p> {% if infobox.attributes %} <div class="attributes"> <table> {% for attribute in infobox.attributes %} <tr> - <td>{{ attribute.label }}</td> + <td><bdi>{{ attribute.label }}</bdi></td> {% if attribute.image %} <td><img src="{{ image_proxify(attribute.image.src) }}" alt="{{ attribute.image.alt }}" /></td> {% else %} - <td>{{ attribute.value }}</td> + <td><bdi>{{ attribute.value }}</bdi></td> {% endif %} </tr> {% endfor %} @@ -24,7 +24,7 @@ <div class="urls"> <ul> {% for url in infobox.urls %} - <li class="url"><a href="{{ url.url }}" rel="noreferrer">{{ url.title }}</a></li> + <li class="url"><bdi><a href="{{ url.url }}" rel="noreferrer">{{ url.title }}</a></bdi></li> {% endfor %} </ul> </div> @@ -34,7 +34,7 @@ <div class="relatedTopics"> {% for topic in infobox.relatedTopics %} <div> - <h3>{{ topic.name }}</h3> + <h3><bdi>{{ topic.name }}</bdi></h3> {% for suggestion in topic.suggestions %} <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}"> <input type="hidden" name="q" value="{{ suggestion }}"> diff --git a/searx/templates/oscar/about.html b/searx/templates/oscar/about.html index e1f378294..39ef3663e 100644 --- a/searx/templates/oscar/about.html +++ b/searx/templates/oscar/about.html @@ -27,7 +27,7 @@ Searx can be added to your browser's search bar; moreover, it can be set as the <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://github.com/asciimoo/searx/wiki/Searx-instances">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> +<br />The more decentralized the Internet is, the more freedom we have!</p> <h2>More about searx</h2> diff --git a/searx/templates/oscar/advanced.html b/searx/templates/oscar/advanced.html new file mode 100644 index 000000000..2c694cf5f --- /dev/null +++ b/searx/templates/oscar/advanced.html @@ -0,0 +1,9 @@ +<input type="checkbox" name="advanced_search" id="check-advanced" {% if advanced_search %} checked="checked"{% endif %}> +<label for="check-advanced"> + <span class="glyphicon glyphicon-cog"></span> + {{ _('Advanced settings') }} +</label> +<div id="advanced-search-container"> + {% include 'oscar/categories.html' %} + {% include 'oscar/time-range.html' %} +</div> diff --git a/searx/templates/oscar/base.html b/searx/templates/oscar/base.html index 649d91f4d..a1f1c1a90 100644 --- a/searx/templates/oscar/base.html +++ b/searx/templates/oscar/base.html @@ -90,8 +90,5 @@ {% for script in scripts %} <script src="{{ url_for('static', filename=script) }}"></script> {% endfor %} - <script type="text/javascript"> - $(function() { $('a[data-toggle="modal"]').attr('href', '#'); }); - </script> </body> </html> diff --git a/searx/templates/oscar/categories.html b/searx/templates/oscar/categories.html index 834cffcce..241d262ea 100644 --- a/searx/templates/oscar/categories.html +++ b/searx/templates/oscar/categories.html @@ -1,42 +1,14 @@ -<!-- used if scripts are disabled --> -<noscript> -<div id="categories" class="btn-group btn-toggle"> +<div id="categories"> {% if rtl %} -{% for category in categories | reverse %} - <!--<div class="checkbox">--> - <input class="hidden" type="checkbox" id="checkbox_{{ category|replace(' ', '_') }}_nojs" name="category_{{ category }}" {% if category in selected_categories %}checked="checked"{% endif %} /> - <label class="btn btn-sm btn-primary active label_hide_if_not_checked" for="checkbox_{{ category|replace(' ', '_') }}_nojs">{{ _(category) }}</label> - <label class="btn btn-sm btn-default label_hide_if_checked" for="checkbox_{{ category|replace(' ', '_') }}_nojs">{{ _(category) }}</label> - <!--</div>--> - {% if category in selected_categories %}<input class="hidden" type="checkbox" id="checkbox_{{ category|replace(' ', '_') }}_dis_activation" name="category_{{ category }}" value="off" checked="checked"/>{% endif %} -{% endfor %} + {% for category in categories | reverse %} + <input class="hidden" type="checkbox" id="checkbox_{{ category|replace(' ', '_') }}" name="category_{{ category }}" {% if category in selected_categories %}checked="checked"{% endif %} /> + <label for="checkbox_{{ category|replace(' ', '_') }}">{{ _(category) }}</label> + </label> + {% endfor %} {% else %} -{% for category in categories %} - <!--<div class="checkbox">--> - <input class="hidden" type="checkbox" id="checkbox_{{ category|replace(' ', '_') }}_nojs" name="category_{{ category }}" {% if category in selected_categories %}checked="checked"{% endif %} /> - <label class="btn btn-sm btn-primary active label_hide_if_not_checked" for="checkbox_{{ category|replace(' ', '_') }}_nojs">{{ _(category) }}</label> - <label class="btn btn-sm btn-default label_hide_if_checked" for="checkbox_{{ category|replace(' ', '_') }}_nojs">{{ _(category) }}</label> - <!--</div>--> - {% if category in selected_categories %}<input class="hidden" type="checkbox" id="checkbox_{{ category|replace(' ', '_') }}_dis_activation" name="category_{{ category }}" value="off" checked="checked"/>{% endif %} -{% endfor %} + {% for category in categories %} + <input class="hidden" type="checkbox" id="checkbox_{{ category|replace(' ', '_') }}" name="category_{{ category }}" {% if category in selected_categories %}checked="checked"{% endif %} /> + <label for="checkbox_{{ category|replace(' ', '_') }}">{{ _(category) }}</label> + {% endfor %} {% endif %} </div> -</noscript> - -<div id="categories" class="btn-group btn-toggle hide_if_nojs" data-toggle="buttons"> -{% if rtl %} -{% for category in categories | reverse %} - <label class="btn btn-sm {% if category in selected_categories %}btn-primary active{% else %}btn-default{% endif %}" data-btn-class="primary"> - <input class="hidden" type="checkbox" id="checkbox_{{ category|replace(' ', '_') }}" name="category_{{ category }}" {% if category in selected_categories %}checked="checked"{% endif %} />{{ _(category) }} - </label> -{% endfor %} -{% else %} -{% for category in categories %} - <label class="btn btn-sm {% if category in selected_categories %}btn-primary active{% else %}btn-default{% endif %}" data-btn-class="primary"> - <input class="hidden" type="checkbox" id="checkbox_{{ category|replace(' ', '_') }}" name="category_{{ category }}" {% if category in selected_categories %}checked="checked"{% endif %} />{{ _(category) }} - </label> -{% endfor %} -{% endif %} -</div> - - diff --git a/searx/templates/oscar/infobox.html b/searx/templates/oscar/infobox.html index c72cfb638..70d12f2fb 100644 --- a/searx/templates/oscar/infobox.html +++ b/searx/templates/oscar/infobox.html @@ -1,21 +1,20 @@ <div class="panel panel-default infobox"> <div class="panel-heading"> - <bdi><h4 class="panel-title infobox_part">{{ infobox.infobox }}</h4></bdi> + <h4 class="panel-title infobox_part"><bdi>{{ infobox.infobox }}</bdi></h4> </div> <div class="panel-body"> - <bdi> {% if infobox.img_src %}<img class="img-responsive center-block infobox_part" src="{{ image_proxify(infobox.img_src) }}" alt="{{ infobox.infobox }}" />{% endif %} - {% if infobox.content %}<p class="infobox_part">{{ infobox.content }}</p>{% endif %} + {% if infobox.content %}<bdi><p class="infobox_part">{{ infobox.content }}</bdi></p>{% endif %} {% if infobox.attributes %} <table class="table table-striped infobox_part"> {% for attribute in infobox.attributes %} <tr> - <td>{{ attribute.label }}</td> + <td><bdi>{{ attribute.label }}</bdi></td> {% if attribute.image %} <td><img class="img-responsive" src="{{ image_proxify(attribute.image.src) }}" alt="{{ attribute.image.alt }}" /></td> {% else %} - <td>{{ attribute.value }}</td> + <td><bdi>{{ attribute.value }}</bdi></td> {% endif %} </tr> {% endfor %} @@ -24,11 +23,12 @@ {% if infobox.urls %} <div class="infobox_part"> + <bdi> {% for url in infobox.urls %} <p class="btn btn-default btn-xs"><a href="{{ url.url }}" rel="noreferrer">{{ url.title }}</a></p> {% endfor %} + </bdi> </div> {% endif %} - </bdi> </div> </div> diff --git a/searx/templates/oscar/macros.html b/searx/templates/oscar/macros.html index a826b0e1e..bf51d5940 100644 --- a/searx/templates/oscar/macros.html +++ b/searx/templates/oscar/macros.html @@ -68,9 +68,11 @@ {%- endmacro %}
{% macro checkbox_toggle(id, blocked) -%}
- <div class="checkbox">
- <input class="hidden" type="checkbox" id="{{ id }}" name="{{ id }}"{% if blocked %} checked="checked"{% endif %} />
- <label class="btn btn-success label_hide_if_checked" for="{{ id }}">{{ _('Block') }}</label>
- <label class="btn btn-danger label_hide_if_not_checked" for="{{ id }}">{{ _('Allow') }}</label>
+ <div class="onoffswitch">
+ <input type="checkbox" id="{{ id }}" name="{{ id }}"{% if blocked %} checked="checked"{% endif %} class="onoffswitch-checkbox">
+ <label class="onoffswitch-label" for="{{ id }}">
+ <span class="onoffswitch-inner"></span>
+ <span class="onoffswitch-switch"></span>
+ </label>
</div>
{%- endmacro %}
diff --git a/searx/templates/oscar/preferences.html b/searx/templates/oscar/preferences.html index 18308bd23..0e3848b7c 100644 --- a/searx/templates/oscar/preferences.html +++ b/searx/templates/oscar/preferences.html @@ -36,7 +36,7 @@ <label class="col-sm-3 col-md-2">{{ _('Default categories') }}</label> {% else %} <label class="col-sm-3 col-md-2">{{ _('Default categories') }}</label> - <div class="col-sm-11 col-md-10"> + <div class="col-sm-11 col-md-10 search-categories"> {% include 'oscar/categories.html' %} </div> {% endif %} @@ -164,7 +164,9 @@ {% if not search_engine.private %} <tr> {% if not rtl %} - <td>{{ checkbox_toggle('engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_'), (search_engine.name, categ) in disabled_engines) }}</td> + <td class="onoff-checkbox"> + {{ checkbox_toggle('engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_'), (search_engine.name, categ) in disabled_engines) }} + </td> <th>{{ search_engine.name }}</th> <td>{{ shortcuts[search_engine.name] }}</td> <td><input type="checkbox" {{ "checked" if search_engine.safesearch==True else ""}} readonly="readonly" disabled="disabled"></td> @@ -176,7 +178,9 @@ <td><input type="checkbox" {{ "checked" if search_engine.safesearch==True else ""}} readonly="readonly" disabled="disabled"></td> <td>{{ shortcuts[search_engine.name] }}</td> <th>{{ search_engine.name }}</th> - <td>{{ checkbox_toggle('engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_'), (search_engine.name, categ) in disabled_engines) }}</td> + <td class="onoff-checkbox"> + {{ checkbox_toggle('engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_'), (search_engine.name, categ) in disabled_engines) }} + </td> {% endif %} </tr> {% endif %} @@ -203,7 +207,9 @@ <div class="panel-body"> <div class="col-xs-6 col-sm-4 col-md-6">{{ _(plugin.description) }}</div> <div class="col-xs-6 col-sm-4 col-md-6"> + <div class="onoff-checkbox"> {{ checkbox_toggle('plugin_' + plugin.id, plugin.id not in allowed_plugins) }} + </div> </div> </div> </div> diff --git a/searx/templates/oscar/results.html b/searx/templates/oscar/results.html index 39cdff494..e71be325a 100644 --- a/searx/templates/oscar/results.html +++ b/searx/templates/oscar/results.html @@ -1,6 +1,6 @@ {% extends "oscar/base.html" %}
{% block title %}{{ q }} - {% endblock %}
-{% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q }}" href="{{ url_for('index') }}?q={{ q|urlencode }}&format=rss&{% for category in selected_categories %}category_{{ category }}=1&{% endfor %}pageno={{ pageno }}">{% endblock %}
+{% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q }}" href="{{ url_for('index') }}?q={{ q|urlencode }}&format=rss&{% for category in selected_categories %}category_{{ category }}=1&{% endfor %}pageno={{ pageno }}&time_range={{ time_range }}">{% endblock %}
{% block content %}
<div class="row">
<div class="col-sm-8" id="main_results">
@@ -41,6 +41,7 @@ {% for category in selected_categories %}<input type="hidden" name="category_{{ category }}" value="1"/>{% endfor %}
<input type="hidden" name="q" value="{{ q }}" />
<input type="hidden" name="pageno" value="{{ pageno+1 }}" />
+ <input type="hidden" name="time_range" value="{{ time_range }}" />
<button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-backward"></span> {{ _('next page') }}</button>
</form>
</div>
@@ -48,6 +49,7 @@ <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" class="pull-left">
{% for category in selected_categories %}<input type="hidden" name="category_{{ category }}" value="1"/>{% endfor %}
<input type="hidden" name="pageno" value="{{ pageno-1 }}" />
+ <input type="hidden" name="time_range" value="{{ time_range }}" />
<button type="submit" class="btn btn-default" {% if pageno == 1 %}disabled{% endif %}><span class="glyphicon glyphicon-forward"></span> {{ _('previous page') }}</button>
</form>
</div>
@@ -60,6 +62,7 @@ <input type="hidden" name="q" value="{{ q }}" />
{% for category in selected_categories %}<input type="hidden" name="category_{{ category }}" value="1"/>{% endfor %}
<input type="hidden" name="pageno" value="{{ pageno-1 }}" />
+ <input type="hidden" name="time_range" value="{{ time_range }}" />
<button type="submit" class="btn btn-default" {% if pageno == 1 %}disabled{% endif %}><span class="glyphicon glyphicon-backward"></span> {{ _('previous page') }}</button>
</form>
</div>
@@ -68,6 +71,7 @@ {% for category in selected_categories %}<input type="hidden" name="category_{{ category }}" value="1"/>{% endfor %}
<input type="hidden" name="q" value="{{ q }}" />
<input type="hidden" name="pageno" value="{{ pageno+1 }}" />
+ <input type="hidden" name="time_range" value="{{ time_range }}" />
<button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-forward"></span> {{ _('next page') }}</button>
</form>
</div>
@@ -118,7 +122,7 @@ <form role="form">
<div class="form-group">
<label for="search_url">{{ _('Search URL') }}</label>
- <input id="search_url" type="url" class="form-control select-all-on-click cursor-text" name="search_url" value="{{ base_url }}?q={{ q|urlencode }}{% if selected_categories %}&categories={{ selected_categories|join(",") | replace(' ','+') }}{% endif %}{% if pageno > 1 %}&pageno={{ pageno }}{% endif %}" readonly>
+ <input id="search_url" type="url" class="form-control select-all-on-click cursor-text" name="search_url" value="{{ base_url }}?q={{ q|urlencode }}{% if selected_categories %}&categories={{ selected_categories|join(",") | replace(' ','+') }}{% endif %}{% if pageno > 1 %}&pageno={{ pageno }}{% endif %}{% if time_range %}&time_range={{ time_range }}{% endif %}" readonly>
</div>
</form>
@@ -130,6 +134,7 @@ <input type="hidden" name="format" value="{{ output_type }}">
{% for category in selected_categories %}<input type="hidden" name="category_{{ category }}" value="1">{% endfor %}
<input type="hidden" name="pageno" value="{{ pageno }}">
+ <input type="hidden" name="time_range" value="{{ time_range }}" />
<button type="submit" class="btn btn-default">{{ output_type }}</button>
</form>
{% endfor %}
diff --git a/searx/templates/oscar/search.html b/searx/templates/oscar/search.html index e48c80fd5..6a3b2d294 100644 --- a/searx/templates/oscar/search.html +++ b/searx/templates/oscar/search.html @@ -6,7 +6,7 @@ <button type="submit" class="btn btn-default"><span class="hide_if_nojs">{{ icon('search') }}</span><span class="hidden active_if_nojs">{{ _('Start search') }}</span></button>
</span>
</div>
- <div class="search_categories">
- {% include 'oscar/categories.html' %}
- </div><!-- / #search_categories -->
+ <div class="input-group col-sm-12 advanced">
+ {% include 'oscar/advanced.html' %}
+ </div>
</form><!-- / #search_form_full -->
diff --git a/searx/templates/oscar/search_full.html b/searx/templates/oscar/search_full.html index 26dae418c..6fdae4028 100644 --- a/searx/templates/oscar/search_full.html +++ b/searx/templates/oscar/search_full.html @@ -11,11 +11,8 @@ <button type="submit" class="btn btn-default input-lg"><span class="hide_if_nojs">{{ icon('search') }}</span><span class="hidden active_if_nojs">{{ _('Start search') }}</span></button>
</span>
</div>
+ <div class="col-md-8 col-md-offset-2 advanced">
+ {% include 'oscar/advanced.html' %}
+ </div>
- <button type="button" class="btn btn-link btn-collapse center-block collapsed hide_if_nojs" data-toggle="collapse" data-target="#search_categories" data-btn-text-collapsed="{{ _('Show search filters') }}" data-btn-text-not-collapsed="{{ _('Hide search filters') }}">{{ _('Show search filters') }}</button>
- <div class="row collapse active_if_nojs margin_top_if_nojs" id="search_categories">
- <div class="col-md-12 text-center">
- {% include 'oscar/categories.html' %}
- </div>
- </div><!-- / #search_categories -->
</form><!-- / #search_form_full -->
diff --git a/searx/templates/oscar/time-range.html b/searx/templates/oscar/time-range.html new file mode 100644 index 000000000..4a13c4fdb --- /dev/null +++ b/searx/templates/oscar/time-range.html @@ -0,0 +1,14 @@ +<select name="time_range" id="time-range"> + <option id="time-range-anytime" value="" {{ "selected" if time_range=="" or not time_range else ""}}> + {{ _('Anytime') }} + </option> + <option id="time-range-day" value="day" {{ "selected" if time_range=="day" else ""}}> + {{ _('Last day') }} + </option> + <option id="time-range-week" value="week" {{ "selected" if time_range=="week" else ""}}> + {{ _('Last week') }} + </option> + <option id="time-range-month" value="month" {{ "selected" if time_range=="month" else ""}}> + {{ _('Last month') }} + </option> +</select> diff --git a/searx/utils.py b/searx/utils.py index 219135a4b..aa8ce92a1 100644 --- a/searx/utils.py +++ b/searx/utils.py @@ -63,7 +63,7 @@ def highlight_content(content, query): regex_parts = [] for chunk in query.split(): if len(chunk) == 1: - regex_parts.append(u'\W+{0}\W+'.format(re.escape(chunk))) + regex_parts.append(u'\\W+{0}\\W+'.format(re.escape(chunk))) else: regex_parts.append(u'{0}'.format(re.escape(chunk))) query_regex = u'({0})'.format('|'.join(regex_parts)) @@ -206,7 +206,13 @@ def format_date_by_locale(date, locale_string): if locale_string == 'all': locale_string = settings['ui']['default_locale'] or 'en_US' - return format_date(date, locale=locale_string) + # to avoid crashing if locale is not supported by babel + try: + formatted_date = format_date(date, locale=locale_string) + except: + formatted_date = format_date(date, "YYYY-MM-dd") + + return formatted_date def dict_subset(d, properties): diff --git a/searx/webapp.py b/searx/webapp.py index 00a203636..460681b35 100644 --- a/searx/webapp.py +++ b/searx/webapp.py @@ -380,7 +380,9 @@ def index(): plugins.call('post_search', request, locals()) - for result in search.result_container.get_ordered_results(): + results = search.result_container.get_ordered_results() + + for result in results: plugins.call('on_result', request, locals()) if not search.paging and engines[result['engine']].paging: @@ -418,16 +420,20 @@ def index(): else: result['publishedDate'] = format_date(result['publishedDate']) + number_of_results = search.result_container.results_number() + if number_of_results < search.result_container.results_length(): + number_of_results = 0 + if search.request_data.get('format') == 'json': return Response(json.dumps({'query': search.query, - 'number_of_results': search.result_container.number_of_results, - 'results': search.result_container.get_ordered_results()}), + 'number_of_results': number_of_results, + 'results': results}), mimetype='application/json') elif search.request_data.get('format') == 'csv': csv = UnicodeWriter(cStringIO.StringIO()) keys = ('title', 'url', 'content', 'host', 'engine', 'score') csv.writerow(keys) - for row in search.result_container.get_ordered_results(): + for row in results: row['host'] = row['parsed_url'].netloc csv.writerow([row.get(key, '') for key in keys]) csv.stream.seek(0) @@ -438,21 +444,23 @@ def index(): elif search.request_data.get('format') == 'rss': response_rss = render( 'opensearch_response_rss.xml', - results=search.result_container.get_ordered_results(), + results=results, q=search.request_data['q'], - number_of_results=search.result_container.number_of_results, + number_of_results=number_of_results, base_url=get_base_url() ) return Response(response_rss, mimetype='text/xml') return render( 'results.html', - results=search.result_container.get_ordered_results(), + results=results, q=search.request_data['q'], selected_categories=search.categories, paging=search.paging, - number_of_results=format_decimal(search.result_container.number_of_results), + number_of_results=format_decimal(number_of_results), pageno=search.pageno, + advanced_search=search.is_advanced, + time_range=search.time_range, base_url=get_base_url(), suggestions=search.result_container.suggestions, answers=search.result_container.answers, |