update searx
|
@ -1,22 +1,31 @@
|
||||||
server:
|
server:
|
||||||
port : 8888
|
port : 8888
|
||||||
secret_key : "ultrasecretkey" # change this!
|
secret_key : "ultrasecretkey" # change this!
|
||||||
debug : True
|
debug : False # Debug mode, only for development
|
||||||
request_timeout : 2.0 # seconds
|
request_timeout : 2.0 # seconds
|
||||||
base_url : ynhbaseurl
|
base_url : ynhbaseurl # Set custom base_url. Possible values: False or "https://your.custom.host/location/"
|
||||||
|
themes_path : "" # Custom ui themes path
|
||||||
|
default_theme : default # ui theme
|
||||||
|
https_rewrite : True # Force rewrite result urls. See searx/https_rewrite.py
|
||||||
|
useragent_suffix : "" # suffix of searx_useragent, could contain informations like an email address to the administrator
|
||||||
|
|
||||||
engines:
|
engines:
|
||||||
- name : wikipedia
|
- name : wikipedia
|
||||||
engine : wikipedia
|
engine : mediawiki
|
||||||
number_of_results : 1
|
|
||||||
paging : False
|
|
||||||
shortcut : wp
|
shortcut : wp
|
||||||
|
base_url : 'https://{language}.wikipedia.org/'
|
||||||
|
number_of_results : 1
|
||||||
|
|
||||||
- name : bing
|
- name : bing
|
||||||
engine : bing
|
engine : bing
|
||||||
locale : en-US
|
locale : en-US
|
||||||
shortcut : bi
|
shortcut : bi
|
||||||
|
|
||||||
|
- name : bing images
|
||||||
|
engine : bing_images
|
||||||
|
locale : en-US
|
||||||
|
shortcut : bii
|
||||||
|
|
||||||
- name : bing news
|
- name : bing news
|
||||||
engine : bing_news
|
engine : bing_news
|
||||||
locale : en-US
|
locale : en-US
|
||||||
|
@ -29,7 +38,6 @@ engines:
|
||||||
|
|
||||||
- name : deviantart
|
- name : deviantart
|
||||||
engine : deviantart
|
engine : deviantart
|
||||||
categories : images
|
|
||||||
shortcut : da
|
shortcut : da
|
||||||
timeout: 3.0
|
timeout: 3.0
|
||||||
|
|
||||||
|
@ -37,15 +45,25 @@ engines:
|
||||||
engine : duckduckgo_definitions
|
engine : duckduckgo_definitions
|
||||||
shortcut : ddd
|
shortcut : ddd
|
||||||
|
|
||||||
|
- name : wikidata
|
||||||
|
engine : wikidata
|
||||||
|
shortcut : wd
|
||||||
|
|
||||||
- name : duckduckgo
|
- name : duckduckgo
|
||||||
engine : duckduckgo
|
engine : duckduckgo
|
||||||
locale : en-us
|
|
||||||
shortcut : ddg
|
shortcut : ddg
|
||||||
|
|
||||||
- name : filecrop
|
# api-key required: http://www.faroo.com/hp/api/api.html#key
|
||||||
engine : filecrop
|
# - name : faroo
|
||||||
categories : files
|
# engine : faroo
|
||||||
shortcut : fc
|
# shortcut : fa
|
||||||
|
# api_key : 'apikey' # required!
|
||||||
|
|
||||||
|
# down - website is under criminal investigation by the UK
|
||||||
|
# - name : filecrop
|
||||||
|
# engine : filecrop
|
||||||
|
# categories : files
|
||||||
|
# shortcut : fc
|
||||||
|
|
||||||
- name : flickr
|
- name : flickr
|
||||||
engine : flickr
|
engine : flickr
|
||||||
|
@ -53,9 +71,12 @@ engines:
|
||||||
shortcut : fl
|
shortcut : fl
|
||||||
timeout: 3.0
|
timeout: 3.0
|
||||||
|
|
||||||
|
- name : general-file
|
||||||
|
engine : generalfile
|
||||||
|
shortcut : gf
|
||||||
|
|
||||||
- name : github
|
- name : github
|
||||||
engine : github
|
engine : github
|
||||||
categories : it
|
|
||||||
shortcut : gh
|
shortcut : gh
|
||||||
|
|
||||||
- name : google
|
- name : google
|
||||||
|
@ -70,25 +91,24 @@ engines:
|
||||||
engine : google_news
|
engine : google_news
|
||||||
shortcut : gon
|
shortcut : gon
|
||||||
|
|
||||||
|
- name : openstreetmap
|
||||||
|
engine : openstreetmap
|
||||||
|
shortcut : osm
|
||||||
|
|
||||||
- name : piratebay
|
- name : piratebay
|
||||||
engine : piratebay
|
engine : piratebay
|
||||||
categories : videos, music, files
|
|
||||||
shortcut : tpb
|
shortcut : tpb
|
||||||
|
|
||||||
- name : soundcloud
|
- name : soundcloud
|
||||||
engine : soundcloud
|
engine : soundcloud
|
||||||
categories : music
|
|
||||||
shortcut : sc
|
shortcut : sc
|
||||||
|
|
||||||
- name : stackoverflow
|
- name : stackoverflow
|
||||||
engine : stackoverflow
|
engine : stackoverflow
|
||||||
categories : it
|
|
||||||
shortcut : st
|
shortcut : st
|
||||||
|
|
||||||
- name : startpage
|
- name : startpage
|
||||||
engine : startpage
|
engine : startpage
|
||||||
base_url : 'https://startpage.com/'
|
|
||||||
search_url : 'https://startpage.com/do/search'
|
|
||||||
shortcut : sp
|
shortcut : sp
|
||||||
|
|
||||||
# +30% page load time
|
# +30% page load time
|
||||||
|
@ -99,15 +119,14 @@ engines:
|
||||||
|
|
||||||
- name : twitter
|
- name : twitter
|
||||||
engine : twitter
|
engine : twitter
|
||||||
categories : social media
|
|
||||||
shortcut : tw
|
shortcut : tw
|
||||||
|
|
||||||
# maybe in a fun category
|
# maybe in a fun category
|
||||||
# - name : uncyclopedia
|
# - name : uncyclopedia
|
||||||
# engine : mediawiki
|
# engine : mediawiki
|
||||||
# categories : general
|
|
||||||
# shortcut : unc
|
# shortcut : unc
|
||||||
# url : https://uncyclopedia.wikia.com/
|
# base_url : https://uncyclopedia.wikia.com/
|
||||||
|
# number_of_results : 5
|
||||||
|
|
||||||
# tmp suspended - too slow, too many errors
|
# tmp suspended - too slow, too many errors
|
||||||
# - name : urbandictionary
|
# - name : urbandictionary
|
||||||
|
@ -128,24 +147,24 @@ engines:
|
||||||
|
|
||||||
- name : youtube
|
- name : youtube
|
||||||
engine : youtube
|
engine : youtube
|
||||||
categories : videos
|
|
||||||
shortcut : yt
|
shortcut : yt
|
||||||
|
|
||||||
- name : dailymotion
|
- name : dailymotion
|
||||||
engine : dailymotion
|
engine : dailymotion
|
||||||
locale : en_US
|
|
||||||
categories : videos
|
|
||||||
shortcut : dm
|
shortcut : dm
|
||||||
|
|
||||||
- name : vimeo
|
- name : vimeo
|
||||||
engine : vimeo
|
engine : vimeo
|
||||||
categories : videos
|
locale : en-US
|
||||||
results_xpath : //div[@id="browse_content"]/ol/li
|
|
||||||
url_xpath : ./a/@href
|
|
||||||
title_xpath : ./a/div[@class="data"]/p[@class="title"]/text()
|
|
||||||
content_xpath : ./a/img/@src
|
|
||||||
shortcut : vm
|
shortcut : vm
|
||||||
|
|
||||||
|
# - name : yacy
|
||||||
|
# engine : yacy
|
||||||
|
# shortcut : ya
|
||||||
|
# base_url : 'http://localhost:8090'
|
||||||
|
# number_of_results : 5
|
||||||
|
# timeout: 3.0
|
||||||
|
|
||||||
locales:
|
locales:
|
||||||
en : English
|
en : English
|
||||||
de : Deutsch
|
de : Deutsch
|
||||||
|
@ -154,3 +173,4 @@ locales:
|
||||||
es : Español
|
es : Español
|
||||||
it : Italiano
|
it : Italiano
|
||||||
nl : Nederlands
|
nl : Nederlands
|
||||||
|
ja : 日本語 (Japanese)
|
||||||
|
|
|
@ -52,7 +52,7 @@ if [ "$path" != "/" ];
|
||||||
then
|
then
|
||||||
sudo sed -i -e "s@ynhbaseurl@https://$domain$path/@g" $final_path/searx/settings.yml
|
sudo sed -i -e "s@ynhbaseurl@https://$domain$path/@g" $final_path/searx/settings.yml
|
||||||
else
|
else
|
||||||
sudo sed -i -e "s@ynhbaseurl@https://$domain$path@g" $final_path/searx/settings.yml
|
sudo sed -i -e "s@ynhbaseurl@False@g" $final_path/searx/settings.yml
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Set permissions to searx directory
|
# Set permissions to searx directory
|
||||||
|
|
|
@ -24,3 +24,6 @@ generally made searx better:
|
||||||
- Alejandro León Aznar
|
- Alejandro León Aznar
|
||||||
- rike
|
- rike
|
||||||
- dp
|
- dp
|
||||||
|
- Martin Zimmermann
|
||||||
|
- @courgette
|
||||||
|
- @kernc
|
||||||
|
|
|
@ -29,9 +29,9 @@ flake8: .installed.cfg
|
||||||
@bin/flake8 ./searx/
|
@bin/flake8 ./searx/
|
||||||
|
|
||||||
coverage: .installed.cfg
|
coverage: .installed.cfg
|
||||||
@bin/coverage run --source=./searx/ --branch bin/test
|
@bin/coverage run bin/test
|
||||||
@bin/coverage report --show-missing
|
@bin/coverage report
|
||||||
@bin/coverage html --directory ./coverage
|
@bin/coverage html
|
||||||
|
|
||||||
production: bin/buildout production.cfg setup.py
|
production: bin/buildout production.cfg setup.py
|
||||||
bin/buildout -c production.cfg $(options)
|
bin/buildout -c production.cfg $(options)
|
||||||
|
@ -44,13 +44,15 @@ minimal: bin/buildout minimal.cfg setup.py
|
||||||
bin/buildout -c minimal.cfg $(options)
|
bin/buildout -c minimal.cfg $(options)
|
||||||
|
|
||||||
styles:
|
styles:
|
||||||
@lessc -x searx/static/less/style.less > searx/static/css/style.css
|
@lessc -x searx/static/default/less/style.less > searx/static/default/css/style.css
|
||||||
|
@lessc -x searx/static/oscar/less/bootstrap/bootstrap.less > searx/static/oscar/css/bootstrap.min.css
|
||||||
|
@lessc -x searx/static/oscar/less/oscar/oscar.less > searx/static/oscar/css/oscar.min.css
|
||||||
|
|
||||||
locales:
|
locales:
|
||||||
@pybabel compile -d searx/translations
|
@pybabel compile -d searx/translations
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@rm -rf .installed.cfg .mr.developer.cfg bin parts develop-eggs \
|
@rm -rf .installed.cfg .mr.developer.cfg bin parts develop-eggs \
|
||||||
searx.egg-info lib include .coverage coverage searx/static/css/*.css
|
searx.egg-info lib include .coverage coverage searx/static/default/css/*.css
|
||||||
|
|
||||||
.PHONY: all tests robot flake8 coverage production minimal styles locales clean
|
.PHONY: all tests robot flake8 coverage production minimal styles locales clean
|
||||||
|
|
|
@ -16,9 +16,9 @@ Features
|
||||||
|
|
||||||
- Tracking free
|
- Tracking free
|
||||||
- Supports multiple output formats
|
- Supports multiple output formats
|
||||||
- json ``curl https://searx.0x2a.tk/?format=json&q=[query]``
|
- json ``curl https://searx.me/?format=json&q=[query]``
|
||||||
- csv ``curl https://searx.0x2a.tk/?format=csv&q=[query]``
|
- csv ``curl https://searx.me/?format=csv&q=[query]``
|
||||||
- opensearch/rss ``curl https://searx.0x2a.tk/?format=rss&q=[query]``
|
- opensearch/rss ``curl https://searx.me/?format=rss&q=[query]``
|
||||||
- Opensearch support (you can set as default search engine)
|
- Opensearch support (you can set as default search engine)
|
||||||
- Configurable search engines/categories
|
- Configurable search engines/categories
|
||||||
- Different search languages
|
- Different search languages
|
||||||
|
|
|
@ -1,5 +1,23 @@
|
||||||
|
'''
|
||||||
|
searx is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
searx is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with searx. If not, see < http://www.gnu.org/licenses/ >.
|
||||||
|
|
||||||
|
(C) 2013- by Adam Tauber, <asciimoo@gmail.com>
|
||||||
|
'''
|
||||||
|
|
||||||
from os import environ
|
from os import environ
|
||||||
from os.path import realpath, dirname, join, abspath
|
from os.path import realpath, dirname, join, abspath
|
||||||
|
from searx.https_rewrite import load_https_rules
|
||||||
try:
|
try:
|
||||||
from yaml import load
|
from yaml import load
|
||||||
except:
|
except:
|
||||||
|
@ -10,11 +28,24 @@ except:
|
||||||
searx_dir = abspath(dirname(__file__))
|
searx_dir = abspath(dirname(__file__))
|
||||||
engine_dir = dirname(realpath(__file__))
|
engine_dir = dirname(realpath(__file__))
|
||||||
|
|
||||||
|
# if possible set path to settings using the
|
||||||
|
# enviroment variable SEARX_SETTINGS_PATH
|
||||||
if 'SEARX_SETTINGS_PATH' in environ:
|
if 'SEARX_SETTINGS_PATH' in environ:
|
||||||
settings_path = environ['SEARX_SETTINGS_PATH']
|
settings_path = environ['SEARX_SETTINGS_PATH']
|
||||||
|
# otherwise using default path
|
||||||
else:
|
else:
|
||||||
settings_path = join(searx_dir, 'settings.yml')
|
settings_path = join(searx_dir, 'settings.yml')
|
||||||
|
|
||||||
|
if 'SEARX_HTTPS_REWRITE_PATH' in environ:
|
||||||
|
https_rewrite_path = environ['SEARX_HTTPS_REWRITE_PATH']
|
||||||
|
else:
|
||||||
|
https_rewrite_path = join(searx_dir, 'https_rules')
|
||||||
|
|
||||||
|
# load settings
|
||||||
with open(settings_path) as settings_yaml:
|
with open(settings_path) as settings_yaml:
|
||||||
settings = load(settings_yaml)
|
settings = load(settings_yaml)
|
||||||
|
|
||||||
|
# load https rules only if https rewrite is enabled
|
||||||
|
if settings.get('server', {}).get('https_rewrite'):
|
||||||
|
# loade https rules
|
||||||
|
load_https_rules(https_rewrite_path)
|
||||||
|
|
|
@ -1,3 +1,21 @@
|
||||||
|
'''
|
||||||
|
searx is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
searx is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with searx. If not, see < http://www.gnu.org/licenses/ >.
|
||||||
|
|
||||||
|
(C) 2013- by Adam Tauber, <asciimoo@gmail.com>
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from requests import get
|
from requests import get
|
||||||
from json import loads
|
from json import loads
|
||||||
|
@ -21,6 +39,16 @@ def dbpedia(query):
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def duckduckgo(query):
|
||||||
|
# duckduckgo autocompleter
|
||||||
|
url = 'https://ac.duckduckgo.com/ac/?{0}&type=list'
|
||||||
|
|
||||||
|
resp = loads(get(url.format(urlencode(dict(q=query)))).text)
|
||||||
|
if len(resp) > 1:
|
||||||
|
return resp[1]
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
def google(query):
|
def google(query):
|
||||||
# google autocompleter
|
# google autocompleter
|
||||||
autocomplete_url = 'http://suggestqueries.google.com/complete/search?client=toolbar&' # noqa
|
autocomplete_url = 'http://suggestqueries.google.com/complete/search?client=toolbar&' # noqa
|
||||||
|
@ -41,13 +69,14 @@ def wikipedia(query):
|
||||||
# wikipedia autocompleter
|
# wikipedia autocompleter
|
||||||
url = 'https://en.wikipedia.org/w/api.php?action=opensearch&{0}&limit=10&namespace=0&format=json' # noqa
|
url = 'https://en.wikipedia.org/w/api.php?action=opensearch&{0}&limit=10&namespace=0&format=json' # noqa
|
||||||
|
|
||||||
resp = loads(get(url.format(urlencode(dict(q=query)))).text)
|
resp = loads(get(url.format(urlencode(dict(search=query)))).text)
|
||||||
if len(resp) > 1:
|
if len(resp) > 1:
|
||||||
return resp[1]
|
return resp[1]
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
backends = {'dbpedia': dbpedia,
|
backends = {'dbpedia': dbpedia,
|
||||||
|
'duckduckgo': duckduckgo,
|
||||||
'google': google,
|
'google': google,
|
||||||
'wikipedia': wikipedia
|
'wikipedia': wikipedia
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,19 +19,12 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
|
||||||
from os.path import realpath, dirname, splitext, join
|
from os.path import realpath, dirname, splitext, join
|
||||||
import sys
|
import sys
|
||||||
from imp import load_source
|
from imp import load_source
|
||||||
from itertools import izip_longest, chain
|
|
||||||
from operator import itemgetter
|
|
||||||
from urlparse import urlparse
|
|
||||||
from datetime import datetime
|
|
||||||
import grequests
|
|
||||||
from flask.ext.babel import gettext
|
from flask.ext.babel import gettext
|
||||||
|
from operator import itemgetter
|
||||||
from searx import settings
|
from searx import settings
|
||||||
from searx.utils import gen_useragent
|
|
||||||
|
|
||||||
engine_dir = dirname(realpath(__file__))
|
engine_dir = dirname(realpath(__file__))
|
||||||
|
|
||||||
number_of_searches = 0
|
|
||||||
|
|
||||||
engines = {}
|
engines = {}
|
||||||
|
|
||||||
categories = {'general': []}
|
categories = {'general': []}
|
||||||
|
@ -48,7 +41,7 @@ def load_module(filename):
|
||||||
module.name = modname
|
module.name = modname
|
||||||
return module
|
return module
|
||||||
|
|
||||||
if not 'engines' in settings or not settings['engines']:
|
if 'engines' not in settings or not settings['engines']:
|
||||||
print '[E] Error no engines found. Edit your settings.yml'
|
print '[E] Error no engines found. Edit your settings.yml'
|
||||||
exit(2)
|
exit(2)
|
||||||
|
|
||||||
|
@ -75,15 +68,15 @@ for engine_data in settings['engines']:
|
||||||
engine.categories = ['general']
|
engine.categories = ['general']
|
||||||
|
|
||||||
if not hasattr(engine, 'language_support'):
|
if not hasattr(engine, 'language_support'):
|
||||||
#engine.language_support = False
|
# engine.language_support = False
|
||||||
engine.language_support = True
|
engine.language_support = True
|
||||||
|
|
||||||
if not hasattr(engine, 'timeout'):
|
if not hasattr(engine, 'timeout'):
|
||||||
#engine.language_support = False
|
# engine.language_support = False
|
||||||
engine.timeout = settings['server']['request_timeout']
|
engine.timeout = settings['server']['request_timeout']
|
||||||
|
|
||||||
if not hasattr(engine, 'shortcut'):
|
if not hasattr(engine, 'shortcut'):
|
||||||
#engine.shortcut = '''
|
# engine.shortcut = '''
|
||||||
engine.shortcut = ''
|
engine.shortcut = ''
|
||||||
|
|
||||||
# checking required variables
|
# checking required variables
|
||||||
|
@ -114,154 +107,6 @@ for engine_data in settings['engines']:
|
||||||
engine_shortcuts[engine.shortcut] = engine.name
|
engine_shortcuts[engine.shortcut] = engine.name
|
||||||
|
|
||||||
|
|
||||||
def default_request_params():
|
|
||||||
return {
|
|
||||||
'method': 'GET', 'headers': {}, 'data': {}, 'url': '', 'cookies': {}}
|
|
||||||
|
|
||||||
|
|
||||||
def make_callback(engine_name, results, suggestions, callback, params):
|
|
||||||
# creating a callback wrapper for the search engine results
|
|
||||||
def process_callback(response, **kwargs):
|
|
||||||
cb_res = []
|
|
||||||
response.search_params = params
|
|
||||||
engines[engine_name].stats['page_load_time'] += \
|
|
||||||
(datetime.now() - params['started']).total_seconds()
|
|
||||||
try:
|
|
||||||
search_results = callback(response)
|
|
||||||
except Exception, e:
|
|
||||||
engines[engine_name].stats['errors'] += 1
|
|
||||||
results[engine_name] = cb_res
|
|
||||||
print '[E] Error with engine "{0}":\n\t{1}'.format(
|
|
||||||
engine_name, str(e))
|
|
||||||
return
|
|
||||||
for result in search_results:
|
|
||||||
result['engine'] = engine_name
|
|
||||||
if 'suggestion' in result:
|
|
||||||
# TODO type checks
|
|
||||||
suggestions.add(result['suggestion'])
|
|
||||||
continue
|
|
||||||
cb_res.append(result)
|
|
||||||
results[engine_name] = cb_res
|
|
||||||
return process_callback
|
|
||||||
|
|
||||||
|
|
||||||
def score_results(results):
|
|
||||||
flat_res = filter(
|
|
||||||
None, chain.from_iterable(izip_longest(*results.values())))
|
|
||||||
flat_len = len(flat_res)
|
|
||||||
engines_len = len(results)
|
|
||||||
results = []
|
|
||||||
# deduplication + scoring
|
|
||||||
for i, res in enumerate(flat_res):
|
|
||||||
res['parsed_url'] = urlparse(res['url'])
|
|
||||||
res['host'] = res['parsed_url'].netloc
|
|
||||||
|
|
||||||
if res['host'].startswith('www.'):
|
|
||||||
res['host'] = res['host'].replace('www.', '', 1)
|
|
||||||
|
|
||||||
res['engines'] = [res['engine']]
|
|
||||||
weight = 1.0
|
|
||||||
|
|
||||||
if hasattr(engines[res['engine']], 'weight'):
|
|
||||||
weight = float(engines[res['engine']].weight)
|
|
||||||
|
|
||||||
score = int((flat_len - i) / engines_len) * weight + 1
|
|
||||||
duplicated = False
|
|
||||||
|
|
||||||
for new_res in results:
|
|
||||||
p1 = res['parsed_url'].path[:-1] if res['parsed_url'].path.endswith('/') else res['parsed_url'].path # noqa
|
|
||||||
p2 = new_res['parsed_url'].path[:-1] if new_res['parsed_url'].path.endswith('/') else new_res['parsed_url'].path # noqa
|
|
||||||
if res['host'] == new_res['host'] and\
|
|
||||||
p1 == p2 and\
|
|
||||||
res['parsed_url'].query == new_res['parsed_url'].query and\
|
|
||||||
res.get('template') == new_res.get('template'):
|
|
||||||
duplicated = new_res
|
|
||||||
break
|
|
||||||
if duplicated:
|
|
||||||
if res.get('content') > duplicated.get('content'):
|
|
||||||
duplicated['content'] = res['content']
|
|
||||||
duplicated['score'] += score
|
|
||||||
duplicated['engines'].append(res['engine'])
|
|
||||||
if duplicated['parsed_url'].scheme == 'https':
|
|
||||||
continue
|
|
||||||
elif res['parsed_url'].scheme == 'https':
|
|
||||||
duplicated['url'] = res['parsed_url'].geturl()
|
|
||||||
duplicated['parsed_url'] = res['parsed_url']
|
|
||||||
else:
|
|
||||||
res['score'] = score
|
|
||||||
results.append(res)
|
|
||||||
return sorted(results, key=itemgetter('score'), reverse=True)
|
|
||||||
|
|
||||||
|
|
||||||
def search(query, request, selected_engines, pageno=1, lang='all'):
|
|
||||||
global engines, categories, number_of_searches
|
|
||||||
requests = []
|
|
||||||
results = {}
|
|
||||||
suggestions = set()
|
|
||||||
number_of_searches += 1
|
|
||||||
#user_agent = request.headers.get('User-Agent', '')
|
|
||||||
user_agent = gen_useragent()
|
|
||||||
|
|
||||||
for selected_engine in selected_engines:
|
|
||||||
if selected_engine['name'] not in engines:
|
|
||||||
continue
|
|
||||||
|
|
||||||
engine = engines[selected_engine['name']]
|
|
||||||
|
|
||||||
if pageno > 1 and not engine.paging:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if lang != 'all' and not engine.language_support:
|
|
||||||
continue
|
|
||||||
|
|
||||||
request_params = default_request_params()
|
|
||||||
request_params['headers']['User-Agent'] = user_agent
|
|
||||||
request_params['category'] = selected_engine['category']
|
|
||||||
request_params['started'] = datetime.now()
|
|
||||||
request_params['pageno'] = pageno
|
|
||||||
request_params['language'] = lang
|
|
||||||
request_params = engine.request(query.encode('utf-8'), request_params)
|
|
||||||
|
|
||||||
callback = make_callback(
|
|
||||||
selected_engine['name'],
|
|
||||||
results,
|
|
||||||
suggestions,
|
|
||||||
engine.response,
|
|
||||||
request_params
|
|
||||||
)
|
|
||||||
|
|
||||||
request_args = dict(
|
|
||||||
headers=request_params['headers'],
|
|
||||||
hooks=dict(response=callback),
|
|
||||||
cookies=request_params['cookies'],
|
|
||||||
timeout=engine.timeout
|
|
||||||
)
|
|
||||||
|
|
||||||
if request_params['method'] == 'GET':
|
|
||||||
req = grequests.get
|
|
||||||
else:
|
|
||||||
req = grequests.post
|
|
||||||
request_args['data'] = request_params['data']
|
|
||||||
|
|
||||||
# ignoring empty urls
|
|
||||||
if not request_params['url']:
|
|
||||||
continue
|
|
||||||
|
|
||||||
requests.append(req(request_params['url'], **request_args))
|
|
||||||
grequests.map(requests)
|
|
||||||
for engine_name, engine_results in results.items():
|
|
||||||
engines[engine_name].stats['search_count'] += 1
|
|
||||||
engines[engine_name].stats['result_count'] += len(engine_results)
|
|
||||||
|
|
||||||
results = score_results(results)
|
|
||||||
|
|
||||||
for result in results:
|
|
||||||
for res_engine in result['engines']:
|
|
||||||
engines[result['engine']].stats['score_count'] += result['score']
|
|
||||||
|
|
||||||
return results, suggestions
|
|
||||||
|
|
||||||
|
|
||||||
def get_engines_stats():
|
def get_engines_stats():
|
||||||
# TODO refactor
|
# TODO refactor
|
||||||
pageloads = []
|
pageloads = []
|
||||||
|
@ -297,16 +142,29 @@ def get_engines_stats():
|
||||||
})
|
})
|
||||||
|
|
||||||
for engine in pageloads:
|
for engine in pageloads:
|
||||||
engine['percentage'] = int(engine['avg'] / max_pageload * 100)
|
if max_pageload:
|
||||||
|
engine['percentage'] = int(engine['avg'] / max_pageload * 100)
|
||||||
|
else:
|
||||||
|
engine['percentage'] = 0
|
||||||
|
|
||||||
for engine in results:
|
for engine in results:
|
||||||
engine['percentage'] = int(engine['avg'] / max_results * 100)
|
if max_results:
|
||||||
|
engine['percentage'] = int(engine['avg'] / max_results * 100)
|
||||||
|
else:
|
||||||
|
engine['percentage'] = 0
|
||||||
|
|
||||||
for engine in scores:
|
for engine in scores:
|
||||||
engine['percentage'] = int(engine['avg'] / max_score * 100)
|
if max_score:
|
||||||
|
engine['percentage'] = int(engine['avg'] / max_score * 100)
|
||||||
|
else:
|
||||||
|
engine['percentage'] = 0
|
||||||
|
|
||||||
for engine in scores_per_result:
|
for engine in scores_per_result:
|
||||||
engine['percentage'] = int(engine['avg'] / max_score_per_result * 100)
|
if max_score_per_result:
|
||||||
|
engine['percentage'] = int(engine['avg']
|
||||||
|
/ max_score_per_result * 100)
|
||||||
|
else:
|
||||||
|
engine['percentage'] = 0
|
||||||
|
|
||||||
for engine in errors:
|
for engine in errors:
|
||||||
if max_errors:
|
if max_errors:
|
||||||
|
|
|
@ -1,49 +1,82 @@
|
||||||
|
## Bing (Web)
|
||||||
|
#
|
||||||
|
# @website https://www.bing.com
|
||||||
|
# @provide-api yes (http://datamarket.azure.com/dataset/bing/search), max. 5000 query/month
|
||||||
|
#
|
||||||
|
# @using-api no (because of query limit)
|
||||||
|
# @results HTML (using search portal)
|
||||||
|
# @stable no (HTML can change)
|
||||||
|
# @parse url, title, content
|
||||||
|
#
|
||||||
|
# @todo publishedDate
|
||||||
|
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from cgi import escape
|
from cgi import escape
|
||||||
from lxml import html
|
from lxml import html
|
||||||
|
|
||||||
base_url = 'http://www.bing.com/'
|
# engine dependent config
|
||||||
search_string = 'search?{query}&first={offset}'
|
categories = ['general']
|
||||||
paging = True
|
paging = True
|
||||||
language_support = True
|
language_support = True
|
||||||
|
|
||||||
|
# search-url
|
||||||
|
base_url = 'https://www.bing.com/'
|
||||||
|
search_string = 'search?{query}&first={offset}'
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
offset = (params['pageno'] - 1) * 10 + 1
|
offset = (params['pageno'] - 1) * 10 + 1
|
||||||
|
|
||||||
if params['language'] == 'all':
|
if params['language'] == 'all':
|
||||||
language = 'en-US'
|
language = 'en-US'
|
||||||
else:
|
else:
|
||||||
language = params['language'].replace('_', '-')
|
language = params['language'].replace('_', '-')
|
||||||
|
|
||||||
search_path = search_string.format(
|
search_path = search_string.format(
|
||||||
query=urlencode({'q': query, 'setmkt': language}),
|
query=urlencode({'q': query, 'setmkt': language}),
|
||||||
offset=offset)
|
offset=offset)
|
||||||
|
|
||||||
params['cookies']['SRCHHPGUSR'] = \
|
params['cookies']['SRCHHPGUSR'] = \
|
||||||
'NEWWND=0&NRSLT=-1&SRCHLANG=' + language.split('-')[0]
|
'NEWWND=0&NRSLT=-1&SRCHLANG=' + language.split('-')[0]
|
||||||
#if params['category'] == 'images':
|
|
||||||
# params['url'] = base_url + 'images/' + search_path
|
|
||||||
params['url'] = base_url + search_path
|
params['url'] = base_url + search_path
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
global base_url
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
dom = html.fromstring(resp.content)
|
dom = html.fromstring(resp.content)
|
||||||
|
|
||||||
|
# parse results
|
||||||
for result in dom.xpath('//div[@class="sa_cc"]'):
|
for result in dom.xpath('//div[@class="sa_cc"]'):
|
||||||
link = result.xpath('.//h3/a')[0]
|
link = result.xpath('.//h3/a')[0]
|
||||||
url = link.attrib.get('href')
|
url = link.attrib.get('href')
|
||||||
title = ' '.join(link.xpath('.//text()'))
|
title = ' '.join(link.xpath('.//text()'))
|
||||||
content = escape(' '.join(result.xpath('.//p//text()')))
|
content = escape(' '.join(result.xpath('.//p//text()')))
|
||||||
results.append({'url': url, 'title': title, 'content': content})
|
|
||||||
|
|
||||||
|
# append result
|
||||||
|
results.append({'url': url,
|
||||||
|
'title': title,
|
||||||
|
'content': content})
|
||||||
|
|
||||||
|
# return results if something is found
|
||||||
if results:
|
if results:
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
# parse results again if nothing is found yet
|
||||||
for result in dom.xpath('//li[@class="b_algo"]'):
|
for result in dom.xpath('//li[@class="b_algo"]'):
|
||||||
link = result.xpath('.//h2/a')[0]
|
link = result.xpath('.//h2/a')[0]
|
||||||
url = link.attrib.get('href')
|
url = link.attrib.get('href')
|
||||||
title = ' '.join(link.xpath('.//text()'))
|
title = ' '.join(link.xpath('.//text()'))
|
||||||
content = escape(' '.join(result.xpath('.//p//text()')))
|
content = escape(' '.join(result.xpath('.//p//text()')))
|
||||||
results.append({'url': url, 'title': title, 'content': content})
|
|
||||||
|
# append result
|
||||||
|
results.append({'url': url,
|
||||||
|
'title': title,
|
||||||
|
'content': content})
|
||||||
|
|
||||||
|
# return results
|
||||||
return results
|
return results
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
## Bing (Images)
|
||||||
|
#
|
||||||
|
# @website https://www.bing.com/images
|
||||||
|
# @provide-api yes (http://datamarket.azure.com/dataset/bing/search), max. 5000 query/month
|
||||||
|
#
|
||||||
|
# @using-api no (because of query limit)
|
||||||
|
# @results HTML (using search portal)
|
||||||
|
# @stable no (HTML can change)
|
||||||
|
# @parse url, title, img_src
|
||||||
|
#
|
||||||
|
# @todo currently there are up to 35 images receive per page, because bing does not parse count=10. limited response to 10 images
|
||||||
|
|
||||||
|
from urllib import urlencode
|
||||||
|
from cgi import escape
|
||||||
|
from lxml import html
|
||||||
|
from yaml import load
|
||||||
|
import re
|
||||||
|
|
||||||
|
# engine dependent config
|
||||||
|
categories = ['images']
|
||||||
|
paging = True
|
||||||
|
|
||||||
|
# search-url
|
||||||
|
base_url = 'https://www.bing.com/'
|
||||||
|
search_string = 'images/search?{query}&count=10&first={offset}'
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
|
def request(query, params):
|
||||||
|
offset = (params['pageno'] - 1) * 10 + 1
|
||||||
|
|
||||||
|
# required for cookie
|
||||||
|
language = 'en-US'
|
||||||
|
|
||||||
|
search_path = search_string.format(
|
||||||
|
query=urlencode({'q': query}),
|
||||||
|
offset=offset)
|
||||||
|
|
||||||
|
params['cookies']['SRCHHPGUSR'] = \
|
||||||
|
'NEWWND=0&NRSLT=-1&SRCHLANG=' + language.split('-')[0]
|
||||||
|
|
||||||
|
params['url'] = base_url + search_path
|
||||||
|
|
||||||
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
|
def response(resp):
|
||||||
|
results = []
|
||||||
|
|
||||||
|
dom = html.fromstring(resp.content)
|
||||||
|
|
||||||
|
# init regex for yaml-parsing
|
||||||
|
p = re.compile( '({|,)([a-z]+):(")')
|
||||||
|
|
||||||
|
# parse results
|
||||||
|
for result in dom.xpath('//div[@class="dg_u"]'):
|
||||||
|
link = result.xpath('./a')[0]
|
||||||
|
|
||||||
|
# parse yaml-data (it is required to add a space, to make it parsable)
|
||||||
|
yaml_data = load(p.sub( r'\1\2: \3', link.attrib.get('m')))
|
||||||
|
|
||||||
|
title = link.attrib.get('t1')
|
||||||
|
#url = 'http://' + link.attrib.get('t3')
|
||||||
|
url = yaml_data.get('surl')
|
||||||
|
img_src = yaml_data.get('imgurl')
|
||||||
|
|
||||||
|
# append result
|
||||||
|
results.append({'template': 'images.html',
|
||||||
|
'url': url,
|
||||||
|
'title': title,
|
||||||
|
'content': '',
|
||||||
|
'img_src': img_src})
|
||||||
|
|
||||||
|
# TODO stop parsing if 10 images are found
|
||||||
|
if len(results) >= 10:
|
||||||
|
break
|
||||||
|
|
||||||
|
# return results
|
||||||
|
return results
|
|
@ -1,51 +1,100 @@
|
||||||
|
## Bing (News)
|
||||||
|
#
|
||||||
|
# @website https://www.bing.com/news
|
||||||
|
# @provide-api yes (http://datamarket.azure.com/dataset/bing/search), max. 5000 query/month
|
||||||
|
#
|
||||||
|
# @using-api no (because of query limit)
|
||||||
|
# @results HTML (using search portal)
|
||||||
|
# @stable no (HTML can change)
|
||||||
|
# @parse url, title, content, publishedDate
|
||||||
|
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from cgi import escape
|
from cgi import escape
|
||||||
from lxml import html
|
from lxml import html
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from dateutil import parser
|
||||||
|
import re
|
||||||
|
|
||||||
|
# engine dependent config
|
||||||
categories = ['news']
|
categories = ['news']
|
||||||
|
|
||||||
base_url = 'http://www.bing.com/'
|
|
||||||
search_string = 'news/search?{query}&first={offset}'
|
|
||||||
paging = True
|
paging = True
|
||||||
language_support = True
|
language_support = True
|
||||||
|
|
||||||
|
# search-url
|
||||||
|
base_url = 'https://www.bing.com/'
|
||||||
|
search_string = 'news/search?{query}&first={offset}'
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
offset = (params['pageno'] - 1) * 10 + 1
|
offset = (params['pageno'] - 1) * 10 + 1
|
||||||
|
|
||||||
if params['language'] == 'all':
|
if params['language'] == 'all':
|
||||||
language = 'en-US'
|
language = 'en-US'
|
||||||
else:
|
else:
|
||||||
language = params['language'].replace('_', '-')
|
language = params['language'].replace('_', '-')
|
||||||
|
|
||||||
search_path = search_string.format(
|
search_path = search_string.format(
|
||||||
query=urlencode({'q': query, 'setmkt': language}),
|
query=urlencode({'q': query, 'setmkt': language}),
|
||||||
offset=offset)
|
offset=offset)
|
||||||
|
|
||||||
params['cookies']['SRCHHPGUSR'] = \
|
params['cookies']['SRCHHPGUSR'] = \
|
||||||
'NEWWND=0&NRSLT=-1&SRCHLANG=' + language.split('-')[0]
|
'NEWWND=0&NRSLT=-1&SRCHLANG=' + language.split('-')[0]
|
||||||
#if params['category'] == 'images':
|
|
||||||
# params['url'] = base_url + 'images/' + search_path
|
|
||||||
params['url'] = base_url + search_path
|
params['url'] = base_url + search_path
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
global base_url
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
dom = html.fromstring(resp.content)
|
dom = html.fromstring(resp.content)
|
||||||
for result in dom.xpath('//div[@class="sa_cc"]'):
|
|
||||||
link = result.xpath('.//h3/a')[0]
|
# parse results
|
||||||
|
for result in dom.xpath('//div[@class="sn_r"]'):
|
||||||
|
link = result.xpath('.//div[@class="newstitle"]/a')[0]
|
||||||
url = link.attrib.get('href')
|
url = link.attrib.get('href')
|
||||||
title = ' '.join(link.xpath('.//text()'))
|
title = ' '.join(link.xpath('.//text()'))
|
||||||
content = escape(' '.join(result.xpath('.//p//text()')))
|
contentXPath = result.xpath('.//div[@class="sn_txt"]/div//span[@class="sn_snip"]//text()')
|
||||||
results.append({'url': url, 'title': title, 'content': content})
|
if contentXPath != None:
|
||||||
|
content = escape(' '.join(contentXPath))
|
||||||
|
|
||||||
|
# parse publishedDate
|
||||||
|
publishedDateXPath = result.xpath('.//div[@class="sn_txt"]/div//span[contains(@class,"sn_ST")]//span[contains(@class,"sn_tm")]//text()')
|
||||||
|
if publishedDateXPath != None:
|
||||||
|
publishedDate = escape(' '.join(publishedDateXPath))
|
||||||
|
|
||||||
if results:
|
if re.match("^[0-9]+ minute(s|) ago$", publishedDate):
|
||||||
return results
|
timeNumbers = re.findall(r'\d+', publishedDate)
|
||||||
|
publishedDate = datetime.now()\
|
||||||
|
- timedelta(minutes=int(timeNumbers[0]))
|
||||||
|
elif re.match("^[0-9]+ hour(s|) ago$", publishedDate):
|
||||||
|
timeNumbers = re.findall(r'\d+', publishedDate)
|
||||||
|
publishedDate = datetime.now()\
|
||||||
|
- timedelta(hours=int(timeNumbers[0]))
|
||||||
|
elif re.match("^[0-9]+ hour(s|), [0-9]+ minute(s|) ago$", publishedDate):
|
||||||
|
timeNumbers = re.findall(r'\d+', publishedDate)
|
||||||
|
publishedDate = datetime.now()\
|
||||||
|
- timedelta(hours=int(timeNumbers[0]))\
|
||||||
|
- timedelta(minutes=int(timeNumbers[1]))
|
||||||
|
elif re.match("^[0-9]+ day(s|) ago$", publishedDate):
|
||||||
|
timeNumbers = re.findall(r'\d+', publishedDate)
|
||||||
|
publishedDate = datetime.now()\
|
||||||
|
- timedelta(days=int(timeNumbers[0]))
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
# FIXME use params['language'] to parse either mm/dd or dd/mm
|
||||||
|
publishedDate = parser.parse(publishedDate, dayfirst=False)
|
||||||
|
except TypeError:
|
||||||
|
# FIXME
|
||||||
|
publishedDate = datetime.now()
|
||||||
|
|
||||||
|
# append result
|
||||||
|
results.append({'url': url,
|
||||||
|
'title': title,
|
||||||
|
'publishedDate': publishedDate,
|
||||||
|
'content': content})
|
||||||
|
|
||||||
for result in dom.xpath('//li[@class="b_algo"]'):
|
# return results
|
||||||
link = result.xpath('.//h2/a')[0]
|
|
||||||
url = link.attrib.get('href')
|
|
||||||
title = ' '.join(link.xpath('.//text()'))
|
|
||||||
content = escape(' '.join(result.xpath('.//p//text()')))
|
|
||||||
results.append({'url': url, 'title': title, 'content': content})
|
|
||||||
return results
|
return results
|
||||||
|
|
|
@ -31,7 +31,6 @@ def request(query, params):
|
||||||
|
|
||||||
|
|
||||||
def response(resp):
|
def response(resp):
|
||||||
global base_url
|
|
||||||
results = []
|
results = []
|
||||||
try:
|
try:
|
||||||
_, conversion_rate, _ = resp.text.split(',', 2)
|
_, conversion_rate, _ = resp.text.split(',', 2)
|
||||||
|
@ -39,16 +38,14 @@ def response(resp):
|
||||||
except:
|
except:
|
||||||
return results
|
return results
|
||||||
|
|
||||||
title = '{0} {1} in {2} is {3}'.format(
|
answer = '{0} {1} = {2} {3} (1 {1} = {4} {3})'.format(
|
||||||
resp.search_params['ammount'],
|
resp.search_params['ammount'],
|
||||||
resp.search_params['from'],
|
resp.search_params['from'],
|
||||||
|
resp.search_params['ammount'] * conversion_rate,
|
||||||
resp.search_params['to'],
|
resp.search_params['to'],
|
||||||
resp.search_params['ammount'] * conversion_rate
|
conversion_rate
|
||||||
)
|
)
|
||||||
|
|
||||||
content = '1 {0} is {1} {2}'.format(resp.search_params['from'],
|
|
||||||
conversion_rate,
|
|
||||||
resp.search_params['to'])
|
|
||||||
now_date = datetime.now().strftime('%Y%m%d')
|
now_date = datetime.now().strftime('%Y%m%d')
|
||||||
url = 'http://finance.yahoo.com/currency/converter-results/{0}/{1}-{2}-to-{3}.html' # noqa
|
url = 'http://finance.yahoo.com/currency/converter-results/{0}/{1}-{2}-to-{3}.html' # noqa
|
||||||
url = url.format(
|
url = url.format(
|
||||||
|
@ -57,6 +54,7 @@ def response(resp):
|
||||||
resp.search_params['from'].lower(),
|
resp.search_params['from'].lower(),
|
||||||
resp.search_params['to'].lower()
|
resp.search_params['to'].lower()
|
||||||
)
|
)
|
||||||
results.append({'title': title, 'content': content, 'url': url})
|
|
||||||
|
results.append({'answer' : answer, 'url': url})
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
|
@ -1,45 +1,66 @@
|
||||||
|
## Dailymotion (Videos)
|
||||||
|
#
|
||||||
|
# @website https://www.dailymotion.com
|
||||||
|
# @provide-api yes (http://www.dailymotion.com/developer)
|
||||||
|
#
|
||||||
|
# @using-api yes
|
||||||
|
# @results JSON
|
||||||
|
# @stable yes
|
||||||
|
# @parse url, title, thumbnail
|
||||||
|
#
|
||||||
|
# @todo set content-parameter with correct data
|
||||||
|
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from json import loads
|
from json import loads
|
||||||
from lxml import html
|
from lxml import html
|
||||||
|
|
||||||
|
# engine dependent config
|
||||||
categories = ['videos']
|
categories = ['videos']
|
||||||
locale = 'en_US'
|
|
||||||
|
|
||||||
# see http://www.dailymotion.com/doc/api/obj-video.html
|
|
||||||
search_url = 'https://api.dailymotion.com/videos?fields=title,description,duration,url,thumbnail_360_url&sort=relevance&limit=25&page={pageno}&{query}' # noqa
|
|
||||||
|
|
||||||
# TODO use video result template
|
|
||||||
content_tpl = '<a href="{0}" title="{0}" ><img src="{1}" /></a><br />'
|
|
||||||
|
|
||||||
paging = True
|
paging = True
|
||||||
|
language_support = True
|
||||||
|
|
||||||
|
# search-url
|
||||||
|
# see http://www.dailymotion.com/doc/api/obj-video.html
|
||||||
|
search_url = 'https://api.dailymotion.com/videos?fields=title,description,duration,url,thumbnail_360_url&sort=relevance&limit=5&page={pageno}&{query}' # noqa
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
|
if params['language'] == 'all':
|
||||||
|
locale = 'en-US'
|
||||||
|
else:
|
||||||
|
locale = params['language']
|
||||||
|
|
||||||
params['url'] = search_url.format(
|
params['url'] = search_url.format(
|
||||||
query=urlencode({'search': query, 'localization': locale}),
|
query=urlencode({'search': query, 'localization': locale}),
|
||||||
pageno=params['pageno'])
|
pageno=params['pageno'])
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
search_res = loads(resp.text)
|
search_res = loads(resp.text)
|
||||||
|
|
||||||
|
# return empty array if there are no results
|
||||||
if not 'list' in search_res:
|
if not 'list' in search_res:
|
||||||
return results
|
return []
|
||||||
|
|
||||||
|
# parse results
|
||||||
for res in search_res['list']:
|
for res in search_res['list']:
|
||||||
title = res['title']
|
title = res['title']
|
||||||
url = res['url']
|
url = res['url']
|
||||||
if res['thumbnail_360_url']:
|
#content = res['description']
|
||||||
content = content_tpl.format(url, res['thumbnail_360_url'])
|
content = ''
|
||||||
else:
|
thumbnail = res['thumbnail_360_url']
|
||||||
content = ''
|
|
||||||
if res['description']:
|
results.append({'template': 'videos.html',
|
||||||
description = text_content_from_html(res['description'])
|
'url': url,
|
||||||
content += description[:500]
|
'title': title,
|
||||||
results.append({'url': url, 'title': title, 'content': content})
|
'content': content,
|
||||||
|
'thumbnail': thumbnail})
|
||||||
|
|
||||||
|
# return results
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def text_content_from_html(html_string):
|
|
||||||
desc_html = html.fragment_fromstring(html_string, create_parent=True)
|
|
||||||
return desc_html.text_content()
|
|
||||||
|
|
|
@ -1,36 +1,61 @@
|
||||||
|
## Deviantart (Images)
|
||||||
|
#
|
||||||
|
# @website https://www.deviantart.com/
|
||||||
|
# @provide-api yes (https://www.deviantart.com/developers/) (RSS)
|
||||||
|
#
|
||||||
|
# @using-api no (TODO, rewrite to api)
|
||||||
|
# @results HTML
|
||||||
|
# @stable no (HTML can change)
|
||||||
|
# @parse url, title, thumbnail, img_src
|
||||||
|
#
|
||||||
|
# @todo rewrite to api
|
||||||
|
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from urlparse import urljoin
|
from urlparse import urljoin
|
||||||
from lxml import html
|
from lxml import html
|
||||||
|
|
||||||
|
# engine dependent config
|
||||||
categories = ['images']
|
categories = ['images']
|
||||||
|
paging = True
|
||||||
|
|
||||||
|
# search-url
|
||||||
base_url = 'https://www.deviantart.com/'
|
base_url = 'https://www.deviantart.com/'
|
||||||
search_url = base_url+'search?offset={offset}&{query}'
|
search_url = base_url+'search?offset={offset}&{query}'
|
||||||
|
|
||||||
paging = True
|
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
offset = (params['pageno'] - 1) * 24
|
offset = (params['pageno'] - 1) * 24
|
||||||
|
|
||||||
params['url'] = search_url.format(offset=offset,
|
params['url'] = search_url.format(offset=offset,
|
||||||
query=urlencode({'q': query}))
|
query=urlencode({'q': query}))
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
global base_url
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
|
# return empty array if a redirection code is returned
|
||||||
if resp.status_code == 302:
|
if resp.status_code == 302:
|
||||||
return results
|
return []
|
||||||
|
|
||||||
dom = html.fromstring(resp.text)
|
dom = html.fromstring(resp.text)
|
||||||
|
|
||||||
|
# parse results
|
||||||
for result in dom.xpath('//div[contains(@class, "tt-a tt-fh")]'):
|
for result in dom.xpath('//div[contains(@class, "tt-a tt-fh")]'):
|
||||||
link = result.xpath('.//a[contains(@class, "thumb")]')[0]
|
link = result.xpath('.//a[contains(@class, "thumb")]')[0]
|
||||||
url = urljoin(base_url, link.attrib.get('href'))
|
url = urljoin(base_url, link.attrib.get('href'))
|
||||||
title_links = result.xpath('.//span[@class="details"]//a[contains(@class, "t")]') # noqa
|
title_links = result.xpath('.//span[@class="details"]//a[contains(@class, "t")]') # noqa
|
||||||
title = ''.join(title_links[0].xpath('.//text()'))
|
title = ''.join(title_links[0].xpath('.//text()'))
|
||||||
img_src = link.xpath('.//img')[0].attrib['src']
|
img_src = link.xpath('.//img')[0].attrib['src']
|
||||||
|
|
||||||
|
# append result
|
||||||
results.append({'url': url,
|
results.append({'url': url,
|
||||||
'title': title,
|
'title': title,
|
||||||
'img_src': img_src,
|
'img_src': img_src,
|
||||||
'template': 'images.html'})
|
'template': 'images.html'})
|
||||||
|
|
||||||
|
# return results
|
||||||
return results
|
return results
|
||||||
|
|
|
@ -1,65 +1,74 @@
|
||||||
|
## DuckDuckGo (Web)
|
||||||
|
#
|
||||||
|
# @website https://duckduckgo.com/
|
||||||
|
# @provide-api yes (https://duckduckgo.com/api), but not all results from search-site
|
||||||
|
#
|
||||||
|
# @using-api no
|
||||||
|
# @results HTML (using search portal)
|
||||||
|
# @stable no (HTML can change)
|
||||||
|
# @parse url, title, content
|
||||||
|
#
|
||||||
|
# @todo rewrite to api
|
||||||
|
# @todo language support (the current used site does not support language-change)
|
||||||
|
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from lxml.html import fromstring
|
from lxml.html import fromstring
|
||||||
from searx.utils import html_to_text
|
from searx.utils import html_to_text
|
||||||
|
|
||||||
|
# engine dependent config
|
||||||
|
categories = ['general']
|
||||||
|
paging = True
|
||||||
|
language_support = True
|
||||||
|
|
||||||
|
# search-url
|
||||||
url = 'https://duckduckgo.com/html?{query}&s={offset}'
|
url = 'https://duckduckgo.com/html?{query}&s={offset}'
|
||||||
locale = 'us-en'
|
|
||||||
|
# specific xpath variables
|
||||||
|
result_xpath = '//div[@class="results_links results_links_deep web-result"]' # noqa
|
||||||
|
url_xpath = './/a[@class="large"]/@href'
|
||||||
|
title_xpath = './/a[@class="large"]//text()'
|
||||||
|
content_xpath = './/div[@class="snippet"]//text()'
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
offset = (params['pageno'] - 1) * 30
|
offset = (params['pageno'] - 1) * 30
|
||||||
q = urlencode({'q': query,
|
|
||||||
'l': locale})
|
if params['language'] == 'all':
|
||||||
params['url'] = url.format(query=q, offset=offset)
|
locale = 'en-us'
|
||||||
|
else:
|
||||||
|
locale = params['language'].replace('_','-').lower()
|
||||||
|
|
||||||
|
params['url'] = url.format(
|
||||||
|
query=urlencode({'q': query, 'kl': locale}),
|
||||||
|
offset=offset)
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
result_xpath = '//div[@class="results_links results_links_deep web-result"]' # noqa
|
|
||||||
url_xpath = './/a[@class="large"]/@href'
|
|
||||||
title_xpath = './/a[@class="large"]//text()'
|
|
||||||
content_xpath = './/div[@class="snippet"]//text()'
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
doc = fromstring(resp.text)
|
doc = fromstring(resp.text)
|
||||||
|
|
||||||
|
# parse results
|
||||||
for r in doc.xpath(result_xpath):
|
for r in doc.xpath(result_xpath):
|
||||||
try:
|
try:
|
||||||
res_url = r.xpath(url_xpath)[-1]
|
res_url = r.xpath(url_xpath)[-1]
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not res_url:
|
if not res_url:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
title = html_to_text(''.join(r.xpath(title_xpath)))
|
title = html_to_text(''.join(r.xpath(title_xpath)))
|
||||||
content = html_to_text(''.join(r.xpath(content_xpath)))
|
content = html_to_text(''.join(r.xpath(content_xpath)))
|
||||||
|
|
||||||
|
# append result
|
||||||
results.append({'title': title,
|
results.append({'title': title,
|
||||||
'content': content,
|
'content': content,
|
||||||
'url': res_url})
|
'url': res_url})
|
||||||
|
|
||||||
|
# return results
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
#from json import loads
|
|
||||||
#search_url = url + 'd.js?{query}&p=1&s={offset}'
|
|
||||||
#
|
|
||||||
#paging = True
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#def request(query, params):
|
|
||||||
# offset = (params['pageno'] - 1) * 30
|
|
||||||
# q = urlencode({'q': query,
|
|
||||||
# 'l': locale})
|
|
||||||
# params['url'] = search_url.format(query=q, offset=offset)
|
|
||||||
# return params
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#def response(resp):
|
|
||||||
# results = []
|
|
||||||
# search_res = loads(resp.text[resp.text.find('[{'):-2])[:-1]
|
|
||||||
# for r in search_res:
|
|
||||||
# if not r.get('t'):
|
|
||||||
# continue
|
|
||||||
# results.append({'title': r['t'],
|
|
||||||
# 'content': html_to_text(r['a']),
|
|
||||||
# 'url': r['u']})
|
|
||||||
# return results
|
|
||||||
|
|
|
@ -1,10 +1,25 @@
|
||||||
import json
|
import json
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
|
from lxml import html
|
||||||
|
from searx.engines.xpath import extract_text
|
||||||
|
|
||||||
url = 'http://api.duckduckgo.com/?{query}&format=json&pretty=0&no_redirect=1'
|
url = 'https://api.duckduckgo.com/?{query}&format=json&pretty=0&no_redirect=1&d=1'
|
||||||
|
|
||||||
|
def result_to_text(url, text, htmlResult):
|
||||||
|
# TODO : remove result ending with "Meaning" or "Category"
|
||||||
|
dom = html.fromstring(htmlResult)
|
||||||
|
a = dom.xpath('//a')
|
||||||
|
if len(a)>=1:
|
||||||
|
return extract_text(a[0])
|
||||||
|
else:
|
||||||
|
return text
|
||||||
|
|
||||||
|
def html_to_text(htmlFragment):
|
||||||
|
dom = html.fromstring(htmlFragment)
|
||||||
|
return extract_text(dom)
|
||||||
|
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
|
# TODO add kl={locale}
|
||||||
params['url'] = url.format(query=urlencode({'q': query}))
|
params['url'] = url.format(query=urlencode({'q': query}))
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
@ -12,12 +27,111 @@ def request(query, params):
|
||||||
def response(resp):
|
def response(resp):
|
||||||
search_res = json.loads(resp.text)
|
search_res = json.loads(resp.text)
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
|
content = ''
|
||||||
|
heading = search_res.get('Heading', '')
|
||||||
|
attributes = []
|
||||||
|
urls = []
|
||||||
|
infobox_id = None
|
||||||
|
relatedTopics = []
|
||||||
|
|
||||||
|
# add answer if there is one
|
||||||
|
answer = search_res.get('Answer', '')
|
||||||
|
if answer != '':
|
||||||
|
results.append({ 'answer' : html_to_text(answer) })
|
||||||
|
|
||||||
|
# add infobox
|
||||||
if 'Definition' in search_res:
|
if 'Definition' in search_res:
|
||||||
if search_res.get('AbstractURL'):
|
content = content + search_res.get('Definition', '')
|
||||||
res = {'title': search_res.get('Heading', ''),
|
|
||||||
'content': search_res.get('Definition', ''),
|
if 'Abstract' in search_res:
|
||||||
'url': search_res.get('AbstractURL', ''),
|
content = content + search_res.get('Abstract', '')
|
||||||
'class': 'definition_result'}
|
|
||||||
results.append(res)
|
|
||||||
|
# image
|
||||||
|
image = search_res.get('Image', '')
|
||||||
|
image = None if image == '' else image
|
||||||
|
|
||||||
|
# attributes
|
||||||
|
if 'Infobox' in search_res:
|
||||||
|
infobox = search_res.get('Infobox', None)
|
||||||
|
if 'content' in infobox:
|
||||||
|
for info in infobox.get('content'):
|
||||||
|
attributes.append({'label': info.get('label'), 'value': info.get('value')})
|
||||||
|
|
||||||
|
# urls
|
||||||
|
for ddg_result in search_res.get('Results', []):
|
||||||
|
if 'FirstURL' in ddg_result:
|
||||||
|
firstURL = ddg_result.get('FirstURL', '')
|
||||||
|
text = ddg_result.get('Text', '')
|
||||||
|
urls.append({'title':text, 'url':firstURL})
|
||||||
|
results.append({'title':heading, 'url': firstURL})
|
||||||
|
|
||||||
|
# related topics
|
||||||
|
for ddg_result in search_res.get('RelatedTopics', None):
|
||||||
|
if 'FirstURL' in ddg_result:
|
||||||
|
suggestion = result_to_text(ddg_result.get('FirstURL', None), ddg_result.get('Text', None), ddg_result.get('Result', None))
|
||||||
|
if suggestion != heading:
|
||||||
|
results.append({'suggestion': suggestion})
|
||||||
|
elif 'Topics' in ddg_result:
|
||||||
|
suggestions = []
|
||||||
|
relatedTopics.append({ 'name' : ddg_result.get('Name', ''), 'suggestions': suggestions })
|
||||||
|
for topic_result in ddg_result.get('Topics', []):
|
||||||
|
suggestion = result_to_text(topic_result.get('FirstURL', None), topic_result.get('Text', None), topic_result.get('Result', None))
|
||||||
|
if suggestion != heading:
|
||||||
|
suggestions.append(suggestion)
|
||||||
|
|
||||||
|
# abstract
|
||||||
|
abstractURL = search_res.get('AbstractURL', '')
|
||||||
|
if abstractURL != '':
|
||||||
|
# add as result ? problem always in english
|
||||||
|
infobox_id = abstractURL
|
||||||
|
urls.append({'title': search_res.get('AbstractSource'), 'url': abstractURL})
|
||||||
|
|
||||||
|
# definition
|
||||||
|
definitionURL = search_res.get('DefinitionURL', '')
|
||||||
|
if definitionURL != '':
|
||||||
|
# add as result ? as answer ? problem always in english
|
||||||
|
infobox_id = definitionURL
|
||||||
|
urls.append({'title': search_res.get('DefinitionSource'), 'url': definitionURL})
|
||||||
|
|
||||||
|
# entity
|
||||||
|
entity = search_res.get('Entity', None)
|
||||||
|
# TODO continent / country / department / location / waterfall / mountain range : link to map search, get weather, near by locations
|
||||||
|
# TODO musician : link to music search
|
||||||
|
# TODO concert tour : ??
|
||||||
|
# TODO film / actor / television / media franchise : links to IMDB / rottentomatoes (or scrap result)
|
||||||
|
# TODO music : link tu musicbrainz / last.fm
|
||||||
|
# TODO book : ??
|
||||||
|
# TODO artist / playwright : ??
|
||||||
|
# TODO compagny : ??
|
||||||
|
# TODO software / os : ??
|
||||||
|
# TODO software engineer : ??
|
||||||
|
# TODO prepared food : ??
|
||||||
|
# TODO website : ??
|
||||||
|
# TODO performing art : ??
|
||||||
|
# TODO prepared food : ??
|
||||||
|
# TODO programming language : ??
|
||||||
|
# TODO file format : ??
|
||||||
|
|
||||||
|
if len(heading)>0:
|
||||||
|
# TODO get infobox.meta.value where .label='article_title'
|
||||||
|
if image==None and len(attributes)==0 and len(urls)==1 and len(relatedTopics)==0 and len(content)==0:
|
||||||
|
results.append({
|
||||||
|
'url': urls[0]['url'],
|
||||||
|
'title': heading,
|
||||||
|
'content': content
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
results.append({
|
||||||
|
'infobox': heading,
|
||||||
|
'id': infobox_id,
|
||||||
|
'entity': entity,
|
||||||
|
'content': content,
|
||||||
|
'img_src' : image,
|
||||||
|
'attributes': attributes,
|
||||||
|
'urls': urls,
|
||||||
|
'relatedTopics': relatedTopics
|
||||||
|
})
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
|
## Dummy
|
||||||
|
#
|
||||||
|
# @results empty array
|
||||||
|
# @stable yes
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
return []
|
return []
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
## Faroo (Web, News)
|
||||||
|
#
|
||||||
|
# @website http://www.faroo.com
|
||||||
|
# @provide-api yes (http://www.faroo.com/hp/api/api.html), require API-key
|
||||||
|
#
|
||||||
|
# @using-api yes
|
||||||
|
# @results JSON
|
||||||
|
# @stable yes
|
||||||
|
# @parse url, title, content, publishedDate, img_src
|
||||||
|
|
||||||
|
from urllib import urlencode
|
||||||
|
from json import loads
|
||||||
|
import datetime
|
||||||
|
from searx.utils import searx_useragent
|
||||||
|
|
||||||
|
# engine dependent config
|
||||||
|
categories = ['general', 'news']
|
||||||
|
paging = True
|
||||||
|
language_support = True
|
||||||
|
number_of_results = 10
|
||||||
|
api_key = None
|
||||||
|
|
||||||
|
# search-url
|
||||||
|
url = 'http://www.faroo.com/'
|
||||||
|
search_url = url + 'api?{query}&start={offset}&length={number_of_results}&l={language}&src={categorie}&i=false&f=json&key={api_key}'
|
||||||
|
|
||||||
|
search_category = {'general': 'web',
|
||||||
|
'news': 'news'}
|
||||||
|
|
||||||
|
# do search-request
|
||||||
|
def request(query, params):
|
||||||
|
offset = (params['pageno']-1) * number_of_results + 1
|
||||||
|
categorie = search_category.get(params['category'], 'web')
|
||||||
|
|
||||||
|
if params['language'] == 'all':
|
||||||
|
language = 'en'
|
||||||
|
else:
|
||||||
|
language = params['language'].split('_')[0]
|
||||||
|
|
||||||
|
# skip, if language is not supported
|
||||||
|
if language != 'en' and\
|
||||||
|
language != 'de' and\
|
||||||
|
language != 'zh':
|
||||||
|
return params
|
||||||
|
|
||||||
|
params['url'] = search_url.format(offset=offset,
|
||||||
|
number_of_results=number_of_results,
|
||||||
|
query=urlencode({'q': query}),
|
||||||
|
language=language,
|
||||||
|
categorie=categorie,
|
||||||
|
api_key=api_key )
|
||||||
|
|
||||||
|
# using searx User-Agent
|
||||||
|
params['headers']['User-Agent'] = searx_useragent()
|
||||||
|
|
||||||
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
|
def response(resp):
|
||||||
|
# HTTP-Code 401: api-key is not valide
|
||||||
|
if resp.status_code == 401:
|
||||||
|
raise Exception("API key is not valide")
|
||||||
|
return []
|
||||||
|
|
||||||
|
# HTTP-Code 429: rate limit exceeded
|
||||||
|
if resp.status_code == 429:
|
||||||
|
raise Exception("rate limit has been exceeded!")
|
||||||
|
return []
|
||||||
|
|
||||||
|
results = []
|
||||||
|
|
||||||
|
search_res = loads(resp.text)
|
||||||
|
|
||||||
|
# return empty array if there are no results
|
||||||
|
if not search_res.get('results', {}):
|
||||||
|
return []
|
||||||
|
|
||||||
|
# parse results
|
||||||
|
for result in search_res['results']:
|
||||||
|
if result['news']:
|
||||||
|
# timestamp (how many milliseconds have passed between now and the beginning of 1970)
|
||||||
|
publishedDate = datetime.datetime.fromtimestamp(result['date']/1000.0)
|
||||||
|
|
||||||
|
# append news result
|
||||||
|
results.append({'url': result['url'],
|
||||||
|
'title': result['title'],
|
||||||
|
'publishedDate': publishedDate,
|
||||||
|
'content': result['kwic']})
|
||||||
|
|
||||||
|
else:
|
||||||
|
# append general result
|
||||||
|
# TODO, publishedDate correct?
|
||||||
|
results.append({'url': result['url'],
|
||||||
|
'title': result['title'],
|
||||||
|
'content': result['kwic']})
|
||||||
|
|
||||||
|
# append image result if image url is set
|
||||||
|
# TODO, show results with an image like in faroo
|
||||||
|
if result['iurl']:
|
||||||
|
results.append({'template': 'images.html',
|
||||||
|
'url': result['url'],
|
||||||
|
'title': result['title'],
|
||||||
|
'content': result['kwic'],
|
||||||
|
'img_src': result['iurl']})
|
||||||
|
|
||||||
|
# return results
|
||||||
|
return results
|
|
@ -0,0 +1,60 @@
|
||||||
|
## General Files (Files)
|
||||||
|
#
|
||||||
|
# @website http://www.general-files.org
|
||||||
|
# @provide-api no (nothing found)
|
||||||
|
#
|
||||||
|
# @using-api no (because nothing found)
|
||||||
|
# @results HTML (using search portal)
|
||||||
|
# @stable no (HTML can change)
|
||||||
|
# @parse url, title, content
|
||||||
|
#
|
||||||
|
# @todo detect torrents?
|
||||||
|
|
||||||
|
from lxml import html
|
||||||
|
|
||||||
|
# engine dependent config
|
||||||
|
categories = ['files']
|
||||||
|
paging = True
|
||||||
|
|
||||||
|
# search-url
|
||||||
|
base_url = 'http://www.general-file.com'
|
||||||
|
search_url = base_url + '/files-{letter}/{query}/{pageno}'
|
||||||
|
|
||||||
|
# specific xpath variables
|
||||||
|
result_xpath = '//table[@class="block-file"]'
|
||||||
|
title_xpath = './/h2/a//text()'
|
||||||
|
url_xpath = './/h2/a/@href'
|
||||||
|
content_xpath = './/p//text()'
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
|
def request(query, params):
|
||||||
|
|
||||||
|
params['url'] = search_url.format(query=query,
|
||||||
|
letter=query[0],
|
||||||
|
pageno=params['pageno'])
|
||||||
|
|
||||||
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
|
def response(resp):
|
||||||
|
results = []
|
||||||
|
|
||||||
|
dom = html.fromstring(resp.text)
|
||||||
|
|
||||||
|
# parse results
|
||||||
|
for result in dom.xpath(result_xpath):
|
||||||
|
url = result.xpath(url_xpath)[0]
|
||||||
|
|
||||||
|
# skip fast download links
|
||||||
|
if not url.startswith('/'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# append result
|
||||||
|
results.append({'url': base_url + url,
|
||||||
|
'title': ''.join(result.xpath(title_xpath)),
|
||||||
|
'content': ''.join(result.xpath(content_xpath))})
|
||||||
|
|
||||||
|
# return results
|
||||||
|
return results
|
|
@ -1,32 +1,59 @@
|
||||||
|
## Github (It)
|
||||||
|
#
|
||||||
|
# @website https://github.com/
|
||||||
|
# @provide-api yes (https://developer.github.com/v3/)
|
||||||
|
#
|
||||||
|
# @using-api yes
|
||||||
|
# @results JSON
|
||||||
|
# @stable yes (using api)
|
||||||
|
# @parse url, title, content
|
||||||
|
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from json import loads
|
from json import loads
|
||||||
from cgi import escape
|
from cgi import escape
|
||||||
|
|
||||||
|
# engine dependent config
|
||||||
categories = ['it']
|
categories = ['it']
|
||||||
|
|
||||||
|
# search-url
|
||||||
search_url = 'https://api.github.com/search/repositories?sort=stars&order=desc&{query}' # noqa
|
search_url = 'https://api.github.com/search/repositories?sort=stars&order=desc&{query}' # noqa
|
||||||
|
|
||||||
accept_header = 'application/vnd.github.preview.text-match+json'
|
accept_header = 'application/vnd.github.preview.text-match+json'
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
global search_url
|
|
||||||
params['url'] = search_url.format(query=urlencode({'q': query}))
|
params['url'] = search_url.format(query=urlencode({'q': query}))
|
||||||
|
|
||||||
params['headers']['Accept'] = accept_header
|
params['headers']['Accept'] = accept_header
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
search_res = loads(resp.text)
|
search_res = loads(resp.text)
|
||||||
|
|
||||||
|
# check if items are recieved
|
||||||
if not 'items' in search_res:
|
if not 'items' in search_res:
|
||||||
return results
|
return []
|
||||||
|
|
||||||
|
# parse results
|
||||||
for res in search_res['items']:
|
for res in search_res['items']:
|
||||||
title = res['name']
|
title = res['name']
|
||||||
url = res['html_url']
|
url = res['html_url']
|
||||||
|
|
||||||
if res['description']:
|
if res['description']:
|
||||||
content = escape(res['description'][:500])
|
content = escape(res['description'][:500])
|
||||||
else:
|
else:
|
||||||
content = ''
|
content = ''
|
||||||
results.append({'url': url, 'title': title, 'content': content})
|
|
||||||
|
# append result
|
||||||
|
results.append({'url': url,
|
||||||
|
'title': title,
|
||||||
|
'content': content})
|
||||||
|
|
||||||
|
# return results
|
||||||
return results
|
return results
|
||||||
|
|
|
@ -1,37 +1,115 @@
|
||||||
#!/usr/bin/env python
|
## Google (Web)
|
||||||
|
#
|
||||||
|
# @website https://www.google.com
|
||||||
|
# @provide-api yes (https://developers.google.com/custom-search/)
|
||||||
|
#
|
||||||
|
# @using-api no
|
||||||
|
# @results HTML
|
||||||
|
# @stable no (HTML can change)
|
||||||
|
# @parse url, title, content, suggestion
|
||||||
|
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from json import loads
|
from urlparse import unquote,urlparse,parse_qsl
|
||||||
|
from lxml import html
|
||||||
|
from searx.engines.xpath import extract_text, extract_url
|
||||||
|
|
||||||
|
# engine dependent config
|
||||||
categories = ['general']
|
categories = ['general']
|
||||||
|
|
||||||
url = 'https://ajax.googleapis.com/'
|
|
||||||
search_url = url + 'ajax/services/search/web?v=2.0&start={offset}&rsz=large&safe=off&filter=off&{query}&hl={language}' # noqa
|
|
||||||
|
|
||||||
paging = True
|
paging = True
|
||||||
language_support = True
|
language_support = True
|
||||||
|
|
||||||
|
# search-url
|
||||||
|
google_hostname = 'www.google.com'
|
||||||
|
search_path = '/search'
|
||||||
|
redirect_path = '/url'
|
||||||
|
images_path = '/images'
|
||||||
|
search_url = 'https://' + google_hostname + search_path + '?{query}&start={offset}&gbv=1'
|
||||||
|
|
||||||
|
# specific xpath variables
|
||||||
|
results_xpath= '//li[@class="g"]'
|
||||||
|
url_xpath = './/h3/a/@href'
|
||||||
|
title_xpath = './/h3'
|
||||||
|
content_xpath = './/span[@class="st"]'
|
||||||
|
suggestion_xpath = '//p[@class="_Bmc"]'
|
||||||
|
|
||||||
|
images_xpath = './/div/a'
|
||||||
|
image_url_xpath = './@href'
|
||||||
|
image_img_src_xpath = './img/@src'
|
||||||
|
|
||||||
|
# remove google-specific tracking-url
|
||||||
|
def parse_url(url_string):
|
||||||
|
parsed_url = urlparse(url_string)
|
||||||
|
if parsed_url.netloc in [google_hostname, ''] and parsed_url.path==redirect_path:
|
||||||
|
query = dict(parse_qsl(parsed_url.query))
|
||||||
|
return query['q']
|
||||||
|
else:
|
||||||
|
return url_string
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
offset = (params['pageno'] - 1) * 8
|
offset = (params['pageno'] - 1) * 10
|
||||||
language = 'en-US'
|
|
||||||
if params['language'] != 'all':
|
if params['language'] == 'all':
|
||||||
language = params['language'].replace('_', '-')
|
language = 'en'
|
||||||
|
else:
|
||||||
|
language = params['language'].replace('_','-').lower()
|
||||||
|
|
||||||
params['url'] = search_url.format(offset=offset,
|
params['url'] = search_url.format(offset=offset,
|
||||||
query=urlencode({'q': query}),
|
query=urlencode({'q': query}))
|
||||||
language=language)
|
|
||||||
|
params['headers']['Accept-Language'] = language
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
results = []
|
results = []
|
||||||
search_res = loads(resp.text)
|
|
||||||
|
|
||||||
if not search_res.get('responseData', {}).get('results'):
|
dom = html.fromstring(resp.text)
|
||||||
return []
|
|
||||||
|
# parse results
|
||||||
|
for result in dom.xpath(results_xpath):
|
||||||
|
title = extract_text(result.xpath(title_xpath)[0])
|
||||||
|
try:
|
||||||
|
url = parse_url(extract_url(result.xpath(url_xpath), search_url))
|
||||||
|
parsed_url = urlparse(url)
|
||||||
|
if parsed_url.netloc==google_hostname and parsed_url.path==search_path:
|
||||||
|
# remove the link to google news
|
||||||
|
continue
|
||||||
|
|
||||||
|
if parsed_url.netloc==google_hostname and parsed_url.path==images_path:
|
||||||
|
# images result
|
||||||
|
results = results + parse_images(result)
|
||||||
|
else:
|
||||||
|
# normal result
|
||||||
|
content = extract_text(result.xpath(content_xpath)[0])
|
||||||
|
# append result
|
||||||
|
results.append({'url': url,
|
||||||
|
'title': title,
|
||||||
|
'content': content})
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# parse suggestion
|
||||||
|
for suggestion in dom.xpath(suggestion_xpath):
|
||||||
|
# append suggestion
|
||||||
|
results.append({'suggestion': extract_text(suggestion)})
|
||||||
|
|
||||||
|
# return results
|
||||||
|
return results
|
||||||
|
|
||||||
|
def parse_images(result):
|
||||||
|
results = []
|
||||||
|
for image in result.xpath(images_xpath):
|
||||||
|
url = parse_url(extract_text(image.xpath(image_url_xpath)[0]))
|
||||||
|
img_src = extract_text(image.xpath(image_img_src_xpath)[0])
|
||||||
|
|
||||||
|
# append result
|
||||||
|
results.append({'url': url,
|
||||||
|
'title': '',
|
||||||
|
'content': '',
|
||||||
|
'img_src': img_src,
|
||||||
|
'template': 'images.html'})
|
||||||
|
|
||||||
for result in search_res['responseData']['results']:
|
|
||||||
results.append({'url': result['unescapedUrl'],
|
|
||||||
'title': result['titleNoFormatting'],
|
|
||||||
'content': result['content']})
|
|
||||||
return results
|
return results
|
||||||
|
|
|
@ -1,36 +1,58 @@
|
||||||
#!/usr/bin/env python
|
## Google (Images)
|
||||||
|
#
|
||||||
|
# @website https://www.google.com
|
||||||
|
# @provide-api yes (https://developers.google.com/web-search/docs/), deprecated!
|
||||||
|
#
|
||||||
|
# @using-api yes
|
||||||
|
# @results JSON
|
||||||
|
# @stable yes (but deprecated)
|
||||||
|
# @parse url, title, img_src
|
||||||
|
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from json import loads
|
from json import loads
|
||||||
|
|
||||||
|
# engine dependent config
|
||||||
categories = ['images']
|
categories = ['images']
|
||||||
|
paging = True
|
||||||
|
|
||||||
|
# search-url
|
||||||
url = 'https://ajax.googleapis.com/'
|
url = 'https://ajax.googleapis.com/'
|
||||||
search_url = url + 'ajax/services/search/images?v=1.0&start={offset}&rsz=large&safe=off&filter=off&{query}' # noqa
|
search_url = url + 'ajax/services/search/images?v=1.0&start={offset}&rsz=large&safe=off&filter=off&{query}' # noqa
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
offset = (params['pageno'] - 1) * 8
|
offset = (params['pageno'] - 1) * 8
|
||||||
|
|
||||||
params['url'] = search_url.format(query=urlencode({'q': query}),
|
params['url'] = search_url.format(query=urlencode({'q': query}),
|
||||||
offset=offset)
|
offset=offset)
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
search_res = loads(resp.text)
|
search_res = loads(resp.text)
|
||||||
if not search_res.get('responseData'):
|
|
||||||
return []
|
# return empty array if there are no results
|
||||||
if not search_res['responseData'].get('results'):
|
if not search_res.get('responseData', {}).get('results'):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
# parse results
|
||||||
for result in search_res['responseData']['results']:
|
for result in search_res['responseData']['results']:
|
||||||
href = result['originalContextUrl']
|
href = result['originalContextUrl']
|
||||||
title = result['title']
|
title = result['title']
|
||||||
if not result['url']:
|
if not result['url']:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# append result
|
||||||
results.append({'url': href,
|
results.append({'url': href,
|
||||||
'title': title,
|
'title': title,
|
||||||
'content': '',
|
'content': '',
|
||||||
'img_src': result['url'],
|
'img_src': result['url'],
|
||||||
'template': 'images.html'})
|
'template': 'images.html'})
|
||||||
|
|
||||||
|
# return results
|
||||||
return results
|
return results
|
||||||
|
|
|
@ -1,43 +1,62 @@
|
||||||
#!/usr/bin/env python
|
## Google (News)
|
||||||
|
#
|
||||||
|
# @website https://www.google.com
|
||||||
|
# @provide-api yes (https://developers.google.com/web-search/docs/), deprecated!
|
||||||
|
#
|
||||||
|
# @using-api yes
|
||||||
|
# @results JSON
|
||||||
|
# @stable yes (but deprecated)
|
||||||
|
# @parse url, title, content, publishedDate
|
||||||
|
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from json import loads
|
from json import loads
|
||||||
from dateutil import parser
|
from dateutil import parser
|
||||||
|
|
||||||
|
# search-url
|
||||||
categories = ['news']
|
categories = ['news']
|
||||||
|
|
||||||
url = 'https://ajax.googleapis.com/'
|
|
||||||
search_url = url + 'ajax/services/search/news?v=2.0&start={offset}&rsz=large&safe=off&filter=off&{query}&hl={language}' # noqa
|
|
||||||
|
|
||||||
paging = True
|
paging = True
|
||||||
language_support = True
|
language_support = True
|
||||||
|
|
||||||
|
# engine dependent config
|
||||||
|
url = 'https://ajax.googleapis.com/'
|
||||||
|
search_url = url + 'ajax/services/search/news?v=2.0&start={offset}&rsz=large&safe=off&filter=off&{query}&hl={language}' # noqa
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
offset = (params['pageno'] - 1) * 8
|
offset = (params['pageno'] - 1) * 8
|
||||||
|
|
||||||
language = 'en-US'
|
language = 'en-US'
|
||||||
if params['language'] != 'all':
|
if params['language'] != 'all':
|
||||||
language = params['language'].replace('_', '-')
|
language = params['language'].replace('_', '-')
|
||||||
|
|
||||||
params['url'] = search_url.format(offset=offset,
|
params['url'] = search_url.format(offset=offset,
|
||||||
query=urlencode({'q': query}),
|
query=urlencode({'q': query}),
|
||||||
language=language)
|
language=language)
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
search_res = loads(resp.text)
|
search_res = loads(resp.text)
|
||||||
|
|
||||||
|
# return empty array if there are no results
|
||||||
if not search_res.get('responseData', {}).get('results'):
|
if not search_res.get('responseData', {}).get('results'):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
# parse results
|
||||||
for result in search_res['responseData']['results']:
|
for result in search_res['responseData']['results']:
|
||||||
|
# parse publishedDate
|
||||||
# Mon, 10 Mar 2014 16:26:15 -0700
|
|
||||||
publishedDate = parser.parse(result['publishedDate'])
|
publishedDate = parser.parse(result['publishedDate'])
|
||||||
|
|
||||||
|
# append result
|
||||||
results.append({'url': result['unescapedUrl'],
|
results.append({'url': result['unescapedUrl'],
|
||||||
'title': result['titleNoFormatting'],
|
'title': result['titleNoFormatting'],
|
||||||
'publishedDate': publishedDate,
|
'publishedDate': publishedDate,
|
||||||
'content': result['content']})
|
'content': result['content']})
|
||||||
|
|
||||||
|
# return results
|
||||||
return results
|
return results
|
||||||
|
|
|
@ -1,22 +1,78 @@
|
||||||
|
## general mediawiki-engine (Web)
|
||||||
|
#
|
||||||
|
# @website websites built on mediawiki (https://www.mediawiki.org)
|
||||||
|
# @provide-api yes (http://www.mediawiki.org/wiki/API:Search)
|
||||||
|
#
|
||||||
|
# @using-api yes
|
||||||
|
# @results JSON
|
||||||
|
# @stable yes
|
||||||
|
# @parse url, title
|
||||||
|
#
|
||||||
|
# @todo content
|
||||||
|
|
||||||
from json import loads
|
from json import loads
|
||||||
|
from string import Formatter
|
||||||
from urllib import urlencode, quote
|
from urllib import urlencode, quote
|
||||||
|
|
||||||
url = 'https://en.wikipedia.org/'
|
# engine dependent config
|
||||||
|
categories = ['general']
|
||||||
|
language_support = True
|
||||||
|
paging = True
|
||||||
|
number_of_results = 1
|
||||||
|
|
||||||
search_url = url + 'w/api.php?action=query&list=search&{query}&srprop=timestamp&format=json&sroffset={offset}' # noqa
|
# search-url
|
||||||
|
base_url = 'https://{language}.wikipedia.org/'
|
||||||
number_of_results = 10
|
search_url = base_url + 'w/api.php?action=query'\
|
||||||
|
'&list=search'\
|
||||||
|
'&{query}'\
|
||||||
|
'&srprop=timestamp'\
|
||||||
|
'&format=json'\
|
||||||
|
'&sroffset={offset}'\
|
||||||
|
'&srlimit={limit}'
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
offset = (params['pageno'] - 1) * 10
|
offset = (params['pageno'] - 1) * number_of_results
|
||||||
params['url'] = search_url.format(query=urlencode({'srsearch': query}),
|
string_args = dict(query=urlencode({'srsearch': query}),
|
||||||
offset=offset)
|
offset=offset,
|
||||||
|
limit=number_of_results)
|
||||||
|
format_strings = list(Formatter().parse(base_url))
|
||||||
|
|
||||||
|
if params['language'] == 'all':
|
||||||
|
language = 'en'
|
||||||
|
else:
|
||||||
|
language = params['language'].split('_')[0]
|
||||||
|
|
||||||
|
if len(format_strings) > 1:
|
||||||
|
string_args['language'] = language
|
||||||
|
|
||||||
|
# write search-language back to params, required in response
|
||||||
|
params['language'] = language
|
||||||
|
|
||||||
|
params['url'] = search_url.format(**string_args)
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
|
results = []
|
||||||
|
|
||||||
search_results = loads(resp.text)
|
search_results = loads(resp.text)
|
||||||
res = search_results.get('query', {}).get('search', [])
|
|
||||||
return [{'url': url + 'wiki/' + quote(result['title'].replace(' ', '_').encode('utf-8')), # noqa
|
# return empty array if there are no results
|
||||||
'title': result['title']} for result in res[:int(number_of_results)]]
|
if not search_results.get('query', {}).get('search'):
|
||||||
|
return []
|
||||||
|
|
||||||
|
# parse results
|
||||||
|
for result in search_results['query']['search']:
|
||||||
|
url = base_url.format(language=resp.search_params['language']) + 'wiki/' + quote(result['title'].replace(' ', '_').encode('utf-8'))
|
||||||
|
|
||||||
|
# append result
|
||||||
|
results.append({'url': url,
|
||||||
|
'title': result['title'],
|
||||||
|
'content': ''})
|
||||||
|
|
||||||
|
# return results
|
||||||
|
return results
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
## OpenStreetMap (Map)
|
||||||
|
#
|
||||||
|
# @website https://openstreetmap.org/
|
||||||
|
# @provide-api yes (http://wiki.openstreetmap.org/wiki/Nominatim)
|
||||||
|
#
|
||||||
|
# @using-api yes
|
||||||
|
# @results JSON
|
||||||
|
# @stable yes
|
||||||
|
# @parse url, title
|
||||||
|
|
||||||
|
from json import loads
|
||||||
|
|
||||||
|
# engine dependent config
|
||||||
|
categories = ['map']
|
||||||
|
paging = False
|
||||||
|
|
||||||
|
# search-url
|
||||||
|
url = 'https://nominatim.openstreetmap.org/search/{query}?format=json&polygon_geojson=1&addressdetails=1'
|
||||||
|
|
||||||
|
result_base_url = 'https://openstreetmap.org/{osm_type}/{osm_id}'
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
|
def request(query, params):
|
||||||
|
params['url'] = url.format(query=query)
|
||||||
|
|
||||||
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
|
def response(resp):
|
||||||
|
results = []
|
||||||
|
json = loads(resp.text)
|
||||||
|
|
||||||
|
# parse results
|
||||||
|
for r in json:
|
||||||
|
title = r['display_name']
|
||||||
|
osm_type = r.get('osm_type', r.get('type'))
|
||||||
|
url = result_base_url.format(osm_type=osm_type,
|
||||||
|
osm_id=r['osm_id'])
|
||||||
|
|
||||||
|
osm = {'type':osm_type,
|
||||||
|
'id':r['osm_id']}
|
||||||
|
|
||||||
|
geojson = r.get('geojson')
|
||||||
|
|
||||||
|
# if no geojson is found and osm_type is a node, add geojson Point
|
||||||
|
if not geojson and\
|
||||||
|
osm_type == 'node':
|
||||||
|
geojson = {u'type':u'Point',
|
||||||
|
u'coordinates':[r['lon'],r['lat']]}
|
||||||
|
|
||||||
|
address_raw = r.get('address')
|
||||||
|
address = {}
|
||||||
|
|
||||||
|
# get name
|
||||||
|
if r['class'] == 'amenity' or\
|
||||||
|
r['class'] == 'shop' or\
|
||||||
|
r['class'] == 'tourism' or\
|
||||||
|
r['class'] == 'leisure':
|
||||||
|
if address_raw.get('address29'):
|
||||||
|
address = {'name':address_raw.get('address29')}
|
||||||
|
else:
|
||||||
|
address = {'name':address_raw.get(r['type'])}
|
||||||
|
|
||||||
|
# add rest of adressdata, if something is already found
|
||||||
|
if address.get('name'):
|
||||||
|
address.update({'house_number':address_raw.get('house_number'),
|
||||||
|
'road':address_raw.get('road'),
|
||||||
|
'locality':address_raw.get('city',
|
||||||
|
address_raw.get('town',
|
||||||
|
address_raw.get('village'))),
|
||||||
|
'postcode':address_raw.get('postcode'),
|
||||||
|
'country':address_raw.get('country'),
|
||||||
|
'country_code':address_raw.get('country_code')})
|
||||||
|
else:
|
||||||
|
address = None
|
||||||
|
|
||||||
|
# append result
|
||||||
|
results.append({'template': 'map.html',
|
||||||
|
'title': title,
|
||||||
|
'content': '',
|
||||||
|
'longitude': r['lon'],
|
||||||
|
'latitude': r['lat'],
|
||||||
|
'boundingbox': r['boundingbox'],
|
||||||
|
'geojson': geojson,
|
||||||
|
'address': address,
|
||||||
|
'osm': osm,
|
||||||
|
'url': url})
|
||||||
|
|
||||||
|
# return results
|
||||||
|
return results
|
|
@ -1,39 +1,61 @@
|
||||||
|
## Piratebay (Videos, Music, Files)
|
||||||
|
#
|
||||||
|
# @website https://thepiratebay.se
|
||||||
|
# @provide-api no (nothing found)
|
||||||
|
#
|
||||||
|
# @using-api no
|
||||||
|
# @results HTML (using search portal)
|
||||||
|
# @stable yes (HTML can change)
|
||||||
|
# @parse url, title, content, seed, leech, magnetlink
|
||||||
|
|
||||||
from urlparse import urljoin
|
from urlparse import urljoin
|
||||||
from cgi import escape
|
from cgi import escape
|
||||||
from urllib import quote
|
from urllib import quote
|
||||||
from lxml import html
|
from lxml import html
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
|
||||||
categories = ['videos', 'music']
|
# engine dependent config
|
||||||
|
categories = ['videos', 'music', 'files']
|
||||||
|
paging = True
|
||||||
|
|
||||||
|
# search-url
|
||||||
url = 'https://thepiratebay.se/'
|
url = 'https://thepiratebay.se/'
|
||||||
search_url = url + 'search/{search_term}/{pageno}/99/{search_type}'
|
search_url = url + 'search/{search_term}/{pageno}/99/{search_type}'
|
||||||
search_types = {'videos': '200',
|
|
||||||
'music': '100',
|
|
||||||
'files': '0'}
|
|
||||||
|
|
||||||
|
# piratebay specific type-definitions
|
||||||
|
search_types = {'files': '0',
|
||||||
|
'music': '100',
|
||||||
|
'videos': '200'}
|
||||||
|
|
||||||
|
# specific xpath variables
|
||||||
magnet_xpath = './/a[@title="Download this torrent using magnet"]'
|
magnet_xpath = './/a[@title="Download this torrent using magnet"]'
|
||||||
content_xpath = './/font[@class="detDesc"]//text()'
|
content_xpath = './/font[@class="detDesc"]//text()'
|
||||||
|
|
||||||
paging = True
|
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
search_type = search_types.get(params['category'], '200')
|
search_type = search_types.get(params['category'], '0')
|
||||||
|
|
||||||
params['url'] = search_url.format(search_term=quote(query),
|
params['url'] = search_url.format(search_term=quote(query),
|
||||||
search_type=search_type,
|
search_type=search_type,
|
||||||
pageno=params['pageno'] - 1)
|
pageno=params['pageno'] - 1)
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
dom = html.fromstring(resp.text)
|
dom = html.fromstring(resp.text)
|
||||||
|
|
||||||
search_res = dom.xpath('//table[@id="searchResult"]//tr')
|
search_res = dom.xpath('//table[@id="searchResult"]//tr')
|
||||||
|
|
||||||
|
# return empty array if nothing is found
|
||||||
if not search_res:
|
if not search_res:
|
||||||
return results
|
return []
|
||||||
|
|
||||||
|
# parse results
|
||||||
for result in search_res[1:]:
|
for result in search_res[1:]:
|
||||||
link = result.xpath('.//div[@class="detName"]//a')[0]
|
link = result.xpath('.//div[@class="detName"]//a')[0]
|
||||||
href = urljoin(url, link.attrib.get('href'))
|
href = urljoin(url, link.attrib.get('href'))
|
||||||
|
@ -41,17 +63,21 @@ def response(resp):
|
||||||
content = escape(' '.join(result.xpath(content_xpath)))
|
content = escape(' '.join(result.xpath(content_xpath)))
|
||||||
seed, leech = result.xpath('.//td[@align="right"]/text()')[:2]
|
seed, leech = result.xpath('.//td[@align="right"]/text()')[:2]
|
||||||
|
|
||||||
|
# convert seed to int if possible
|
||||||
if seed.isdigit():
|
if seed.isdigit():
|
||||||
seed = int(seed)
|
seed = int(seed)
|
||||||
else:
|
else:
|
||||||
seed = 0
|
seed = 0
|
||||||
|
|
||||||
|
# convert leech to int if possible
|
||||||
if leech.isdigit():
|
if leech.isdigit():
|
||||||
leech = int(leech)
|
leech = int(leech)
|
||||||
else:
|
else:
|
||||||
leech = 0
|
leech = 0
|
||||||
|
|
||||||
magnetlink = result.xpath(magnet_xpath)[0]
|
magnetlink = result.xpath(magnet_xpath)[0]
|
||||||
|
|
||||||
|
# append result
|
||||||
results.append({'url': href,
|
results.append({'url': href,
|
||||||
'title': title,
|
'title': title,
|
||||||
'content': content,
|
'content': content,
|
||||||
|
@ -60,4 +86,5 @@ def response(resp):
|
||||||
'magnetlink': magnetlink.attrib['href'],
|
'magnetlink': magnetlink.attrib['href'],
|
||||||
'template': 'torrent.html'})
|
'template': 'torrent.html'})
|
||||||
|
|
||||||
|
# return results sorted by seeder
|
||||||
return sorted(results, key=itemgetter('seed'), reverse=True)
|
return sorted(results, key=itemgetter('seed'), reverse=True)
|
||||||
|
|
|
@ -1,31 +1,55 @@
|
||||||
|
## Soundcloud (Music)
|
||||||
|
#
|
||||||
|
# @website https://soundcloud.com
|
||||||
|
# @provide-api yes (https://developers.soundcloud.com/)
|
||||||
|
#
|
||||||
|
# @using-api yes
|
||||||
|
# @results JSON
|
||||||
|
# @stable yes
|
||||||
|
# @parse url, title, content
|
||||||
|
|
||||||
from json import loads
|
from json import loads
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
|
|
||||||
|
# engine dependent config
|
||||||
categories = ['music']
|
categories = ['music']
|
||||||
|
|
||||||
guest_client_id = 'b45b1aa10f1ac2941910a7f0d10f8e28'
|
|
||||||
url = 'https://api.soundcloud.com/'
|
|
||||||
search_url = url + 'search?{query}&facet=model&limit=20&offset={offset}&linked_partitioning=1&client_id='+guest_client_id # noqa
|
|
||||||
|
|
||||||
paging = True
|
paging = True
|
||||||
|
|
||||||
|
# api-key
|
||||||
|
guest_client_id = 'b45b1aa10f1ac2941910a7f0d10f8e28'
|
||||||
|
|
||||||
|
# search-url
|
||||||
|
url = 'https://api.soundcloud.com/'
|
||||||
|
search_url = url + 'search?{query}&facet=model&limit=20&offset={offset}&linked_partitioning=1&client_id={client_id}'
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
offset = (params['pageno'] - 1) * 20
|
offset = (params['pageno'] - 1) * 20
|
||||||
|
|
||||||
params['url'] = search_url.format(query=urlencode({'q': query}),
|
params['url'] = search_url.format(query=urlencode({'q': query}),
|
||||||
offset=offset)
|
offset=offset,
|
||||||
|
client_id=guest_client_id)
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
global base_url
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
search_res = loads(resp.text)
|
search_res = loads(resp.text)
|
||||||
|
|
||||||
|
# parse results
|
||||||
for result in search_res.get('collection', []):
|
for result in search_res.get('collection', []):
|
||||||
if result['kind'] in ('track', 'playlist'):
|
if result['kind'] in ('track', 'playlist'):
|
||||||
title = result['title']
|
title = result['title']
|
||||||
content = result['description']
|
content = result['description']
|
||||||
|
|
||||||
|
# append result
|
||||||
results.append({'url': result['permalink_url'],
|
results.append({'url': result['permalink_url'],
|
||||||
'title': title,
|
'title': title,
|
||||||
'content': content})
|
'content': content})
|
||||||
|
|
||||||
|
# return results
|
||||||
return results
|
return results
|
||||||
|
|
|
@ -1,30 +1,58 @@
|
||||||
|
## Stackoverflow (It)
|
||||||
|
#
|
||||||
|
# @website https://stackoverflow.com/
|
||||||
|
# @provide-api not clear (https://api.stackexchange.com/docs/advanced-search)
|
||||||
|
#
|
||||||
|
# @using-api no
|
||||||
|
# @results HTML
|
||||||
|
# @stable no (HTML can change)
|
||||||
|
# @parse url, title, content
|
||||||
|
|
||||||
from urlparse import urljoin
|
from urlparse import urljoin
|
||||||
from cgi import escape
|
from cgi import escape
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from lxml import html
|
from lxml import html
|
||||||
|
|
||||||
|
# engine dependent config
|
||||||
categories = ['it']
|
categories = ['it']
|
||||||
|
|
||||||
url = 'http://stackoverflow.com/'
|
|
||||||
search_url = url+'search?{query}&page={pageno}'
|
|
||||||
result_xpath = './/div[@class="excerpt"]//text()'
|
|
||||||
|
|
||||||
paging = True
|
paging = True
|
||||||
|
|
||||||
|
# search-url
|
||||||
|
url = 'http://stackoverflow.com/'
|
||||||
|
search_url = url+'search?{query}&page={pageno}'
|
||||||
|
|
||||||
|
# specific xpath variables
|
||||||
|
results_xpath = '//div[contains(@class,"question-summary")]'
|
||||||
|
link_xpath = './/div[@class="result-link"]//a|.//div[@class="summary"]//h3//a'
|
||||||
|
title_xpath = './/text()'
|
||||||
|
content_xpath = './/div[@class="excerpt"]//text()'
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
params['url'] = search_url.format(query=urlencode({'q': query}),
|
params['url'] = search_url.format(query=urlencode({'q': query}),
|
||||||
pageno=params['pageno'])
|
pageno=params['pageno'])
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
dom = html.fromstring(resp.text)
|
dom = html.fromstring(resp.text)
|
||||||
for result in dom.xpath('//div[@class="question-summary search-result"]'):
|
|
||||||
link = result.xpath('.//div[@class="result-link"]//a')[0]
|
# parse results
|
||||||
|
for result in dom.xpath(results_xpath):
|
||||||
|
link = result.xpath(link_xpath)[0]
|
||||||
href = urljoin(url, link.attrib.get('href'))
|
href = urljoin(url, link.attrib.get('href'))
|
||||||
title = escape(' '.join(link.xpath('.//text()')))
|
title = escape(' '.join(link.xpath(title_xpath)))
|
||||||
content = escape(' '.join(result.xpath(result_xpath)))
|
content = escape(' '.join(result.xpath(content_xpath)))
|
||||||
results.append({'url': href, 'title': title, 'content': content})
|
|
||||||
|
# append result
|
||||||
|
results.append({'url': href,
|
||||||
|
'title': title,
|
||||||
|
'content': content})
|
||||||
|
|
||||||
|
# return results
|
||||||
return results
|
return results
|
||||||
|
|
|
@ -1,46 +1,87 @@
|
||||||
|
# Startpage (Web)
|
||||||
|
#
|
||||||
|
# @website https://startpage.com
|
||||||
|
# @provide-api no (nothing found)
|
||||||
|
#
|
||||||
|
# @using-api no
|
||||||
|
# @results HTML
|
||||||
|
# @stable no (HTML can change)
|
||||||
|
# @parse url, title, content
|
||||||
|
#
|
||||||
|
# @todo paging
|
||||||
|
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from lxml import html
|
from lxml import html
|
||||||
|
from cgi import escape
|
||||||
|
import re
|
||||||
|
|
||||||
base_url = None
|
# engine dependent config
|
||||||
search_url = None
|
categories = ['general']
|
||||||
|
# there is a mechanism to block "bot" search
|
||||||
|
# (probably the parameter qid), require
|
||||||
|
# storing of qid's between mulitble search-calls
|
||||||
|
|
||||||
# TODO paging
|
# paging = False
|
||||||
paging = False
|
language_support = True
|
||||||
# TODO complete list of country mapping
|
|
||||||
country_map = {'en_US': 'eng',
|
# search-url
|
||||||
'en_UK': 'uk',
|
base_url = 'https://startpage.com/'
|
||||||
'nl_NL': 'ned'}
|
search_url = base_url + 'do/search'
|
||||||
|
|
||||||
|
# specific xpath variables
|
||||||
|
# ads xpath //div[@id="results"]/div[@id="sponsored"]//div[@class="result"]
|
||||||
|
# not ads: div[@class="result"] are the direct childs of div[@id="results"]
|
||||||
|
results_xpath = '//div[@class="result"]'
|
||||||
|
link_xpath = './/h3/a'
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
|
offset = (params['pageno'] - 1) * 10
|
||||||
query = urlencode({'q': query})[2:]
|
query = urlencode({'q': query})[2:]
|
||||||
|
|
||||||
params['url'] = search_url
|
params['url'] = search_url
|
||||||
params['method'] = 'POST'
|
params['method'] = 'POST'
|
||||||
params['data'] = {'query': query,
|
params['data'] = {'query': query,
|
||||||
'startat': (params['pageno'] - 1) * 10} # offset
|
'startat': offset}
|
||||||
country = country_map.get(params['language'], 'eng')
|
|
||||||
params['cookies']['preferences'] = \
|
# set language if specified
|
||||||
'lang_homepageEEEs/air/{country}/N1NsslEEE1N1Nfont_sizeEEEmediumN1Nrecent_results_filterEEE1N1Nlanguage_uiEEEenglishN1Ndisable_open_in_new_windowEEE0N1Ncolor_schemeEEEnewN1Nnum_of_resultsEEE10N1N'.format(country=country) # noqa
|
if params['language'] != 'all':
|
||||||
|
params['data']['with_language'] = ('lang_' +
|
||||||
|
params['language'].split('_')[0])
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
dom = html.fromstring(resp.content)
|
dom = html.fromstring(resp.content)
|
||||||
# ads xpath //div[@id="results"]/div[@id="sponsored"]//div[@class="result"]
|
|
||||||
# not ads: div[@class="result"] are the direct childs of div[@id="results"]
|
# parse results
|
||||||
for result in dom.xpath('//div[@class="result"]'):
|
for result in dom.xpath(results_xpath):
|
||||||
link = result.xpath('.//h3/a')[0]
|
links = result.xpath(link_xpath)
|
||||||
url = link.attrib.get('href')
|
if not links:
|
||||||
if url.startswith('http://www.google.')\
|
continue
|
||||||
or url.startswith('https://www.google.'):
|
link = links[0]
|
||||||
|
url = link.attrib.get('href')
|
||||||
|
title = escape(link.text_content())
|
||||||
|
|
||||||
|
# block google-ad url's
|
||||||
|
if re.match("^http(s|)://www.google.[a-z]+/aclk.*$", url):
|
||||||
continue
|
continue
|
||||||
title = link.text_content()
|
|
||||||
|
|
||||||
content = ''
|
|
||||||
if result.xpath('./p[@class="desc"]'):
|
if result.xpath('./p[@class="desc"]'):
|
||||||
content = result.xpath('./p[@class="desc"]')[0].text_content()
|
content = escape(result.xpath('./p[@class="desc"]')[0]
|
||||||
|
.text_content())
|
||||||
|
else:
|
||||||
|
content = ''
|
||||||
|
|
||||||
results.append({'url': url, 'title': title, 'content': content})
|
# append result
|
||||||
|
results.append({'url': url,
|
||||||
|
'title': title,
|
||||||
|
'content': content})
|
||||||
|
|
||||||
|
# return results
|
||||||
return results
|
return results
|
||||||
|
|
|
@ -1,32 +1,63 @@
|
||||||
|
## Twitter (Social media)
|
||||||
|
#
|
||||||
|
# @website https://www.bing.com/news
|
||||||
|
# @provide-api yes (https://dev.twitter.com/docs/using-search)
|
||||||
|
#
|
||||||
|
# @using-api no
|
||||||
|
# @results HTML (using search portal)
|
||||||
|
# @stable no (HTML can change)
|
||||||
|
# @parse url, title, content
|
||||||
|
#
|
||||||
|
# @todo publishedDate
|
||||||
|
|
||||||
from urlparse import urljoin
|
from urlparse import urljoin
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from lxml import html
|
from lxml import html
|
||||||
from cgi import escape
|
from cgi import escape
|
||||||
|
|
||||||
|
# engine dependent config
|
||||||
categories = ['social media']
|
categories = ['social media']
|
||||||
|
language_support = True
|
||||||
|
|
||||||
|
# search-url
|
||||||
base_url = 'https://twitter.com/'
|
base_url = 'https://twitter.com/'
|
||||||
search_url = base_url+'search?'
|
search_url = base_url+'search?'
|
||||||
|
|
||||||
|
# specific xpath variables
|
||||||
|
results_xpath = '//li[@data-item-type="tweet"]'
|
||||||
|
link_xpath = './/small[@class="time"]//a'
|
||||||
title_xpath = './/span[@class="username js-action-profile-name"]//text()'
|
title_xpath = './/span[@class="username js-action-profile-name"]//text()'
|
||||||
content_xpath = './/p[@class="js-tweet-text tweet-text"]//text()'
|
content_xpath = './/p[@class="js-tweet-text tweet-text"]//text()'
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
global search_url
|
|
||||||
params['url'] = search_url + urlencode({'q': query})
|
params['url'] = search_url + urlencode({'q': query})
|
||||||
|
|
||||||
|
# set language if specified
|
||||||
|
if params['language'] != 'all':
|
||||||
|
params['cookies']['lang'] = params['language'].split('_')[0]
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
global base_url
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
dom = html.fromstring(resp.text)
|
dom = html.fromstring(resp.text)
|
||||||
for tweet in dom.xpath('//li[@data-item-type="tweet"]'):
|
|
||||||
link = tweet.xpath('.//small[@class="time"]//a')[0]
|
# parse results
|
||||||
|
for tweet in dom.xpath(results_xpath):
|
||||||
|
link = tweet.xpath(link_xpath)[0]
|
||||||
url = urljoin(base_url, link.attrib.get('href'))
|
url = urljoin(base_url, link.attrib.get('href'))
|
||||||
title = ''.join(tweet.xpath(title_xpath))
|
title = ''.join(tweet.xpath(title_xpath))
|
||||||
content = escape(''.join(tweet.xpath(content_xpath)))
|
content = escape(''.join(tweet.xpath(content_xpath)))
|
||||||
|
|
||||||
|
# append result
|
||||||
results.append({'url': url,
|
results.append({'url': url,
|
||||||
'title': title,
|
'title': title,
|
||||||
'content': content})
|
'content': content})
|
||||||
|
|
||||||
|
# return results
|
||||||
return results
|
return results
|
||||||
|
|
|
@ -1,43 +1,58 @@
|
||||||
|
## Vimeo (Videos)
|
||||||
|
#
|
||||||
|
# @website https://vimeo.com/
|
||||||
|
# @provide-api yes (http://developer.vimeo.com/api), they have a maximum count of queries/hour
|
||||||
|
#
|
||||||
|
# @using-api no (TODO, rewrite to api)
|
||||||
|
# @results HTML (using search portal)
|
||||||
|
# @stable no (HTML can change)
|
||||||
|
# @parse url, title, publishedDate, thumbnail
|
||||||
|
#
|
||||||
|
# @todo rewrite to api
|
||||||
|
# @todo set content-parameter with correct data
|
||||||
|
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from HTMLParser import HTMLParser
|
from HTMLParser import HTMLParser
|
||||||
from lxml import html
|
from lxml import html
|
||||||
from searx.engines.xpath import extract_text
|
from searx.engines.xpath import extract_text
|
||||||
from dateutil import parser
|
from dateutil import parser
|
||||||
|
|
||||||
base_url = 'http://vimeo.com'
|
# engine dependent config
|
||||||
search_url = base_url + '/search?{query}'
|
categories = ['videos']
|
||||||
url_xpath = None
|
paging = True
|
||||||
content_xpath = None
|
|
||||||
title_xpath = None
|
# search-url
|
||||||
results_xpath = ''
|
base_url = 'https://vimeo.com'
|
||||||
content_tpl = '<a href="{0}"> <img src="{2}"/> </a>'
|
search_url = base_url + '/search/page:{pageno}?{query}'
|
||||||
|
|
||||||
|
# specific xpath variables
|
||||||
|
url_xpath = './a/@href'
|
||||||
|
content_xpath = './a/img/@src'
|
||||||
|
title_xpath = './a/div[@class="data"]/p[@class="title"]/text()'
|
||||||
|
results_xpath = '//div[@id="browse_content"]/ol/li'
|
||||||
publishedDate_xpath = './/p[@class="meta"]//attribute::datetime'
|
publishedDate_xpath = './/p[@class="meta"]//attribute::datetime'
|
||||||
|
|
||||||
# the cookie set by vimeo contains all the following values,
|
|
||||||
# but only __utma seems to be requiered
|
|
||||||
cookie = {
|
|
||||||
#'vuid':'918282893.1027205400'
|
|
||||||
# 'ab_bs':'%7B%223%22%3A279%7D'
|
|
||||||
'__utma': '00000000.000#0000000.0000000000.0000000000.0000000000.0'
|
|
||||||
# '__utmb':'18302654.1.10.1388942090'
|
|
||||||
#, '__utmc':'18302654'
|
|
||||||
#, '__utmz':'18#302654.1388942090.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)' # noqa
|
|
||||||
#, '__utml':'search'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
params['url'] = search_url.format(query=urlencode({'q': query}))
|
params['url'] = search_url.format(pageno=params['pageno'] ,
|
||||||
params['cookies'] = cookie
|
query=urlencode({'q': query}))
|
||||||
|
|
||||||
|
# TODO required?
|
||||||
|
params['cookies']['__utma'] = '00000000.000#0000000.0000000000.0000000000.0000000000.0'
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
dom = html.fromstring(resp.text)
|
dom = html.fromstring(resp.text)
|
||||||
|
|
||||||
p = HTMLParser()
|
p = HTMLParser()
|
||||||
|
|
||||||
|
# parse results
|
||||||
for result in dom.xpath(results_xpath):
|
for result in dom.xpath(results_xpath):
|
||||||
url = base_url + result.xpath(url_xpath)[0]
|
url = base_url + result.xpath(url_xpath)[0]
|
||||||
title = p.unescape(extract_text(result.xpath(title_xpath)))
|
title = p.unescape(extract_text(result.xpath(title_xpath)))
|
||||||
|
@ -45,10 +60,13 @@ def response(resp):
|
||||||
publishedDate = parser.parse(extract_text(
|
publishedDate = parser.parse(extract_text(
|
||||||
result.xpath(publishedDate_xpath)[0]))
|
result.xpath(publishedDate_xpath)[0]))
|
||||||
|
|
||||||
|
# append result
|
||||||
results.append({'url': url,
|
results.append({'url': url,
|
||||||
'title': title,
|
'title': title,
|
||||||
'content': content_tpl.format(url, title, thumbnail),
|
'content': '',
|
||||||
'template': 'videos.html',
|
'template': 'videos.html',
|
||||||
'publishedDate': publishedDate,
|
'publishedDate': publishedDate,
|
||||||
'thumbnail': thumbnail})
|
'thumbnail': thumbnail})
|
||||||
|
|
||||||
|
# return results
|
||||||
return results
|
return results
|
||||||
|
|
|
@ -0,0 +1,238 @@
|
||||||
|
import json
|
||||||
|
from requests import get
|
||||||
|
from urllib import urlencode
|
||||||
|
|
||||||
|
resultCount=1
|
||||||
|
urlSearch = 'https://www.wikidata.org/w/api.php?action=query&list=search&format=json&srnamespace=0&srprop=sectiontitle&{query}'
|
||||||
|
urlDetail = 'https://www.wikidata.org/w/api.php?action=wbgetentities&format=json&props=labels%7Cinfo%7Csitelinks%7Csitelinks%2Furls%7Cdescriptions%7Cclaims&{query}'
|
||||||
|
urlMap = 'https://www.openstreetmap.org/?lat={latitude}&lon={longitude}&zoom={zoom}&layers=M'
|
||||||
|
|
||||||
|
def request(query, params):
|
||||||
|
params['url'] = urlSearch.format(query=urlencode({'srsearch': query, 'srlimit': resultCount}))
|
||||||
|
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', ''))
|
||||||
|
|
||||||
|
language = resp.search_params['language'].split('_')[0]
|
||||||
|
if language == 'all':
|
||||||
|
language = 'en'
|
||||||
|
url = urlDetail.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)
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def getDetail(jsonresponse, wikidata_id, language):
|
||||||
|
results = []
|
||||||
|
urls = []
|
||||||
|
attributes = []
|
||||||
|
|
||||||
|
result = jsonresponse.get('entities', {}).get(wikidata_id, {})
|
||||||
|
|
||||||
|
title = result.get('labels', {}).get(language, {}).get('value', None)
|
||||||
|
if title == None:
|
||||||
|
title = result.get('labels', {}).get('en', {}).get('value', None)
|
||||||
|
if title == None:
|
||||||
|
return results
|
||||||
|
|
||||||
|
description = result.get('descriptions', {}).get(language, {}).get('value', None)
|
||||||
|
if description == None:
|
||||||
|
description = result.get('descriptions', {}).get('en', {}).get('value', '')
|
||||||
|
|
||||||
|
claims = result.get('claims', {})
|
||||||
|
official_website = get_string(claims, 'P856', None)
|
||||||
|
if official_website != None:
|
||||||
|
urls.append({ 'title' : 'Official site', 'url': official_website })
|
||||||
|
results.append({ 'title': title, 'url' : official_website })
|
||||||
|
|
||||||
|
wikipedia_link_count = 0
|
||||||
|
if language != 'en':
|
||||||
|
wikipedia_link_count += add_url(urls, 'Wikipedia (' + language + ')', get_wikilink(result, language + 'wiki'))
|
||||||
|
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 != None:
|
||||||
|
add_url(urls, 'Wikipedia (' + misc_language + ')', get_wikilink(result, misc_language + 'wiki'))
|
||||||
|
|
||||||
|
if language != 'en':
|
||||||
|
add_url(urls, 'Wiki voyage (' + language + ')', get_wikilink(result, language + 'wikivoyage'))
|
||||||
|
add_url(urls, 'Wiki voyage (en)', get_wikilink(result, 'enwikivoyage'))
|
||||||
|
|
||||||
|
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 != None:
|
||||||
|
add_url(urls, 'MusicBrainz', 'http://musicbrainz.org/work/' + musicbrainz_work_id)
|
||||||
|
|
||||||
|
musicbrainz_artist_id = get_string(claims, 'P434')
|
||||||
|
if musicbrainz_artist_id != 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 != 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 != 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 != None:
|
||||||
|
attributes.append({'label' : 'Postal code(s)', 'value' : postal_code})
|
||||||
|
|
||||||
|
date_of_birth = get_time(claims, 'P569', None)
|
||||||
|
if date_of_birth != None:
|
||||||
|
attributes.append({'label' : 'Date of birth', 'value' : date_of_birth})
|
||||||
|
|
||||||
|
date_of_death = get_time(claims, 'P570', None)
|
||||||
|
if date_of_death != None:
|
||||||
|
attributes.append({'label' : 'Date of death', 'value' : date_of_death})
|
||||||
|
|
||||||
|
if len(attributes)==0 and len(urls)==2 and len(description)==0:
|
||||||
|
results.append({
|
||||||
|
'url': urls[0]['url'],
|
||||||
|
'title': title,
|
||||||
|
'content': description
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
results.append({
|
||||||
|
'infobox' : title,
|
||||||
|
'id' : wikipedia_en_link,
|
||||||
|
'content' : description,
|
||||||
|
'attributes' : attributes,
|
||||||
|
'urls' : urls
|
||||||
|
})
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def add_url(urls, title, url):
|
||||||
|
if url != None:
|
||||||
|
urls.append({'title' : title, 'url' : url})
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def get_mainsnak(claims, propertyName):
|
||||||
|
propValue = claims.get(propertyName, {})
|
||||||
|
if len(propValue) == 0:
|
||||||
|
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 != 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, 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 != None:
|
||||||
|
value = datavalue.get('value', '')
|
||||||
|
result.append(value.get('time', ''))
|
||||||
|
|
||||||
|
if len(result) == 0:
|
||||||
|
return defaultValue
|
||||||
|
else:
|
||||||
|
return ', '.join(result)
|
||||||
|
|
||||||
|
|
||||||
|
def get_geolink(claims, propertyName, defaultValue=''):
|
||||||
|
mainsnak = get_mainsnak(claims, propertyName)
|
||||||
|
|
||||||
|
if mainsnak == 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)
|
||||||
|
|
||||||
|
# there is no zoom information, deduce from precision (error prone)
|
||||||
|
# samples :
|
||||||
|
# 13 --> 5
|
||||||
|
# 1 --> 6
|
||||||
|
# 0.016666666666667 --> 9
|
||||||
|
# 0.00027777777777778 --> 19
|
||||||
|
# wolframalpha : quadratic fit { {13, 5}, {1, 6}, {0.0166666, 9}, {0.0002777777,19}}
|
||||||
|
# 14.1186-8.8322 x+0.625447 x^2
|
||||||
|
if precision < 0.0003:
|
||||||
|
zoom = 19
|
||||||
|
else:
|
||||||
|
zoom = int(15 - precision*8.8322 + precision*precision*0.625447)
|
||||||
|
|
||||||
|
url = urlMap.replace('{latitude}', str(value.get('latitude',0))).replace('{longitude}', str(value.get('longitude',0))).replace('{zoom}', str(zoom))
|
||||||
|
|
||||||
|
return url
|
||||||
|
|
||||||
|
|
||||||
|
def get_wikilink(result, wikiid):
|
||||||
|
url = result.get('sitelinks', {}).get(wikiid, {}).get('url', None)
|
||||||
|
if url == None:
|
||||||
|
return url
|
||||||
|
elif 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
|
|
@ -1,40 +1,89 @@
|
||||||
|
## Yacy (Web, Images, Videos, Music, Files)
|
||||||
|
#
|
||||||
|
# @website http://yacy.net
|
||||||
|
# @provide-api yes (http://www.yacy-websuche.de/wiki/index.php/Dev:APIyacysearch)
|
||||||
|
#
|
||||||
|
# @using-api yes
|
||||||
|
# @results JSON
|
||||||
|
# @stable yes
|
||||||
|
# @parse (general) url, title, content, publishedDate
|
||||||
|
# @parse (images) url, title, img_src
|
||||||
|
#
|
||||||
|
# @todo parse video, audio and file results
|
||||||
|
|
||||||
from json import loads
|
from json import loads
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
|
from dateutil import parser
|
||||||
|
|
||||||
url = 'http://localhost:8090'
|
# engine dependent config
|
||||||
search_url = '/yacysearch.json?{query}&maximumRecords=10'
|
categories = ['general', 'images'] #TODO , 'music', 'videos', 'files'
|
||||||
|
paging = True
|
||||||
|
language_support = True
|
||||||
|
number_of_results = 5
|
||||||
|
|
||||||
|
# search-url
|
||||||
|
base_url = 'http://localhost:8090'
|
||||||
|
search_url = '/yacysearch.json?{query}&startRecord={offset}&maximumRecords={limit}&contentdom={search_type}&resource=global'
|
||||||
|
|
||||||
|
# yacy specific type-definitions
|
||||||
|
search_types = {'general': 'text',
|
||||||
|
'images': 'image',
|
||||||
|
'files': 'app',
|
||||||
|
'music': 'audio',
|
||||||
|
'videos': 'video'}
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
params['url'] = url + search_url.format(query=urlencode({'query': query}))
|
offset = (params['pageno'] - 1) * number_of_results
|
||||||
|
search_type = search_types.get(params['category'], '0')
|
||||||
|
|
||||||
|
params['url'] = base_url + search_url.format(query=urlencode({'query': query}),
|
||||||
|
offset=offset,
|
||||||
|
limit=number_of_results,
|
||||||
|
search_type=search_type)
|
||||||
|
|
||||||
|
# add language tag if specified
|
||||||
|
if params['language'] != 'all':
|
||||||
|
params['url'] += '&lr=lang_' + params['language'].split('_')[0]
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
|
results = []
|
||||||
|
|
||||||
raw_search_results = loads(resp.text)
|
raw_search_results = loads(resp.text)
|
||||||
|
|
||||||
|
# return empty array if there are no results
|
||||||
if not raw_search_results:
|
if not raw_search_results:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
search_results = raw_search_results.get('channels', {})[0].get('items', [])
|
search_results = raw_search_results.get('channels', {})[0].get('items', [])
|
||||||
|
|
||||||
results = []
|
if resp.search_params['category'] == 'general':
|
||||||
|
# parse general results
|
||||||
|
for result in search_results:
|
||||||
|
publishedDate = parser.parse(result['pubDate'])
|
||||||
|
|
||||||
for result in search_results:
|
# append result
|
||||||
tmp_result = {}
|
results.append({'url': result['link'],
|
||||||
tmp_result['title'] = result['title']
|
'title': result['title'],
|
||||||
tmp_result['url'] = result['link']
|
'content': result['description'],
|
||||||
tmp_result['content'] = ''
|
'publishedDate': publishedDate})
|
||||||
|
|
||||||
if result['description']:
|
elif resp.search_params['category'] == 'images':
|
||||||
tmp_result['content'] += result['description'] + "<br/>"
|
# parse image results
|
||||||
|
for result in search_results:
|
||||||
|
# append result
|
||||||
|
results.append({'url': result['url'],
|
||||||
|
'title': result['title'],
|
||||||
|
'content': '',
|
||||||
|
'img_src': result['image'],
|
||||||
|
'template': 'images.html'})
|
||||||
|
|
||||||
if result['pubDate']:
|
#TODO parse video, audio and file results
|
||||||
tmp_result['content'] += result['pubDate'] + "<br/>"
|
|
||||||
|
|
||||||
if result['size'] != '-1':
|
|
||||||
tmp_result['content'] += result['sizename']
|
|
||||||
|
|
||||||
results.append(tmp_result)
|
|
||||||
|
|
||||||
|
# return results
|
||||||
return results
|
return results
|
||||||
|
|
|
@ -1,64 +1,101 @@
|
||||||
#!/usr/bin/env python
|
## Yahoo (Web)
|
||||||
|
#
|
||||||
|
# @website https://search.yahoo.com/web
|
||||||
|
# @provide-api yes (https://developer.yahoo.com/boss/search/), $0.80/1000 queries
|
||||||
|
#
|
||||||
|
# @using-api no (because pricing)
|
||||||
|
# @results HTML (using search portal)
|
||||||
|
# @stable no (HTML can change)
|
||||||
|
# @parse url, title, content, suggestion
|
||||||
|
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from urlparse import unquote
|
from urlparse import unquote
|
||||||
from lxml import html
|
from lxml import html
|
||||||
from searx.engines.xpath import extract_text, extract_url
|
from searx.engines.xpath import extract_text, extract_url
|
||||||
|
|
||||||
|
# engine dependent config
|
||||||
categories = ['general']
|
categories = ['general']
|
||||||
search_url = 'http://search.yahoo.com/search?{query}&b={offset}'
|
paging = True
|
||||||
|
language_support = True
|
||||||
|
|
||||||
|
# search-url
|
||||||
|
search_url = 'https://search.yahoo.com/search?{query}&b={offset}&fl=1&vl=lang_{lang}'
|
||||||
|
|
||||||
|
# specific xpath variables
|
||||||
results_xpath = '//div[@class="res"]'
|
results_xpath = '//div[@class="res"]'
|
||||||
url_xpath = './/h3/a/@href'
|
url_xpath = './/h3/a/@href'
|
||||||
title_xpath = './/h3/a'
|
title_xpath = './/h3/a'
|
||||||
content_xpath = './/div[@class="abstr"]'
|
content_xpath = './/div[@class="abstr"]'
|
||||||
suggestion_xpath = '//div[@id="satat"]//a'
|
suggestion_xpath = '//div[@id="satat"]//a'
|
||||||
|
|
||||||
paging = True
|
|
||||||
|
|
||||||
|
|
||||||
|
# remove yahoo-specific tracking-url
|
||||||
def parse_url(url_string):
|
def parse_url(url_string):
|
||||||
endings = ['/RS', '/RK']
|
endings = ['/RS', '/RK']
|
||||||
endpositions = []
|
endpositions = []
|
||||||
start = url_string.find('http', url_string.find('/RU=')+1)
|
start = url_string.find('http', url_string.find('/RU=')+1)
|
||||||
|
|
||||||
for ending in endings:
|
for ending in endings:
|
||||||
endpos = url_string.rfind(ending)
|
endpos = url_string.rfind(ending)
|
||||||
if endpos > -1:
|
if endpos > -1:
|
||||||
endpositions.append(endpos)
|
endpositions.append(endpos)
|
||||||
|
|
||||||
end = min(endpositions)
|
if start==0 or len(endpositions) == 0:
|
||||||
return unquote(url_string[start:end])
|
return url_string
|
||||||
|
else:
|
||||||
|
end = min(endpositions)
|
||||||
|
return unquote(url_string[start:end])
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
offset = (params['pageno'] - 1) * 10 + 1
|
offset = (params['pageno'] - 1) * 10 + 1
|
||||||
|
|
||||||
if params['language'] == 'all':
|
if params['language'] == 'all':
|
||||||
language = 'en'
|
language = 'en'
|
||||||
else:
|
else:
|
||||||
language = params['language'].split('_')[0]
|
language = params['language'].split('_')[0]
|
||||||
|
|
||||||
params['url'] = search_url.format(offset=offset,
|
params['url'] = search_url.format(offset=offset,
|
||||||
query=urlencode({'p': query}))
|
query=urlencode({'p': query}),
|
||||||
|
lang=language)
|
||||||
|
|
||||||
|
# TODO required?
|
||||||
params['cookies']['sB'] = 'fl=1&vl=lang_{lang}&sh=1&rw=new&v=1'\
|
params['cookies']['sB'] = 'fl=1&vl=lang_{lang}&sh=1&rw=new&v=1'\
|
||||||
.format(lang=language)
|
.format(lang=language)
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
dom = html.fromstring(resp.text)
|
dom = html.fromstring(resp.text)
|
||||||
|
|
||||||
|
# parse results
|
||||||
for result in dom.xpath(results_xpath):
|
for result in dom.xpath(results_xpath):
|
||||||
try:
|
try:
|
||||||
url = parse_url(extract_url(result.xpath(url_xpath), search_url))
|
url = parse_url(extract_url(result.xpath(url_xpath), search_url))
|
||||||
title = extract_text(result.xpath(title_xpath)[0])
|
title = extract_text(result.xpath(title_xpath)[0])
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
content = extract_text(result.xpath(content_xpath)[0])
|
|
||||||
results.append({'url': url, 'title': title, 'content': content})
|
|
||||||
|
|
||||||
|
content = extract_text(result.xpath(content_xpath)[0])
|
||||||
|
|
||||||
|
# append result
|
||||||
|
results.append({'url': url,
|
||||||
|
'title': title,
|
||||||
|
'content': content})
|
||||||
|
|
||||||
|
# if no suggestion found, return results
|
||||||
if not suggestion_xpath:
|
if not suggestion_xpath:
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
# parse suggestion
|
||||||
for suggestion in dom.xpath(suggestion_xpath):
|
for suggestion in dom.xpath(suggestion_xpath):
|
||||||
|
# append suggestion
|
||||||
results.append({'suggestion': extract_text(suggestion)})
|
results.append({'suggestion': extract_text(suggestion)})
|
||||||
|
|
||||||
|
# return results
|
||||||
return results
|
return results
|
||||||
|
|
|
@ -1,4 +1,13 @@
|
||||||
#!/usr/bin/env python
|
# Yahoo (News)
|
||||||
|
#
|
||||||
|
# @website https://news.yahoo.com
|
||||||
|
# @provide-api yes (https://developer.yahoo.com/boss/search/)
|
||||||
|
# $0.80/1000 queries
|
||||||
|
#
|
||||||
|
# @using-api no (because pricing)
|
||||||
|
# @results HTML (using search portal)
|
||||||
|
# @stable no (HTML can change)
|
||||||
|
# @parse url, title, content, publishedDate
|
||||||
|
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from lxml import html
|
from lxml import html
|
||||||
|
@ -8,8 +17,15 @@ from datetime import datetime, timedelta
|
||||||
import re
|
import re
|
||||||
from dateutil import parser
|
from dateutil import parser
|
||||||
|
|
||||||
|
# engine dependent config
|
||||||
categories = ['news']
|
categories = ['news']
|
||||||
search_url = 'http://news.search.yahoo.com/search?{query}&b={offset}'
|
paging = True
|
||||||
|
language_support = True
|
||||||
|
|
||||||
|
# search-url
|
||||||
|
search_url = 'https://news.search.yahoo.com/search?{query}&b={offset}&fl=1&vl=lang_{lang}' # noqa
|
||||||
|
|
||||||
|
# specific xpath variables
|
||||||
results_xpath = '//div[@class="res"]'
|
results_xpath = '//div[@class="res"]'
|
||||||
url_xpath = './/h3/a/@href'
|
url_xpath = './/h3/a/@href'
|
||||||
title_xpath = './/h3/a'
|
title_xpath = './/h3/a'
|
||||||
|
@ -17,30 +33,39 @@ content_xpath = './/div[@class="abstr"]'
|
||||||
publishedDate_xpath = './/span[@class="timestamp"]'
|
publishedDate_xpath = './/span[@class="timestamp"]'
|
||||||
suggestion_xpath = '//div[@id="satat"]//a'
|
suggestion_xpath = '//div[@id="satat"]//a'
|
||||||
|
|
||||||
paging = True
|
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
offset = (params['pageno'] - 1) * 10 + 1
|
offset = (params['pageno'] - 1) * 10 + 1
|
||||||
|
|
||||||
if params['language'] == 'all':
|
if params['language'] == 'all':
|
||||||
language = 'en'
|
language = 'en'
|
||||||
else:
|
else:
|
||||||
language = params['language'].split('_')[0]
|
language = params['language'].split('_')[0]
|
||||||
|
|
||||||
params['url'] = search_url.format(offset=offset,
|
params['url'] = search_url.format(offset=offset,
|
||||||
query=urlencode({'p': query}))
|
query=urlencode({'p': query}),
|
||||||
|
lang=language)
|
||||||
|
|
||||||
|
# TODO required?
|
||||||
params['cookies']['sB'] = 'fl=1&vl=lang_{lang}&sh=1&rw=new&v=1'\
|
params['cookies']['sB'] = 'fl=1&vl=lang_{lang}&sh=1&rw=new&v=1'\
|
||||||
.format(lang=language)
|
.format(lang=language)
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
dom = html.fromstring(resp.text)
|
dom = html.fromstring(resp.text)
|
||||||
|
|
||||||
|
# parse results
|
||||||
for result in dom.xpath(results_xpath):
|
for result in dom.xpath(results_xpath):
|
||||||
url = parse_url(extract_url(result.xpath(url_xpath), search_url))
|
url = parse_url(extract_url(result.xpath(url_xpath), search_url))
|
||||||
title = extract_text(result.xpath(title_xpath)[0])
|
title = extract_text(result.xpath(title_xpath)[0])
|
||||||
content = extract_text(result.xpath(content_xpath)[0])
|
content = extract_text(result.xpath(content_xpath)[0])
|
||||||
|
|
||||||
|
# parse publishedDate
|
||||||
publishedDate = extract_text(result.xpath(publishedDate_xpath)[0])
|
publishedDate = extract_text(result.xpath(publishedDate_xpath)[0])
|
||||||
|
|
||||||
if re.match("^[0-9]+ minute(s|) ago$", publishedDate):
|
if re.match("^[0-9]+ minute(s|) ago$", publishedDate):
|
||||||
|
@ -58,15 +83,11 @@ def response(resp):
|
||||||
if publishedDate.year == 1900:
|
if publishedDate.year == 1900:
|
||||||
publishedDate = publishedDate.replace(year=datetime.now().year)
|
publishedDate = publishedDate.replace(year=datetime.now().year)
|
||||||
|
|
||||||
|
# append result
|
||||||
results.append({'url': url,
|
results.append({'url': url,
|
||||||
'title': title,
|
'title': title,
|
||||||
'content': content,
|
'content': content,
|
||||||
'publishedDate': publishedDate})
|
'publishedDate': publishedDate})
|
||||||
|
|
||||||
if not suggestion_xpath:
|
# return results
|
||||||
return results
|
|
||||||
|
|
||||||
for suggestion in dom.xpath(suggestion_xpath):
|
|
||||||
results.append({'suggestion': extract_text(suggestion)})
|
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
|
@ -1,54 +1,78 @@
|
||||||
|
## Youtube (Videos)
|
||||||
|
#
|
||||||
|
# @website https://www.youtube.com/
|
||||||
|
# @provide-api yes (http://gdata-samples-youtube-search-py.appspot.com/)
|
||||||
|
#
|
||||||
|
# @using-api yes
|
||||||
|
# @results JSON
|
||||||
|
# @stable yes
|
||||||
|
# @parse url, title, content, publishedDate, thumbnail
|
||||||
|
|
||||||
from json import loads
|
from json import loads
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from dateutil import parser
|
from dateutil import parser
|
||||||
|
|
||||||
categories = ['videos']
|
# engine dependent config
|
||||||
|
categories = ['videos', 'music']
|
||||||
search_url = ('https://gdata.youtube.com/feeds/api/videos'
|
|
||||||
'?alt=json&{query}&start-index={index}&max-results=25') # noqa
|
|
||||||
|
|
||||||
paging = True
|
paging = True
|
||||||
|
language_support = True
|
||||||
|
|
||||||
|
# search-url
|
||||||
|
base_url = 'https://gdata.youtube.com/feeds/api/videos'
|
||||||
|
search_url = base_url + '?alt=json&{query}&start-index={index}&max-results=5' # noqa
|
||||||
|
|
||||||
|
|
||||||
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
index = (params['pageno'] - 1) * 25 + 1
|
index = (params['pageno'] - 1) * 5 + 1
|
||||||
|
|
||||||
params['url'] = search_url.format(query=urlencode({'q': query}),
|
params['url'] = search_url.format(query=urlencode({'q': query}),
|
||||||
index=index)
|
index=index)
|
||||||
|
|
||||||
|
# add language tag if specified
|
||||||
|
if params['language'] != 'all':
|
||||||
|
params['url'] += '&lr=' + params['language'].split('_')[0]
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
# get response from search-request
|
||||||
def response(resp):
|
def response(resp):
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
search_results = loads(resp.text)
|
search_results = loads(resp.text)
|
||||||
|
|
||||||
|
# return empty array if there are no results
|
||||||
if not 'feed' in search_results:
|
if not 'feed' in search_results:
|
||||||
return results
|
return []
|
||||||
|
|
||||||
feed = search_results['feed']
|
feed = search_results['feed']
|
||||||
|
|
||||||
|
# parse results
|
||||||
for result in feed['entry']:
|
for result in feed['entry']:
|
||||||
url = [x['href'] for x in result['link'] if x['type'] == 'text/html']
|
url = [x['href'] for x in result['link'] if x['type'] == 'text/html']
|
||||||
|
|
||||||
if not url:
|
if not url:
|
||||||
return
|
return
|
||||||
|
|
||||||
# remove tracking
|
# remove tracking
|
||||||
url = url[0].replace('feature=youtube_gdata', '')
|
url = url[0].replace('feature=youtube_gdata', '')
|
||||||
if url.endswith('&'):
|
if url.endswith('&'):
|
||||||
url = url[:-1]
|
url = url[:-1]
|
||||||
|
|
||||||
title = result['title']['$t']
|
title = result['title']['$t']
|
||||||
content = ''
|
content = ''
|
||||||
thumbnail = ''
|
thumbnail = ''
|
||||||
|
|
||||||
#"2013-12-31T15:22:51.000Z"
|
|
||||||
pubdate = result['published']['$t']
|
pubdate = result['published']['$t']
|
||||||
publishedDate = parser.parse(pubdate)
|
publishedDate = parser.parse(pubdate)
|
||||||
|
|
||||||
if result['media$group']['media$thumbnail']:
|
if result['media$group']['media$thumbnail']:
|
||||||
thumbnail = result['media$group']['media$thumbnail'][0]['url']
|
thumbnail = result['media$group']['media$thumbnail'][0]['url']
|
||||||
content += '<a href="{0}" title="{0}" ><img src="{1}" /></a>'.format(url, thumbnail) # noqa
|
|
||||||
|
|
||||||
if content:
|
content = result['content']['$t']
|
||||||
content += '<br />' + result['content']['$t']
|
|
||||||
else:
|
|
||||||
content = result['content']['$t']
|
|
||||||
|
|
||||||
|
# append result
|
||||||
results.append({'url': url,
|
results.append({'url': url,
|
||||||
'title': title,
|
'title': title,
|
||||||
'content': content,
|
'content': content,
|
||||||
|
@ -56,4 +80,5 @@ def response(resp):
|
||||||
'publishedDate': publishedDate,
|
'publishedDate': publishedDate,
|
||||||
'thumbnail': thumbnail})
|
'thumbnail': thumbnail})
|
||||||
|
|
||||||
|
# return results
|
||||||
return results
|
return results
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
'''
|
||||||
|
searx is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
searx is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with searx. If not, see < http://www.gnu.org/licenses/ >.
|
||||||
|
|
||||||
|
(C) 2013- by Adam Tauber, <asciimoo@gmail.com>
|
||||||
|
'''
|
||||||
|
|
||||||
|
import re
|
||||||
|
from lxml import etree
|
||||||
|
from os import listdir
|
||||||
|
from os.path import isfile, isdir, join
|
||||||
|
|
||||||
|
|
||||||
|
# https://gitweb.torproject.org/\
|
||||||
|
# pde/https-everywhere.git/tree/4.0:/src/chrome/content/rules
|
||||||
|
|
||||||
|
# HTTPS rewrite rules
|
||||||
|
https_rules = []
|
||||||
|
|
||||||
|
|
||||||
|
# load single ruleset from a xml file
|
||||||
|
def load_single_https_ruleset(filepath):
|
||||||
|
ruleset = ()
|
||||||
|
|
||||||
|
# init parser
|
||||||
|
parser = etree.XMLParser()
|
||||||
|
|
||||||
|
# load and parse xml-file
|
||||||
|
try:
|
||||||
|
tree = etree.parse(filepath, parser)
|
||||||
|
except:
|
||||||
|
# TODO, error message
|
||||||
|
return ()
|
||||||
|
|
||||||
|
# get root node
|
||||||
|
root = tree.getroot()
|
||||||
|
|
||||||
|
# check if root is a node with the name ruleset
|
||||||
|
# TODO improve parsing
|
||||||
|
if root.tag != 'ruleset':
|
||||||
|
return ()
|
||||||
|
|
||||||
|
# check if rule is deactivated by default
|
||||||
|
if root.attrib.get('default_off'):
|
||||||
|
return ()
|
||||||
|
|
||||||
|
# check if rule does only work for specific platforms
|
||||||
|
if root.attrib.get('platform'):
|
||||||
|
return ()
|
||||||
|
|
||||||
|
hosts = []
|
||||||
|
rules = []
|
||||||
|
exclusions = []
|
||||||
|
|
||||||
|
# parse childs from ruleset
|
||||||
|
for ruleset in root:
|
||||||
|
# this child define a target
|
||||||
|
if ruleset.tag == 'target':
|
||||||
|
# check if required tags available
|
||||||
|
if not ruleset.attrib.get('host'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# convert host-rule to valid regex
|
||||||
|
host = ruleset.attrib.get('host')\
|
||||||
|
.replace('.', '\.').replace('*', '.*')
|
||||||
|
|
||||||
|
# append to host list
|
||||||
|
hosts.append(host)
|
||||||
|
|
||||||
|
# this child define a rule
|
||||||
|
elif ruleset.tag == 'rule':
|
||||||
|
# check if required tags available
|
||||||
|
if not ruleset.attrib.get('from')\
|
||||||
|
or not ruleset.attrib.get('to'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# TODO hack, which convert a javascript regex group
|
||||||
|
# into a valid python regex group
|
||||||
|
rule_from = ruleset.attrib.get('from').replace('$', '\\')
|
||||||
|
rule_to = ruleset.attrib.get('to').replace('$', '\\')
|
||||||
|
|
||||||
|
# TODO, not working yet because of the hack above,
|
||||||
|
# currently doing that in webapp.py
|
||||||
|
# rule_from_rgx = re.compile(rule_from, re.I)
|
||||||
|
|
||||||
|
# append rule
|
||||||
|
rules.append((rule_from, rule_to))
|
||||||
|
|
||||||
|
# this child define an exclusion
|
||||||
|
elif ruleset.tag == 'exclusion':
|
||||||
|
# check if required tags available
|
||||||
|
if not ruleset.attrib.get('pattern'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
exclusion_rgx = re.compile(ruleset.attrib.get('pattern'))
|
||||||
|
|
||||||
|
# append exclusion
|
||||||
|
exclusions.append(exclusion_rgx)
|
||||||
|
|
||||||
|
# convert list of possible hosts to a simple regex
|
||||||
|
# TODO compress regex to improve performance
|
||||||
|
try:
|
||||||
|
target_hosts = re.compile('^(' + '|'.join(hosts) + ')', re.I | re.U)
|
||||||
|
except:
|
||||||
|
return ()
|
||||||
|
|
||||||
|
# return ruleset
|
||||||
|
return (target_hosts, rules, exclusions)
|
||||||
|
|
||||||
|
|
||||||
|
# load all https rewrite rules
|
||||||
|
def load_https_rules(rules_path):
|
||||||
|
# check if directory exists
|
||||||
|
if not isdir(rules_path):
|
||||||
|
print("[E] directory not found: '" + rules_path + "'")
|
||||||
|
return
|
||||||
|
|
||||||
|
# search all xml files which are stored in the https rule directory
|
||||||
|
xml_files = [join(rules_path, f)
|
||||||
|
for f in listdir(rules_path)
|
||||||
|
if isfile(join(rules_path, f)) and f[-4:] == '.xml']
|
||||||
|
|
||||||
|
# load xml-files
|
||||||
|
for ruleset_file in xml_files:
|
||||||
|
# calculate rewrite-rules
|
||||||
|
ruleset = load_single_https_ruleset(ruleset_file)
|
||||||
|
|
||||||
|
# skip if no ruleset returned
|
||||||
|
if not ruleset:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# append ruleset
|
||||||
|
https_rules.append(ruleset)
|
||||||
|
|
||||||
|
print(' * {n} https-rules loaded'.format(n=len(https_rules)))
|
|
@ -0,0 +1,17 @@
|
||||||
|
<!--
|
||||||
|
This directory contains web site rewriting rules for the
|
||||||
|
HTTPS Everywhere software, available from
|
||||||
|
https://www.eff.org/https-everywhere
|
||||||
|
|
||||||
|
These rules were contributed to the project by users and aim to
|
||||||
|
enable routine secure access to as many different web sites as
|
||||||
|
possible. They are automatically installed together with the
|
||||||
|
HTTPS Everywhere software. The presence of these rules does not
|
||||||
|
mean that an HTTPS Everywhere user accessed, or intended to
|
||||||
|
access, any particular web site.
|
||||||
|
|
||||||
|
For information about how to create additional HTTPS Everywhere
|
||||||
|
rewriting rules to add support for new sites, please see
|
||||||
|
|
||||||
|
https://www.eff.org/https-everywhere/rulesets
|
||||||
|
-->
|
|
@ -0,0 +1,56 @@
|
||||||
|
<!--
|
||||||
|
For other Microsoft coverage, see Microsoft.xml.
|
||||||
|
|
||||||
|
|
||||||
|
CDN buckets:
|
||||||
|
|
||||||
|
- a134.lm.akamai.net
|
||||||
|
|
||||||
|
- akam.bing.com
|
||||||
|
- *.mm.bing.net
|
||||||
|
|
||||||
|
|
||||||
|
Nonfunctional domains:
|
||||||
|
|
||||||
|
- m2.cn.bing.com
|
||||||
|
- origin.bj1.bing.com
|
||||||
|
- blogs.bing.com
|
||||||
|
|
||||||
|
|
||||||
|
Fully covered domains:
|
||||||
|
|
||||||
|
- bing.com subdomains:
|
||||||
|
|
||||||
|
- (www.)
|
||||||
|
- c.bing (tracking beacons)
|
||||||
|
- cn.bing
|
||||||
|
- h.bing
|
||||||
|
- ssl
|
||||||
|
- testfamilysafety.bing
|
||||||
|
- udc.bing
|
||||||
|
- (www.)bing
|
||||||
|
|
||||||
|
- *.mm.bing.net
|
||||||
|
- api.bing.com
|
||||||
|
|
||||||
|
-->
|
||||||
|
<ruleset name="Bing">
|
||||||
|
|
||||||
|
<target host="bing.com" />
|
||||||
|
<target host="*.bing.com" />
|
||||||
|
<target host="*.mm.bing.net" />
|
||||||
|
|
||||||
|
|
||||||
|
<securecookie host=".*\.bing\.com$" name=".+" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://((?:c|cn|h|ssl|testfamilysafety|udc|www)\.)?bing\.com/"
|
||||||
|
to="https://$1bing.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://([^/:@]*)\.mm\.bing\.net/"
|
||||||
|
to="https://$1.mm.bing.com/"/>
|
||||||
|
|
||||||
|
<rule from="^http://([^/:@]*)\.api\.bing\.net/"
|
||||||
|
to="https://$1.api.bing.com/"/>
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,69 @@
|
||||||
|
<!--
|
||||||
|
Nonfunctional domains:
|
||||||
|
|
||||||
|
- blog.dailymotion.com
|
||||||
|
- press.dailymotion.com (shows steaw.com, CN: www.steaw.com)
|
||||||
|
- proxy-46.dailymotion.com
|
||||||
|
- publicite.dailymotion.com
|
||||||
|
- publisher.dailymotion.com (reset)
|
||||||
|
- vid.ak.dmcdn.net (403, Akamai)
|
||||||
|
- vid2.ak.dmcdn.net (504, akamai)
|
||||||
|
|
||||||
|
|
||||||
|
Problematic domains:
|
||||||
|
|
||||||
|
- ak2.static.dailymotion.com (mismatched, CN: *.dmcdn.net)
|
||||||
|
- support.dmcloud.net (mismatched, CN: *.zendesk.com)
|
||||||
|
|
||||||
|
|
||||||
|
Partially covered domains:
|
||||||
|
|
||||||
|
- (www.)dailymotion.com
|
||||||
|
|
||||||
|
- cdn/manifest/video/\w+.mnft 403s
|
||||||
|
- crossdomain.xml breaks videos
|
||||||
|
|
||||||
|
-->
|
||||||
|
<ruleset name="Dailymotion (default off)" default_off="breaks some embedded videos">
|
||||||
|
|
||||||
|
<target host="dailymotion.com" />
|
||||||
|
<!--
|
||||||
|
* for cross-domain cookie.
|
||||||
|
-->
|
||||||
|
<target host="*.dailymotion.com" />
|
||||||
|
<!--
|
||||||
|
https://mail1.eff.org/pipermail/https-everywhere-rules/2012-July/001241.html
|
||||||
|
-->
|
||||||
|
<exclusion pattern="^http://(?:www\.)?dailymotion\.com/(?:cdn/[\w-]+/video/|crossdomain\.xml$)" />
|
||||||
|
<target host="ak2.static.dailymotion.com" />
|
||||||
|
<target host="*.dmcdn.net" />
|
||||||
|
<target host="dmcloud.net" />
|
||||||
|
<target host="*.dmcloud.net" />
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Testing wrt embedded breakage.
|
||||||
|
|
||||||
|
securecookie host="^.*\.dailymotion\.com$" name=".+" /-->
|
||||||
|
<!--
|
||||||
|
Omniture tracking cookies:
|
||||||
|
-->
|
||||||
|
<securecookie host="^\.dailymotion\.com$" name="^s_\w+$" />
|
||||||
|
<securecookie host="^www\.dailymotion\.com$" name=".+" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://(erroracct\.|www\.)?dailymotion\.com/"
|
||||||
|
to="https://$1dailymotion.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://(s\d|static(?:\d|s\d-ssl))\.dmcdn\.net/"
|
||||||
|
to="https://$1.dmcdn.net/" />
|
||||||
|
|
||||||
|
<rule from="^https?://ak2\.static\.dailymotion\.com/"
|
||||||
|
to="https://static1-ssl.dmcdn.net/" />
|
||||||
|
|
||||||
|
<rule from="^http://(s\.|www\.)?dmcloud\.net/"
|
||||||
|
to="https://$1dmcloud.net/" />
|
||||||
|
|
||||||
|
<rule from="^https?://support\.dmcloud\.net/"
|
||||||
|
to="https://dmcloud.zendesk.com/" />
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,53 @@
|
||||||
|
<!--
|
||||||
|
For problematic rules, see Deviantart-mismatches.xml.
|
||||||
|
|
||||||
|
|
||||||
|
Other deviantArt rulesets:
|
||||||
|
|
||||||
|
- Sta.sh.xml
|
||||||
|
|
||||||
|
|
||||||
|
ToDo: Find edgecast URL for /(fc|th)\d+.
|
||||||
|
|
||||||
|
|
||||||
|
Mixed content:
|
||||||
|
|
||||||
|
- Images on *.....com from e.deviantart.net *
|
||||||
|
|
||||||
|
* Secured by us
|
||||||
|
|
||||||
|
-->
|
||||||
|
<ruleset name="DeviantArt (pending)" default_off="site operator says not ready yet">
|
||||||
|
|
||||||
|
<target host="deviantart.com" />
|
||||||
|
<target host="*.deviantart.com" />
|
||||||
|
<target host="deviantart.net" />
|
||||||
|
<target host="*.deviantart.net" />
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Not secured by server:
|
||||||
|
-->
|
||||||
|
<!--securecookie host="^\.deviantart\.com$" name="^userinfo$" /-->
|
||||||
|
|
||||||
|
<securecookie host="^\.deviantart\.com$" name=".*" />
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Redirects from com to net, but does so successfully by itself.
|
||||||
|
-->
|
||||||
|
<rule from="^http://([aei]|fc\d\d|s[ht]|th\d\d)\.deviantart\.(com|net)/"
|
||||||
|
to="https://$1.deviantart.$2/" />
|
||||||
|
|
||||||
|
<!-- This handles everything that isn't in the first rule.
|
||||||
|
Namely, usernames, backend, fc, th, and (www.).
|
||||||
|
These domains present a cert that is only
|
||||||
|
valid for .com.
|
||||||
|
Note that .net isn't used on DA, but.net does
|
||||||
|
redirect to .com, and we shouldn't break what would
|
||||||
|
otherwise work.
|
||||||
|
Mustn't rewrite from https here, as doing so
|
||||||
|
would conflict with the first rule.
|
||||||
|
-->
|
||||||
|
<rule from="^http://([^/:@\.]+\.)?deviantart\.(?:com|net)/"
|
||||||
|
to="https://$1deviantart.com/" />
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,38 @@
|
||||||
|
<!--
|
||||||
|
Problematic domains:
|
||||||
|
|
||||||
|
- www.dukgo.com (mismatched, CN: dukgo.com)
|
||||||
|
|
||||||
|
|
||||||
|
Fully covered domains:
|
||||||
|
|
||||||
|
- (www.)dukgo.com (www → ^)
|
||||||
|
|
||||||
|
-->
|
||||||
|
<ruleset name="DuckDuckGo">
|
||||||
|
<target host="duckduckgo.com" />
|
||||||
|
<target host="*.duckduckgo.com" />
|
||||||
|
<target host="ddg.gg" />
|
||||||
|
<target host="duck.co" />
|
||||||
|
<target host="i.duck.co" />
|
||||||
|
<target host="dukgo.com" />
|
||||||
|
<target host="www.dukgo.com" />
|
||||||
|
|
||||||
|
<exclusion pattern="^http://(help|meme)\.duckduckgo\.com/" />
|
||||||
|
|
||||||
|
<securecookie host="^duck\.co$" name=".*"/>
|
||||||
|
|
||||||
|
<rule from="^http://duckduckgo\.com/" to="https://duckduckgo.com/"/>
|
||||||
|
<rule from="^http://([^/:@\.]+)\.duckduckgo\.com/" to="https://$1.duckduckgo.com/"/>
|
||||||
|
<!-- TODO: What does ddg.gg/foo do? Runs query foo, redirects to homepage, or error? -->
|
||||||
|
<rule from="^http://ddg\.gg/$" to="https://duckduckgo.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://duck\.co/" to="https://duck.co/" />
|
||||||
|
|
||||||
|
<rule from="^http://i\.duck\.co/"
|
||||||
|
to="https://duckduckgo.com/"/>
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?dukgo\.com/"
|
||||||
|
to="https://dukgo.com/" />
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,44 @@
|
||||||
|
<!--
|
||||||
|
For other Yahoo coverage, see Yahoo.xml.
|
||||||
|
|
||||||
|
|
||||||
|
These altnames don't exist:
|
||||||
|
|
||||||
|
- www.blog.flickr.net
|
||||||
|
- www.code.flickr.net
|
||||||
|
|
||||||
|
-->
|
||||||
|
<ruleset name="Flickr">
|
||||||
|
|
||||||
|
<target host="flic.kr" />
|
||||||
|
<target host="*.flic.kr" />
|
||||||
|
<target host="flickr.com" />
|
||||||
|
<target host="*.flickr.com" />
|
||||||
|
<target host="*.flickr.net" />
|
||||||
|
<target host="*.staticflickr.com" />
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Not secured by server:
|
||||||
|
-->
|
||||||
|
<!--securecookie host="^\.flic\.kr$" name="^BX$" /-->
|
||||||
|
|
||||||
|
<securecookie host="^\.flic\.kr$" name=".+" />
|
||||||
|
<securecookie host=".*\.flickr\.com$" name=".+" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://flic\.kr/"
|
||||||
|
to="https://flic.kr/" />
|
||||||
|
|
||||||
|
<rule from="^http://(api\.|www\.)?flickr\.com/"
|
||||||
|
to="https://$1flickr.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://s(ecure|tatic)\.flickr\.com/"
|
||||||
|
to="https://s$1.flickr.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://(c2|farm\d+)\.static(\.)?flickr\.com/"
|
||||||
|
to="https://$1.static$2flickr.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://(blog|code)\.flickr\.net/"
|
||||||
|
to="https://$1.flickr.net/" />
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<!--
|
||||||
|
For other GitHub coverage, see Github.xml.
|
||||||
|
-->
|
||||||
|
<ruleset name="GitHub Pages">
|
||||||
|
|
||||||
|
<target host="*.github.io" />
|
||||||
|
|
||||||
|
<rule from="^http://([^/@:\.]+)\.github\.io/"
|
||||||
|
to="https://$1.github.io/" />
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,94 @@
|
||||||
|
<!--
|
||||||
|
Other GitHub rulesets:
|
||||||
|
|
||||||
|
- Github-Pages.xml
|
||||||
|
- Guag.es.xml
|
||||||
|
- Speaker_Deck.com.xml
|
||||||
|
|
||||||
|
|
||||||
|
CDN buckets:
|
||||||
|
|
||||||
|
- github-images.s3.amazonaws.com
|
||||||
|
- github.global.ssl.fastly.net
|
||||||
|
- a248.e.akamai.net/assets.github.com/
|
||||||
|
- a248.e.akamai.net/camo.github.com/
|
||||||
|
- s3.amazonaws.com/github/ | d24z2fz21y4fag.cloudfront.net
|
||||||
|
- github.myshopify.com
|
||||||
|
|
||||||
|
|
||||||
|
Fully covered domains:
|
||||||
|
|
||||||
|
- github.com subdomains:
|
||||||
|
|
||||||
|
- (www.)
|
||||||
|
- assets\d+
|
||||||
|
- assets-cdn
|
||||||
|
- bounty
|
||||||
|
- cloud
|
||||||
|
- f.cloud
|
||||||
|
- codeload
|
||||||
|
- developer
|
||||||
|
- eclipse
|
||||||
|
- enterprise
|
||||||
|
- gist
|
||||||
|
- gist-assets
|
||||||
|
- help
|
||||||
|
- identicons
|
||||||
|
- jobs
|
||||||
|
- mac
|
||||||
|
- mobile
|
||||||
|
- nodeload
|
||||||
|
- octodex
|
||||||
|
- pages
|
||||||
|
- raw
|
||||||
|
- rg3
|
||||||
|
- shop
|
||||||
|
- status
|
||||||
|
- support
|
||||||
|
- training
|
||||||
|
- try
|
||||||
|
- wiki
|
||||||
|
- windows
|
||||||
|
|
||||||
|
- collector.githubapp.com
|
||||||
|
|
||||||
|
- githubusercontent.com
|
||||||
|
|
||||||
|
-->
|
||||||
|
<ruleset name="GitHub">
|
||||||
|
|
||||||
|
<target host="github.com" />
|
||||||
|
<target host="*.github.com" />
|
||||||
|
<target host="github.io" />
|
||||||
|
<target host="*.githubusercontent.com" />
|
||||||
|
<target host="collector.githubapp.com" />
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Secured by server:
|
||||||
|
-->
|
||||||
|
<!--securecookie host="^github\.com$" name="^(_gh_sess|tz|user_session)$" /-->
|
||||||
|
<!--securecookie host="^\.github\.com$" name="^(dotcom_user|logged_in)$" /-->
|
||||||
|
<!--securecookie host="^enterprise\.github\.com$" name="^(_enterprise_web|request_method)$" /-->
|
||||||
|
<!--securecookie host="^gist\.github\.com$" name="^_gist_session$" /-->
|
||||||
|
<!--securecookie host="^help\.github\.com$" name="^_help_session$" /-->
|
||||||
|
<!--
|
||||||
|
Not secured by server:
|
||||||
|
-->
|
||||||
|
<!--securecookie host="^status\.github\.com$" name="^rack\.session$" /-->
|
||||||
|
|
||||||
|
<securecookie host="^(?:.*\.)?github\.com$" name=".+" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://((?:assets\d+|assets-cdn|bounty|cloud|f\.cloud|codeload|developer|eclipse|enterprise|gist|gist-assets|help|identicons|jobs|mac|mobile|nodeload|octodex|pages|raw|rg3|shop|status|support|training|try|wiki|windows|www)\.)?github\.com/"
|
||||||
|
to="https://$1github.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://collector\.githubapp\.com/"
|
||||||
|
to="https://collector.githubapp.com/" />
|
||||||
|
|
||||||
|
<rule from="^https?://github\.io/"
|
||||||
|
to="https://pages.github.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://([^/@:\.]+)\.githubusercontent\.com/"
|
||||||
|
to="https://$1.githubusercontent.com/" />
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,26 @@
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Problematic domains:
|
||||||
|
|
||||||
|
- (www.)apture.com (works, mismatched, CN: *.google.com)
|
||||||
|
|
||||||
|
-->
|
||||||
|
<ruleset name="Google (mismatches)" default_off="mismatches">
|
||||||
|
|
||||||
|
<!-- Akamai -->
|
||||||
|
<target host="js.admeld.com"/>
|
||||||
|
<target host="apture.com" />
|
||||||
|
<target host="www.apture.com" />
|
||||||
|
<target host="googleartproject.com"/>
|
||||||
|
<target host="www.googleartproject.com"/>
|
||||||
|
|
||||||
|
<rule from="^http://js\.admeld\.com/"
|
||||||
|
to="https://js.admeld.com/"/>
|
||||||
|
|
||||||
|
<rule from="^https?://(?:www\.)?apture\.com/"
|
||||||
|
to="https://apture.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?googleartproject\.com/"
|
||||||
|
to="https://www.googleartproject.com/"/>
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<!--
|
||||||
|
For other Google coverage, see GoogleServices.xml.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<ruleset name="Google.org">
|
||||||
|
|
||||||
|
<target host="google.org" />
|
||||||
|
<target host="www.google.org" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://(www\.)?google\.org/"
|
||||||
|
to="https://$1google.org/" />
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,143 @@
|
||||||
|
<!--
|
||||||
|
For other Google coverage, see GoogleServices.xml.
|
||||||
|
|
||||||
|
|
||||||
|
Nonfunctional domains:
|
||||||
|
|
||||||
|
- hosted.gmodules.com *
|
||||||
|
- img0.gmodules.com *
|
||||||
|
- p.gmodules.com *
|
||||||
|
|
||||||
|
* 404; mismatched, CN: *.googleusercontent.com
|
||||||
|
|
||||||
|
|
||||||
|
Problematic domains:
|
||||||
|
|
||||||
|
- gmodules.com (503, CN: www.google.com)
|
||||||
|
- www.gmodules.com (503, CN: *.googleusercontent.com)
|
||||||
|
- gstatic.com (404, valid cert)
|
||||||
|
- api.recaptcha.net (works; mismatched, CN: google.com)
|
||||||
|
|
||||||
|
|
||||||
|
Partially covered domains:
|
||||||
|
|
||||||
|
- (www.)gmodules.com (→ www.google.com)
|
||||||
|
- (www.)google.com
|
||||||
|
- chart.apis.google.com (→ chart.googleapis.com)
|
||||||
|
|
||||||
|
|
||||||
|
Fully covered domains:
|
||||||
|
|
||||||
|
- api.google.com
|
||||||
|
|
||||||
|
- *.clients.google.com:
|
||||||
|
|
||||||
|
- linkhelp
|
||||||
|
|
||||||
|
- ssl.google-analytics.com
|
||||||
|
- www.google-analytics.com
|
||||||
|
|
||||||
|
- googleapis.com subdomains:
|
||||||
|
|
||||||
|
- ajax
|
||||||
|
- chart
|
||||||
|
- *.commondatastorage
|
||||||
|
- fonts
|
||||||
|
- *.storage
|
||||||
|
- www
|
||||||
|
|
||||||
|
- gstatic.com subdomains:
|
||||||
|
|
||||||
|
- (www.) (^ → www)
|
||||||
|
- csi
|
||||||
|
- encrypted-tbn\d
|
||||||
|
- g0
|
||||||
|
- *.metric
|
||||||
|
- ssl
|
||||||
|
- t\d
|
||||||
|
|
||||||
|
- api.recaptcha.net (→ www.google.com)
|
||||||
|
- api-secure.recaptcha.net
|
||||||
|
- gdata.youtube.com
|
||||||
|
|
||||||
|
|
||||||
|
ssl.google-analytics.com/ga.js sets __utm\w wildcard
|
||||||
|
cookies on whichever domain it is loaded from.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<ruleset name="Google APIs">
|
||||||
|
|
||||||
|
<target host="gmodules.com" />
|
||||||
|
<target host="www.gmodules.com" />
|
||||||
|
<target host="google.com" />
|
||||||
|
<target host="apis.google.com" />
|
||||||
|
<target host="*.apis.google.com" />
|
||||||
|
<target host="*.clients.google.com" />
|
||||||
|
<target host="www.google.com" />
|
||||||
|
<target host="*.google-analytics.com" />
|
||||||
|
<target host="*.googleapis.com" />
|
||||||
|
<target host="gstatic.com" />
|
||||||
|
<target host="*.gstatic.com" />
|
||||||
|
<!-- Captive portal detection redirects to this URL, and many captive
|
||||||
|
portals break TLS, so exempt this redirect URL.
|
||||||
|
See GitHub bug #368
|
||||||
|
-->
|
||||||
|
<exclusion pattern="^http://www\.gstatic\.com/generate_204" />
|
||||||
|
<target host="*.recaptcha.net" />
|
||||||
|
<target host="gdata.youtube.com" />
|
||||||
|
<exclusion pattern="^http://gdata\.youtube\.com/crossdomain\.xml" />
|
||||||
|
|
||||||
|
|
||||||
|
<securecookie host="^ssl\.google-analytics\.com$" name=".+" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?gmodules\.com/ig/images/"
|
||||||
|
to="https://www.google.com/ig/images/" />
|
||||||
|
|
||||||
|
<!-- jsapi was causing problems on some sites that embed google maps:
|
||||||
|
https://trac.torproject.org/projects/tor/ticket/2335
|
||||||
|
Apparently now fixed; thanks, Google!
|
||||||
|
-->
|
||||||
|
<rule from="^http://(?:www\.)?google\.com/(afsonline/|chart|jsapi|recaptcha/|uds)"
|
||||||
|
to="https://www.google.com/$1" />
|
||||||
|
|
||||||
|
<rule from="^http://(api|[\w-]+\.client)s\.google\.com/"
|
||||||
|
to="https://$1s.google.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://chart\.apis\.google\.com/chart"
|
||||||
|
to="https://chart.googleapis.com/chart" />
|
||||||
|
|
||||||
|
<rule from="^http://(ssl|www)\.google-analytics\.com/"
|
||||||
|
to="https://$1.google-analytics.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://(ajax|chart|fonts|www)\.googleapis\.com/"
|
||||||
|
to="https://$1.googleapis.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://([^@:\./]+\.)?(commondata)?storage\.googleapis\.com/"
|
||||||
|
to="https://$1$2storage.googleapis.com/" />
|
||||||
|
|
||||||
|
<!-- There is an interesting question about whether we should
|
||||||
|
append &strip=1 to all cache URLs. This causes them to load
|
||||||
|
without images and styles, which is more secure but can look
|
||||||
|
worse.
|
||||||
|
Without &strip=1, the images and styles from the cached
|
||||||
|
pages still load from the original, typically unencrypted, page.
|
||||||
|
With &strip=1, the cached page will be text-only and
|
||||||
|
will come exclusively from Google's HTTPS server.
|
||||||
|
-->
|
||||||
|
<rule from="^http://(?:www\.)?gstatic\.com/"
|
||||||
|
to="https://www.gstatic.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://(csi|encrypted-tbn\d|g0|[\w-]+\.metric|ssl|t\d)\.gstatic\.com/"
|
||||||
|
to="https://$1.gstatic.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://api\.recaptcha\.net/"
|
||||||
|
to="https://www.google.com/recaptcha/api/" />
|
||||||
|
|
||||||
|
<rule from="^http://api-secure\.recaptcha\.net/"
|
||||||
|
to="https://api-secure.recaptcha.net/" />
|
||||||
|
|
||||||
|
<rule from="^http://gdata\.youtube\.com/"
|
||||||
|
to="https://gdata.youtube.com/" />
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<ruleset name="GoogleCanada">
|
||||||
|
<target host="google.ca" />
|
||||||
|
<target host="*.google.ca" />
|
||||||
|
<rule from="^http://([^/:@\.]+)\.google\.ca/finance" to="https://$1.google.ca/finance"/>
|
||||||
|
</ruleset>
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
<!--
|
||||||
|
For other Google coverage, see GoogleServices.xml.
|
||||||
|
|
||||||
|
|
||||||
|
Problematic domains:
|
||||||
|
|
||||||
|
- www.google.bo *
|
||||||
|
- www.google.co *
|
||||||
|
- www.google.ec *
|
||||||
|
- www.google.in *
|
||||||
|
- www.google.kr *
|
||||||
|
- www.google.com.kz **
|
||||||
|
- www.google.com.lk *
|
||||||
|
- www.google.mx **
|
||||||
|
- www.google.sg *
|
||||||
|
- www.google.sl *
|
||||||
|
- www.google.ug *
|
||||||
|
- www.google.vn *
|
||||||
|
|
||||||
|
* 404; mismatched, CN: google.com
|
||||||
|
** Works; mismatched, CN: google.com
|
||||||
|
|
||||||
|
-->
|
||||||
|
<ruleset name="Google Images">
|
||||||
|
|
||||||
|
<target host="google.*" />
|
||||||
|
<target host="www.google.*" />
|
||||||
|
<target host="google.co.*" />
|
||||||
|
<target host="www.google.co.*" />
|
||||||
|
<target host="google.com" />
|
||||||
|
<target host="images.google.com" />
|
||||||
|
<target host="google.com.*" />
|
||||||
|
<target host="www.google.com.*" />
|
||||||
|
<!--
|
||||||
|
Only handle image-related paths in this ruleset:
|
||||||
|
-->
|
||||||
|
<exclusion pattern="^http://(?:www\.)?google(?:\.com?)?\.\w{2,3}/(?!(?:advanced_image_search|imghp|.*tb(?:m=isch|s=sbi)))" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?google\.com/"
|
||||||
|
to="https://www.google.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://images\.google\.com/"
|
||||||
|
to="https://images.google.com/" />
|
||||||
|
|
||||||
|
<!-- First handle problematic domains:
|
||||||
|
-->
|
||||||
|
<rule from="^http://(?:www\.)?google\.co/"
|
||||||
|
to="https://www.google.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?google\.(?:co\.)?(in|kr|ug)/"
|
||||||
|
to="https://www.google.co.$1/" />
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?google\.(?:com\.)?(kz|lk)/"
|
||||||
|
to="https://www.google.$1/" />
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?google\.(?:com\.)?(bo|ec|mx|sg|sl|vn)/"
|
||||||
|
to="https://www.google.com.$1/" />
|
||||||
|
|
||||||
|
<!-- And then the rest:
|
||||||
|
-->
|
||||||
|
<rule from="^http://(?:www\.)?google\.(com?\.)?(ae|ar|at|au|bg|bh|br|ca|ch|cl|co|cr|cu|de|eg|es|fi|fr|gh|gt|hr|id|ie|il|it|jo|jp|jm|ke|kw|lb|ly|my|na|ng|nl|no|nz|om|pa|pe|pk|pl|pt|py|qa|ro|ru|rw|sa|se|sv|th|tr|uk|uy|ve|za|zw)/"
|
||||||
|
to="https://www.google.$1$2/" />
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,78 @@
|
||||||
|
<ruleset name="Search www.google.com">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Enabling this ruleset should cause searches to go to
|
||||||
|
https://www.google.com rather than https://encrypted.google.com. Note that
|
||||||
|
the filename is important; it must be before GoogleSearch.xml in a bash
|
||||||
|
expansion of src/chrome/content/rules/*.xml in order to take precedence.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<target host="*.google.com" />
|
||||||
|
<target host="google.com" />
|
||||||
|
<target host="www.google.com.*" />
|
||||||
|
<target host="google.com.*" />
|
||||||
|
<target host="www.google.co.*" />
|
||||||
|
<target host="google.co.*" />
|
||||||
|
<target host="www.google.*" />
|
||||||
|
<target host="google.*" />
|
||||||
|
<!-- beyond clients1 these do not currently exist in the ccTLDs,
|
||||||
|
but just in case... -->
|
||||||
|
<target host="clients1.google.com.*" />
|
||||||
|
<target host="clients2.google.com.*" />
|
||||||
|
<target host="clients3.google.com.*" />
|
||||||
|
<target host="clients4.google.com.*" />
|
||||||
|
<target host="clients5.google.com.*" />
|
||||||
|
<target host="clients6.google.com.*" />
|
||||||
|
<target host="clients1.google.co.*" />
|
||||||
|
<target host="clients2.google.co.*" />
|
||||||
|
<target host="clients3.google.co.*" />
|
||||||
|
<target host="clients4.google.co.*" />
|
||||||
|
<target host="clients5.google.co.*" />
|
||||||
|
<target host="clients6.google.co.*" />
|
||||||
|
<target host="clients1.google.*" />
|
||||||
|
<target host="clients2.google.*" />
|
||||||
|
<target host="clients3.google.*" />
|
||||||
|
<target host="clients4.google.*" />
|
||||||
|
<target host="clients5.google.*" />
|
||||||
|
<target host="clients6.google.*" />
|
||||||
|
|
||||||
|
<rule from="^http://www\.google\.com/$"
|
||||||
|
to="https://www.google.com/"/>
|
||||||
|
|
||||||
|
<!-- The most basic case. -->
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?google\.com/search"
|
||||||
|
to="https://www.google.com/search"/>
|
||||||
|
|
||||||
|
<!-- A very annoying exception that we seem to need for the basic case -->
|
||||||
|
|
||||||
|
<exclusion pattern="^http://(?:www\.)?google\.com/search.*tbs=shop" />
|
||||||
|
<exclusion pattern="^http://clients[0-9]\.google\.com/.*client=products.*" />
|
||||||
|
<exclusion pattern="^http://suggestqueries\.google\.com/.*client=.*" />
|
||||||
|
|
||||||
|
<!-- https://trac.torproject.org/projects/tor/ticket/9713 -->
|
||||||
|
|
||||||
|
<exclusion pattern="^http://clients[0-9]\.google\.com/ocsp" />
|
||||||
|
|
||||||
|
<!-- This is necessary for image results links from web search results -->
|
||||||
|
|
||||||
|
<exclusion pattern="^http://(?:www\.)?google\.com/search.*tbm=isch.*" />
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?google\.com/webhp"
|
||||||
|
to="https://www.google.com/webhp"/>
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?google\.com/#"
|
||||||
|
to="https://www.google.com/#"/>
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?google\.com/$"
|
||||||
|
to="https://www.google.com/"/>
|
||||||
|
|
||||||
|
<!-- Completion urls look like this:
|
||||||
|
|
||||||
|
http://clients2.google.co.jp/complete/search?hl=ja&client=hp&expIds=17259,24660,24729,24745&q=m&cp=1 HTTP/1.1\r\n
|
||||||
|
|
||||||
|
-->
|
||||||
|
<rule from="^http://clients[0-9]\.google\.com/complete/search"
|
||||||
|
to="https://clients1.google.com/complete/search"/>
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,67 @@
|
||||||
|
<!--
|
||||||
|
Problematic domains:
|
||||||
|
|
||||||
|
- khms *
|
||||||
|
- khms[0-3] *
|
||||||
|
|
||||||
|
* $ 404s
|
||||||
|
|
||||||
|
|
||||||
|
Fully covered domains:
|
||||||
|
|
||||||
|
- google.com subdomains:
|
||||||
|
|
||||||
|
- khms
|
||||||
|
- khms[0-3]
|
||||||
|
|
||||||
|
-->
|
||||||
|
<ruleset name="Google Maps">
|
||||||
|
|
||||||
|
<target host="maps.google.*" />
|
||||||
|
<!--
|
||||||
|
https://trac.torproject.org/projects/tor/ticket/8627
|
||||||
|
-->
|
||||||
|
<exclusion pattern="^http://maps\.google\.com/local_url" />
|
||||||
|
<exclusion pattern="^http://maps\.google\.gr/transitathens" />
|
||||||
|
<target host="maps.google.co.*" />
|
||||||
|
<target host="khms.google.com" />
|
||||||
|
<target host="khms0.google.com" />
|
||||||
|
<target host="khms1.google.com" />
|
||||||
|
<target host="khms2.google.com" />
|
||||||
|
<target host="khms3.google.com" />
|
||||||
|
<target host="maps-api-ssl.google.com" />
|
||||||
|
<target host="mw2.google.com" />
|
||||||
|
<target host="maps.google.com.*" />
|
||||||
|
<target host="maps.googleapis.com" />
|
||||||
|
<!--
|
||||||
|
https://mail1.eff.org/pipermail/https-everywhere-rules/2012-September/001317.html
|
||||||
|
-->
|
||||||
|
<!--exclusion pattern="^http://maps\.googleapis\.com/map(files/lib/map_1_20\.swf|sapi/publicapi\?file=flashapi)" /-->
|
||||||
|
<exclusion pattern="^http://maps\.googleapis\.com/map(?:files/lib/map_\d+_\d+\.swf|sapi/publicapi\?file=flashapi)" />
|
||||||
|
<target host="maps.gstatic.com" />
|
||||||
|
|
||||||
|
|
||||||
|
<!--securecookie host="^maps\.google\.(com?\.)?(au|ca|gh|ie|in|jm|ke|lk|my|n[agz]|pk|rw|sl|sg|ug|uk|za|zw)$" name=".+" /-->
|
||||||
|
<securecookie host="^maps\.google\.[\w.]{2,6}$" name=".+" />
|
||||||
|
<securecookie host="^maps\.g(?:oogle|oogleapis|static)\.com$" name=".+" />
|
||||||
|
<securecookie host="^maps-api-ssl\.google\.com$" name=".+" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://maps\.google\.([^/]+)/"
|
||||||
|
to="https://maps.google.$1/" />
|
||||||
|
|
||||||
|
<!-- http://khms.../$ 404s:
|
||||||
|
-->
|
||||||
|
<rule from="^http://khms\d?\.google\.com/+\??$"
|
||||||
|
to="https://www.google.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://(khms\d?|maps-api-ssl|mw2)\.google\.com/"
|
||||||
|
to="https://$1.google.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://maps\.g(oogleapis|static)\.com/"
|
||||||
|
to="https://maps.g$1.com/" />
|
||||||
|
|
||||||
|
<rule from="^https://maps\.googleapis\.com/map(?=files/lib/map_\d+_\d+\.swf|sapi/publicapi\?file=flashapi)"
|
||||||
|
to="http://maps.googleapis.com/map" downgrade="1" />
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<ruleset name="GoogleMelange">
|
||||||
|
<target host="www.google-melange.com" />
|
||||||
|
<target host="google-melange.com" />
|
||||||
|
|
||||||
|
<rule from="^http://(www\.)?google-melange\.com/" to="https://www.google-melange.com/" />
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,135 @@
|
||||||
|
<ruleset name="Google Search">
|
||||||
|
|
||||||
|
<target host="google.com" />
|
||||||
|
<target host="*.google.com" />
|
||||||
|
<target host="google.com.*" />
|
||||||
|
<target host="www.google.com.*" />
|
||||||
|
<target host="google.co.*" />
|
||||||
|
<target host="www.google.co.*" />
|
||||||
|
<target host="google.*" />
|
||||||
|
<target host="www.google.*" />
|
||||||
|
<!--
|
||||||
|
Beyond clients1 these do not currently
|
||||||
|
exist in the ccTLDs, but just in case...
|
||||||
|
-->
|
||||||
|
<target host="clients1.google.com.*" />
|
||||||
|
<target host="clients2.google.com.*" />
|
||||||
|
<target host="clients3.google.com.*" />
|
||||||
|
<target host="clients4.google.com.*" />
|
||||||
|
<target host="clients5.google.com.*" />
|
||||||
|
<target host="clients6.google.com.*" />
|
||||||
|
<target host="clients1.google.co.*" />
|
||||||
|
<target host="clients2.google.co.*" />
|
||||||
|
<target host="clients3.google.co.*" />
|
||||||
|
<target host="clients4.google.co.*" />
|
||||||
|
<target host="clients5.google.co.*" />
|
||||||
|
<target host="clients6.google.co.*" />
|
||||||
|
<target host="clients1.google.*" />
|
||||||
|
<target host="clients2.google.*" />
|
||||||
|
<target host="clients3.google.*" />
|
||||||
|
<target host="clients4.google.*" />
|
||||||
|
<target host="clients5.google.*" />
|
||||||
|
<target host="clients6.google.*" />
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Some Google pages can generate naive links back to the
|
||||||
|
unencrypted version of encrypted.google.com, which is
|
||||||
|
a 301 but theoretically vulnerable to SSL stripping.
|
||||||
|
-->
|
||||||
|
<rule from="^http://encrypted\.google\.com/"
|
||||||
|
to="https://encrypted.google.com/" />
|
||||||
|
|
||||||
|
<!-- The most basic case.
|
||||||
|
-->
|
||||||
|
<rule from="^http://(?:www\.)?google\.com/search"
|
||||||
|
to="https://encrypted.google.com/search" />
|
||||||
|
|
||||||
|
<!-- A very annoying exception that we
|
||||||
|
seem to need for the basic case
|
||||||
|
-->
|
||||||
|
<exclusion pattern="^http://(?:www\.)?google\.com/search.*tbs=shop" />
|
||||||
|
<exclusion pattern="^http://clients\d\.google\.com/.*client=products.*" />
|
||||||
|
<exclusion pattern="^http://suggestqueries\.google\.com/.*client=.*" />
|
||||||
|
|
||||||
|
<!-- https://trac.torproject.org/projects/tor/ticket/9713
|
||||||
|
-->
|
||||||
|
|
||||||
|
<exclusion pattern="^http://clients[0-9]\.google\.com/ocsp" />
|
||||||
|
|
||||||
|
|
||||||
|
<!-- This is necessary for image results
|
||||||
|
links from web search results
|
||||||
|
-->
|
||||||
|
<exclusion pattern="^http://(?:www\.)?google\.com/search.*tbm=isch.*" />
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?google\.com/about"
|
||||||
|
to="https://www.google.com/about" />
|
||||||
|
|
||||||
|
<!-- There are two distinct cases for these firefox searches -->
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?google(?:\.com?)?\.[a-z]{2}/firefox/?$"
|
||||||
|
to="https://encrypted.google.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?google(?:\.com?)?\.[a-z]{2}/firefox"
|
||||||
|
to="https://encrypted.google.com/webhp" />
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?google\.com/webhp"
|
||||||
|
to="https://encrypted.google.com/webhp" />
|
||||||
|
|
||||||
|
<rule from="^http://codesearch\.google\.com/"
|
||||||
|
to="https://codesearch.google.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?google\.com/codesearch"
|
||||||
|
to="https://www.google.com/codesearch" />
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?google\.com/#"
|
||||||
|
to="https://encrypted.google.com/#" />
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?google\.com/$"
|
||||||
|
to="https://encrypted.google.com/" />
|
||||||
|
|
||||||
|
<!-- Google supports IPv6 search, including
|
||||||
|
HTTPS with a valid certificate! -->
|
||||||
|
<rule from="^http://ipv6\.google\.com/"
|
||||||
|
to="https://ipv6.google.com/" />
|
||||||
|
|
||||||
|
<!-- most google international sites look like
|
||||||
|
"google.fr", some look like "google.co.jp",
|
||||||
|
and some crazy ones like "google.com.au" -->
|
||||||
|
|
||||||
|
<rule from="^http://(www\.)?google(\.com?)?\.([a-z]{2})/(search\?|#)"
|
||||||
|
to="https://$1google$2.$3/$4" />
|
||||||
|
|
||||||
|
<!-- Language preference setting -->
|
||||||
|
<rule from="^http://(www\.)?google(\.com?)?\.([a-z]{2})/setprefs"
|
||||||
|
to="https://$1google$2.$3/setprefs" />
|
||||||
|
|
||||||
|
<!-- Completion urls look like this:
|
||||||
|
|
||||||
|
http://clients2.google.co.jp/complete/search?hl=ja&client=hp&expIds=17259,24660,24729,24745&q=m&cp=1 HTTP/1.1\r\n
|
||||||
|
|
||||||
|
-->
|
||||||
|
<rule from="^http://clients\d\.google\.com/complete/search"
|
||||||
|
to="https://clients1.google.com/complete/search" />
|
||||||
|
|
||||||
|
<rule from="^http://clients\d\.google(\.com?\.[a-z]{2})/complete/search"
|
||||||
|
to="https://clients1.google.$1/complete/search" />
|
||||||
|
|
||||||
|
<rule from="^http://clients\d\.google\.([a-z]{2})/complete/search"
|
||||||
|
to="https://clients1.google.$1/complete/search" />
|
||||||
|
|
||||||
|
<rule from="^http://suggestqueries\.google\.com/complete/search"
|
||||||
|
to="https://clients1.google.com/complete/search" />
|
||||||
|
|
||||||
|
<rule from="^http://(www\.)?google\.(com?\.)?([a-z]{2})/(?:webhp)?$"
|
||||||
|
to="https://$1google.$2$3/" />
|
||||||
|
|
||||||
|
<!-- If there are URL parameters, keep them. -->
|
||||||
|
<rule from="^http://(www\.)?google\.(com?\.)?([a-z]{2})/(?:webhp)?\?"
|
||||||
|
to="https://$1google.$2$3/webhp?" />
|
||||||
|
|
||||||
|
<!-- teapot -->
|
||||||
|
<rule from="^http://(www\.)?google(\.com?)?\.([a-z]{2})/teapot"
|
||||||
|
to="https://$1google$2.$3/teapot" />
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,345 @@
|
||||||
|
<!--
|
||||||
|
Other Google rulesets:
|
||||||
|
|
||||||
|
- 2mdn.net.xml
|
||||||
|
- Admeld.xml
|
||||||
|
- ChannelIntelligence.com.xml
|
||||||
|
- Doubleclick.net.xml
|
||||||
|
- FeedBurner.xml
|
||||||
|
- Google.org.xml
|
||||||
|
- GoogleAPIs.xml
|
||||||
|
- Google_App_Engine.xml
|
||||||
|
- GoogleImages.xml
|
||||||
|
- GoogleShopping.xml
|
||||||
|
- Ingress.xml
|
||||||
|
- Meebo.xml
|
||||||
|
- Orkut.xml
|
||||||
|
- Postini.xml
|
||||||
|
- WebM_Project.org.xml
|
||||||
|
|
||||||
|
|
||||||
|
Nonfunctional domains:
|
||||||
|
|
||||||
|
- feedproxy.google.com (404, valid cert)
|
||||||
|
- partnerpage.google.com *
|
||||||
|
- safebrowsing.clients.google.com (404, mismatched)
|
||||||
|
- (www.)googlesyndicatedsearch.com (404; mismatched, CN: google.com)
|
||||||
|
- buttons.googlesyndication.com *
|
||||||
|
|
||||||
|
* 404, valid cert
|
||||||
|
|
||||||
|
|
||||||
|
Nonfunctional google.com paths:
|
||||||
|
|
||||||
|
- analytics (redirects to http)
|
||||||
|
- imgres
|
||||||
|
- gadgets *
|
||||||
|
- hangouts (404)
|
||||||
|
- u/ (404)
|
||||||
|
|
||||||
|
* Redirects to http
|
||||||
|
|
||||||
|
|
||||||
|
Problematic domains:
|
||||||
|
|
||||||
|
- www.goo.gl (404; mismatched, CN: *.google.com)
|
||||||
|
|
||||||
|
- google.com subdomains:
|
||||||
|
|
||||||
|
- books (googlebooks/, images/, & intl/ 404, but works when rewritten to www)
|
||||||
|
- cbks0 ****
|
||||||
|
- earth *
|
||||||
|
- gg ($ 404s)
|
||||||
|
- knoll *
|
||||||
|
- scholar **
|
||||||
|
- trends *
|
||||||
|
|
||||||
|
- news.google.cctld **
|
||||||
|
- scholar.google.cctld **
|
||||||
|
- *-opensocial.googleusercontent.com ***
|
||||||
|
|
||||||
|
**** $ 404s
|
||||||
|
* 404, valid cert
|
||||||
|
** Redirects to http, valid cert
|
||||||
|
*** Breaks followers widget - https://trac.torproject.org/projects/tor/ticket/7294
|
||||||
|
|
||||||
|
|
||||||
|
Partially covered domains:
|
||||||
|
|
||||||
|
- google.cctld subdomains:
|
||||||
|
|
||||||
|
- scholar (→ www)
|
||||||
|
|
||||||
|
- google.com subdomains:
|
||||||
|
|
||||||
|
- (www.)
|
||||||
|
- cbks0 ($ 404s)
|
||||||
|
- gg ($ 404s)
|
||||||
|
- news (→ www)
|
||||||
|
- scholar (→ www)
|
||||||
|
|
||||||
|
- *.googleusercontent.com (*-opensocial excluded)
|
||||||
|
|
||||||
|
|
||||||
|
Fully covered domains:
|
||||||
|
|
||||||
|
- lh[3-6].ggpht.com
|
||||||
|
- (www.)goo.gl (www → ^)
|
||||||
|
|
||||||
|
- google.com subdomains:
|
||||||
|
|
||||||
|
- accounts
|
||||||
|
- adwords
|
||||||
|
- apis
|
||||||
|
- appengine
|
||||||
|
- books (→ encrypted)
|
||||||
|
- calendar
|
||||||
|
- checkout
|
||||||
|
- chrome
|
||||||
|
- clients[12]
|
||||||
|
- code
|
||||||
|
- *.corp
|
||||||
|
- developers
|
||||||
|
- dl
|
||||||
|
- docs
|
||||||
|
- docs\d
|
||||||
|
- \d.docs
|
||||||
|
- drive
|
||||||
|
- earth (→ www)
|
||||||
|
- encrypted
|
||||||
|
- encrypted-tbn[123]
|
||||||
|
- feedburner
|
||||||
|
- fiber
|
||||||
|
- finance
|
||||||
|
- glass
|
||||||
|
- groups
|
||||||
|
- health
|
||||||
|
- helpouts
|
||||||
|
- history
|
||||||
|
- hostedtalkgadget
|
||||||
|
- id
|
||||||
|
- investor
|
||||||
|
- knol
|
||||||
|
- knoll (→ knol)
|
||||||
|
- lh\d
|
||||||
|
- mail
|
||||||
|
- chatenabled.mail
|
||||||
|
- pack
|
||||||
|
- picasaweb
|
||||||
|
- pki
|
||||||
|
- play
|
||||||
|
- plus
|
||||||
|
- plusone
|
||||||
|
- productforums
|
||||||
|
- profiles
|
||||||
|
- safebrowsing-cache
|
||||||
|
- cert-test.sandbox
|
||||||
|
- plus.sandbox
|
||||||
|
- sb-ssl
|
||||||
|
- script
|
||||||
|
- security
|
||||||
|
- services
|
||||||
|
- servicessites
|
||||||
|
- sites
|
||||||
|
- spreadsheets
|
||||||
|
- spreadsheets\d
|
||||||
|
- support
|
||||||
|
- talk
|
||||||
|
- talkgadget
|
||||||
|
- tbn2 (→ encrypted-tbn2)
|
||||||
|
- tools
|
||||||
|
- trends (→ www)
|
||||||
|
|
||||||
|
- partner.googleadservices.com
|
||||||
|
- (www.)googlecode.com
|
||||||
|
- *.googlecode.com (per-project subdomains)
|
||||||
|
- googlesource.com
|
||||||
|
- *.googlesource.com
|
||||||
|
- pagead2.googlesyndication.com
|
||||||
|
- tpc.googlesyndication.com
|
||||||
|
- mail-attachment.googleusercontent.com
|
||||||
|
- webcache.googleusercontent.com
|
||||||
|
|
||||||
|
|
||||||
|
XXX: Needs more testing
|
||||||
|
|
||||||
|
-->
|
||||||
|
<ruleset name="Google Services">
|
||||||
|
|
||||||
|
<target host="*.ggpht.com" />
|
||||||
|
<target host="gmail.com" />
|
||||||
|
<target host="www.gmail.com" />
|
||||||
|
<target host="goo.gl" />
|
||||||
|
<target host="www.goo.gl" />
|
||||||
|
<target host="google.*" />
|
||||||
|
<target host="accounts.google.*" />
|
||||||
|
<target host="adwords.google.*" />
|
||||||
|
<target host="finance.google.*" />
|
||||||
|
<target host="groups.google.*" />
|
||||||
|
<target host="it.google.*" />
|
||||||
|
<target host="news.google.*" />
|
||||||
|
<exclusion pattern="^http://(?:news\.)?google\.com/(?:archivesearch|newspapers)" />
|
||||||
|
<target host="picasaweb.google.*" />
|
||||||
|
<target host="scholar.google.*" />
|
||||||
|
<target host="www.google.*" />
|
||||||
|
<target host="*.google.ca" />
|
||||||
|
<target host="google.co.*" />
|
||||||
|
<target host="accounts.google.co.*" />
|
||||||
|
<target host="adwords.google.co.*" />
|
||||||
|
<target host="finance.google.co.*" />
|
||||||
|
<target host="groups.google.co.*" />
|
||||||
|
<target host="id.google.co.*" />
|
||||||
|
<target host="news.google.co.*" />
|
||||||
|
<target host="picasaweb.google.co.*" />
|
||||||
|
<target host="scholar.google.co.*" />
|
||||||
|
<target host="www.google.co.*" />
|
||||||
|
<target host="google.com" />
|
||||||
|
<target host="*.google.com" />
|
||||||
|
<exclusion pattern="^http://(?:www\.)?google\.com/analytics/*(?:/[^/]+)?(?:\?.*)?$" />
|
||||||
|
<!--exclusion pattern="^http://books\.google\.com/(?!books/(\w+\.js|css/|javascript/)|favicon\.ico|googlebooks/|images/|intl/)" /-->
|
||||||
|
<exclusion pattern="^http://cbks0\.google\.com/(?:$|\?)" />
|
||||||
|
<exclusion pattern="^http://gg\.google\.com/(?!csi(?:$|\?))" />
|
||||||
|
<target host="google.com.*" />
|
||||||
|
<target host="accounts.google.com.*" />
|
||||||
|
<target host="adwords.google.com.*" />
|
||||||
|
<target host="groups.google.com.*" />
|
||||||
|
<target host="id.google.com.*" />
|
||||||
|
<target host="news.google.com.*" />
|
||||||
|
<target host="picasaweb.google.com.*" />
|
||||||
|
<target host="scholar.google.com.*" />
|
||||||
|
<target host="www.google.com.*" />
|
||||||
|
<target host="partner.googleadservices.com" />
|
||||||
|
<target host="googlecode.com" />
|
||||||
|
<target host="*.googlecode.com" />
|
||||||
|
<target host="googlemail.com" />
|
||||||
|
<target host="www.googlemail.com" />
|
||||||
|
<target host="googlesource.com" />
|
||||||
|
<target host="*.googlesource.com" />
|
||||||
|
<target host="*.googlesyndication.com" />
|
||||||
|
<target host="www.googletagservices.com" />
|
||||||
|
<target host="googleusercontent.com" />
|
||||||
|
<target host="*.googleusercontent.com" />
|
||||||
|
<!--
|
||||||
|
Necessary for the Followers widget:
|
||||||
|
|
||||||
|
https://trac.torproject.org/projects/tor/ticket/7294
|
||||||
|
-->
|
||||||
|
<exclusion pattern="http://[^@:\./]+-opensocial\.googleusercontent\.com" />
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Can we secure any of these wildcard cookies safely?
|
||||||
|
-->
|
||||||
|
<!--securecookie host="^\.google\.com$" name="^(hl|I4SUserLocale|NID|PREF|S)$" /-->
|
||||||
|
<!--securecookie host="^\.google\.[\w.]{2,6}$" name="^(hl|I4SUserLocale|NID|PREF|S|S_awfe)$" /-->
|
||||||
|
<securecookie host="^(?:accounts|adwords|\.code|login\.corp|developers|docs|\d\.docs|fiber|mail|picasaweb|plus|\.?productforums|support)\.google\.[\w.]{2,6}$" name=".+" />
|
||||||
|
<securecookie host="^www\.google\.com$" name="^GoogleAccountsLocale_session$" />
|
||||||
|
<securecookie host="^mail-attachment\.googleusercontent\.com$" name=".+" />
|
||||||
|
<securecookie host="^gmail\.com$" name=".+" />
|
||||||
|
<securecookie host="^www\.gmail\.com$" name=".+" />
|
||||||
|
<securecookie host="^googlemail\.com$" name=".+" />
|
||||||
|
<securecookie host="^www\.googlemail\.com$" name=".+" />
|
||||||
|
|
||||||
|
|
||||||
|
<!-- - lh 3-6 exist
|
||||||
|
- All appear identical
|
||||||
|
- Identical to lh\d.googleusercontent.com
|
||||||
|
-->
|
||||||
|
<rule from="^http://lh(\d)\.ggpht\.com/"
|
||||||
|
to="https://lh$1.ggpht.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://lh(\d)\.google\.ca/"
|
||||||
|
to="https://lh$1.google.ca/" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://(www\.)?g(oogle)?mail\.com/"
|
||||||
|
to="https://$1g$2mail.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?goo\.gl/"
|
||||||
|
to="https://goo.gl/" />
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Redirects to http when rewritten to www:
|
||||||
|
-->
|
||||||
|
<rule from="^http://books\.google\.com/"
|
||||||
|
to="https://encrypted.google.com/" />
|
||||||
|
|
||||||
|
<!-- tisp$ 404s:
|
||||||
|
-->
|
||||||
|
<rule from="^http://(?:www\.)?google\.((?:com?\.)?\w{2,3})/tisp(?=$|\?)"
|
||||||
|
to="https://www.google.$1/tisp/" />
|
||||||
|
|
||||||
|
<!-- Paths that work on all in google.*
|
||||||
|
-->
|
||||||
|
<rule from="^http://(?:www\.)?google\.((?:com?\.)?\w{2,3})/(accounts|adplanner|ads|adsense|adwords|analytics|bookmarks|chrome|contacts|coop|cse|css|culturalinstitute|doodles|earth|favicon\.ico|finance|get|goodtoknow|googleblogs|grants|green|hostednews|images|intl|js|landing|logos|mapmaker|newproducts|news|nexus|patents|policies|prdhp|profiles|products|reader|s2|settings|shopping|support|tisp|tools|transparencyreport|trends|urchin|webmasters)(?=$|[?/])"
|
||||||
|
to="https://www.google.$1/$2" />
|
||||||
|
|
||||||
|
<!-- Paths that 404 on .ccltd, but work on .com:
|
||||||
|
-->
|
||||||
|
<rule from="^http://(?:www\.)?google\.(?:com?\.)?\w{2,3}/(?=calendar|dictionary|doubleclick|help|ideas|pacman|postini|powermeter|url)"
|
||||||
|
to="https://www.google.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?google\.(?:com?\.)?\w{2,3}/custom"
|
||||||
|
to="https://www.google.com/cse" />
|
||||||
|
|
||||||
|
<!-- Paths that only exist/work on .com
|
||||||
|
-->
|
||||||
|
<rule from="^http://(?:www\.)?google\.com/(\+|appsstatus|books|buzz|extern_js|glass|googlebooks|ig|insights|moderator|phone|safebrowsing|videotargetting|webfonts)(?=$|[?/])"
|
||||||
|
to="https://www.google.com/$1" />
|
||||||
|
|
||||||
|
<!-- Subdomains that work on all in google.*
|
||||||
|
-->
|
||||||
|
<rule from="^http://(accounts|adwords|finance|groups|id|picasaweb|)\.google\.((?:com?\.)?\w{2,3})/"
|
||||||
|
to="https://$1.google.$2/" />
|
||||||
|
|
||||||
|
<!-- Subdomains that only exist/work on .com
|
||||||
|
-->
|
||||||
|
<rule from="^http://(apis|appengine|books|calendar|cbks0|chat|checkout|chrome|clients[12]|code|[\w-]+\.corp|developers|dl|docs\d?|\d\.docs|drive|encrypted|encrypted-tbn[123]|feedburner|fiber|fonts|gg|glass||health|helpouts|history|(?:hosted)?talkgadget|investor|lh\d|(?:chatenabled\.)?mail|pack|pki|play|plus(?:\.sandbox)?|plusone|productforums|profiles|safebrowsing-cache|cert-test\.sandbox|sb-ssl|script|security|services|servicessites|sites|spreadsheets\d?|support|talk|tools)\.google\.com/"
|
||||||
|
to="https://$1.google.com/" />
|
||||||
|
|
||||||
|
<exclusion pattern="^http://clients[0-9]\.google\.com/ocsp"/>
|
||||||
|
|
||||||
|
<rule from="^http://earth\.google\.com/"
|
||||||
|
to="https://www.google.com/earth/" />
|
||||||
|
|
||||||
|
<rule from="^http://scholar\.google\.((?:com?\.)?\w{2,3})/intl/"
|
||||||
|
to="https://www.google.$1/intl/" />
|
||||||
|
|
||||||
|
<rule from="^http://(?:encrypted-)?tbn2\.google\.com/"
|
||||||
|
to="https://encrypted-tbn2.google.com/" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://knoll?\.google\.com/"
|
||||||
|
to="https://knol.google.com/" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://news\.google\.(?:com?\.)?\w{2,3}/(?:$|news|newshp)"
|
||||||
|
to="https://www.google.com/news" />
|
||||||
|
|
||||||
|
<rule from="^http://trends\.google\.com/"
|
||||||
|
to="https://www.google.com/trends" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://([^/:@\.]+\.)?googlecode\.com/"
|
||||||
|
to="https://$1googlecode.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://([^\./]\.)?googlesource\.com/"
|
||||||
|
to="https://$1googlesource.com/" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://partner\.googleadservices\.com/"
|
||||||
|
to="https://partner.googleadservices.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://(pagead2|tpc)\.googlesyndication\.com/"
|
||||||
|
to="https://$1.googlesyndication.com/" />
|
||||||
|
|
||||||
|
<!-- !www doesn't exist.
|
||||||
|
-->
|
||||||
|
<rule from="^http://www\.googletagservices\.com/tag/js/"
|
||||||
|
to="https://www.googletagservices.com/tag/js/" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://([^@:\./]+)\.googleusercontent\.com/"
|
||||||
|
to="https://$1.googleusercontent.com/" />
|
||||||
|
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,28 @@
|
||||||
|
<!--
|
||||||
|
For other Google coverage, see GoogleServices.xml.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<ruleset name="Google Shopping">
|
||||||
|
|
||||||
|
<target host="google.*" />
|
||||||
|
<target host="www.google.*" />
|
||||||
|
<target host="google.co.*" />
|
||||||
|
<target host="www.google.co.*" />
|
||||||
|
<target host="*.google.com" />
|
||||||
|
<target host="google.com.*" />
|
||||||
|
<target host="www.google.com.*" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://encrypted\.google\.com/(prdhp|shopping)"
|
||||||
|
to="https://www.google.com/$1" />
|
||||||
|
|
||||||
|
<rule from="^http://shopping\.google\.com/"
|
||||||
|
to="https://shopping.google.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://(?:encrypted|www)\.google\.com/(.*tbm=shop)"
|
||||||
|
to="https://www.google.com/$1" />
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?google\.((?:com?\.)?(?:ae|ar|at|au|bg|bh|bo|br|ca|ch|cl|cr|co|cu|de|ec|eg|es|fi|fr|gh|gt|hr|id|ie|il|in|it|jm|jo|jp|ke|kr|kw|kz|lb|lk|ly|mx|my|na|ng|nl|no|nz|om|pa|pe|pk|pl|pt|py|qa|ro|ru|rw|sa|sg|sl|se|sv|th|tr|ug|uk|uy|ve|vn|za|zw))/(?=prdhp|shopping)"
|
||||||
|
to="https://www.google.com/$1" />
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<ruleset name="GoogleSorry">
|
||||||
|
<target host="sorry.google.com" />
|
||||||
|
<target host="www.google.com" />
|
||||||
|
<target host="google.com" />
|
||||||
|
|
||||||
|
<rule from="^http://((sorry|www)\.)?google\.com/sorry/" to="https://sorry.google.com/sorry/" />
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<ruleset name="Google Translate (broken)" default_off="redirect loops">
|
||||||
|
<target host="translate.googleapis.com" />
|
||||||
|
<target host="translate.google.com" />
|
||||||
|
|
||||||
|
<rule from="^http://translate\.googleapis\.com/" to="https://translate.googleapis.com/"/>
|
||||||
|
<rule from="^http://translate\.google\.com/"
|
||||||
|
to="https://translate.google.com/" />
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,83 @@
|
||||||
|
<ruleset name="Google Videos">
|
||||||
|
<target host="*.google.com" />
|
||||||
|
<target host="google.com" />
|
||||||
|
<target host="www.google.com.*" />
|
||||||
|
<target host="google.com.*" />
|
||||||
|
<target host="www.google.co.*" />
|
||||||
|
<target host="google.co.*" />
|
||||||
|
<target host="www.google.*" />
|
||||||
|
<target host="google.*" />
|
||||||
|
|
||||||
|
<rule from="^http://encrypted\.google\.com/videohp"
|
||||||
|
to="https://encrypted.google.com/videohp" />
|
||||||
|
|
||||||
|
<!-- https://videos.google.com is currently broken; work around that... -->
|
||||||
|
<rule from="^https?://videos?\.google\.com/$"
|
||||||
|
to="https://encrypted.google.com/videohp" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.com/videohp"
|
||||||
|
to="https://encrypted.google.com/videohp" />
|
||||||
|
<rule from="^http://(?:images|www|encrypted)\.google\.com/(.*tbm=isch)"
|
||||||
|
to="https://encrypted.google.com/$1" />
|
||||||
|
|
||||||
|
<rule
|
||||||
|
from="^http://(?:www\.)?google\.(?:com?\.)?(?:au|ca|gh|ie|in|jm|ke|lk|my|na|ng|nz|pk|rw|sl|sg|ug|uk|za|zw)/videohp"
|
||||||
|
to="https://encrypted.google.com/videohp" />
|
||||||
|
<rule
|
||||||
|
from="^http://(?:www\.)?google\.(?:com?\.)?(?:ar|bo|cl|co|cu|cr|ec|es|gt|mx|pa|pe|py|sv|uy|ve)/videohp$"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=es" />
|
||||||
|
<rule
|
||||||
|
from="^http://(?:www\.)?google\.(?:com\.)?(?:ae|bh|eg|jo|kw|lb|ly|om|qa|sa)/videohp$"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=ar" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.(?:at|ch|de)/videohp$"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=de" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.(fr|nl|it|pl|ru|bg|pt|ro|hr|fi|no)/videohp$"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=$1" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.com?\.(id|th|tr)/videohp$"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=$1" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.com\.il/videohp$"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=he" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.com\.kr/videohp$"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=ko" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.com\.kz/videohp$"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=kk" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.com\.jp/videohp$"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=ja" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.com\.vn/videohp$"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=vi" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.com\.br/videohp$"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=pt-BR" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.se/videohp$"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=sv" />
|
||||||
|
|
||||||
|
<!-- If there are URL parameters, keep them. -->
|
||||||
|
<rule
|
||||||
|
from="^http://(?:www\.)?google\.(?:com?\.)?(?:ar|bo|cl|co|cu|cr|ec|es|gt|mx|pa|pe|py|sv|uy|ve)/videohp\?"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=es&" />
|
||||||
|
<rule
|
||||||
|
from="^http://(?:www\.)?google\.(?:com\.)?(?:ae|bh|eg|jo|kw|lb|ly|om|qa|sa)/videohp\?"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=ar&" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.(?:at|ch|de)/videohp\?"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=de&" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.(fr|nl|it|pl|ru|bg|pt|ro|hr|fi|no)/videohp\?"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=$1&" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.com?\.(id|th|tr)/videohp\?"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=$1&" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.com\.il/videohp\?"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=he&" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.com\.kr/videohp\?"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=ko&" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.com\.kz/videohp\?"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=kk&" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.com\.jp/videohp\?"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=ja&" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.com\.vn/videohp\?"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=vi&" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.com\.br/videohp\?"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=pt-BR&" />
|
||||||
|
<rule from="^http://(?:www\.)?google\.se/videohp\?"
|
||||||
|
to="https://encrypted.google.com/videohp?hl=sv&" />
|
||||||
|
|
||||||
|
<rule from="^http://video\.google\.com/ThumbnailServer2"
|
||||||
|
to="https://video.google.com/ThumbnailServer2" />
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,17 @@
|
||||||
|
<!--
|
||||||
|
gwbhrd.appspot.com
|
||||||
|
|
||||||
|
-->
|
||||||
|
<ruleset name="GoogleWatchBlog">
|
||||||
|
|
||||||
|
<target host="googlewatchblog.de" />
|
||||||
|
<target host="*.googlewatchblog.de" />
|
||||||
|
|
||||||
|
|
||||||
|
<securecookie host="^(?:www)?\.googlewatchblog\.de$" name=".+" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://(static\.|www\.)?googlewatchblog\.de/"
|
||||||
|
to="https://$1googlewatchblog.de/" />
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,21 @@
|
||||||
|
<!--
|
||||||
|
For other Google coverage, see GoogleServices.xml.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<ruleset name="Google App Engine">
|
||||||
|
|
||||||
|
<target host="appspot.com" />
|
||||||
|
<target host="*.appspot.com" />
|
||||||
|
<!--
|
||||||
|
Redirects to http for some reason.
|
||||||
|
-->
|
||||||
|
<exclusion pattern="^http://photomunchers\.appspot\.com/" />
|
||||||
|
|
||||||
|
|
||||||
|
<securecookie host="^.+\.appspot\.com$" name=".+" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://([^@:\./]+\.)?appspot\.com/"
|
||||||
|
to="https://$1appspot.com/" />
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,16 @@
|
||||||
|
<!-- This rule was automatically generated based on an HSTS
|
||||||
|
preload rule in the Chromium browser. See
|
||||||
|
https://src.chromium.org/viewvc/chrome/trunk/src/net/base/transport_security_state.cc
|
||||||
|
for the list of preloads. Sites are added to the Chromium HSTS
|
||||||
|
preload list on request from their administrators, so HTTPS should
|
||||||
|
work properly everywhere on this site.
|
||||||
|
|
||||||
|
Because Chromium and derived browsers automatically force HTTPS for
|
||||||
|
every access to this site, this rule applies only to Firefox. -->
|
||||||
|
<ruleset name="Googleplex.com (default off)" platform="firefox" default_off="Certificate error">
|
||||||
|
<target host="googleplex.com" />
|
||||||
|
|
||||||
|
<securecookie host="^googleplex\.com$" name=".+" />
|
||||||
|
|
||||||
|
<rule from="^http://googleplex\.com/" to="https://googleplex.com/" />
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<ruleset name="OpenStreetMap">
|
||||||
|
|
||||||
|
<target host="openstreetmap.org"/>
|
||||||
|
<target host="*.openstreetmap.org"/>
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?openstreetmap\.org/"
|
||||||
|
to="https://www.openstreetmap.org/"/>
|
||||||
|
|
||||||
|
<rule from="^http://tile\.openstreetmap\.org/"
|
||||||
|
to="https://a.tile.openstreetmap.org/"/>
|
||||||
|
|
||||||
|
<rule from="^http://(blog|help|lists|nominatim|piwik|taginfo|[abc]\.tile|trac|wiki)\.openstreetmap\.org/"
|
||||||
|
to="https://$1.openstreetmap.org/"/>
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<!--
|
||||||
|
www: cert only matches ^rawgithub.com
|
||||||
|
|
||||||
|
-->
|
||||||
|
<ruleset name="rawgithub.com">
|
||||||
|
|
||||||
|
<target host="rawgithub.com" />
|
||||||
|
<target host="www.rawgithub.com" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?rawgithub\.com/"
|
||||||
|
to="https://rawgithub.com/" />
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,101 @@
|
||||||
|
<!--
|
||||||
|
|
||||||
|
CDN buckets:
|
||||||
|
|
||||||
|
- akmedia-a.akamaihd.net
|
||||||
|
|
||||||
|
- soundcloud.assistly.com
|
||||||
|
|
||||||
|
- help.soundcloud.com
|
||||||
|
|
||||||
|
- cs70.wac.edgecastcdn.net
|
||||||
|
|
||||||
|
- a1.sndcdn.com
|
||||||
|
- i1.sndcdn.com
|
||||||
|
- w1.sndcdn.com
|
||||||
|
|
||||||
|
- wpc.658D.edgecastcdn.net
|
||||||
|
- m-a.sndcdn.com.edgesuite.net
|
||||||
|
- soundcloud.gettyimages.com
|
||||||
|
|
||||||
|
- scbackstage.wpengine.netdna-cdn.com
|
||||||
|
|
||||||
|
- ssl doesn't exist
|
||||||
|
- backstage.soundcloud.com
|
||||||
|
|
||||||
|
- soundcloud.wpengine.netdna-cdn.com
|
||||||
|
|
||||||
|
- -ssl doesn't exist
|
||||||
|
- blog.soundcloud.com
|
||||||
|
|
||||||
|
- gs1.wpc.v2cdn.netcdn.net
|
||||||
|
- gs1.wpc.v2cdn.net
|
||||||
|
|
||||||
|
- ec-media.soundcloud.com
|
||||||
|
|
||||||
|
Nonfunctional soundcloud.com subdomains:
|
||||||
|
|
||||||
|
- help (redirects to http, mismatched, CN: *.assistly.com)
|
||||||
|
- m (redirects to http)
|
||||||
|
- media
|
||||||
|
- status (times out)
|
||||||
|
|
||||||
|
|
||||||
|
Problematic domains:
|
||||||
|
|
||||||
|
- m-a.sndcdn.com (works, akamai)
|
||||||
|
|
||||||
|
|
||||||
|
Partially covered domains:
|
||||||
|
|
||||||
|
- backstage.soundcloud.com
|
||||||
|
|
||||||
|
|
||||||
|
Fully covered domains:
|
||||||
|
|
||||||
|
- sndcdn.com subdomains:
|
||||||
|
|
||||||
|
- a[12]
|
||||||
|
- api
|
||||||
|
- i[1-4]
|
||||||
|
- w[12]
|
||||||
|
- wis
|
||||||
|
|
||||||
|
- soundcloud.com subdomains:
|
||||||
|
|
||||||
|
- (www.)
|
||||||
|
- api
|
||||||
|
- blog
|
||||||
|
- connect
|
||||||
|
- developers
|
||||||
|
- ec-media
|
||||||
|
- eventlogger
|
||||||
|
- help-assets
|
||||||
|
- media
|
||||||
|
- visuals
|
||||||
|
- w
|
||||||
|
|
||||||
|
-->
|
||||||
|
<ruleset name="Soundcloud (partial)">
|
||||||
|
|
||||||
|
<target host="scbackstage.wpengine.netdna-cdn.com" />
|
||||||
|
<target host="soundcloud.wpengine.netdna-cdn.com" />
|
||||||
|
<target host="*.sndcdn.com" />
|
||||||
|
<target host="soundcloud.com" />
|
||||||
|
<target host="*.soundcloud.com" />
|
||||||
|
<exclusion pattern="^https?://(?:scbackstage\.wpengine\.netdna-cdn|backstage\.soundcloud)\.com/(?!wp-content/)" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://([aiw]\d|api|wis)\.sndcdn\.com/"
|
||||||
|
to="https://$1.sndcdn.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://((?:api|backstage|blog|connect|developers|ec-media|eventlogger|help-assets|media|visuals|w|www)\.)?soundcloud\.com/"
|
||||||
|
to="https://$1soundcloud.com/" />
|
||||||
|
|
||||||
|
<rule from="^https?://scbackstage\.wpengine\.netdna-cdn\.com/"
|
||||||
|
to="https://backstage.soundcloud.com/" />
|
||||||
|
|
||||||
|
<rule from="^https?://soundcloud\.wpengine\.netdna-cdn\.com/"
|
||||||
|
to="https://blog.soundcloud.com/" />
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,36 @@
|
||||||
|
<!--
|
||||||
|
Nonfunctional:
|
||||||
|
|
||||||
|
- image.bayimg.com
|
||||||
|
- (www.)thepiratebay.sx (http reply)
|
||||||
|
|
||||||
|
|
||||||
|
For problematic rules, see ThePirateBay-mismatches.xml.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<ruleset name="The Pirate Bay (partial)">
|
||||||
|
|
||||||
|
<target host="suprbay.org" />
|
||||||
|
<target host="*.suprbay.org" />
|
||||||
|
<!-- * for cross-domain cookie -->
|
||||||
|
<target host="*.forum.suprbay.org" />
|
||||||
|
<target host="thepiratebay.org"/>
|
||||||
|
<target host="*.thepiratebay.org"/>
|
||||||
|
<target host="thepiratebay.se"/>
|
||||||
|
<target host="*.thepiratebay.se"/>
|
||||||
|
|
||||||
|
<securecookie host="^.*\.suprbay\.org$" name=".*" />
|
||||||
|
<securecookie host="^(.*\.)?thepiratebay\.se$" name=".*"/>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Cert doesn't match (www.), redirects like so. -->
|
||||||
|
<rule from="^https?://(?:forum\.|www\.)?suprbay\.org/"
|
||||||
|
to="https://forum.suprbay.org/" />
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?thepiratebay\.(?:org|se)/"
|
||||||
|
to="https://thepiratebay.se/"/>
|
||||||
|
|
||||||
|
<rule from="^http://(rss|static|torrents)\.thepiratebay\.(?:org|se)/"
|
||||||
|
to="https://$1.thepiratebay.se/"/>
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,18 @@
|
||||||
|
<ruleset name="Tor Project">
|
||||||
|
|
||||||
|
<target host="torproject.org" />
|
||||||
|
<target host="*.torproject.org" />
|
||||||
|
<exclusion pattern="^http://torperf\.torproject\.org/" />
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Not secured by server:
|
||||||
|
-->
|
||||||
|
<!--securecookie host="^\.blog\.torproject\.org$" name="^SESS[0-9a-f]{32}$" /-->
|
||||||
|
|
||||||
|
<securecookie host="^(?:.*\.)?torproject\.org$" name=".+" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://([^/:@\.]+\.)?torproject\.org/"
|
||||||
|
to="https://$1torproject.org/" />
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,169 @@
|
||||||
|
<!--
|
||||||
|
Other Twitter rulesets:
|
||||||
|
|
||||||
|
- Twitter_Community.com.xml
|
||||||
|
|
||||||
|
|
||||||
|
Nonfunctional domains:
|
||||||
|
|
||||||
|
- status.twitter.com *
|
||||||
|
- status.twitter.jp *
|
||||||
|
|
||||||
|
* Tumblr
|
||||||
|
|
||||||
|
|
||||||
|
CDN buckets:
|
||||||
|
|
||||||
|
- a1095.g.akamai.net/=/1095/134446/1d/platform.twitter.com/ | platform2.twitter.com.edgesuite.net
|
||||||
|
|
||||||
|
- platform2.twitter.com
|
||||||
|
|
||||||
|
- twitter-any.s3.amazonaws.com
|
||||||
|
- twitter-blog.s3.amazonaws.com
|
||||||
|
|
||||||
|
- d2rdfnizen5apl.cloudfront.net
|
||||||
|
|
||||||
|
- s.twimg.com
|
||||||
|
|
||||||
|
- ssl2.twitter.com.edgekey.net
|
||||||
|
- twitter.github.com
|
||||||
|
|
||||||
|
|
||||||
|
Problematic domains:
|
||||||
|
|
||||||
|
- twimg.com subdomains:
|
||||||
|
|
||||||
|
- a5 *
|
||||||
|
- s (cloudfront)
|
||||||
|
|
||||||
|
- twitter.com subdomains:
|
||||||
|
|
||||||
|
- platform[0-3] (403, akamai)
|
||||||
|
|
||||||
|
* akamai
|
||||||
|
|
||||||
|
|
||||||
|
Fully covered domains:
|
||||||
|
|
||||||
|
- (www.)t.co (www → ^)
|
||||||
|
|
||||||
|
- twimg.com subdomains:
|
||||||
|
|
||||||
|
- a[5-9] (→ si0)
|
||||||
|
- a\d
|
||||||
|
- abs
|
||||||
|
- dnt
|
||||||
|
- ea
|
||||||
|
- g
|
||||||
|
- g2
|
||||||
|
- gu
|
||||||
|
- hca
|
||||||
|
- jp
|
||||||
|
- ma
|
||||||
|
- ma[0123]
|
||||||
|
- o
|
||||||
|
- p
|
||||||
|
- pbs
|
||||||
|
- r
|
||||||
|
- s (→ d2rdfnizen5apl.cloudfront.net)
|
||||||
|
- si[0-5]
|
||||||
|
- syndication
|
||||||
|
- cdn.syndication
|
||||||
|
- tailfeather
|
||||||
|
- ton
|
||||||
|
- v
|
||||||
|
- widgets
|
||||||
|
|
||||||
|
- twitter.com subdomains:
|
||||||
|
|
||||||
|
- (www.)
|
||||||
|
- 201[012]
|
||||||
|
- about
|
||||||
|
- ads
|
||||||
|
- analytics
|
||||||
|
- api
|
||||||
|
- cdn.api
|
||||||
|
- urls.api
|
||||||
|
- blog
|
||||||
|
- business
|
||||||
|
- preview.cdn
|
||||||
|
- preview-dev.cdn
|
||||||
|
- preview-stage.cdn
|
||||||
|
- de
|
||||||
|
- dev
|
||||||
|
- en
|
||||||
|
- engineering
|
||||||
|
- es
|
||||||
|
- firefox
|
||||||
|
- fr
|
||||||
|
- it
|
||||||
|
- ja
|
||||||
|
- jp
|
||||||
|
- m
|
||||||
|
- media
|
||||||
|
- mobile
|
||||||
|
- music
|
||||||
|
- oauth
|
||||||
|
- p
|
||||||
|
- pic
|
||||||
|
- platform
|
||||||
|
- platform[0-3] (→ platform)
|
||||||
|
- widgets.platform
|
||||||
|
- search
|
||||||
|
- static
|
||||||
|
- support
|
||||||
|
- transparency
|
||||||
|
- upload
|
||||||
|
|
||||||
|
|
||||||
|
These altnames don't exist:
|
||||||
|
|
||||||
|
- i3.twimg.com
|
||||||
|
- p-dev.twimg.com
|
||||||
|
- vmtc.twimg.com
|
||||||
|
|
||||||
|
- cdn-dev.api.twitter.com
|
||||||
|
|
||||||
|
-->
|
||||||
|
<ruleset name="Twitter">
|
||||||
|
|
||||||
|
<target host="t.co" />
|
||||||
|
<target host="*.t.co" />
|
||||||
|
<target host="*.twimg.com" />
|
||||||
|
<target host="twitter.com" />
|
||||||
|
<target host="*.twitter.com" />
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Secured by server:
|
||||||
|
-->
|
||||||
|
<!--securecookie host="^\.twitter\.com$" name="^_twitter_sess$" /-->
|
||||||
|
<!--securecookie host="^support\.twitter\.com$" name="^_help_center_session$" /-->
|
||||||
|
<!--
|
||||||
|
Not secured by server:
|
||||||
|
-->
|
||||||
|
<!--securecookie host="^\.t\.co$" name="^muc$" /-->
|
||||||
|
<!--securecookie host="^\.twitter\.com$" name="^guest_id$" /-->
|
||||||
|
|
||||||
|
<securecookie host="^\.t\.co$" name=".+" />
|
||||||
|
<securecookie host="^(?:.*\.)?twitter\.com$" name=".+" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?t\.co/"
|
||||||
|
to="https://t.co/" />
|
||||||
|
|
||||||
|
<rule from="^http://a[5-9]\.twimg\.com/"
|
||||||
|
to="https://si0.twimg.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://(abs|a\d|dnt|ea|g[2u]?|hca|jp|ma\d?|o|p|pbs|r|si\d|(?:cdn\.)?syndication|tailfeather|ton|v|widgets)\.twimg\.com/"
|
||||||
|
to="https://$1.twimg.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://s\.twimg\.com/"
|
||||||
|
to="https://d2rdfnizen5apl.cloudfront.net/" />
|
||||||
|
|
||||||
|
<rule from="^http://((?:201\d|about|ads|analytics|blog|(?:cdn\.|urls\.)?api|business|preview(?:-dev|-stage)?\.cdn|de|dev|engineering|en|es|firefox|fr|it|ja|jp|m|media|mobile|music|oauth|p|pic|platform|widgets\.platform|search|static|support|transparency|upload|www)\.)?twitter\.com/"
|
||||||
|
to="https://$1twitter.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://platform\d\.twitter\.com/"
|
||||||
|
to="https://platform.twitter.com/" />
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,75 @@
|
||||||
|
<!--
|
||||||
|
CDN buckets:
|
||||||
|
|
||||||
|
- av.vimeo.com.edgesuite.net
|
||||||
|
|
||||||
|
- a808.g.akamai.net
|
||||||
|
|
||||||
|
- pdl.vimeocdn.com.edgesuite.net
|
||||||
|
|
||||||
|
- a1189.g.akamai.net
|
||||||
|
|
||||||
|
|
||||||
|
Problematic subdomains:
|
||||||
|
|
||||||
|
- av (pdl.../crossdomain.xml restricts to port 80)
|
||||||
|
- pdl (works, akamai)
|
||||||
|
|
||||||
|
|
||||||
|
Partially covered subdomains:
|
||||||
|
|
||||||
|
- developer (some pages redirect to http)
|
||||||
|
- pdl (→ akamai)
|
||||||
|
|
||||||
|
|
||||||
|
Fully covered subdomains:
|
||||||
|
|
||||||
|
- (www.)
|
||||||
|
- secure
|
||||||
|
|
||||||
|
|
||||||
|
Default off per https://trac.torproject.org/projects/tor/ticket/7569 -->
|
||||||
|
<ruleset name="Vimeo (default off)" default_off="breaks some video embedding">
|
||||||
|
|
||||||
|
<target host="vimeo.com" />
|
||||||
|
<target host="*.vimeo.com" />
|
||||||
|
<exclusion pattern="^http://av\.vimeo\.com/crossdomain\.xml" />
|
||||||
|
<!--exclusion pattern="^http://developer\.vimeo\.com/($|\?|(apps|guidelines|help|player)($|[?/]))" /-->
|
||||||
|
<exclusion pattern="^http://developer\.vimeo\.com/(?!apis(?:$|[?/])|favicon\.ico)" />
|
||||||
|
<target host="*.vimeocdn.com" />
|
||||||
|
<!--
|
||||||
|
Uses crossdomain.xml from s3.amazonaws.com, which sets secure="false"
|
||||||
|
|
||||||
|
https://mail1.eff.org/pipermail/https-everywhere/2012-October/001583.html
|
||||||
|
-->
|
||||||
|
<exclusion pattern="^http://a\.vimeocdn\.com/p/flash/moogaloop/" />
|
||||||
|
|
||||||
|
<!-- We cannot secure streams because crossdomain.xml
|
||||||
|
restricts to port 80 :(
|
||||||
|
-->
|
||||||
|
<exclusion pattern="^http://pdl\.vimeocdn\.com/(?!crossdomain\.xml)" />
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Tracking cookies:
|
||||||
|
-->
|
||||||
|
<securecookie host="^\.(?:player\.)?vimeo\.com$" name="^__utm\w$" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://((?:developer|player|secure|www)\.)?vimeo\.com/"
|
||||||
|
to="https://$1vimeo.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://av\.vimeo\.com/"
|
||||||
|
to="https://a248.e.akamai.net/f/808/9207/8m/av.vimeo.com/" />
|
||||||
|
|
||||||
|
<!-- a & b: Akamai -->
|
||||||
|
<rule from="^http://(?:secure-)?([ab])\.vimeocdn\.com/"
|
||||||
|
to="https://secure-$1.vimeocdn.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://i\.vimeocdn\.com/"
|
||||||
|
to="https://i.vimeocdn.com/" />
|
||||||
|
|
||||||
|
<rule from="^http://pdl\.vimeocdn\.com/"
|
||||||
|
to="https://a248.e.akamai.net/f/1189/4415/8d/pdl.vimeocdn.com/" />
|
||||||
|
|
||||||
|
</ruleset>
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
<ruleset name="WikiLeaks">
|
||||||
|
|
||||||
|
<target host="wikileaks.org" />
|
||||||
|
<target host="*.wikileaks.org" />
|
||||||
|
|
||||||
|
|
||||||
|
<securecookie host="^(?:w*\.)?wikileaks\.org$" name=".+" />
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://((?:chat|search|shop|www)\.)?wikileaks\.org/"
|
||||||
|
to="https://$1wikileaks.org/" />
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,107 @@
|
||||||
|
<!--
|
||||||
|
Wikipedia and other Wikimedia Foundation wikis previously had no real HTTPS support, and
|
||||||
|
URLs had to be rewritten to https://secure.wikimedia.org/$wikitype/$language/ . This is no
|
||||||
|
longer the case, see https://blog.wikimedia.org/2011/10/03/native-https-support-enabled-for-all-wikimedia-foundation-wikis/ ,
|
||||||
|
so this file is a lot simpler these days.
|
||||||
|
|
||||||
|
|
||||||
|
Mixed content:
|
||||||
|
|
||||||
|
- Images, on:
|
||||||
|
|
||||||
|
- stats.wikimedia.org from upload.wikimedia.org *
|
||||||
|
- stats.wikimedia.org from wikimediafoundation.org *
|
||||||
|
|
||||||
|
* Secured by us
|
||||||
|
|
||||||
|
-->
|
||||||
|
<ruleset name="Wikimedia">
|
||||||
|
|
||||||
|
<target host="enwp.org" />
|
||||||
|
<target host="frwp.org" />
|
||||||
|
|
||||||
|
<target host="mediawiki.org" />
|
||||||
|
<target host="www.mediawiki.org" />
|
||||||
|
<target host="wikimedia.org" />
|
||||||
|
<target host="*.wikimedia.org" />
|
||||||
|
<exclusion pattern="^http://(?:apt|cs|cz|parsoid-lb\.eqiad|status|torrus|ubuntu)\.wikimedia\.org" />
|
||||||
|
<!-- https://mail1.eff.org/pipermail/https-everywhere-rules/2012-June/001189.html -->
|
||||||
|
<exclusion pattern="^http://lists\.wikimedia\.org/pipermail(?:$|/)" />
|
||||||
|
<target host="wikimediafoundation.org" />
|
||||||
|
<target host="www.wikimediafoundation.org" />
|
||||||
|
|
||||||
|
<!-- Wikimedia projects (also some wikimedia.org subdomains) -->
|
||||||
|
<target host="wikibooks.org" />
|
||||||
|
<target host="*.wikibooks.org" />
|
||||||
|
<target host="wikidata.org" />
|
||||||
|
<target host="*.wikidata.org" />
|
||||||
|
<target host="wikinews.org" />
|
||||||
|
<target host="*.wikinews.org" />
|
||||||
|
<target host="wikipedia.org" />
|
||||||
|
<target host="*.wikipedia.org" />
|
||||||
|
<target host="wikiquote.org" />
|
||||||
|
<target host="*.wikiquote.org" />
|
||||||
|
<target host="wikisource.org" />
|
||||||
|
<target host="*.wikisource.org" />
|
||||||
|
<target host="wikiversity.org" />
|
||||||
|
<target host="*.wikiversity.org" />
|
||||||
|
<target host="wikivoyage.org" />
|
||||||
|
<target host="*.wikivoyage.org" />
|
||||||
|
<target host="wiktionary.org" />
|
||||||
|
<target host="*.wiktionary.org" />
|
||||||
|
|
||||||
|
<!-- Wikimedia chapters -->
|
||||||
|
<target host="wikimedia.ca" />
|
||||||
|
<target host="www.wikimedia.ca" />
|
||||||
|
|
||||||
|
<!-- Wikimedia Tool Labs -->
|
||||||
|
<target host="tools.wmflabs.org" />
|
||||||
|
<target host="icinga.wmflabs.org" />
|
||||||
|
<target host="ganglia.wmflabs.org" />
|
||||||
|
|
||||||
|
<!-- Not secured by server:
|
||||||
|
-->
|
||||||
|
<!--securecookie host="^\.wiki(books|ipedia)\.org$" name="^GeoIP$" /-->
|
||||||
|
|
||||||
|
<securecookie host="^^\.wik(?:ibooks|idata|imedia|inews|ipedia|iquote|isource|iversity|ivoyage|tionary)\.org$" name="^GeoIP$" />
|
||||||
|
<securecookie host="^([^@:/]+\.)?wik(ibooks|idata|inews|ipedia|iquote|isource|iversity|ivoyage|tionary)\.org$" name=".*" />
|
||||||
|
<securecookie host="^(species|commons|meta|incubator|wikitech).wikimedia.org$" name=".*" />
|
||||||
|
<securecookie host="^(?:www\.)?mediawiki\.org$" name=".*" />
|
||||||
|
<securecookie host="^wikimediafoundation.org$" name=".*" />
|
||||||
|
|
||||||
|
<rule from="^http://(en|fr)wp\.org/"
|
||||||
|
to="https://$1.wikipedia.org/wiki/" />
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?mediawiki\.org/"
|
||||||
|
to="https://www.mediawiki.org/" />
|
||||||
|
|
||||||
|
<rule from="^https?://download\.wikipedia\.org/"
|
||||||
|
to="https://dumps.wikimedia.org/" />
|
||||||
|
|
||||||
|
<rule from="^https?://(download|dataset2|sitemap)\.wikimedia\.org/"
|
||||||
|
to="https://dumps.wikimedia.org/" />
|
||||||
|
|
||||||
|
<rule from="^https?://(labs-ns[01]|virt0)\.wikimedia\.org/"
|
||||||
|
to="https://wikitech.wikimedia.org/" />
|
||||||
|
|
||||||
|
<rule from="^https?://noboard\.chapters\.wikimedia\.org/"
|
||||||
|
to="https://noboard-chapters.wikimedia.org/" />
|
||||||
|
|
||||||
|
<rule from="^https?://wg\.en\.wikipedia\.org/"
|
||||||
|
to="https://wg-en.wikipedia.org/" />
|
||||||
|
|
||||||
|
<rule from="^https?://arbcom\.(de|en|fi|nl)\.wikipedia\.org/"
|
||||||
|
to="https://arbcom-$1.wikipedia.org/" />
|
||||||
|
|
||||||
|
<rule from="^http://([^@:/]+\.)?wik(ibooks|idata|imedia|inews|ipedia|iquote|isource|iversity|ivoyage|tionary)\.org/"
|
||||||
|
to="https://$1wik$2.org/" />
|
||||||
|
|
||||||
|
<rule from="^http://(www\.)?wikimediafoundation\.org/"
|
||||||
|
to="https://$1wikimediafoundation.org/" />
|
||||||
|
|
||||||
|
<rule from="^http://(www\.)?wikimedia\.ca/"
|
||||||
|
to="https://wikimedia.ca/" />
|
||||||
|
|
||||||
|
<rule from="^http://([^@:/]+)\.wmflabs\.org/"
|
||||||
|
to="https://$1.wmflabs.org/" />
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,46 @@
|
||||||
|
<ruleset name="YouTube (partial)">
|
||||||
|
|
||||||
|
<target host="youtube.com" />
|
||||||
|
<target host="*.youtube.com" />
|
||||||
|
<exclusion pattern="^http://(?:www\.)?youtube\.com/crossdomain\.xml"/>
|
||||||
|
<exclusion pattern="^http://(?:www\.)?youtube\.com/(?:apiplayer|api_video_info)"/>
|
||||||
|
<exclusion pattern="^http://(?:[^/@:\.]+\.)?ytimg\.com/.*apiplayer[0-9]*\.swf"/>
|
||||||
|
<target host="*.ytimg.com" />
|
||||||
|
<target host="youtu.be" />
|
||||||
|
<target host="youtube-nocookie.com"/>
|
||||||
|
<target host="www.youtube-nocookie.com"/>
|
||||||
|
<target host="*.googlevideo.com"/>
|
||||||
|
<exclusion pattern="^http://([^/@:\.]+)\.googlevideo\.com/crossdomain\.xml"/>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Not secured by server:
|
||||||
|
-->
|
||||||
|
<!--securecookie host="^\.youtube\.com$" name="^(GEUP|PREF|VISITOR_INFO1_LIVE|YSC)$" /-->
|
||||||
|
|
||||||
|
<!-- observed ^. cookies:
|
||||||
|
- use_hitbox
|
||||||
|
- VISITOR_INFO1_LIVE
|
||||||
|
- recently_watched_video_id_list
|
||||||
|
- .youtube.com -->
|
||||||
|
<securecookie host="^\.youtube\.com" name=".*"/>
|
||||||
|
|
||||||
|
|
||||||
|
<rule from="^http://(www\.)?youtube\.com/"
|
||||||
|
to="https://$1youtube.com/"/>
|
||||||
|
|
||||||
|
<rule from="^http://(br|de|es|fr|il|img|insight|jp|m|nl|uk)\.youtube\.com/"
|
||||||
|
to="https://$1.youtube.com/"/>
|
||||||
|
|
||||||
|
<rule from="^http://([^/@:\.]+)\.ytimg\.com/"
|
||||||
|
to="https://$1.ytimg.com/"/>
|
||||||
|
|
||||||
|
<rule from="^http://youtu\.be/"
|
||||||
|
to="https://youtu.be/"/>
|
||||||
|
|
||||||
|
<rule from="^http://(?:www\.)?youtube-nocookie\.com/"
|
||||||
|
to="https://www.youtube-nocookie.com/"/>
|
||||||
|
|
||||||
|
<rule from="^http://([^/@:\.]+)\.googlevideo\.com/"
|
||||||
|
to="https://$1.googlevideo.com/"/>
|
||||||
|
|
||||||
|
</ruleset>
|
|
@ -1,3 +1,21 @@
|
||||||
|
'''
|
||||||
|
searx is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
searx is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with searx. If not, see < http://www.gnu.org/licenses/ >.
|
||||||
|
|
||||||
|
(C) 2013- by Adam Tauber, <asciimoo@gmail.com>
|
||||||
|
'''
|
||||||
|
|
||||||
|
# list of language codes
|
||||||
language_codes = (
|
language_codes = (
|
||||||
("ar_XA", "Arabic", "Arabia"),
|
("ar_XA", "Arabic", "Arabia"),
|
||||||
("bg_BG", "Bulgarian", "Bulgaria"),
|
("bg_BG", "Bulgarian", "Bulgaria"),
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
'''
|
||||||
|
searx is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
searx is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with searx. If not, see < http://www.gnu.org/licenses/ >.
|
||||||
|
|
||||||
|
(C) 2014 by Thomas Pointhuber, <thomas.pointhuber@gmx.at>
|
||||||
|
'''
|
||||||
|
|
||||||
|
from searx.languages import language_codes
|
||||||
|
from searx.engines import (
|
||||||
|
categories, engines, engine_shortcuts
|
||||||
|
)
|
||||||
|
import string
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
class Query(object):
|
||||||
|
"""parse query"""
|
||||||
|
|
||||||
|
def __init__(self, query, blocked_engines):
|
||||||
|
self.query = query
|
||||||
|
self.blocked_engines = []
|
||||||
|
|
||||||
|
if blocked_engines:
|
||||||
|
self.blocked_engines = blocked_engines
|
||||||
|
|
||||||
|
self.query_parts = []
|
||||||
|
self.engines = []
|
||||||
|
self.languages = []
|
||||||
|
|
||||||
|
# parse query, if tags are set, which
|
||||||
|
# change the serch engine or search-language
|
||||||
|
def parse_query(self):
|
||||||
|
self.query_parts = []
|
||||||
|
|
||||||
|
# split query, including whitespaces
|
||||||
|
raw_query_parts = re.split(r'(\s+)', self.query)
|
||||||
|
|
||||||
|
parse_next = True
|
||||||
|
|
||||||
|
for query_part in raw_query_parts:
|
||||||
|
if not parse_next:
|
||||||
|
self.query_parts[-1] += query_part
|
||||||
|
continue
|
||||||
|
|
||||||
|
parse_next = False
|
||||||
|
|
||||||
|
# part does only contain spaces, skip
|
||||||
|
if query_part.isspace()\
|
||||||
|
or query_part == '':
|
||||||
|
parse_next = True
|
||||||
|
self.query_parts.append(query_part)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# this force a language
|
||||||
|
if query_part[0] == ':':
|
||||||
|
lang = query_part[1:].lower()
|
||||||
|
|
||||||
|
# check if any language-code is equal with
|
||||||
|
# declared language-codes
|
||||||
|
for lc in language_codes:
|
||||||
|
lang_id, lang_name, country = map(str.lower, lc)
|
||||||
|
|
||||||
|
# if correct language-code is found
|
||||||
|
# set it as new search-language
|
||||||
|
if lang == lang_id\
|
||||||
|
or lang_id.startswith(lang)\
|
||||||
|
or lang == lang_name\
|
||||||
|
or lang == country:
|
||||||
|
parse_next = True
|
||||||
|
self.languages.append(lang)
|
||||||
|
break
|
||||||
|
|
||||||
|
# this force a engine or category
|
||||||
|
if query_part[0] == '!':
|
||||||
|
prefix = query_part[1:].replace('_', ' ')
|
||||||
|
|
||||||
|
# check if prefix is equal with engine shortcut
|
||||||
|
if prefix in engine_shortcuts\
|
||||||
|
and not engine_shortcuts[prefix] in self.blocked_engines:
|
||||||
|
parse_next = True
|
||||||
|
self.engines.append({'category': 'none',
|
||||||
|
'name': engine_shortcuts[prefix]})
|
||||||
|
|
||||||
|
# check if prefix is equal with engine name
|
||||||
|
elif prefix in engines\
|
||||||
|
and prefix not in self.blocked_engines:
|
||||||
|
parse_next = True
|
||||||
|
self.engines.append({'category': 'none',
|
||||||
|
'name': prefix})
|
||||||
|
|
||||||
|
# check if prefix is equal with categorie name
|
||||||
|
elif prefix in categories:
|
||||||
|
# using all engines for that search, which
|
||||||
|
# are declared under that categorie name
|
||||||
|
parse_next = True
|
||||||
|
self.engines.extend({'category': prefix,
|
||||||
|
'name': engine.name}
|
||||||
|
for engine in categories[prefix]
|
||||||
|
if engine not in self.blocked_engines)
|
||||||
|
|
||||||
|
# append query part to query_part list
|
||||||
|
self.query_parts.append(query_part)
|
||||||
|
|
||||||
|
def changeSearchQuery(self, search_query):
|
||||||
|
if len(self.query_parts):
|
||||||
|
self.query_parts[-1] = search_query
|
||||||
|
else:
|
||||||
|
self.query_parts.append(search_query)
|
||||||
|
|
||||||
|
def getSearchQuery(self):
|
||||||
|
if len(self.query_parts):
|
||||||
|
return self.query_parts[-1]
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def getFullQuery(self):
|
||||||
|
# get full querry including whitespaces
|
||||||
|
return string.join(self.query_parts, '')
|
|
@ -1,7 +1,293 @@
|
||||||
|
'''
|
||||||
|
searx is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
searx is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with searx. If not, see < http://www.gnu.org/licenses/ >.
|
||||||
|
|
||||||
|
(C) 2013- by Adam Tauber, <asciimoo@gmail.com>
|
||||||
|
'''
|
||||||
|
|
||||||
|
import grequests
|
||||||
|
import re
|
||||||
|
from itertools import izip_longest, chain
|
||||||
|
from datetime import datetime
|
||||||
|
from operator import itemgetter
|
||||||
|
from urlparse import urlparse, unquote
|
||||||
from searx.engines import (
|
from searx.engines import (
|
||||||
categories, engines, engine_shortcuts
|
categories, engines
|
||||||
)
|
)
|
||||||
from searx.languages import language_codes
|
from searx.languages import language_codes
|
||||||
|
from searx.utils import gen_useragent
|
||||||
|
from searx.query import Query
|
||||||
|
|
||||||
|
|
||||||
|
number_of_searches = 0
|
||||||
|
|
||||||
|
|
||||||
|
# get default reqest parameter
|
||||||
|
def default_request_params():
|
||||||
|
return {
|
||||||
|
'method': 'GET', 'headers': {}, 'data': {}, 'url': '', 'cookies': {}}
|
||||||
|
|
||||||
|
|
||||||
|
# create a callback wrapper for the search engine results
|
||||||
|
def make_callback(engine_name,
|
||||||
|
results,
|
||||||
|
suggestions,
|
||||||
|
answers,
|
||||||
|
infoboxes,
|
||||||
|
callback,
|
||||||
|
params):
|
||||||
|
|
||||||
|
# creating a callback wrapper for the search engine results
|
||||||
|
def process_callback(response, **kwargs):
|
||||||
|
cb_res = []
|
||||||
|
response.search_params = params
|
||||||
|
|
||||||
|
# callback
|
||||||
|
try:
|
||||||
|
search_results = callback(response)
|
||||||
|
except Exception, e:
|
||||||
|
# increase errors stats
|
||||||
|
engines[engine_name].stats['errors'] += 1
|
||||||
|
results[engine_name] = cb_res
|
||||||
|
|
||||||
|
# print engine name and specific error message
|
||||||
|
print '[E] Error with engine "{0}":\n\t{1}'.format(
|
||||||
|
engine_name, str(e))
|
||||||
|
return
|
||||||
|
|
||||||
|
# add results
|
||||||
|
for result in search_results:
|
||||||
|
result['engine'] = engine_name
|
||||||
|
|
||||||
|
# if it is a suggestion, add it to list of suggestions
|
||||||
|
if 'suggestion' in result:
|
||||||
|
# TODO type checks
|
||||||
|
suggestions.add(result['suggestion'])
|
||||||
|
continue
|
||||||
|
|
||||||
|
# if it is an answer, add it to list of answers
|
||||||
|
if 'answer' in result:
|
||||||
|
answers.add(result['answer'])
|
||||||
|
continue
|
||||||
|
|
||||||
|
# if it is an infobox, add it to list of infoboxes
|
||||||
|
if 'infobox' in result:
|
||||||
|
infoboxes.append(result)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# append result
|
||||||
|
cb_res.append(result)
|
||||||
|
|
||||||
|
results[engine_name] = cb_res
|
||||||
|
|
||||||
|
# update stats with current page-load-time
|
||||||
|
engines[engine_name].stats['page_load_time'] += \
|
||||||
|
(datetime.now() - params['started']).total_seconds()
|
||||||
|
|
||||||
|
return process_callback
|
||||||
|
|
||||||
|
|
||||||
|
# return the meaningful length of the content for a result
|
||||||
|
def content_result_len(content):
|
||||||
|
if isinstance(content, basestring):
|
||||||
|
content = re.sub('[,;:!?\./\\\\ ()-_]', '', content)
|
||||||
|
return len(content)
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
# score results and remove duplications
|
||||||
|
def score_results(results):
|
||||||
|
# calculate scoring parameters
|
||||||
|
flat_res = filter(
|
||||||
|
None, chain.from_iterable(izip_longest(*results.values())))
|
||||||
|
flat_len = len(flat_res)
|
||||||
|
engines_len = len(results)
|
||||||
|
|
||||||
|
results = []
|
||||||
|
|
||||||
|
# pass 1: deduplication + scoring
|
||||||
|
for i, res in enumerate(flat_res):
|
||||||
|
|
||||||
|
res['parsed_url'] = urlparse(res['url'])
|
||||||
|
|
||||||
|
res['host'] = res['parsed_url'].netloc
|
||||||
|
|
||||||
|
if res['host'].startswith('www.'):
|
||||||
|
res['host'] = res['host'].replace('www.', '', 1)
|
||||||
|
|
||||||
|
res['engines'] = [res['engine']]
|
||||||
|
|
||||||
|
weight = 1.0
|
||||||
|
|
||||||
|
# strip multiple spaces and cariage returns from content
|
||||||
|
if res.get('content'):
|
||||||
|
res['content'] = re.sub(' +', ' ',
|
||||||
|
res['content'].strip().replace('\n', ''))
|
||||||
|
|
||||||
|
# get weight of this engine if possible
|
||||||
|
if hasattr(engines[res['engine']], 'weight'):
|
||||||
|
weight = float(engines[res['engine']].weight)
|
||||||
|
|
||||||
|
# calculate score for that engine
|
||||||
|
score = int((flat_len - i) / engines_len) * weight + 1
|
||||||
|
|
||||||
|
# check for duplicates
|
||||||
|
duplicated = False
|
||||||
|
for new_res in results:
|
||||||
|
# remove / from the end of the url if required
|
||||||
|
p1 = res['parsed_url'].path[:-1]\
|
||||||
|
if res['parsed_url'].path.endswith('/')\
|
||||||
|
else res['parsed_url'].path
|
||||||
|
p2 = new_res['parsed_url'].path[:-1]\
|
||||||
|
if new_res['parsed_url'].path.endswith('/')\
|
||||||
|
else new_res['parsed_url'].path
|
||||||
|
|
||||||
|
# check if that result is a duplicate
|
||||||
|
if res['host'] == new_res['host'] and\
|
||||||
|
unquote(p1) == unquote(p2) and\
|
||||||
|
res['parsed_url'].query == new_res['parsed_url'].query and\
|
||||||
|
res.get('template') == new_res.get('template'):
|
||||||
|
duplicated = new_res
|
||||||
|
break
|
||||||
|
|
||||||
|
# merge duplicates together
|
||||||
|
if duplicated:
|
||||||
|
# using content with more text
|
||||||
|
if content_result_len(res.get('content', '')) >\
|
||||||
|
content_result_len(duplicated.get('content', '')):
|
||||||
|
duplicated['content'] = res['content']
|
||||||
|
|
||||||
|
# increase result-score
|
||||||
|
duplicated['score'] += score
|
||||||
|
|
||||||
|
# add engine to list of result-engines
|
||||||
|
duplicated['engines'].append(res['engine'])
|
||||||
|
|
||||||
|
# using https if possible
|
||||||
|
if duplicated['parsed_url'].scheme == 'https':
|
||||||
|
continue
|
||||||
|
elif res['parsed_url'].scheme == 'https':
|
||||||
|
duplicated['url'] = res['parsed_url'].geturl()
|
||||||
|
duplicated['parsed_url'] = res['parsed_url']
|
||||||
|
|
||||||
|
# if there is no duplicate found, append result
|
||||||
|
else:
|
||||||
|
res['score'] = score
|
||||||
|
results.append(res)
|
||||||
|
|
||||||
|
results = sorted(results, key=itemgetter('score'), reverse=True)
|
||||||
|
|
||||||
|
# pass 2 : group results by category and template
|
||||||
|
gresults = []
|
||||||
|
categoryPositions = {}
|
||||||
|
|
||||||
|
for i, res in enumerate(results):
|
||||||
|
# FIXME : handle more than one category per engine
|
||||||
|
category = engines[res['engine']].categories[0] + ':' + ''\
|
||||||
|
if 'template' not in res\
|
||||||
|
else res['template']
|
||||||
|
|
||||||
|
current = None if category not in categoryPositions\
|
||||||
|
else categoryPositions[category]
|
||||||
|
|
||||||
|
# group with previous results using the same category
|
||||||
|
# if the group can accept more result and is not too far
|
||||||
|
# from the current position
|
||||||
|
if current is not None and (current['count'] > 0)\
|
||||||
|
and (len(gresults) - current['index'] < 20):
|
||||||
|
# group with the previous results using
|
||||||
|
# the same category with this one
|
||||||
|
index = current['index']
|
||||||
|
gresults.insert(index, res)
|
||||||
|
|
||||||
|
# update every index after the current one
|
||||||
|
# (including the current one)
|
||||||
|
for k in categoryPositions:
|
||||||
|
v = categoryPositions[k]['index']
|
||||||
|
if v >= index:
|
||||||
|
categoryPositions[k]['index'] = v+1
|
||||||
|
|
||||||
|
# update this category
|
||||||
|
current['count'] -= 1
|
||||||
|
|
||||||
|
else:
|
||||||
|
# same category
|
||||||
|
gresults.append(res)
|
||||||
|
|
||||||
|
# update categoryIndex
|
||||||
|
categoryPositions[category] = {'index': len(gresults), 'count': 8}
|
||||||
|
|
||||||
|
# return gresults
|
||||||
|
return gresults
|
||||||
|
|
||||||
|
|
||||||
|
def merge_two_infoboxes(infobox1, infobox2):
|
||||||
|
if 'urls' in infobox2:
|
||||||
|
urls1 = infobox1.get('urls', None)
|
||||||
|
if urls1 is None:
|
||||||
|
urls1 = []
|
||||||
|
infobox1.set('urls', urls1)
|
||||||
|
|
||||||
|
urlSet = set()
|
||||||
|
for url in infobox1.get('urls', []):
|
||||||
|
urlSet.add(url.get('url', None))
|
||||||
|
|
||||||
|
for url in infobox2.get('urls', []):
|
||||||
|
if url.get('url', None) not in urlSet:
|
||||||
|
urls1.append(url)
|
||||||
|
|
||||||
|
if 'attributes' in infobox2:
|
||||||
|
attributes1 = infobox1.get('attributes', None)
|
||||||
|
if attributes1 is None:
|
||||||
|
attributes1 = []
|
||||||
|
infobox1.set('attributes', attributes1)
|
||||||
|
|
||||||
|
attributeSet = set()
|
||||||
|
for attribute in infobox1.get('attributes', []):
|
||||||
|
if attribute.get('label', None) not in attributeSet:
|
||||||
|
attributeSet.add(attribute.get('label', None))
|
||||||
|
|
||||||
|
for attribute in infobox2.get('attributes', []):
|
||||||
|
attributes1.append(attribute)
|
||||||
|
|
||||||
|
if 'content' in infobox2:
|
||||||
|
content1 = infobox1.get('content', None)
|
||||||
|
content2 = infobox2.get('content', '')
|
||||||
|
if content1 is not None:
|
||||||
|
if content_result_len(content2) > content_result_len(content1):
|
||||||
|
infobox1['content'] = content2
|
||||||
|
else:
|
||||||
|
infobox1.set('content', content2)
|
||||||
|
|
||||||
|
|
||||||
|
def merge_infoboxes(infoboxes):
|
||||||
|
results = []
|
||||||
|
infoboxes_id = {}
|
||||||
|
for infobox in infoboxes:
|
||||||
|
add_infobox = True
|
||||||
|
infobox_id = infobox.get('id', None)
|
||||||
|
if infobox_id is not None:
|
||||||
|
existingIndex = infoboxes_id.get(infobox_id, None)
|
||||||
|
if existingIndex is not None:
|
||||||
|
merge_two_infoboxes(results[existingIndex], infobox)
|
||||||
|
add_infobox = False
|
||||||
|
|
||||||
|
if add_infobox:
|
||||||
|
results.append(infobox)
|
||||||
|
infoboxes_id[infobox_id] = len(results)-1
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
class Search(object):
|
class Search(object):
|
||||||
|
@ -9,6 +295,7 @@ class Search(object):
|
||||||
"""Search information container"""
|
"""Search information container"""
|
||||||
|
|
||||||
def __init__(self, request):
|
def __init__(self, request):
|
||||||
|
# init vars
|
||||||
super(Search, self).__init__()
|
super(Search, self).__init__()
|
||||||
self.query = None
|
self.query = None
|
||||||
self.engines = []
|
self.engines = []
|
||||||
|
@ -16,18 +303,25 @@ class Search(object):
|
||||||
self.paging = False
|
self.paging = False
|
||||||
self.pageno = 1
|
self.pageno = 1
|
||||||
self.lang = 'all'
|
self.lang = 'all'
|
||||||
|
|
||||||
|
# set blocked engines
|
||||||
if request.cookies.get('blocked_engines'):
|
if request.cookies.get('blocked_engines'):
|
||||||
self.blocked_engines = request.cookies['blocked_engines'].split(',') # noqa
|
self.blocked_engines = request.cookies['blocked_engines'].split(',') # noqa
|
||||||
else:
|
else:
|
||||||
self.blocked_engines = []
|
self.blocked_engines = []
|
||||||
|
|
||||||
self.results = []
|
self.results = []
|
||||||
self.suggestions = []
|
self.suggestions = []
|
||||||
|
self.answers = []
|
||||||
|
self.infoboxes = []
|
||||||
self.request_data = {}
|
self.request_data = {}
|
||||||
|
|
||||||
|
# set specific language if set
|
||||||
if request.cookies.get('language')\
|
if request.cookies.get('language')\
|
||||||
and request.cookies['language'] in (x[0] for x in language_codes):
|
and request.cookies['language'] in (x[0] for x in language_codes):
|
||||||
self.lang = request.cookies['language']
|
self.lang = request.cookies['language']
|
||||||
|
|
||||||
|
# set request method
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
self.request_data = request.form
|
self.request_data = request.form
|
||||||
else:
|
else:
|
||||||
|
@ -37,78 +331,177 @@ class Search(object):
|
||||||
if not self.request_data.get('q'):
|
if not self.request_data.get('q'):
|
||||||
raise Exception('noquery')
|
raise Exception('noquery')
|
||||||
|
|
||||||
self.query = self.request_data['q']
|
# set pagenumber
|
||||||
|
|
||||||
pageno_param = self.request_data.get('pageno', '1')
|
pageno_param = self.request_data.get('pageno', '1')
|
||||||
if not pageno_param.isdigit() or int(pageno_param) < 1:
|
if not pageno_param.isdigit() or int(pageno_param) < 1:
|
||||||
raise Exception('wrong pagenumber')
|
raise Exception('wrong pagenumber')
|
||||||
|
|
||||||
self.pageno = int(pageno_param)
|
self.pageno = int(pageno_param)
|
||||||
|
|
||||||
self.parse_query()
|
# parse query, if tags are set, which change
|
||||||
|
# the serch engine or search-language
|
||||||
|
query_obj = Query(self.request_data['q'], self.blocked_engines)
|
||||||
|
query_obj.parse_query()
|
||||||
|
|
||||||
|
# set query
|
||||||
|
self.query = query_obj.getSearchQuery()
|
||||||
|
|
||||||
|
# get last selected language in query, if possible
|
||||||
|
# TODO support search with multible languages
|
||||||
|
if len(query_obj.languages):
|
||||||
|
self.lang = query_obj.languages[-1]
|
||||||
|
|
||||||
|
self.engines = query_obj.engines
|
||||||
|
|
||||||
self.categories = []
|
self.categories = []
|
||||||
|
|
||||||
|
# if engines are calculated from query,
|
||||||
|
# set categories by using that informations
|
||||||
if self.engines:
|
if self.engines:
|
||||||
self.categories = list(set(engine['category']
|
self.categories = list(set(engine['category']
|
||||||
for engine in self.engines))
|
for engine in self.engines))
|
||||||
|
|
||||||
|
# otherwise, using defined categories to
|
||||||
|
# calculate which engines should be used
|
||||||
else:
|
else:
|
||||||
|
# set used categories
|
||||||
for pd_name, pd in self.request_data.items():
|
for pd_name, pd in self.request_data.items():
|
||||||
if pd_name.startswith('category_'):
|
if pd_name.startswith('category_'):
|
||||||
category = pd_name[9:]
|
category = pd_name[9:]
|
||||||
if not category in categories:
|
# if category is not found in list, skip
|
||||||
|
if category not in categories:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# add category to list
|
||||||
self.categories.append(category)
|
self.categories.append(category)
|
||||||
|
|
||||||
|
# if no category is specified for this search,
|
||||||
|
# using user-defined default-configuration which
|
||||||
|
# (is stored in cookie)
|
||||||
if not self.categories:
|
if not self.categories:
|
||||||
cookie_categories = request.cookies.get('categories', '')
|
cookie_categories = request.cookies.get('categories', '')
|
||||||
cookie_categories = cookie_categories.split(',')
|
cookie_categories = cookie_categories.split(',')
|
||||||
for ccateg in cookie_categories:
|
for ccateg in cookie_categories:
|
||||||
if ccateg in categories:
|
if ccateg in categories:
|
||||||
self.categories.append(ccateg)
|
self.categories.append(ccateg)
|
||||||
|
|
||||||
|
# if still no category is specified, using general
|
||||||
|
# as default-category
|
||||||
if not self.categories:
|
if not self.categories:
|
||||||
self.categories = ['general']
|
self.categories = ['general']
|
||||||
|
|
||||||
|
# using all engines for that search, which are
|
||||||
|
# declared under the specific categories
|
||||||
for categ in self.categories:
|
for categ in self.categories:
|
||||||
self.engines.extend({'category': categ,
|
self.engines.extend({'category': categ,
|
||||||
'name': x.name}
|
'name': x.name}
|
||||||
for x in categories[categ]
|
for x in categories[categ]
|
||||||
if not x.name in self.blocked_engines)
|
if x.name not in self.blocked_engines)
|
||||||
|
|
||||||
def parse_query(self):
|
# do search-request
|
||||||
query_parts = self.query.split()
|
def search(self, request):
|
||||||
modified = False
|
global number_of_searches
|
||||||
if query_parts[0].startswith(':'):
|
|
||||||
lang = query_parts[0][1:].lower()
|
|
||||||
|
|
||||||
for lc in language_codes:
|
# init vars
|
||||||
lang_id, lang_name, country = map(str.lower, lc)
|
requests = []
|
||||||
if lang == lang_id\
|
results = {}
|
||||||
or lang_id.startswith(lang)\
|
suggestions = set()
|
||||||
or lang == lang_name\
|
answers = set()
|
||||||
or lang == country:
|
infoboxes = []
|
||||||
self.lang = lang
|
|
||||||
modified = True
|
|
||||||
break
|
|
||||||
|
|
||||||
elif query_parts[0].startswith('!'):
|
# increase number of searches
|
||||||
prefix = query_parts[0][1:].replace('_', ' ')
|
number_of_searches += 1
|
||||||
|
|
||||||
if prefix in engine_shortcuts\
|
# set default useragent
|
||||||
and not engine_shortcuts[prefix] in self.blocked_engines:
|
# user_agent = request.headers.get('User-Agent', '')
|
||||||
modified = True
|
user_agent = gen_useragent()
|
||||||
self.engines.append({'category': 'none',
|
|
||||||
'name': engine_shortcuts[prefix]})
|
# start search-reqest for all selected engines
|
||||||
elif prefix in engines\
|
for selected_engine in self.engines:
|
||||||
and not prefix in self.blocked_engines:
|
if selected_engine['name'] not in engines:
|
||||||
modified = True
|
continue
|
||||||
self.engines.append({'category': 'none',
|
|
||||||
'name': prefix})
|
engine = engines[selected_engine['name']]
|
||||||
elif prefix in categories:
|
|
||||||
modified = True
|
# if paging is not supported, skip
|
||||||
self.engines.extend({'category': prefix,
|
if self.pageno > 1 and not engine.paging:
|
||||||
'name': engine.name}
|
continue
|
||||||
for engine in categories[prefix]
|
|
||||||
if not engine in self.blocked_engines)
|
# if search-language is set and engine does not
|
||||||
if modified:
|
# provide language-support, skip
|
||||||
self.query = self.query.replace(query_parts[0], '', 1).strip()
|
if self.lang != 'all' and not engine.language_support:
|
||||||
self.parse_query()
|
continue
|
||||||
|
|
||||||
|
# set default request parameters
|
||||||
|
request_params = default_request_params()
|
||||||
|
request_params['headers']['User-Agent'] = user_agent
|
||||||
|
request_params['category'] = selected_engine['category']
|
||||||
|
request_params['started'] = datetime.now()
|
||||||
|
request_params['pageno'] = self.pageno
|
||||||
|
request_params['language'] = self.lang
|
||||||
|
|
||||||
|
# update request parameters dependent on
|
||||||
|
# search-engine (contained in engines folder)
|
||||||
|
request_params = engine.request(self.query.encode('utf-8'),
|
||||||
|
request_params)
|
||||||
|
|
||||||
|
if request_params['url'] is None:
|
||||||
|
# TODO add support of offline engines
|
||||||
|
pass
|
||||||
|
|
||||||
|
# create a callback wrapper for the search engine results
|
||||||
|
callback = make_callback(
|
||||||
|
selected_engine['name'],
|
||||||
|
results,
|
||||||
|
suggestions,
|
||||||
|
answers,
|
||||||
|
infoboxes,
|
||||||
|
engine.response,
|
||||||
|
request_params
|
||||||
|
)
|
||||||
|
|
||||||
|
# create dictionary which contain all
|
||||||
|
# informations about the request
|
||||||
|
request_args = dict(
|
||||||
|
headers=request_params['headers'],
|
||||||
|
hooks=dict(response=callback),
|
||||||
|
cookies=request_params['cookies'],
|
||||||
|
timeout=engine.timeout
|
||||||
|
)
|
||||||
|
|
||||||
|
# specific type of request (GET or POST)
|
||||||
|
if request_params['method'] == 'GET':
|
||||||
|
req = grequests.get
|
||||||
|
else:
|
||||||
|
req = grequests.post
|
||||||
|
request_args['data'] = request_params['data']
|
||||||
|
|
||||||
|
# ignoring empty urls
|
||||||
|
if not request_params['url']:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# append request to list
|
||||||
|
requests.append(req(request_params['url'], **request_args))
|
||||||
|
|
||||||
|
# send all search-request
|
||||||
|
grequests.map(requests)
|
||||||
|
|
||||||
|
# update engine-specific stats
|
||||||
|
for engine_name, engine_results in results.items():
|
||||||
|
engines[engine_name].stats['search_count'] += 1
|
||||||
|
engines[engine_name].stats['result_count'] += len(engine_results)
|
||||||
|
|
||||||
|
# score results and remove duplications
|
||||||
|
results = score_results(results)
|
||||||
|
|
||||||
|
# merge infoboxes according to their ids
|
||||||
|
infoboxes = merge_infoboxes(infoboxes)
|
||||||
|
|
||||||
|
# update engine stats, using calculated score
|
||||||
|
for result in results:
|
||||||
|
for res_engine in result['engines']:
|
||||||
|
engines[result['engine']]\
|
||||||
|
.stats['score_count'] += result['score']
|
||||||
|
|
||||||
|
# return results, suggestions, answers and infoboxes
|
||||||
|
return results, suggestions, answers, infoboxes
|
||||||
|
|
|
@ -1,22 +1,31 @@
|
||||||
server:
|
server:
|
||||||
port : 8888
|
port : 8888
|
||||||
secret_key : "ultrasecretkey" # change this!
|
secret_key : "ultrasecretkey" # change this!
|
||||||
debug : True
|
debug : False # Debug mode, only for development
|
||||||
request_timeout : 2.0 # seconds
|
request_timeout : 2.0 # seconds
|
||||||
base_url : False
|
base_url : False # Set custom base_url. Possible values: False or "https://your.custom.host/location/"
|
||||||
|
themes_path : "" # Custom ui themes path
|
||||||
|
default_theme : default # ui theme
|
||||||
|
https_rewrite : True # Force rewrite result urls. See searx/https_rewrite.py
|
||||||
|
useragent_suffix : "" # suffix of searx_useragent, could contain informations like an email address to the administrator
|
||||||
|
|
||||||
engines:
|
engines:
|
||||||
- name : wikipedia
|
- name : wikipedia
|
||||||
engine : wikipedia
|
engine : mediawiki
|
||||||
number_of_results : 1
|
|
||||||
paging : False
|
|
||||||
shortcut : wp
|
shortcut : wp
|
||||||
|
base_url : 'https://{language}.wikipedia.org/'
|
||||||
|
number_of_results : 1
|
||||||
|
|
||||||
- name : bing
|
- name : bing
|
||||||
engine : bing
|
engine : bing
|
||||||
locale : en-US
|
locale : en-US
|
||||||
shortcut : bi
|
shortcut : bi
|
||||||
|
|
||||||
|
- name : bing images
|
||||||
|
engine : bing_images
|
||||||
|
locale : en-US
|
||||||
|
shortcut : bii
|
||||||
|
|
||||||
- name : bing news
|
- name : bing news
|
||||||
engine : bing_news
|
engine : bing_news
|
||||||
locale : en-US
|
locale : en-US
|
||||||
|
@ -29,7 +38,6 @@ engines:
|
||||||
|
|
||||||
- name : deviantart
|
- name : deviantart
|
||||||
engine : deviantart
|
engine : deviantart
|
||||||
categories : images
|
|
||||||
shortcut : da
|
shortcut : da
|
||||||
timeout: 3.0
|
timeout: 3.0
|
||||||
|
|
||||||
|
@ -37,15 +45,25 @@ engines:
|
||||||
engine : duckduckgo_definitions
|
engine : duckduckgo_definitions
|
||||||
shortcut : ddd
|
shortcut : ddd
|
||||||
|
|
||||||
|
- name : wikidata
|
||||||
|
engine : wikidata
|
||||||
|
shortcut : wd
|
||||||
|
|
||||||
- name : duckduckgo
|
- name : duckduckgo
|
||||||
engine : duckduckgo
|
engine : duckduckgo
|
||||||
locale : en-us
|
|
||||||
shortcut : ddg
|
shortcut : ddg
|
||||||
|
|
||||||
- name : filecrop
|
# api-key required: http://www.faroo.com/hp/api/api.html#key
|
||||||
engine : filecrop
|
# - name : faroo
|
||||||
categories : files
|
# engine : faroo
|
||||||
shortcut : fc
|
# shortcut : fa
|
||||||
|
# api_key : 'apikey' # required!
|
||||||
|
|
||||||
|
# down - website is under criminal investigation by the UK
|
||||||
|
# - name : filecrop
|
||||||
|
# engine : filecrop
|
||||||
|
# categories : files
|
||||||
|
# shortcut : fc
|
||||||
|
|
||||||
- name : flickr
|
- name : flickr
|
||||||
engine : flickr
|
engine : flickr
|
||||||
|
@ -53,9 +71,12 @@ engines:
|
||||||
shortcut : fl
|
shortcut : fl
|
||||||
timeout: 3.0
|
timeout: 3.0
|
||||||
|
|
||||||
|
- name : general-file
|
||||||
|
engine : generalfile
|
||||||
|
shortcut : gf
|
||||||
|
|
||||||
- name : github
|
- name : github
|
||||||
engine : github
|
engine : github
|
||||||
categories : it
|
|
||||||
shortcut : gh
|
shortcut : gh
|
||||||
|
|
||||||
- name : google
|
- name : google
|
||||||
|
@ -70,25 +91,24 @@ engines:
|
||||||
engine : google_news
|
engine : google_news
|
||||||
shortcut : gon
|
shortcut : gon
|
||||||
|
|
||||||
|
- name : openstreetmap
|
||||||
|
engine : openstreetmap
|
||||||
|
shortcut : osm
|
||||||
|
|
||||||
- name : piratebay
|
- name : piratebay
|
||||||
engine : piratebay
|
engine : piratebay
|
||||||
categories : videos, music, files
|
|
||||||
shortcut : tpb
|
shortcut : tpb
|
||||||
|
|
||||||
- name : soundcloud
|
- name : soundcloud
|
||||||
engine : soundcloud
|
engine : soundcloud
|
||||||
categories : music
|
|
||||||
shortcut : sc
|
shortcut : sc
|
||||||
|
|
||||||
- name : stackoverflow
|
- name : stackoverflow
|
||||||
engine : stackoverflow
|
engine : stackoverflow
|
||||||
categories : it
|
|
||||||
shortcut : st
|
shortcut : st
|
||||||
|
|
||||||
- name : startpage
|
- name : startpage
|
||||||
engine : startpage
|
engine : startpage
|
||||||
base_url : 'https://startpage.com/'
|
|
||||||
search_url : 'https://startpage.com/do/search'
|
|
||||||
shortcut : sp
|
shortcut : sp
|
||||||
|
|
||||||
# +30% page load time
|
# +30% page load time
|
||||||
|
@ -99,15 +119,14 @@ engines:
|
||||||
|
|
||||||
- name : twitter
|
- name : twitter
|
||||||
engine : twitter
|
engine : twitter
|
||||||
categories : social media
|
|
||||||
shortcut : tw
|
shortcut : tw
|
||||||
|
|
||||||
# maybe in a fun category
|
# maybe in a fun category
|
||||||
# - name : uncyclopedia
|
# - name : uncyclopedia
|
||||||
# engine : mediawiki
|
# engine : mediawiki
|
||||||
# categories : general
|
|
||||||
# shortcut : unc
|
# shortcut : unc
|
||||||
# url : https://uncyclopedia.wikia.com/
|
# base_url : https://uncyclopedia.wikia.com/
|
||||||
|
# number_of_results : 5
|
||||||
|
|
||||||
# tmp suspended - too slow, too many errors
|
# tmp suspended - too slow, too many errors
|
||||||
# - name : urbandictionary
|
# - name : urbandictionary
|
||||||
|
@ -128,24 +147,24 @@ engines:
|
||||||
|
|
||||||
- name : youtube
|
- name : youtube
|
||||||
engine : youtube
|
engine : youtube
|
||||||
categories : videos
|
|
||||||
shortcut : yt
|
shortcut : yt
|
||||||
|
|
||||||
- name : dailymotion
|
- name : dailymotion
|
||||||
engine : dailymotion
|
engine : dailymotion
|
||||||
locale : en_US
|
|
||||||
categories : videos
|
|
||||||
shortcut : dm
|
shortcut : dm
|
||||||
|
|
||||||
- name : vimeo
|
- name : vimeo
|
||||||
engine : vimeo
|
engine : vimeo
|
||||||
categories : videos
|
locale : en-US
|
||||||
results_xpath : //div[@id="browse_content"]/ol/li
|
|
||||||
url_xpath : ./a/@href
|
|
||||||
title_xpath : ./a/div[@class="data"]/p[@class="title"]/text()
|
|
||||||
content_xpath : ./a/img/@src
|
|
||||||
shortcut : vm
|
shortcut : vm
|
||||||
|
|
||||||
|
# - name : yacy
|
||||||
|
# engine : yacy
|
||||||
|
# shortcut : ya
|
||||||
|
# base_url : 'http://localhost:8090'
|
||||||
|
# number_of_results : 5
|
||||||
|
# timeout: 3.0
|
||||||
|
|
||||||
locales:
|
locales:
|
||||||
en : English
|
en : English
|
||||||
de : Deutsch
|
de : Deutsch
|
||||||
|
@ -154,3 +173,4 @@ locales:
|
||||||
es : Español
|
es : Español
|
||||||
it : Italiano
|
it : Italiano
|
||||||
nl : Nederlands
|
nl : Nederlands
|
||||||
|
ja : 日本語 (Japanese)
|
||||||
|
|
|
@ -4,6 +4,9 @@ server:
|
||||||
debug : False
|
debug : False
|
||||||
request_timeout : 3.0 # seconds
|
request_timeout : 3.0 # seconds
|
||||||
base_url: False
|
base_url: False
|
||||||
|
themes_path : ""
|
||||||
|
default_theme : default
|
||||||
|
https_rewrite : True
|
||||||
|
|
||||||
engines:
|
engines:
|
||||||
- name : general_dummy
|
- name : general_dummy
|
||||||
|
|
|
@ -0,0 +1,582 @@
|
||||||
|
* {
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="search"] {
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
color: #666;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 0;
|
||||||
|
background: #EEE;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
position: relative;
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title h1 {
|
||||||
|
background: url(../img/searx.png) no-repeat;
|
||||||
|
width: 319px;
|
||||||
|
height: 62px;
|
||||||
|
text-indent: -9999px;
|
||||||
|
margin: 0.5em auto 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
max-width: 55em;
|
||||||
|
text-align: center;
|
||||||
|
background: rgba(255,255,255,0.6);
|
||||||
|
padding: 4em 2em;
|
||||||
|
margin: 7% auto 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center.search {
|
||||||
|
position: static;
|
||||||
|
width: auto;
|
||||||
|
background: none;
|
||||||
|
margin: auto;
|
||||||
|
padding-top: 1.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1001px) {
|
||||||
|
.center:after {
|
||||||
|
content: "";
|
||||||
|
z-index: -1;
|
||||||
|
background: url(../img/bg-body-index.jpg) no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
position: fixed;
|
||||||
|
}
|
||||||
|
.center.search:after {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.autocompleter-choices {
|
||||||
|
position: absolute;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: #FFF;
|
||||||
|
}
|
||||||
|
.autocompleter-choices li {
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
}
|
||||||
|
.autocompleter-choices li:hover {
|
||||||
|
background: #3498DB;
|
||||||
|
color: #FFF;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#categories {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top_margin {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -3.5em;
|
||||||
|
width: 100%;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top_margin a {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 1em;
|
||||||
|
color: #FFF;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.top_margin a:hover,
|
||||||
|
.top_margin a:focus {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1000px) {
|
||||||
|
.center { background: none; }
|
||||||
|
.top_margin a {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox_container { margin-top: 1.5em; }
|
||||||
|
.checkbox_container label {
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
color: #333;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
.checkbox_container label:hover {
|
||||||
|
background: #3498DB;
|
||||||
|
color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox_container input[type="checkbox"] {
|
||||||
|
position: absolute;
|
||||||
|
top: -9999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox_container input[type="checkbox"]:checked + label {
|
||||||
|
background: #3498DB;
|
||||||
|
color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
#categories_container > div {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#categories .hidden {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 1em;
|
||||||
|
left: 0;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 0.9em;
|
||||||
|
font-style: italic;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
#categories:hover .hidden {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 900px) {
|
||||||
|
#categories_container { letter-spacing: -5px; }
|
||||||
|
#categories_container > div {
|
||||||
|
letter-spacing: normal;
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
.checkbox_container {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.checkbox_container label {
|
||||||
|
display: block;
|
||||||
|
background: #CCC;
|
||||||
|
padding: 1em;
|
||||||
|
border: 1px solid #FFF;
|
||||||
|
}
|
||||||
|
.top_margin { position: static; }
|
||||||
|
#categories .hidden {
|
||||||
|
position: static;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 900px) and (min-width: 501px) {
|
||||||
|
#categories_container > div {
|
||||||
|
width: 31%;
|
||||||
|
margin-left: 2.333%;
|
||||||
|
}
|
||||||
|
#categories_container > div:nth-child(3n+1) { margin-left: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 500px) {
|
||||||
|
#categories_container > div {
|
||||||
|
width: 48%;
|
||||||
|
margin-left: 2%;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
#categories_container > div:nth-child(2n+1) { margin-left: 0; }
|
||||||
|
.title h1 {
|
||||||
|
background: url(../img/searx-mobile.png) no-repeat;
|
||||||
|
width: 200px;
|
||||||
|
height: 39px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#search_wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.q {
|
||||||
|
padding: 0.5em 3em 0.5em 1em;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 1.5em;
|
||||||
|
border: 0;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search_submit {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
border: 0;
|
||||||
|
background:url("../img/search-icon.png") no-repeat scroll center center / 65% auto #3498db;
|
||||||
|
text-indent: -9999px;
|
||||||
|
width: 5em;
|
||||||
|
height: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search_submit:hover,
|
||||||
|
#search_submit:focus {
|
||||||
|
background-color: #0665A2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar {
|
||||||
|
background: #3498db;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 15em;
|
||||||
|
height: 100%;
|
||||||
|
padding: 1.5em;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 1.5em;
|
||||||
|
width: 15em;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1;
|
||||||
|
padding: 0 1.5em;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.right a {
|
||||||
|
color: #FFF;
|
||||||
|
display: block;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.right a:hover,
|
||||||
|
.right a:focus {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preferences {
|
||||||
|
background: url(../img/preference-icon.png) no-repeat right 0 / 12% auto;
|
||||||
|
padding-right: 1.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preferences:hover,
|
||||||
|
#preferences:focus {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#search_url input {
|
||||||
|
border: 0;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar > div {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar form {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar input[type="submit"] {
|
||||||
|
background: #CCC;
|
||||||
|
border: 0;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar input[type="submit"]:hover,
|
||||||
|
#sidebar input[type="submit"]:focus {
|
||||||
|
color: #FFF;
|
||||||
|
background-color: #0665A2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#results {
|
||||||
|
padding-right: 17em;
|
||||||
|
padding-left: 2em;
|
||||||
|
padding: 0 17em 0 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result p {
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result .content {
|
||||||
|
margin: 0;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result .url {
|
||||||
|
margin-top: 0;
|
||||||
|
color: #FF6530;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result .favicon {
|
||||||
|
float: left;
|
||||||
|
position: relative;
|
||||||
|
top: 0.5em;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.definition_result {
|
||||||
|
background: #CCC;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.definition_result .result_title,
|
||||||
|
.definition_result p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result_title {
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result_title a {
|
||||||
|
color: #3498db;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result_title a:hover,
|
||||||
|
.result_title a:focus {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cache_link {
|
||||||
|
color: #666;
|
||||||
|
font-size: 0.9em;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search.center {
|
||||||
|
padding-right: 17em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#suggestions { margin-bottom: 1em; }
|
||||||
|
|
||||||
|
#suggestions span { color: #666; }
|
||||||
|
|
||||||
|
#suggestions form {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#suggestions input[type="submit"] {
|
||||||
|
color: #333;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
border: 0;
|
||||||
|
background: #CCC;
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
#suggestions input[type="submit"]:hover,
|
||||||
|
#suggestions input[type="submit"]:focus {
|
||||||
|
background: #3498db;
|
||||||
|
color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pagination {
|
||||||
|
margin: 1.5em 0 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pagination form + form {
|
||||||
|
float: right;
|
||||||
|
margin-top: -2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="submit"] {
|
||||||
|
display: inline-block;
|
||||||
|
background: #3498db;
|
||||||
|
color: #FFF;
|
||||||
|
border: 0;
|
||||||
|
padding: 0.6em 1em;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="submit"]:hover,
|
||||||
|
input[type="submit"]:focus {
|
||||||
|
background: #0665A2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
max-width: 60em;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row a {
|
||||||
|
color: #3498db;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row form {
|
||||||
|
letter-spacing: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row form > * { letter-spacing: normal; }
|
||||||
|
|
||||||
|
.row p { margin: 0; }
|
||||||
|
|
||||||
|
.row fieldset {
|
||||||
|
display: inline-block;
|
||||||
|
width: 48%;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row fieldset:last-of-type {
|
||||||
|
display: block;
|
||||||
|
width: auto;
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row fieldset:nth-child(odd) {
|
||||||
|
margin-right: 2%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row fieldset:nth-child(2) {
|
||||||
|
min-height: 10.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 900px) {
|
||||||
|
.row {
|
||||||
|
margin: 0 1em;
|
||||||
|
}
|
||||||
|
.row fieldset { width: 49%; }
|
||||||
|
.row fieldset,
|
||||||
|
.row fieldset:nth-child(odd) {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row fieldset:first-child {
|
||||||
|
width: 100%;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row fieldset:nth-child(even) {
|
||||||
|
margin-right: 2%;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 800px) {
|
||||||
|
.row fieldset { width: 100%; }
|
||||||
|
|
||||||
|
select { width: 100%; }
|
||||||
|
|
||||||
|
table { font-size: 0.8em; }
|
||||||
|
.right {display: none;}
|
||||||
|
#sidebar { display: none; }
|
||||||
|
#results { padding: 0 2em; }
|
||||||
|
.search.center {
|
||||||
|
padding-right: 2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 400px) {
|
||||||
|
.row #categories_container > div {
|
||||||
|
width: 100%;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
border: 0;
|
||||||
|
margin: 1em 0;
|
||||||
|
background: #CCC;
|
||||||
|
padding: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
border: 1px solid #CCC;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th {
|
||||||
|
background: #999;
|
||||||
|
color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr:nth-child(odd) {
|
||||||
|
background: #CCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th,
|
||||||
|
table td {
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
border: 1px solid #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine_checkbox label {
|
||||||
|
padding: 0.5em;
|
||||||
|
background: #3498db;
|
||||||
|
color: #FFF;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine_checkbox .deny {
|
||||||
|
background: #3498db;
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine_checkbox .allow {
|
||||||
|
display: none;
|
||||||
|
background: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine_checkbox input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine_checkbox input:checked + .allow {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine_checkbox input:checked + .allow + .deny{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row input[type="submit"] {
|
||||||
|
font-size: 1em;
|
||||||
|
margin: 1em 0 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .right {
|
||||||
|
position: static;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .right a {
|
||||||
|
color: #333;
|
||||||
|
width: auto;
|
||||||
|
text-align: left;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small_font {
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
background: #EEE;
|
||||||
|
padding: 0 1em;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
border: 1px solid #DDD;
|
||||||
|
padding: 0.5em 0.8em;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
After Width: | Height: | Size: 342 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 7.5 KiB |
|
@ -0,0 +1,203 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="744.09448819"
|
||||||
|
height="1052.3622047"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.4 r9939"
|
||||||
|
sodipodi:docname="searx_logo.svg"
|
||||||
|
inkscape:export-filename="/home/a/magnif.png"
|
||||||
|
inkscape:export-xdpi="203.1774"
|
||||||
|
inkscape:export-ydpi="203.1774">
|
||||||
|
<defs
|
||||||
|
id="defs4">
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient3857">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3859" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3861" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient3790">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#a9a9a9;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3792" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3794" />
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3790"
|
||||||
|
id="radialGradient3798"
|
||||||
|
cx="294.45947"
|
||||||
|
cy="208.37973"
|
||||||
|
fx="294.45947"
|
||||||
|
fy="208.37973"
|
||||||
|
r="107.58125"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3857"
|
||||||
|
id="linearGradient3865"
|
||||||
|
x1="120.68947"
|
||||||
|
y1="239.61774"
|
||||||
|
x2="120.68947"
|
||||||
|
y2="602.17517"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3790"
|
||||||
|
id="linearGradient3912"
|
||||||
|
x1="186.74416"
|
||||||
|
y1="354.42426"
|
||||||
|
x2="255.84358"
|
||||||
|
y2="254.35953"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2227304,0,0,0.89945099,-289.31433,113.40259)" />
|
||||||
|
<filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter4024"
|
||||||
|
x="-0.12996517"
|
||||||
|
width="1.2599303"
|
||||||
|
y="-0.14709377"
|
||||||
|
height="1.2941875">
|
||||||
|
<feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="6.4759344"
|
||||||
|
id="feGaussianBlur4026" />
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter3983"
|
||||||
|
x="-1.0608404"
|
||||||
|
width="3.1216809"
|
||||||
|
y="-0.31017202"
|
||||||
|
height="1.620344">
|
||||||
|
<feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="9.392858"
|
||||||
|
id="feGaussianBlur3985" />
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1.979899"
|
||||||
|
inkscape:cx="30.708726"
|
||||||
|
inkscape:cy="948.08556"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:window-width="1364"
|
||||||
|
inkscape:window-height="663"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="30"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true" />
|
||||||
|
<metadata
|
||||||
|
id="metadata7">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
d="m 70.523181,34.870671 c -7.11959,15.242893 -10.17798,31.779192 -8.22563,48.814566 5.01677,43.774133 41.675309,79.324503 91.536109,95.162893 -6.62576,-22.40752 -5.34093,-44.9362 2.6395,-65.84431 C 108.73618,98.821131 74.828141,70.195435 70.523181,34.870671 z"
|
||||||
|
id="path3814-0-7"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
d="m 303.77876,36.21406 c 7.11959,15.242893 10.17798,31.779192 8.22563,48.814566 -5.01677,43.774134 -41.67531,79.324504 -91.53611,95.162894 6.62576,-22.40752 5.34093,-44.9362 -2.6395,-65.84431 47.73698,-14.18269 81.64502,-42.808386 85.94998,-78.13315 z"
|
||||||
|
id="path3814-0"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
transform="matrix(0.6556593,-0.75505688,0.75505688,0.6556593,0,0)"
|
||||||
|
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
d="m -5.0905523,259.06055 18.4167573,0 c 6.220455,0 11.228257,16.68196 11.228257,37.40349 l 0,172.83701 c 0,20.72153 -5.007802,37.40349 -11.228257,37.40349 l -18.4167573,0 c -6.2204547,0 -11.2282577,-16.68196 -11.2282577,-37.40349 l 0,-172.83701 c 0,-20.72153 5.007803,-37.40349 11.2282577,-37.40349 z"
|
||||||
|
id="rect3804" />
|
||||||
|
<path
|
||||||
|
sodipodi:type="arc"
|
||||||
|
style="fill:url(#radialGradient3798);fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
id="path2987"
|
||||||
|
sodipodi:cx="294.45947"
|
||||||
|
sodipodi:cy="208.37973"
|
||||||
|
sodipodi:rx="107.58125"
|
||||||
|
sodipodi:ry="107.58125"
|
||||||
|
d="m 402.04073,208.37973 a 107.58125,107.58125 0 1 1 -215.16251,0 107.58125,107.58125 0 1 1 215.16251,0 z"
|
||||||
|
transform="translate(-107.07617,-60.609153)" />
|
||||||
|
<path
|
||||||
|
sodipodi:type="arc"
|
||||||
|
style="fill:url(#linearGradient3865);fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
id="path3757"
|
||||||
|
sodipodi:cx="131.82491"
|
||||||
|
sodipodi:cy="299.29346"
|
||||||
|
sodipodi:rx="101.52033"
|
||||||
|
sodipodi:ry="101.52033"
|
||||||
|
d="m 233.34524,299.29346 a 101.52033,101.52033 0 1 1 -203.040667,0 101.52033,101.52033 0 1 1 203.040667,0 z"
|
||||||
|
transform="matrix(0.76865672,0,0,0.76865672,85.80266,-82.535889)" />
|
||||||
|
<path
|
||||||
|
sodipodi:type="arc"
|
||||||
|
style="fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
id="path3800"
|
||||||
|
sodipodi:cx="183.34268"
|
||||||
|
sodipodi:cy="156.35687"
|
||||||
|
sodipodi:rx="27.274118"
|
||||||
|
sodipodi:ry="27.274118"
|
||||||
|
d="m 210.6168,156.35687 a 27.274118,27.274118 0 1 1 -54.54824,0 27.274118,27.274118 0 1 1 54.54824,0 z"
|
||||||
|
transform="translate(5,-7.1428572)" />
|
||||||
|
<path
|
||||||
|
sodipodi:type="arc"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
id="path3802"
|
||||||
|
sodipodi:cx="197.9899"
|
||||||
|
sodipodi:cy="203.32896"
|
||||||
|
sodipodi:rx="5.5558391"
|
||||||
|
sodipodi:ry="5.5558391"
|
||||||
|
d="m 203.54574,203.32896 a 5.5558391,5.5558391 0 1 1 -11.11168,0 5.5558391,5.5558391 0 1 1 11.11168,0 z"
|
||||||
|
transform="translate(1.4847712,-63.564549)" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:0.82211531000000004;fill-rule:nonzero;stroke:none;filter:url(#filter4024)"
|
||||||
|
id="rect3916"
|
||||||
|
width="2.2392972"
|
||||||
|
height="159.43797"
|
||||||
|
x="19.525793"
|
||||||
|
y="337.8396"
|
||||||
|
rx="2.8666623"
|
||||||
|
ry="9.0007057"
|
||||||
|
transform="matrix(0.74466525,-0.84318084,0.84318084,0.74466525,-35.543204,-26.349917)" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 7.1 KiB |
|
@ -0,0 +1,491 @@
|
||||||
|
/*
|
||||||
|
---
|
||||||
|
MooTools: the javascript framework
|
||||||
|
|
||||||
|
web build:
|
||||||
|
- http://mootools.net/core/76bf47062d6c1983d66ce47ad66aa0e0
|
||||||
|
|
||||||
|
packager build:
|
||||||
|
- packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Event Core/Browser Core/Class Core/Class.Extras Core/Slick.Parser Core/Slick.Finder Core/Element Core/Element.Style Core/Element.Event Core/Element.Delegation Core/Element.Dimensions Core/Fx Core/Fx.CSS Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request Core/Request.HTML Core/Request.JSON Core/Cookie Core/JSON Core/DOMReady Core/Swiff
|
||||||
|
|
||||||
|
copyrights:
|
||||||
|
- [MooTools](http://mootools.net)
|
||||||
|
|
||||||
|
licenses:
|
||||||
|
- [MIT License](http://mootools.net/license.txt)
|
||||||
|
...
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(){this.MooTools={version:"1.4.5",build:"ab8ea8824dc3b24b6666867a2c4ed58ebb762cf0"};var o=this.typeOf=function(i){if(i==null){return"null";}if(i.$family!=null){return i.$family();
|
||||||
|
}if(i.nodeName){if(i.nodeType==1){return"element";}if(i.nodeType==3){return(/\S/).test(i.nodeValue)?"textnode":"whitespace";}}else{if(typeof i.length=="number"){if(i.callee){return"arguments";
|
||||||
|
}if("item" in i){return"collection";}}}return typeof i;};var j=this.instanceOf=function(t,i){if(t==null){return false;}var s=t.$constructor||t.constructor;
|
||||||
|
while(s){if(s===i){return true;}s=s.parent;}if(!t.hasOwnProperty){return false;}return t instanceof i;};var f=this.Function;var p=true;for(var k in {toString:1}){p=null;
|
||||||
|
}if(p){p=["hasOwnProperty","valueOf","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","constructor"];}f.prototype.overloadSetter=function(s){var i=this;
|
||||||
|
return function(u,t){if(u==null){return this;}if(s||typeof u!="string"){for(var v in u){i.call(this,v,u[v]);}if(p){for(var w=p.length;w--;){v=p[w];if(u.hasOwnProperty(v)){i.call(this,v,u[v]);
|
||||||
|
}}}}else{i.call(this,u,t);}return this;};};f.prototype.overloadGetter=function(s){var i=this;return function(u){var v,t;if(typeof u!="string"){v=u;}else{if(arguments.length>1){v=arguments;
|
||||||
|
}else{if(s){v=[u];}}}if(v){t={};for(var w=0;w<v.length;w++){t[v[w]]=i.call(this,v[w]);}}else{t=i.call(this,u);}return t;};};f.prototype.extend=function(i,s){this[i]=s;
|
||||||
|
}.overloadSetter();f.prototype.implement=function(i,s){this.prototype[i]=s;}.overloadSetter();var n=Array.prototype.slice;f.from=function(i){return(o(i)=="function")?i:function(){return i;
|
||||||
|
};};Array.from=function(i){if(i==null){return[];}return(a.isEnumerable(i)&&typeof i!="string")?(o(i)=="array")?i:n.call(i):[i];};Number.from=function(s){var i=parseFloat(s);
|
||||||
|
return isFinite(i)?i:null;};String.from=function(i){return i+"";};f.implement({hide:function(){this.$hidden=true;return this;},protect:function(){this.$protected=true;
|
||||||
|
return this;}});var a=this.Type=function(u,t){if(u){var s=u.toLowerCase();var i=function(v){return(o(v)==s);};a["is"+u]=i;if(t!=null){t.prototype.$family=(function(){return s;
|
||||||
|
}).hide();}}if(t==null){return null;}t.extend(this);t.$constructor=a;t.prototype.$constructor=t;return t;};var e=Object.prototype.toString;a.isEnumerable=function(i){return(i!=null&&typeof i.length=="number"&&e.call(i)!="[object Function]");
|
||||||
|
};var q={};var r=function(i){var s=o(i.prototype);return q[s]||(q[s]=[]);};var b=function(t,x){if(x&&x.$hidden){return;}var s=r(this);for(var u=0;u<s.length;
|
||||||
|
u++){var w=s[u];if(o(w)=="type"){b.call(w,t,x);}else{w.call(this,t,x);}}var v=this.prototype[t];if(v==null||!v.$protected){this.prototype[t]=x;}if(this[t]==null&&o(x)=="function"){m.call(this,t,function(i){return x.apply(i,n.call(arguments,1));
|
||||||
|
});}};var m=function(i,t){if(t&&t.$hidden){return;}var s=this[i];if(s==null||!s.$protected){this[i]=t;}};a.implement({implement:b.overloadSetter(),extend:m.overloadSetter(),alias:function(i,s){b.call(this,i,this.prototype[s]);
|
||||||
|
}.overloadSetter(),mirror:function(i){r(this).push(i);return this;}});new a("Type",a);var d=function(s,x,v){var u=(x!=Object),B=x.prototype;if(u){x=new a(s,x);
|
||||||
|
}for(var y=0,w=v.length;y<w;y++){var C=v[y],A=x[C],z=B[C];if(A){A.protect();}if(u&&z){x.implement(C,z.protect());}}if(u){var t=B.propertyIsEnumerable(v[0]);
|
||||||
|
x.forEachMethod=function(G){if(!t){for(var F=0,D=v.length;F<D;F++){G.call(B,B[v[F]],v[F]);}}for(var E in B){G.call(B,B[E],E);}};}return d;};d("String",String,["charAt","charCodeAt","concat","indexOf","lastIndexOf","match","quote","replace","search","slice","split","substr","substring","trim","toLowerCase","toUpperCase"])("Array",Array,["pop","push","reverse","shift","sort","splice","unshift","concat","join","slice","indexOf","lastIndexOf","filter","forEach","every","map","some","reduce","reduceRight"])("Number",Number,["toExponential","toFixed","toLocaleString","toPrecision"])("Function",f,["apply","call","bind"])("RegExp",RegExp,["exec","test"])("Object",Object,["create","defineProperty","defineProperties","keys","getPrototypeOf","getOwnPropertyDescriptor","getOwnPropertyNames","preventExtensions","isExtensible","seal","isSealed","freeze","isFrozen"])("Date",Date,["now"]);
|
||||||
|
Object.extend=m.overloadSetter();Date.extend("now",function(){return +(new Date);});new a("Boolean",Boolean);Number.prototype.$family=function(){return isFinite(this)?"number":"null";
|
||||||
|
}.hide();Number.extend("random",function(s,i){return Math.floor(Math.random()*(i-s+1)+s);});var g=Object.prototype.hasOwnProperty;Object.extend("forEach",function(i,t,u){for(var s in i){if(g.call(i,s)){t.call(u,i[s],s,i);
|
||||||
|
}}});Object.each=Object.forEach;Array.implement({forEach:function(u,v){for(var t=0,s=this.length;t<s;t++){if(t in this){u.call(v,this[t],t,this);}}},each:function(i,s){Array.forEach(this,i,s);
|
||||||
|
return this;}});var l=function(i){switch(o(i)){case"array":return i.clone();case"object":return Object.clone(i);default:return i;}};Array.implement("clone",function(){var s=this.length,t=new Array(s);
|
||||||
|
while(s--){t[s]=l(this[s]);}return t;});var h=function(s,i,t){switch(o(t)){case"object":if(o(s[i])=="object"){Object.merge(s[i],t);}else{s[i]=Object.clone(t);
|
||||||
|
}break;case"array":s[i]=t.clone();break;default:s[i]=t;}return s;};Object.extend({merge:function(z,u,t){if(o(u)=="string"){return h(z,u,t);}for(var y=1,s=arguments.length;
|
||||||
|
y<s;y++){var w=arguments[y];for(var x in w){h(z,x,w[x]);}}return z;},clone:function(i){var t={};for(var s in i){t[s]=l(i[s]);}return t;},append:function(w){for(var v=1,t=arguments.length;
|
||||||
|
v<t;v++){var s=arguments[v]||{};for(var u in s){w[u]=s[u];}}return w;}});["Object","WhiteSpace","TextNode","Collection","Arguments"].each(function(i){new a(i);
|
||||||
|
});var c=Date.now();String.extend("uniqueID",function(){return(c++).toString(36);});})();Array.implement({every:function(c,d){for(var b=0,a=this.length>>>0;
|
||||||
|
b<a;b++){if((b in this)&&!c.call(d,this[b],b,this)){return false;}}return true;},filter:function(d,f){var c=[];for(var e,b=0,a=this.length>>>0;b<a;b++){if(b in this){e=this[b];
|
||||||
|
if(d.call(f,e,b,this)){c.push(e);}}}return c;},indexOf:function(c,d){var b=this.length>>>0;for(var a=(d<0)?Math.max(0,b+d):d||0;a<b;a++){if(this[a]===c){return a;
|
||||||
|
}}return -1;},map:function(c,e){var d=this.length>>>0,b=Array(d);for(var a=0;a<d;a++){if(a in this){b[a]=c.call(e,this[a],a,this);}}return b;},some:function(c,d){for(var b=0,a=this.length>>>0;
|
||||||
|
b<a;b++){if((b in this)&&c.call(d,this[b],b,this)){return true;}}return false;},clean:function(){return this.filter(function(a){return a!=null;});},invoke:function(a){var b=Array.slice(arguments,1);
|
||||||
|
return this.map(function(c){return c[a].apply(c,b);});},associate:function(c){var d={},b=Math.min(this.length,c.length);for(var a=0;a<b;a++){d[c[a]]=this[a];
|
||||||
|
}return d;},link:function(c){var a={};for(var e=0,b=this.length;e<b;e++){for(var d in c){if(c[d](this[e])){a[d]=this[e];delete c[d];break;}}}return a;},contains:function(a,b){return this.indexOf(a,b)!=-1;
|
||||||
|
},append:function(a){this.push.apply(this,a);return this;},getLast:function(){return(this.length)?this[this.length-1]:null;},getRandom:function(){return(this.length)?this[Number.random(0,this.length-1)]:null;
|
||||||
|
},include:function(a){if(!this.contains(a)){this.push(a);}return this;},combine:function(c){for(var b=0,a=c.length;b<a;b++){this.include(c[b]);}return this;
|
||||||
|
},erase:function(b){for(var a=this.length;a--;){if(this[a]===b){this.splice(a,1);}}return this;},empty:function(){this.length=0;return this;},flatten:function(){var d=[];
|
||||||
|
for(var b=0,a=this.length;b<a;b++){var c=typeOf(this[b]);if(c=="null"){continue;}d=d.concat((c=="array"||c=="collection"||c=="arguments"||instanceOf(this[b],Array))?Array.flatten(this[b]):this[b]);
|
||||||
|
}return d;},pick:function(){for(var b=0,a=this.length;b<a;b++){if(this[b]!=null){return this[b];}}return null;},hexToRgb:function(b){if(this.length!=3){return null;
|
||||||
|
}var a=this.map(function(c){if(c.length==1){c+=c;}return c.toInt(16);});return(b)?a:"rgb("+a+")";},rgbToHex:function(d){if(this.length<3){return null;}if(this.length==4&&this[3]==0&&!d){return"transparent";
|
||||||
|
}var b=[];for(var a=0;a<3;a++){var c=(this[a]-0).toString(16);b.push((c.length==1)?"0"+c:c);}return(d)?b:"#"+b.join("");}});String.implement({test:function(a,b){return((typeOf(a)=="regexp")?a:new RegExp(""+a,b)).test(this);
|
||||||
|
},contains:function(a,b){return(b)?(b+this+b).indexOf(b+a+b)>-1:String(this).indexOf(a)>-1;},trim:function(){return String(this).replace(/^\s+|\s+$/g,"");
|
||||||
|
},clean:function(){return String(this).replace(/\s+/g," ").trim();},camelCase:function(){return String(this).replace(/-\D/g,function(a){return a.charAt(1).toUpperCase();
|
||||||
|
});},hyphenate:function(){return String(this).replace(/[A-Z]/g,function(a){return("-"+a.charAt(0).toLowerCase());});},capitalize:function(){return String(this).replace(/\b[a-z]/g,function(a){return a.toUpperCase();
|
||||||
|
});},escapeRegExp:function(){return String(this).replace(/([-.*+?^${}()|[\]\/\\])/g,"\\$1");},toInt:function(a){return parseInt(this,a||10);},toFloat:function(){return parseFloat(this);
|
||||||
|
},hexToRgb:function(b){var a=String(this).match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);return(a)?a.slice(1).hexToRgb(b):null;},rgbToHex:function(b){var a=String(this).match(/\d{1,3}/g);
|
||||||
|
return(a)?a.rgbToHex(b):null;},substitute:function(a,b){return String(this).replace(b||(/\\?\{([^{}]+)\}/g),function(d,c){if(d.charAt(0)=="\\"){return d.slice(1);
|
||||||
|
}return(a[c]!=null)?a[c]:"";});}});Number.implement({limit:function(b,a){return Math.min(a,Math.max(b,this));},round:function(a){a=Math.pow(10,a||0).toFixed(a<0?-a:0);
|
||||||
|
return Math.round(this*a)/a;},times:function(b,c){for(var a=0;a<this;a++){b.call(c,a,this);}},toFloat:function(){return parseFloat(this);},toInt:function(a){return parseInt(this,a||10);
|
||||||
|
}});Number.alias("each","times");(function(b){var a={};b.each(function(c){if(!Number[c]){a[c]=function(){return Math[c].apply(null,[this].concat(Array.from(arguments)));
|
||||||
|
};}});Number.implement(a);})(["abs","acos","asin","atan","atan2","ceil","cos","exp","floor","log","max","min","pow","sin","sqrt","tan"]);Function.extend({attempt:function(){for(var b=0,a=arguments.length;
|
||||||
|
b<a;b++){try{return arguments[b]();}catch(c){}}return null;}});Function.implement({attempt:function(a,c){try{return this.apply(c,Array.from(a));}catch(b){}return null;
|
||||||
|
},bind:function(e){var a=this,b=arguments.length>1?Array.slice(arguments,1):null,d=function(){};var c=function(){var g=e,h=arguments.length;if(this instanceof c){d.prototype=a.prototype;
|
||||||
|
g=new d;}var f=(!b&&!h)?a.call(g):a.apply(g,b&&h?b.concat(Array.slice(arguments)):b||arguments);return g==e?f:g;};return c;},pass:function(b,c){var a=this;
|
||||||
|
if(b!=null){b=Array.from(b);}return function(){return a.apply(c,b||arguments);};},delay:function(b,c,a){return setTimeout(this.pass((a==null?[]:a),c),b);
|
||||||
|
},periodical:function(c,b,a){return setInterval(this.pass((a==null?[]:a),b),c);}});(function(){var a=Object.prototype.hasOwnProperty;Object.extend({subset:function(d,g){var f={};
|
||||||
|
for(var e=0,b=g.length;e<b;e++){var c=g[e];if(c in d){f[c]=d[c];}}return f;},map:function(b,e,f){var d={};for(var c in b){if(a.call(b,c)){d[c]=e.call(f,b[c],c,b);
|
||||||
|
}}return d;},filter:function(b,e,g){var d={};for(var c in b){var f=b[c];if(a.call(b,c)&&e.call(g,f,c,b)){d[c]=f;}}return d;},every:function(b,d,e){for(var c in b){if(a.call(b,c)&&!d.call(e,b[c],c)){return false;
|
||||||
|
}}return true;},some:function(b,d,e){for(var c in b){if(a.call(b,c)&&d.call(e,b[c],c)){return true;}}return false;},keys:function(b){var d=[];for(var c in b){if(a.call(b,c)){d.push(c);
|
||||||
|
}}return d;},values:function(c){var b=[];for(var d in c){if(a.call(c,d)){b.push(c[d]);}}return b;},getLength:function(b){return Object.keys(b).length;},keyOf:function(b,d){for(var c in b){if(a.call(b,c)&&b[c]===d){return c;
|
||||||
|
}}return null;},contains:function(b,c){return Object.keyOf(b,c)!=null;},toQueryString:function(b,c){var d=[];Object.each(b,function(h,g){if(c){g=c+"["+g+"]";
|
||||||
|
}var f;switch(typeOf(h)){case"object":f=Object.toQueryString(h,g);break;case"array":var e={};h.each(function(k,j){e[j]=k;});f=Object.toQueryString(e,g);
|
||||||
|
break;default:f=g+"="+encodeURIComponent(h);}if(h!=null){d.push(f);}});return d.join("&");}});})();(function(){var j=this.document;var g=j.window=this;
|
||||||
|
var a=navigator.userAgent.toLowerCase(),b=navigator.platform.toLowerCase(),h=a.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/)||[null,"unknown",0],d=h[1]=="ie"&&j.documentMode;
|
||||||
|
var n=this.Browser={extend:Function.prototype.extend,name:(h[1]=="version")?h[3]:h[1],version:d||parseFloat((h[1]=="opera"&&h[4])?h[4]:h[2]),Platform:{name:a.match(/ip(?:ad|od|hone)/)?"ios":(a.match(/(?:webos|android)/)||b.match(/mac|win|linux/)||["other"])[0]},Features:{xpath:!!(j.evaluate),air:!!(g.runtime),query:!!(j.querySelector),json:!!(g.JSON)},Plugins:{}};
|
||||||
|
n[n.name]=true;n[n.name+parseInt(n.version,10)]=true;n.Platform[n.Platform.name]=true;n.Request=(function(){var p=function(){return new XMLHttpRequest();
|
||||||
|
};var o=function(){return new ActiveXObject("MSXML2.XMLHTTP");};var e=function(){return new ActiveXObject("Microsoft.XMLHTTP");};return Function.attempt(function(){p();
|
||||||
|
return p;},function(){o();return o;},function(){e();return e;});})();n.Features.xhr=!!(n.Request);var i=(Function.attempt(function(){return navigator.plugins["Shockwave Flash"].description;
|
||||||
|
},function(){return new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version");})||"0 r0").match(/\d+/g);n.Plugins.Flash={version:Number(i[0]||"0."+i[1])||0,build:Number(i[2])||0};
|
||||||
|
n.exec=function(o){if(!o){return o;}if(g.execScript){g.execScript(o);}else{var e=j.createElement("script");e.setAttribute("type","text/javascript");e.text=o;
|
||||||
|
j.head.appendChild(e);j.head.removeChild(e);}return o;};String.implement("stripScripts",function(o){var e="";var p=this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi,function(q,r){e+=r+"\n";
|
||||||
|
return"";});if(o===true){n.exec(e);}else{if(typeOf(o)=="function"){o(e,p);}}return p;});n.extend({Document:this.Document,Window:this.Window,Element:this.Element,Event:this.Event});
|
||||||
|
this.Window=this.$constructor=new Type("Window",function(){});this.$family=Function.from("window").hide();Window.mirror(function(e,o){g[e]=o;});this.Document=j.$constructor=new Type("Document",function(){});
|
||||||
|
j.$family=Function.from("document").hide();Document.mirror(function(e,o){j[e]=o;});j.html=j.documentElement;if(!j.head){j.head=j.getElementsByTagName("head")[0];
|
||||||
|
}if(j.execCommand){try{j.execCommand("BackgroundImageCache",false,true);}catch(f){}}if(this.attachEvent&&!this.addEventListener){var c=function(){this.detachEvent("onunload",c);
|
||||||
|
j.head=j.html=j.window=null;};this.attachEvent("onunload",c);}var l=Array.from;try{l(j.html.childNodes);}catch(f){Array.from=function(o){if(typeof o!="string"&&Type.isEnumerable(o)&&typeOf(o)!="array"){var e=o.length,p=new Array(e);
|
||||||
|
while(e--){p[e]=o[e];}return p;}return l(o);};var k=Array.prototype,m=k.slice;["pop","push","reverse","shift","sort","splice","unshift","concat","join","slice"].each(function(e){var o=k[e];
|
||||||
|
Array[e]=function(p){return o.apply(Array.from(p),m.call(arguments,1));};});}})();(function(){var b={};var a=this.DOMEvent=new Type("DOMEvent",function(c,g){if(!g){g=window;
|
||||||
|
}c=c||g.event;if(c.$extended){return c;}this.event=c;this.$extended=true;this.shift=c.shiftKey;this.control=c.ctrlKey;this.alt=c.altKey;this.meta=c.metaKey;
|
||||||
|
var i=this.type=c.type;var h=c.target||c.srcElement;while(h&&h.nodeType==3){h=h.parentNode;}this.target=document.id(h);if(i.indexOf("key")==0){var d=this.code=(c.which||c.keyCode);
|
||||||
|
this.key=b[d];if(i=="keydown"){if(d>111&&d<124){this.key="f"+(d-111);}else{if(d>95&&d<106){this.key=d-96;}}}if(this.key==null){this.key=String.fromCharCode(d).toLowerCase();
|
||||||
|
}}else{if(i=="click"||i=="dblclick"||i=="contextmenu"||i=="DOMMouseScroll"||i.indexOf("mouse")==0){var j=g.document;j=(!j.compatMode||j.compatMode=="CSS1Compat")?j.html:j.body;
|
||||||
|
this.page={x:(c.pageX!=null)?c.pageX:c.clientX+j.scrollLeft,y:(c.pageY!=null)?c.pageY:c.clientY+j.scrollTop};this.client={x:(c.pageX!=null)?c.pageX-g.pageXOffset:c.clientX,y:(c.pageY!=null)?c.pageY-g.pageYOffset:c.clientY};
|
||||||
|
if(i=="DOMMouseScroll"||i=="mousewheel"){this.wheel=(c.wheelDelta)?c.wheelDelta/120:-(c.detail||0)/3;}this.rightClick=(c.which==3||c.button==2);if(i=="mouseover"||i=="mouseout"){var k=c.relatedTarget||c[(i=="mouseover"?"from":"to")+"Element"];
|
||||||
|
while(k&&k.nodeType==3){k=k.parentNode;}this.relatedTarget=document.id(k);}}else{if(i.indexOf("touch")==0||i.indexOf("gesture")==0){this.rotation=c.rotation;
|
||||||
|
this.scale=c.scale;this.targetTouches=c.targetTouches;this.changedTouches=c.changedTouches;var f=this.touches=c.touches;if(f&&f[0]){var e=f[0];this.page={x:e.pageX,y:e.pageY};
|
||||||
|
this.client={x:e.clientX,y:e.clientY};}}}}if(!this.client){this.client={};}if(!this.page){this.page={};}});a.implement({stop:function(){return this.preventDefault().stopPropagation();
|
||||||
|
},stopPropagation:function(){if(this.event.stopPropagation){this.event.stopPropagation();}else{this.event.cancelBubble=true;}return this;},preventDefault:function(){if(this.event.preventDefault){this.event.preventDefault();
|
||||||
|
}else{this.event.returnValue=false;}return this;}});a.defineKey=function(d,c){b[d]=c;return this;};a.defineKeys=a.defineKey.overloadSetter(true);a.defineKeys({"38":"up","40":"down","37":"left","39":"right","27":"esc","32":"space","8":"backspace","9":"tab","46":"delete","13":"enter"});
|
||||||
|
})();(function(){var a=this.Class=new Type("Class",function(h){if(instanceOf(h,Function)){h={initialize:h};}var g=function(){e(this);if(g.$prototyping){return this;
|
||||||
|
}this.$caller=null;var i=(this.initialize)?this.initialize.apply(this,arguments):this;this.$caller=this.caller=null;return i;}.extend(this).implement(h);
|
||||||
|
g.$constructor=a;g.prototype.$constructor=g;g.prototype.parent=c;return g;});var c=function(){if(!this.$caller){throw new Error('The method "parent" cannot be called.');
|
||||||
|
}var g=this.$caller.$name,h=this.$caller.$owner.parent,i=(h)?h.prototype[g]:null;if(!i){throw new Error('The method "'+g+'" has no parent.');}return i.apply(this,arguments);
|
||||||
|
};var e=function(g){for(var h in g){var j=g[h];switch(typeOf(j)){case"object":var i=function(){};i.prototype=j;g[h]=e(new i);break;case"array":g[h]=j.clone();
|
||||||
|
break;}}return g;};var b=function(g,h,j){if(j.$origin){j=j.$origin;}var i=function(){if(j.$protected&&this.$caller==null){throw new Error('The method "'+h+'" cannot be called.');
|
||||||
|
}var l=this.caller,m=this.$caller;this.caller=m;this.$caller=i;var k=j.apply(this,arguments);this.$caller=m;this.caller=l;return k;}.extend({$owner:g,$origin:j,$name:h});
|
||||||
|
return i;};var f=function(h,i,g){if(a.Mutators.hasOwnProperty(h)){i=a.Mutators[h].call(this,i);if(i==null){return this;}}if(typeOf(i)=="function"){if(i.$hidden){return this;
|
||||||
|
}this.prototype[h]=(g)?i:b(this,h,i);}else{Object.merge(this.prototype,h,i);}return this;};var d=function(g){g.$prototyping=true;var h=new g;delete g.$prototyping;
|
||||||
|
return h;};a.implement("implement",f.overloadSetter());a.Mutators={Extends:function(g){this.parent=g;this.prototype=d(g);},Implements:function(g){Array.from(g).each(function(j){var h=new j;
|
||||||
|
for(var i in h){f.call(this,i,h[i],true);}},this);}};})();(function(){this.Chain=new Class({$chain:[],chain:function(){this.$chain.append(Array.flatten(arguments));
|
||||||
|
return this;},callChain:function(){return(this.$chain.length)?this.$chain.shift().apply(this,arguments):false;},clearChain:function(){this.$chain.empty();
|
||||||
|
return this;}});var a=function(b){return b.replace(/^on([A-Z])/,function(c,d){return d.toLowerCase();});};this.Events=new Class({$events:{},addEvent:function(d,c,b){d=a(d);
|
||||||
|
this.$events[d]=(this.$events[d]||[]).include(c);if(b){c.internal=true;}return this;},addEvents:function(b){for(var c in b){this.addEvent(c,b[c]);}return this;
|
||||||
|
},fireEvent:function(e,c,b){e=a(e);var d=this.$events[e];if(!d){return this;}c=Array.from(c);d.each(function(f){if(b){f.delay(b,this,c);}else{f.apply(this,c);
|
||||||
|
}},this);return this;},removeEvent:function(e,d){e=a(e);var c=this.$events[e];if(c&&!d.internal){var b=c.indexOf(d);if(b!=-1){delete c[b];}}return this;
|
||||||
|
},removeEvents:function(d){var e;if(typeOf(d)=="object"){for(e in d){this.removeEvent(e,d[e]);}return this;}if(d){d=a(d);}for(e in this.$events){if(d&&d!=e){continue;
|
||||||
|
}var c=this.$events[e];for(var b=c.length;b--;){if(b in c){this.removeEvent(e,c[b]);}}}return this;}});this.Options=new Class({setOptions:function(){var b=this.options=Object.merge.apply(null,[{},this.options].append(arguments));
|
||||||
|
if(this.addEvent){for(var c in b){if(typeOf(b[c])!="function"||!(/^on[A-Z]/).test(c)){continue;}this.addEvent(c,b[c]);delete b[c];}}return this;}});})();
|
||||||
|
(function(){var k,n,l,g,a={},c={},m=/\\/g;var e=function(q,p){if(q==null){return null;}if(q.Slick===true){return q;}q=(""+q).replace(/^\s+|\s+$/g,"");g=!!p;
|
||||||
|
var o=(g)?c:a;if(o[q]){return o[q];}k={Slick:true,expressions:[],raw:q,reverse:function(){return e(this.raw,true);}};n=-1;while(q!=(q=q.replace(j,b))){}k.length=k.expressions.length;
|
||||||
|
return o[k.raw]=(g)?h(k):k;};var i=function(o){if(o==="!"){return" ";}else{if(o===" "){return"!";}else{if((/^!/).test(o)){return o.replace(/^!/,"");}else{return"!"+o;
|
||||||
|
}}}};var h=function(u){var r=u.expressions;for(var p=0;p<r.length;p++){var t=r[p];var q={parts:[],tag:"*",combinator:i(t[0].combinator)};for(var o=0;o<t.length;
|
||||||
|
o++){var s=t[o];if(!s.reverseCombinator){s.reverseCombinator=" ";}s.combinator=s.reverseCombinator;delete s.reverseCombinator;}t.reverse().push(q);}return u;
|
||||||
|
};var f=function(o){return o.replace(/[-[\]{}()*+?.\\^$|,#\s]/g,function(p){return"\\"+p;});};var j=new RegExp("^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)".replace(/<combinator>/,"["+f(">+~`!@$%^&={}\\;</")+"]").replace(/<unicode>/g,"(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])").replace(/<unicode1>/g,"(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])"));
|
||||||
|
function b(x,s,D,z,r,C,q,B,A,y,u,F,G,v,p,w){if(s||n===-1){k.expressions[++n]=[];l=-1;if(s){return"";}}if(D||z||l===-1){D=D||" ";var t=k.expressions[n];
|
||||||
|
if(g&&t[l]){t[l].reverseCombinator=i(D);}t[++l]={combinator:D,tag:"*"};}var o=k.expressions[n][l];if(r){o.tag=r.replace(m,"");}else{if(C){o.id=C.replace(m,"");
|
||||||
|
}else{if(q){q=q.replace(m,"");if(!o.classList){o.classList=[];}if(!o.classes){o.classes=[];}o.classList.push(q);o.classes.push({value:q,regexp:new RegExp("(^|\\s)"+f(q)+"(\\s|$)")});
|
||||||
|
}else{if(G){w=w||p;w=w?w.replace(m,""):null;if(!o.pseudos){o.pseudos=[];}o.pseudos.push({key:G.replace(m,""),value:w,type:F.length==1?"class":"element"});
|
||||||
|
}else{if(B){B=B.replace(m,"");u=(u||"").replace(m,"");var E,H;switch(A){case"^=":H=new RegExp("^"+f(u));break;case"$=":H=new RegExp(f(u)+"$");break;case"~=":H=new RegExp("(^|\\s)"+f(u)+"(\\s|$)");
|
||||||
|
break;case"|=":H=new RegExp("^"+f(u)+"(-|$)");break;case"=":E=function(I){return u==I;};break;case"*=":E=function(I){return I&&I.indexOf(u)>-1;};break;
|
||||||
|
case"!=":E=function(I){return u!=I;};break;default:E=function(I){return !!I;};}if(u==""&&(/^[*$^]=$/).test(A)){E=function(){return false;};}if(!E){E=function(I){return I&&H.test(I);
|
||||||
|
};}if(!o.attributes){o.attributes=[];}o.attributes.push({key:B,operator:A,value:u,test:E});}}}}}return"";}var d=(this.Slick||{});d.parse=function(o){return e(o);
|
||||||
|
};d.escapeRegExp=f;if(!this.Slick){this.Slick=d;}}).apply((typeof exports!="undefined")?exports:this);(function(){var k={},m={},d=Object.prototype.toString;
|
||||||
|
k.isNativeCode=function(c){return(/\{\s*\[native code\]\s*\}/).test(""+c);};k.isXML=function(c){return(!!c.xmlVersion)||(!!c.xml)||(d.call(c)=="[object XMLDocument]")||(c.nodeType==9&&c.documentElement.nodeName!="HTML");
|
||||||
|
};k.setDocument=function(w){var p=w.nodeType;if(p==9){}else{if(p){w=w.ownerDocument;}else{if(w.navigator){w=w.document;}else{return;}}}if(this.document===w){return;
|
||||||
|
}this.document=w;var A=w.documentElement,o=this.getUIDXML(A),s=m[o],r;if(s){for(r in s){this[r]=s[r];}return;}s=m[o]={};s.root=A;s.isXMLDocument=this.isXML(w);
|
||||||
|
s.brokenStarGEBTN=s.starSelectsClosedQSA=s.idGetsName=s.brokenMixedCaseQSA=s.brokenGEBCN=s.brokenCheckedQSA=s.brokenEmptyAttributeQSA=s.isHTMLDocument=s.nativeMatchesSelector=false;
|
||||||
|
var q,u,y,z,t;var x,v="slick_uniqueid";var c=w.createElement("div");var n=w.body||w.getElementsByTagName("body")[0]||A;n.appendChild(c);try{c.innerHTML='<a id="'+v+'"></a>';
|
||||||
|
s.isHTMLDocument=!!w.getElementById(v);}catch(C){}if(s.isHTMLDocument){c.style.display="none";c.appendChild(w.createComment(""));u=(c.getElementsByTagName("*").length>1);
|
||||||
|
try{c.innerHTML="foo</foo>";x=c.getElementsByTagName("*");q=(x&&!!x.length&&x[0].nodeName.charAt(0)=="/");}catch(C){}s.brokenStarGEBTN=u||q;try{c.innerHTML='<a name="'+v+'"></a><b id="'+v+'"></b>';
|
||||||
|
s.idGetsName=w.getElementById(v)===c.firstChild;}catch(C){}if(c.getElementsByClassName){try{c.innerHTML='<a class="f"></a><a class="b"></a>';c.getElementsByClassName("b").length;
|
||||||
|
c.firstChild.className="b";z=(c.getElementsByClassName("b").length!=2);}catch(C){}try{c.innerHTML='<a class="a"></a><a class="f b a"></a>';y=(c.getElementsByClassName("a").length!=2);
|
||||||
|
}catch(C){}s.brokenGEBCN=z||y;}if(c.querySelectorAll){try{c.innerHTML="foo</foo>";x=c.querySelectorAll("*");s.starSelectsClosedQSA=(x&&!!x.length&&x[0].nodeName.charAt(0)=="/");
|
||||||
|
}catch(C){}try{c.innerHTML='<a class="MiX"></a>';s.brokenMixedCaseQSA=!c.querySelectorAll(".MiX").length;}catch(C){}try{c.innerHTML='<select><option selected="selected">a</option></select>';
|
||||||
|
s.brokenCheckedQSA=(c.querySelectorAll(":checked").length==0);}catch(C){}try{c.innerHTML='<a class=""></a>';s.brokenEmptyAttributeQSA=(c.querySelectorAll('[class*=""]').length!=0);
|
||||||
|
}catch(C){}}try{c.innerHTML='<form action="s"><input id="action"/></form>';t=(c.firstChild.getAttribute("action")!="s");}catch(C){}s.nativeMatchesSelector=A.matchesSelector||A.mozMatchesSelector||A.webkitMatchesSelector;
|
||||||
|
if(s.nativeMatchesSelector){try{s.nativeMatchesSelector.call(A,":slick");s.nativeMatchesSelector=null;}catch(C){}}}try{A.slick_expando=1;delete A.slick_expando;
|
||||||
|
s.getUID=this.getUIDHTML;}catch(C){s.getUID=this.getUIDXML;}n.removeChild(c);c=x=n=null;s.getAttribute=(s.isHTMLDocument&&t)?function(G,E){var H=this.attributeGetters[E];
|
||||||
|
if(H){return H.call(G);}var F=G.getAttributeNode(E);return(F)?F.nodeValue:null;}:function(F,E){var G=this.attributeGetters[E];return(G)?G.call(F):F.getAttribute(E);
|
||||||
|
};s.hasAttribute=(A&&this.isNativeCode(A.hasAttribute))?function(F,E){return F.hasAttribute(E);}:function(F,E){F=F.getAttributeNode(E);return !!(F&&(F.specified||F.nodeValue));
|
||||||
|
};var D=A&&this.isNativeCode(A.contains),B=w&&this.isNativeCode(w.contains);s.contains=(D&&B)?function(E,F){return E.contains(F);}:(D&&!B)?function(E,F){return E===F||((E===w)?w.documentElement:E).contains(F);
|
||||||
|
}:(A&&A.compareDocumentPosition)?function(E,F){return E===F||!!(E.compareDocumentPosition(F)&16);}:function(E,F){if(F){do{if(F===E){return true;}}while((F=F.parentNode));
|
||||||
|
}return false;};s.documentSorter=(A.compareDocumentPosition)?function(F,E){if(!F.compareDocumentPosition||!E.compareDocumentPosition){return 0;}return F.compareDocumentPosition(E)&4?-1:F===E?0:1;
|
||||||
|
}:("sourceIndex" in A)?function(F,E){if(!F.sourceIndex||!E.sourceIndex){return 0;}return F.sourceIndex-E.sourceIndex;}:(w.createRange)?function(H,F){if(!H.ownerDocument||!F.ownerDocument){return 0;
|
||||||
|
}var G=H.ownerDocument.createRange(),E=F.ownerDocument.createRange();G.setStart(H,0);G.setEnd(H,0);E.setStart(F,0);E.setEnd(F,0);return G.compareBoundaryPoints(Range.START_TO_END,E);
|
||||||
|
}:null;A=null;for(r in s){this[r]=s[r];}};var f=/^([#.]?)((?:[\w-]+|\*))$/,h=/\[.+[*$^]=(?:""|'')?\]/,g={};k.search=function(U,z,H,s){var p=this.found=(s)?null:(H||[]);
|
||||||
|
if(!U){return p;}else{if(U.navigator){U=U.document;}else{if(!U.nodeType){return p;}}}var F,O,V=this.uniques={},I=!!(H&&H.length),y=(U.nodeType==9);if(this.document!==(y?U:U.ownerDocument)){this.setDocument(U);
|
||||||
|
}if(I){for(O=p.length;O--;){V[this.getUID(p[O])]=true;}}if(typeof z=="string"){var r=z.match(f);simpleSelectors:if(r){var u=r[1],v=r[2],A,E;if(!u){if(v=="*"&&this.brokenStarGEBTN){break simpleSelectors;
|
||||||
|
}E=U.getElementsByTagName(v);if(s){return E[0]||null;}for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}else{if(u=="#"){if(!this.isHTMLDocument||!y){break simpleSelectors;
|
||||||
|
}A=U.getElementById(v);if(!A){return p;}if(this.idGetsName&&A.getAttributeNode("id").nodeValue!=v){break simpleSelectors;}if(s){return A||null;}if(!(I&&V[this.getUID(A)])){p.push(A);
|
||||||
|
}}else{if(u=="."){if(!this.isHTMLDocument||((!U.getElementsByClassName||this.brokenGEBCN)&&U.querySelectorAll)){break simpleSelectors;}if(U.getElementsByClassName&&!this.brokenGEBCN){E=U.getElementsByClassName(v);
|
||||||
|
if(s){return E[0]||null;}for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}else{var T=new RegExp("(^|\\s)"+e.escapeRegExp(v)+"(\\s|$)");E=U.getElementsByTagName("*");
|
||||||
|
for(O=0;A=E[O++];){className=A.className;if(!(className&&T.test(className))){continue;}if(s){return A;}if(!(I&&V[this.getUID(A)])){p.push(A);}}}}}}if(I){this.sort(p);
|
||||||
|
}return(s)?null:p;}querySelector:if(U.querySelectorAll){if(!this.isHTMLDocument||g[z]||this.brokenMixedCaseQSA||(this.brokenCheckedQSA&&z.indexOf(":checked")>-1)||(this.brokenEmptyAttributeQSA&&h.test(z))||(!y&&z.indexOf(",")>-1)||e.disableQSA){break querySelector;
|
||||||
|
}var S=z,x=U;if(!y){var C=x.getAttribute("id"),t="slickid__";x.setAttribute("id",t);S="#"+t+" "+S;U=x.parentNode;}try{if(s){return U.querySelector(S)||null;
|
||||||
|
}else{E=U.querySelectorAll(S);}}catch(Q){g[z]=1;break querySelector;}finally{if(!y){if(C){x.setAttribute("id",C);}else{x.removeAttribute("id");}U=x;}}if(this.starSelectsClosedQSA){for(O=0;
|
||||||
|
A=E[O++];){if(A.nodeName>"@"&&!(I&&V[this.getUID(A)])){p.push(A);}}}else{for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}if(I){this.sort(p);
|
||||||
|
}return p;}F=this.Slick.parse(z);if(!F.length){return p;}}else{if(z==null){return p;}else{if(z.Slick){F=z;}else{if(this.contains(U.documentElement||U,z)){(p)?p.push(z):p=z;
|
||||||
|
return p;}else{return p;}}}}this.posNTH={};this.posNTHLast={};this.posNTHType={};this.posNTHTypeLast={};this.push=(!I&&(s||(F.length==1&&F.expressions[0].length==1)))?this.pushArray:this.pushUID;
|
||||||
|
if(p==null){p=[];}var M,L,K;var B,J,D,c,q,G,W;var N,P,o,w,R=F.expressions;search:for(O=0;(P=R[O]);O++){for(M=0;(o=P[M]);M++){B="combinator:"+o.combinator;
|
||||||
|
if(!this[B]){continue search;}J=(this.isXMLDocument)?o.tag:o.tag.toUpperCase();D=o.id;c=o.classList;q=o.classes;G=o.attributes;W=o.pseudos;w=(M===(P.length-1));
|
||||||
|
this.bitUniques={};if(w){this.uniques=V;this.found=p;}else{this.uniques={};this.found=[];}if(M===0){this[B](U,J,D,q,G,W,c);if(s&&w&&p.length){break search;
|
||||||
|
}}else{if(s&&w){for(L=0,K=N.length;L<K;L++){this[B](N[L],J,D,q,G,W,c);if(p.length){break search;}}}else{for(L=0,K=N.length;L<K;L++){this[B](N[L],J,D,q,G,W,c);
|
||||||
|
}}}N=this.found;}}if(I||(F.expressions.length>1)){this.sort(p);}return(s)?(p[0]||null):p;};k.uidx=1;k.uidk="slick-uniqueid";k.getUIDXML=function(n){var c=n.getAttribute(this.uidk);
|
||||||
|
if(!c){c=this.uidx++;n.setAttribute(this.uidk,c);}return c;};k.getUIDHTML=function(c){return c.uniqueNumber||(c.uniqueNumber=this.uidx++);};k.sort=function(c){if(!this.documentSorter){return c;
|
||||||
|
}c.sort(this.documentSorter);return c;};k.cacheNTH={};k.matchNTH=/^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/;k.parseNTHArgument=function(q){var o=q.match(this.matchNTH);
|
||||||
|
if(!o){return false;}var p=o[2]||false;var n=o[1]||1;if(n=="-"){n=-1;}var c=+o[3]||0;o=(p=="n")?{a:n,b:c}:(p=="odd")?{a:2,b:1}:(p=="even")?{a:2,b:0}:{a:0,b:n};
|
||||||
|
return(this.cacheNTH[q]=o);};k.createNTHPseudo=function(p,n,c,o){return function(s,q){var u=this.getUID(s);if(!this[c][u]){var A=s.parentNode;if(!A){return false;
|
||||||
|
}var r=A[p],t=1;if(o){var z=s.nodeName;do{if(r.nodeName!=z){continue;}this[c][this.getUID(r)]=t++;}while((r=r[n]));}else{do{if(r.nodeType!=1){continue;
|
||||||
|
}this[c][this.getUID(r)]=t++;}while((r=r[n]));}}q=q||"n";var v=this.cacheNTH[q]||this.parseNTHArgument(q);if(!v){return false;}var y=v.a,x=v.b,w=this[c][u];
|
||||||
|
if(y==0){return x==w;}if(y>0){if(w<x){return false;}}else{if(x<w){return false;}}return((w-x)%y)==0;};};k.pushArray=function(p,c,r,o,n,q){if(this.matchSelector(p,c,r,o,n,q)){this.found.push(p);
|
||||||
|
}};k.pushUID=function(q,c,s,p,n,r){var o=this.getUID(q);if(!this.uniques[o]&&this.matchSelector(q,c,s,p,n,r)){this.uniques[o]=true;this.found.push(q);}};
|
||||||
|
k.matchNode=function(n,o){if(this.isHTMLDocument&&this.nativeMatchesSelector){try{return this.nativeMatchesSelector.call(n,o.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g,'[$1="$2"]'));
|
||||||
|
}catch(u){}}var t=this.Slick.parse(o);if(!t){return true;}var r=t.expressions,s=0,q;for(q=0;(currentExpression=r[q]);q++){if(currentExpression.length==1){var p=currentExpression[0];
|
||||||
|
if(this.matchSelector(n,(this.isXMLDocument)?p.tag:p.tag.toUpperCase(),p.id,p.classes,p.attributes,p.pseudos)){return true;}s++;}}if(s==t.length){return false;
|
||||||
|
}var c=this.search(this.document,t),v;for(q=0;v=c[q++];){if(v===n){return true;}}return false;};k.matchPseudo=function(q,c,p){var n="pseudo:"+c;if(this[n]){return this[n](q,p);
|
||||||
|
}var o=this.getAttribute(q,c);return(p)?p==o:!!o;};k.matchSelector=function(o,v,c,p,q,s){if(v){var t=(this.isXMLDocument)?o.nodeName:o.nodeName.toUpperCase();
|
||||||
|
if(v=="*"){if(t<"@"){return false;}}else{if(t!=v){return false;}}}if(c&&o.getAttribute("id")!=c){return false;}var r,n,u;if(p){for(r=p.length;r--;){u=this.getAttribute(o,"class");
|
||||||
|
if(!(u&&p[r].regexp.test(u))){return false;}}}if(q){for(r=q.length;r--;){n=q[r];if(n.operator?!n.test(this.getAttribute(o,n.key)):!this.hasAttribute(o,n.key)){return false;
|
||||||
|
}}}if(s){for(r=s.length;r--;){n=s[r];if(!this.matchPseudo(o,n.key,n.value)){return false;}}}return true;};var j={" ":function(q,w,n,r,s,u,p){var t,v,o;
|
||||||
|
if(this.isHTMLDocument){getById:if(n){v=this.document.getElementById(n);if((!v&&q.all)||(this.idGetsName&&v&&v.getAttributeNode("id").nodeValue!=n)){o=q.all[n];
|
||||||
|
if(!o){return;}if(!o[0]){o=[o];}for(t=0;v=o[t++];){var c=v.getAttributeNode("id");if(c&&c.nodeValue==n){this.push(v,w,null,r,s,u);break;}}return;}if(!v){if(this.contains(this.root,q)){return;
|
||||||
|
}else{break getById;}}else{if(this.document!==q&&!this.contains(q,v)){return;}}this.push(v,w,null,r,s,u);return;}getByClass:if(r&&q.getElementsByClassName&&!this.brokenGEBCN){o=q.getElementsByClassName(p.join(" "));
|
||||||
|
if(!(o&&o.length)){break getByClass;}for(t=0;v=o[t++];){this.push(v,w,n,null,s,u);}return;}}getByTag:{o=q.getElementsByTagName(w);if(!(o&&o.length)){break getByTag;
|
||||||
|
}if(!this.brokenStarGEBTN){w=null;}for(t=0;v=o[t++];){this.push(v,w,n,r,s,u);}}},">":function(p,c,r,o,n,q){if((p=p.firstChild)){do{if(p.nodeType==1){this.push(p,c,r,o,n,q);
|
||||||
|
}}while((p=p.nextSibling));}},"+":function(p,c,r,o,n,q){while((p=p.nextSibling)){if(p.nodeType==1){this.push(p,c,r,o,n,q);break;}}},"^":function(p,c,r,o,n,q){p=p.firstChild;
|
||||||
|
if(p){if(p.nodeType==1){this.push(p,c,r,o,n,q);}else{this["combinator:+"](p,c,r,o,n,q);}}},"~":function(q,c,s,p,n,r){while((q=q.nextSibling)){if(q.nodeType!=1){continue;
|
||||||
|
}var o=this.getUID(q);if(this.bitUniques[o]){break;}this.bitUniques[o]=true;this.push(q,c,s,p,n,r);}},"++":function(p,c,r,o,n,q){this["combinator:+"](p,c,r,o,n,q);
|
||||||
|
this["combinator:!+"](p,c,r,o,n,q);},"~~":function(p,c,r,o,n,q){this["combinator:~"](p,c,r,o,n,q);this["combinator:!~"](p,c,r,o,n,q);},"!":function(p,c,r,o,n,q){while((p=p.parentNode)){if(p!==this.document){this.push(p,c,r,o,n,q);
|
||||||
|
}}},"!>":function(p,c,r,o,n,q){p=p.parentNode;if(p!==this.document){this.push(p,c,r,o,n,q);}},"!+":function(p,c,r,o,n,q){while((p=p.previousSibling)){if(p.nodeType==1){this.push(p,c,r,o,n,q);
|
||||||
|
break;}}},"!^":function(p,c,r,o,n,q){p=p.lastChild;if(p){if(p.nodeType==1){this.push(p,c,r,o,n,q);}else{this["combinator:!+"](p,c,r,o,n,q);}}},"!~":function(q,c,s,p,n,r){while((q=q.previousSibling)){if(q.nodeType!=1){continue;
|
||||||
|
}var o=this.getUID(q);if(this.bitUniques[o]){break;}this.bitUniques[o]=true;this.push(q,c,s,p,n,r);}}};for(var i in j){k["combinator:"+i]=j[i];}var l={empty:function(c){var n=c.firstChild;
|
||||||
|
return !(n&&n.nodeType==1)&&!(c.innerText||c.textContent||"").length;},not:function(c,n){return !this.matchNode(c,n);},contains:function(c,n){return(c.innerText||c.textContent||"").indexOf(n)>-1;
|
||||||
|
},"first-child":function(c){while((c=c.previousSibling)){if(c.nodeType==1){return false;}}return true;},"last-child":function(c){while((c=c.nextSibling)){if(c.nodeType==1){return false;
|
||||||
|
}}return true;},"only-child":function(o){var n=o;while((n=n.previousSibling)){if(n.nodeType==1){return false;}}var c=o;while((c=c.nextSibling)){if(c.nodeType==1){return false;
|
||||||
|
}}return true;},"nth-child":k.createNTHPseudo("firstChild","nextSibling","posNTH"),"nth-last-child":k.createNTHPseudo("lastChild","previousSibling","posNTHLast"),"nth-of-type":k.createNTHPseudo("firstChild","nextSibling","posNTHType",true),"nth-last-of-type":k.createNTHPseudo("lastChild","previousSibling","posNTHTypeLast",true),index:function(n,c){return this["pseudo:nth-child"](n,""+(c+1));
|
||||||
|
},even:function(c){return this["pseudo:nth-child"](c,"2n");},odd:function(c){return this["pseudo:nth-child"](c,"2n+1");},"first-of-type":function(c){var n=c.nodeName;
|
||||||
|
while((c=c.previousSibling)){if(c.nodeName==n){return false;}}return true;},"last-of-type":function(c){var n=c.nodeName;while((c=c.nextSibling)){if(c.nodeName==n){return false;
|
||||||
|
}}return true;},"only-of-type":function(o){var n=o,p=o.nodeName;while((n=n.previousSibling)){if(n.nodeName==p){return false;}}var c=o;while((c=c.nextSibling)){if(c.nodeName==p){return false;
|
||||||
|
}}return true;},enabled:function(c){return !c.disabled;},disabled:function(c){return c.disabled;},checked:function(c){return c.checked||c.selected;},focus:function(c){return this.isHTMLDocument&&this.document.activeElement===c&&(c.href||c.type||this.hasAttribute(c,"tabindex"));
|
||||||
|
},root:function(c){return(c===this.root);},selected:function(c){return c.selected;}};for(var b in l){k["pseudo:"+b]=l[b];}var a=k.attributeGetters={"for":function(){return("htmlFor" in this)?this.htmlFor:this.getAttribute("for");
|
||||||
|
},href:function(){return("href" in this)?this.getAttribute("href",2):this.getAttribute("href");},style:function(){return(this.style)?this.style.cssText:this.getAttribute("style");
|
||||||
|
},tabindex:function(){var c=this.getAttributeNode("tabindex");return(c&&c.specified)?c.nodeValue:null;},type:function(){return this.getAttribute("type");
|
||||||
|
},maxlength:function(){var c=this.getAttributeNode("maxLength");return(c&&c.specified)?c.nodeValue:null;}};a.MAXLENGTH=a.maxLength=a.maxlength;var e=k.Slick=(this.Slick||{});
|
||||||
|
e.version="1.1.7";e.search=function(n,o,c){return k.search(n,o,c);};e.find=function(c,n){return k.search(c,n,null,true);};e.contains=function(c,n){k.setDocument(c);
|
||||||
|
return k.contains(c,n);};e.getAttribute=function(n,c){k.setDocument(n);return k.getAttribute(n,c);};e.hasAttribute=function(n,c){k.setDocument(n);return k.hasAttribute(n,c);
|
||||||
|
};e.match=function(n,c){if(!(n&&c)){return false;}if(!c||c===n){return true;}k.setDocument(n);return k.matchNode(n,c);};e.defineAttributeGetter=function(c,n){k.attributeGetters[c]=n;
|
||||||
|
return this;};e.lookupAttributeGetter=function(c){return k.attributeGetters[c];};e.definePseudo=function(c,n){k["pseudo:"+c]=function(p,o){return n.call(p,o);
|
||||||
|
};return this;};e.lookupPseudo=function(c){var n=k["pseudo:"+c];if(n){return function(o){return n.call(this,o);};}return null;};e.override=function(n,c){k.override(n,c);
|
||||||
|
return this;};e.isXML=k.isXML;e.uidOf=function(c){return k.getUIDHTML(c);};if(!this.Slick){this.Slick=e;}}).apply((typeof exports!="undefined")?exports:this);
|
||||||
|
var Element=function(b,g){var h=Element.Constructors[b];if(h){return h(g);}if(typeof b!="string"){return document.id(b).set(g);}if(!g){g={};}if(!(/^[\w-]+$/).test(b)){var e=Slick.parse(b).expressions[0][0];
|
||||||
|
b=(e.tag=="*")?"div":e.tag;if(e.id&&g.id==null){g.id=e.id;}var d=e.attributes;if(d){for(var a,f=0,c=d.length;f<c;f++){a=d[f];if(g[a.key]!=null){continue;
|
||||||
|
}if(a.value!=null&&a.operator=="="){g[a.key]=a.value;}else{if(!a.value&&!a.operator){g[a.key]=true;}}}}if(e.classList&&g["class"]==null){g["class"]=e.classList.join(" ");
|
||||||
|
}}return document.newElement(b,g);};if(Browser.Element){Element.prototype=Browser.Element.prototype;Element.prototype._fireEvent=(function(a){return function(b,c){return a.call(this,b,c);
|
||||||
|
};})(Element.prototype.fireEvent);}new Type("Element",Element).mirror(function(a){if(Array.prototype[a]){return;}var b={};b[a]=function(){var h=[],e=arguments,j=true;
|
||||||
|
for(var g=0,d=this.length;g<d;g++){var f=this[g],c=h[g]=f[a].apply(f,e);j=(j&&typeOf(c)=="element");}return(j)?new Elements(h):h;};Elements.implement(b);
|
||||||
|
});if(!Browser.Element){Element.parent=Object;Element.Prototype={"$constructor":Element,"$family":Function.from("element").hide()};Element.mirror(function(a,b){Element.Prototype[a]=b;
|
||||||
|
});}Element.Constructors={};var IFrame=new Type("IFrame",function(){var e=Array.link(arguments,{properties:Type.isObject,iframe:function(f){return(f!=null);
|
||||||
|
}});var c=e.properties||{},b;if(e.iframe){b=document.id(e.iframe);}var d=c.onload||function(){};delete c.onload;c.id=c.name=[c.id,c.name,b?(b.id||b.name):"IFrame_"+String.uniqueID()].pick();
|
||||||
|
b=new Element(b||"iframe",c);var a=function(){d.call(b.contentWindow);};if(window.frames[c.id]){a();}else{b.addListener("load",a);}return b;});var Elements=this.Elements=function(a){if(a&&a.length){var e={},d;
|
||||||
|
for(var c=0;d=a[c++];){var b=Slick.uidOf(d);if(!e[b]){e[b]=true;this.push(d);}}}};Elements.prototype={length:0};Elements.parent=Array;new Type("Elements",Elements).implement({filter:function(a,b){if(!a){return this;
|
||||||
|
}return new Elements(Array.filter(this,(typeOf(a)=="string")?function(c){return c.match(a);}:a,b));}.protect(),push:function(){var d=this.length;for(var b=0,a=arguments.length;
|
||||||
|
b<a;b++){var c=document.id(arguments[b]);if(c){this[d++]=c;}}return(this.length=d);}.protect(),unshift:function(){var b=[];for(var c=0,a=arguments.length;
|
||||||
|
c<a;c++){var d=document.id(arguments[c]);if(d){b.push(d);}}return Array.prototype.unshift.apply(this,b);}.protect(),concat:function(){var b=new Elements(this);
|
||||||
|
for(var c=0,a=arguments.length;c<a;c++){var d=arguments[c];if(Type.isEnumerable(d)){b.append(d);}else{b.push(d);}}return b;}.protect(),append:function(c){for(var b=0,a=c.length;
|
||||||
|
b<a;b++){this.push(c[b]);}return this;}.protect(),empty:function(){while(this.length){delete this[--this.length];}return this;}.protect()});(function(){var f=Array.prototype.splice,a={"0":0,"1":1,length:2};
|
||||||
|
f.call(a,1,1);if(a[1]==1){Elements.implement("splice",function(){var g=this.length;var e=f.apply(this,arguments);while(g>=this.length){delete this[g--];
|
||||||
|
}return e;}.protect());}Array.forEachMethod(function(g,e){Elements.implement(e,g);});Array.mirror(Elements);var d;try{d=(document.createElement("<input name=x>").name=="x");
|
||||||
|
}catch(b){}var c=function(e){return(""+e).replace(/&/g,"&").replace(/"/g,""");};Document.implement({newElement:function(e,g){if(g&&g.checked!=null){g.defaultChecked=g.checked;
|
||||||
|
}if(d&&g){e="<"+e;if(g.name){e+=' name="'+c(g.name)+'"';}if(g.type){e+=' type="'+c(g.type)+'"';}e+=">";delete g.name;delete g.type;}return this.id(this.createElement(e)).set(g);
|
||||||
|
}});})();(function(){Slick.uidOf(window);Slick.uidOf(document);Document.implement({newTextNode:function(e){return this.createTextNode(e);},getDocument:function(){return this;
|
||||||
|
},getWindow:function(){return this.window;},id:(function(){var e={string:function(E,D,l){E=Slick.find(l,"#"+E.replace(/(\W)/g,"\\$1"));return(E)?e.element(E,D):null;
|
||||||
|
},element:function(D,E){Slick.uidOf(D);if(!E&&!D.$family&&!(/^(?:object|embed)$/i).test(D.tagName)){var l=D.fireEvent;D._fireEvent=function(F,G){return l(F,G);
|
||||||
|
};Object.append(D,Element.Prototype);}return D;},object:function(D,E,l){if(D.toElement){return e.element(D.toElement(l),E);}return null;}};e.textnode=e.whitespace=e.window=e.document=function(l){return l;
|
||||||
|
};return function(D,F,E){if(D&&D.$family&&D.uniqueNumber){return D;}var l=typeOf(D);return(e[l])?e[l](D,F,E||document):null;};})()});if(window.$==null){Window.implement("$",function(e,l){return document.id(e,l,this.document);
|
||||||
|
});}Window.implement({getDocument:function(){return this.document;},getWindow:function(){return this;}});[Document,Element].invoke("implement",{getElements:function(e){return Slick.search(this,e,new Elements);
|
||||||
|
},getElement:function(e){return document.id(Slick.find(this,e));}});var m={contains:function(e){return Slick.contains(this,e);}};if(!document.contains){Document.implement(m);
|
||||||
|
}if(!document.createElement("div").contains){Element.implement(m);}var r=function(E,D){if(!E){return D;}E=Object.clone(Slick.parse(E));var l=E.expressions;
|
||||||
|
for(var e=l.length;e--;){l[e][0].combinator=D;}return E;};Object.forEach({getNext:"~",getPrevious:"!~",getParent:"!"},function(e,l){Element.implement(l,function(D){return this.getElement(r(D,e));
|
||||||
|
});});Object.forEach({getAllNext:"~",getAllPrevious:"!~",getSiblings:"~~",getChildren:">",getParents:"!"},function(e,l){Element.implement(l,function(D){return this.getElements(r(D,e));
|
||||||
|
});});Element.implement({getFirst:function(e){return document.id(Slick.search(this,r(e,">"))[0]);},getLast:function(e){return document.id(Slick.search(this,r(e,">")).getLast());
|
||||||
|
},getWindow:function(){return this.ownerDocument.window;},getDocument:function(){return this.ownerDocument;},getElementById:function(e){return document.id(Slick.find(this,"#"+(""+e).replace(/(\W)/g,"\\$1")));
|
||||||
|
},match:function(e){return !e||Slick.match(this,e);}});if(window.$$==null){Window.implement("$$",function(e){if(arguments.length==1){if(typeof e=="string"){return Slick.search(this.document,e,new Elements);
|
||||||
|
}else{if(Type.isEnumerable(e)){return new Elements(e);}}}return new Elements(arguments);});}var w={before:function(l,e){var D=e.parentNode;if(D){D.insertBefore(l,e);
|
||||||
|
}},after:function(l,e){var D=e.parentNode;if(D){D.insertBefore(l,e.nextSibling);}},bottom:function(l,e){e.appendChild(l);},top:function(l,e){e.insertBefore(l,e.firstChild);
|
||||||
|
}};w.inside=w.bottom;var j={},d={};var k={};Array.forEach(["type","value","defaultValue","accessKey","cellPadding","cellSpacing","colSpan","frameBorder","rowSpan","tabIndex","useMap"],function(e){k[e.toLowerCase()]=e;
|
||||||
|
});k.html="innerHTML";k.text=(document.createElement("div").textContent==null)?"innerText":"textContent";Object.forEach(k,function(l,e){d[e]=function(D,E){D[l]=E;
|
||||||
|
};j[e]=function(D){return D[l];};});var x=["compact","nowrap","ismap","declare","noshade","checked","disabled","readOnly","multiple","selected","noresize","defer","defaultChecked","autofocus","controls","autoplay","loop"];
|
||||||
|
var h={};Array.forEach(x,function(e){var l=e.toLowerCase();h[l]=e;d[l]=function(D,E){D[e]=!!E;};j[l]=function(D){return !!D[e];};});Object.append(d,{"class":function(e,l){("className" in e)?e.className=(l||""):e.setAttribute("class",l);
|
||||||
|
},"for":function(e,l){("htmlFor" in e)?e.htmlFor=l:e.setAttribute("for",l);},style:function(e,l){(e.style)?e.style.cssText=l:e.setAttribute("style",l);
|
||||||
|
},value:function(e,l){e.value=(l!=null)?l:"";}});j["class"]=function(e){return("className" in e)?e.className||null:e.getAttribute("class");};var f=document.createElement("button");
|
||||||
|
try{f.type="button";}catch(z){}if(f.type!="button"){d.type=function(e,l){e.setAttribute("type",l);};}f=null;var p=document.createElement("input");p.value="t";
|
||||||
|
p.type="submit";if(p.value!="t"){d.type=function(l,e){var D=l.value;l.type=e;l.value=D;};}p=null;var q=(function(e){e.random="attribute";return(e.getAttribute("random")=="attribute");
|
||||||
|
})(document.createElement("div"));Element.implement({setProperty:function(l,D){var E=d[l.toLowerCase()];if(E){E(this,D);}else{if(q){var e=this.retrieve("$attributeWhiteList",{});
|
||||||
|
}if(D==null){this.removeAttribute(l);if(q){delete e[l];}}else{this.setAttribute(l,""+D);if(q){e[l]=true;}}}return this;},setProperties:function(e){for(var l in e){this.setProperty(l,e[l]);
|
||||||
|
}return this;},getProperty:function(F){var D=j[F.toLowerCase()];if(D){return D(this);}if(q){var l=this.getAttributeNode(F),E=this.retrieve("$attributeWhiteList",{});
|
||||||
|
if(!l){return null;}if(l.expando&&!E[F]){var G=this.outerHTML;if(G.substr(0,G.search(/\/?['"]?>(?![^<]*<['"])/)).indexOf(F)<0){return null;}E[F]=true;}}var e=Slick.getAttribute(this,F);
|
||||||
|
return(!e&&!Slick.hasAttribute(this,F))?null:e;},getProperties:function(){var e=Array.from(arguments);return e.map(this.getProperty,this).associate(e);
|
||||||
|
},removeProperty:function(e){return this.setProperty(e,null);},removeProperties:function(){Array.each(arguments,this.removeProperty,this);return this;},set:function(D,l){var e=Element.Properties[D];
|
||||||
|
(e&&e.set)?e.set.call(this,l):this.setProperty(D,l);}.overloadSetter(),get:function(l){var e=Element.Properties[l];return(e&&e.get)?e.get.apply(this):this.getProperty(l);
|
||||||
|
}.overloadGetter(),erase:function(l){var e=Element.Properties[l];(e&&e.erase)?e.erase.apply(this):this.removeProperty(l);return this;},hasClass:function(e){return this.className.clean().contains(e," ");
|
||||||
|
},addClass:function(e){if(!this.hasClass(e)){this.className=(this.className+" "+e).clean();}return this;},removeClass:function(e){this.className=this.className.replace(new RegExp("(^|\\s)"+e+"(?:\\s|$)"),"$1");
|
||||||
|
return this;},toggleClass:function(e,l){if(l==null){l=!this.hasClass(e);}return(l)?this.addClass(e):this.removeClass(e);},adopt:function(){var E=this,e,G=Array.flatten(arguments),F=G.length;
|
||||||
|
if(F>1){E=e=document.createDocumentFragment();}for(var D=0;D<F;D++){var l=document.id(G[D],true);if(l){E.appendChild(l);}}if(e){this.appendChild(e);}return this;
|
||||||
|
},appendText:function(l,e){return this.grab(this.getDocument().newTextNode(l),e);},grab:function(l,e){w[e||"bottom"](document.id(l,true),this);return this;
|
||||||
|
},inject:function(l,e){w[e||"bottom"](this,document.id(l,true));return this;},replaces:function(e){e=document.id(e,true);e.parentNode.replaceChild(this,e);
|
||||||
|
return this;},wraps:function(l,e){l=document.id(l,true);return this.replaces(l).grab(l,e);},getSelected:function(){this.selectedIndex;return new Elements(Array.from(this.options).filter(function(e){return e.selected;
|
||||||
|
}));},toQueryString:function(){var e=[];this.getElements("input, select, textarea").each(function(D){var l=D.type;if(!D.name||D.disabled||l=="submit"||l=="reset"||l=="file"||l=="image"){return;
|
||||||
|
}var E=(D.get("tag")=="select")?D.getSelected().map(function(F){return document.id(F).get("value");}):((l=="radio"||l=="checkbox")&&!D.checked)?null:D.get("value");
|
||||||
|
Array.from(E).each(function(F){if(typeof F!="undefined"){e.push(encodeURIComponent(D.name)+"="+encodeURIComponent(F));}});});return e.join("&");}});var i={},A={};
|
||||||
|
var B=function(e){return(A[e]||(A[e]={}));};var v=function(l){var e=l.uniqueNumber;if(l.removeEvents){l.removeEvents();}if(l.clearAttributes){l.clearAttributes();
|
||||||
|
}if(e!=null){delete i[e];delete A[e];}return l;};var C={input:"checked",option:"selected",textarea:"value"};Element.implement({destroy:function(){var e=v(this).getElementsByTagName("*");
|
||||||
|
Array.each(e,v);Element.dispose(this);return null;},empty:function(){Array.from(this.childNodes).each(Element.dispose);return this;},dispose:function(){return(this.parentNode)?this.parentNode.removeChild(this):this;
|
||||||
|
},clone:function(G,E){G=G!==false;var L=this.cloneNode(G),D=[L],F=[this],J;if(G){D.append(Array.from(L.getElementsByTagName("*")));F.append(Array.from(this.getElementsByTagName("*")));
|
||||||
|
}for(J=D.length;J--;){var H=D[J],K=F[J];if(!E){H.removeAttribute("id");}if(H.clearAttributes){H.clearAttributes();H.mergeAttributes(K);H.removeAttribute("uniqueNumber");
|
||||||
|
if(H.options){var O=H.options,e=K.options;for(var I=O.length;I--;){O[I].selected=e[I].selected;}}}var l=C[K.tagName.toLowerCase()];if(l&&K[l]){H[l]=K[l];
|
||||||
|
}}if(Browser.ie){var M=L.getElementsByTagName("object"),N=this.getElementsByTagName("object");for(J=M.length;J--;){M[J].outerHTML=N[J].outerHTML;}}return document.id(L);
|
||||||
|
}});[Element,Window,Document].invoke("implement",{addListener:function(E,D){if(E=="unload"){var e=D,l=this;D=function(){l.removeListener("unload",D);e();
|
||||||
|
};}else{i[Slick.uidOf(this)]=this;}if(this.addEventListener){this.addEventListener(E,D,!!arguments[2]);}else{this.attachEvent("on"+E,D);}return this;},removeListener:function(l,e){if(this.removeEventListener){this.removeEventListener(l,e,!!arguments[2]);
|
||||||
|
}else{this.detachEvent("on"+l,e);}return this;},retrieve:function(l,e){var E=B(Slick.uidOf(this)),D=E[l];if(e!=null&&D==null){D=E[l]=e;}return D!=null?D:null;
|
||||||
|
},store:function(l,e){var D=B(Slick.uidOf(this));D[l]=e;return this;},eliminate:function(e){var l=B(Slick.uidOf(this));delete l[e];return this;}});if(window.attachEvent&&!window.addEventListener){window.addListener("unload",function(){Object.each(i,v);
|
||||||
|
if(window.CollectGarbage){CollectGarbage();}});}Element.Properties={};Element.Properties.style={set:function(e){this.style.cssText=e;},get:function(){return this.style.cssText;
|
||||||
|
},erase:function(){this.style.cssText="";}};Element.Properties.tag={get:function(){return this.tagName.toLowerCase();}};Element.Properties.html={set:function(e){if(e==null){e="";
|
||||||
|
}else{if(typeOf(e)=="array"){e=e.join("");}}this.innerHTML=e;},erase:function(){this.innerHTML="";}};var t=document.createElement("div");t.innerHTML="<nav></nav>";
|
||||||
|
var a=(t.childNodes.length==1);if(!a){var s="abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video".split(" "),b=document.createDocumentFragment(),u=s.length;
|
||||||
|
while(u--){b.createElement(s[u]);}}t=null;var g=Function.attempt(function(){var e=document.createElement("table");e.innerHTML="<tr><td></td></tr>";return true;
|
||||||
|
});var c=document.createElement("tr"),o="<td></td>";c.innerHTML=o;var y=(c.innerHTML==o);c=null;if(!g||!y||!a){Element.Properties.html.set=(function(l){var e={table:[1,"<table>","</table>"],select:[1,"<select>","</select>"],tbody:[2,"<table><tbody>","</tbody></table>"],tr:[3,"<table><tbody><tr>","</tr></tbody></table>"]};
|
||||||
|
e.thead=e.tfoot=e.tbody;return function(D){var E=e[this.get("tag")];if(!E&&!a){E=[0,"",""];}if(!E){return l.call(this,D);}var H=E[0],G=document.createElement("div"),F=G;
|
||||||
|
if(!a){b.appendChild(G);}G.innerHTML=[E[1],D,E[2]].flatten().join("");while(H--){F=F.firstChild;}this.empty().adopt(F.childNodes);if(!a){b.removeChild(G);
|
||||||
|
}G=null;};})(Element.Properties.html.set);}var n=document.createElement("form");n.innerHTML="<select><option>s</option></select>";if(n.firstChild.value!="s"){Element.Properties.value={set:function(G){var l=this.get("tag");
|
||||||
|
if(l!="select"){return this.setProperty("value",G);}var D=this.getElements("option");for(var E=0;E<D.length;E++){var F=D[E],e=F.getAttributeNode("value"),H=(e&&e.specified)?F.value:F.get("text");
|
||||||
|
if(H==G){return F.selected=true;}}},get:function(){var D=this,l=D.get("tag");if(l!="select"&&l!="option"){return this.getProperty("value");}if(l=="select"&&!(D=D.getSelected()[0])){return"";
|
||||||
|
}var e=D.getAttributeNode("value");return(e&&e.specified)?D.value:D.get("text");}};}n=null;if(document.createElement("div").getAttributeNode("id")){Element.Properties.id={set:function(e){this.id=this.getAttributeNode("id").value=e;
|
||||||
|
},get:function(){return this.id||null;},erase:function(){this.id=this.getAttributeNode("id").value="";}};}})();(function(){var i=document.html;var d=document.createElement("div");
|
||||||
|
d.style.color="red";d.style.color=null;var c=d.style.color=="red";d=null;Element.Properties.styles={set:function(k){this.setStyles(k);}};var h=(i.style.opacity!=null),e=(i.style.filter!=null),j=/alpha\(opacity=([\d.]+)\)/i;
|
||||||
|
var a=function(l,k){l.store("$opacity",k);l.style.visibility=k>0||k==null?"visible":"hidden";};var f=(h?function(l,k){l.style.opacity=k;}:(e?function(l,k){var n=l.style;
|
||||||
|
if(!l.currentStyle||!l.currentStyle.hasLayout){n.zoom=1;}if(k==null||k==1){k="";}else{k="alpha(opacity="+(k*100).limit(0,100).round()+")";}var m=n.filter||l.getComputedStyle("filter")||"";
|
||||||
|
n.filter=j.test(m)?m.replace(j,k):m+k;if(!n.filter){n.removeAttribute("filter");}}:a));var g=(h?function(l){var k=l.style.opacity||l.getComputedStyle("opacity");
|
||||||
|
return(k=="")?1:k.toFloat();}:(e?function(l){var m=(l.style.filter||l.getComputedStyle("filter")),k;if(m){k=m.match(j);}return(k==null||m==null)?1:(k[1]/100);
|
||||||
|
}:function(l){var k=l.retrieve("$opacity");if(k==null){k=(l.style.visibility=="hidden"?0:1);}return k;}));var b=(i.style.cssFloat==null)?"styleFloat":"cssFloat";
|
||||||
|
Element.implement({getComputedStyle:function(m){if(this.currentStyle){return this.currentStyle[m.camelCase()];}var l=Element.getDocument(this).defaultView,k=l?l.getComputedStyle(this,null):null;
|
||||||
|
return(k)?k.getPropertyValue((m==b)?"float":m.hyphenate()):null;},setStyle:function(l,k){if(l=="opacity"){if(k!=null){k=parseFloat(k);}f(this,k);return this;
|
||||||
|
}l=(l=="float"?b:l).camelCase();if(typeOf(k)!="string"){var m=(Element.Styles[l]||"@").split(" ");k=Array.from(k).map(function(o,n){if(!m[n]){return"";
|
||||||
|
}return(typeOf(o)=="number")?m[n].replace("@",Math.round(o)):o;}).join(" ");}else{if(k==String(Number(k))){k=Math.round(k);}}this.style[l]=k;if((k==""||k==null)&&c&&this.style.removeAttribute){this.style.removeAttribute(l);
|
||||||
|
}return this;},getStyle:function(q){if(q=="opacity"){return g(this);}q=(q=="float"?b:q).camelCase();var k=this.style[q];if(!k||q=="zIndex"){k=[];for(var p in Element.ShortStyles){if(q!=p){continue;
|
||||||
|
}for(var o in Element.ShortStyles[p]){k.push(this.getStyle(o));}return k.join(" ");}k=this.getComputedStyle(q);}if(k){k=String(k);var m=k.match(/rgba?\([\d\s,]+\)/);
|
||||||
|
if(m){k=k.replace(m[0],m[0].rgbToHex());}}if(Browser.opera||Browser.ie){if((/^(height|width)$/).test(q)&&!(/px$/.test(k))){var l=(q=="width")?["left","right"]:["top","bottom"],n=0;
|
||||||
|
l.each(function(r){n+=this.getStyle("border-"+r+"-width").toInt()+this.getStyle("padding-"+r).toInt();},this);return this["offset"+q.capitalize()]-n+"px";
|
||||||
|
}if(Browser.ie&&(/^border(.+)Width|margin|padding/).test(q)&&isNaN(parseFloat(k))){return"0px";}}return k;},setStyles:function(l){for(var k in l){this.setStyle(k,l[k]);
|
||||||
|
}return this;},getStyles:function(){var k={};Array.flatten(arguments).each(function(l){k[l]=this.getStyle(l);},this);return k;}});Element.Styles={left:"@px",top:"@px",bottom:"@px",right:"@px",width:"@px",height:"@px",maxWidth:"@px",maxHeight:"@px",minWidth:"@px",minHeight:"@px",backgroundColor:"rgb(@, @, @)",backgroundPosition:"@px @px",color:"rgb(@, @, @)",fontSize:"@px",letterSpacing:"@px",lineHeight:"@px",clip:"rect(@px @px @px @px)",margin:"@px @px @px @px",padding:"@px @px @px @px",border:"@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)",borderWidth:"@px @px @px @px",borderStyle:"@ @ @ @",borderColor:"rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)",zIndex:"@",zoom:"@",fontWeight:"@",textIndent:"@px",opacity:"@"};
|
||||||
|
Element.ShortStyles={margin:{},padding:{},border:{},borderWidth:{},borderStyle:{},borderColor:{}};["Top","Right","Bottom","Left"].each(function(q){var p=Element.ShortStyles;
|
||||||
|
var l=Element.Styles;["margin","padding"].each(function(r){var s=r+q;p[r][s]=l[s]="@px";});var o="border"+q;p.border[o]=l[o]="@px @ rgb(@, @, @)";var n=o+"Width",k=o+"Style",m=o+"Color";
|
||||||
|
p[o]={};p.borderWidth[n]=p[o][n]=l[n]="@px";p.borderStyle[k]=p[o][k]=l[k]="@";p.borderColor[m]=p[o][m]=l[m]="rgb(@, @, @)";});})();(function(){Element.Properties.events={set:function(b){this.addEvents(b);
|
||||||
|
}};[Element,Window,Document].invoke("implement",{addEvent:function(f,h){var i=this.retrieve("events",{});if(!i[f]){i[f]={keys:[],values:[]};}if(i[f].keys.contains(h)){return this;
|
||||||
|
}i[f].keys.push(h);var g=f,b=Element.Events[f],d=h,j=this;if(b){if(b.onAdd){b.onAdd.call(this,h,f);}if(b.condition){d=function(k){if(b.condition.call(this,k,f)){return h.call(this,k);
|
||||||
|
}return true;};}if(b.base){g=Function.from(b.base).call(this,f);}}var e=function(){return h.call(j);};var c=Element.NativeEvents[g];if(c){if(c==2){e=function(k){k=new DOMEvent(k,j.getWindow());
|
||||||
|
if(d.call(j,k)===false){k.stop();}};}this.addListener(g,e,arguments[2]);}i[f].values.push(e);return this;},removeEvent:function(e,d){var c=this.retrieve("events");
|
||||||
|
if(!c||!c[e]){return this;}var h=c[e];var b=h.keys.indexOf(d);if(b==-1){return this;}var g=h.values[b];delete h.keys[b];delete h.values[b];var f=Element.Events[e];
|
||||||
|
if(f){if(f.onRemove){f.onRemove.call(this,d,e);}if(f.base){e=Function.from(f.base).call(this,e);}}return(Element.NativeEvents[e])?this.removeListener(e,g,arguments[2]):this;
|
||||||
|
},addEvents:function(b){for(var c in b){this.addEvent(c,b[c]);}return this;},removeEvents:function(b){var d;if(typeOf(b)=="object"){for(d in b){this.removeEvent(d,b[d]);
|
||||||
|
}return this;}var c=this.retrieve("events");if(!c){return this;}if(!b){for(d in c){this.removeEvents(d);}this.eliminate("events");}else{if(c[b]){c[b].keys.each(function(e){this.removeEvent(b,e);
|
||||||
|
},this);delete c[b];}}return this;},fireEvent:function(e,c,b){var d=this.retrieve("events");if(!d||!d[e]){return this;}c=Array.from(c);d[e].keys.each(function(f){if(b){f.delay(b,this,c);
|
||||||
|
}else{f.apply(this,c);}},this);return this;},cloneEvents:function(e,d){e=document.id(e);var c=e.retrieve("events");if(!c){return this;}if(!d){for(var b in c){this.cloneEvents(e,b);
|
||||||
|
}}else{if(c[d]){c[d].keys.each(function(f){this.addEvent(d,f);},this);}}return this;}});Element.NativeEvents={click:2,dblclick:2,mouseup:2,mousedown:2,contextmenu:2,mousewheel:2,DOMMouseScroll:2,mouseover:2,mouseout:2,mousemove:2,selectstart:2,selectend:2,keydown:2,keypress:2,keyup:2,orientationchange:2,touchstart:2,touchmove:2,touchend:2,touchcancel:2,gesturestart:2,gesturechange:2,gestureend:2,focus:2,blur:2,change:2,reset:2,select:2,submit:2,paste:2,input:2,load:2,unload:1,beforeunload:2,resize:1,move:1,DOMContentLoaded:1,readystatechange:1,error:1,abort:1,scroll:1};
|
||||||
|
Element.Events={mousewheel:{base:(Browser.firefox)?"DOMMouseScroll":"mousewheel"}};if("onmouseenter" in document.documentElement){Element.NativeEvents.mouseenter=Element.NativeEvents.mouseleave=2;
|
||||||
|
}else{var a=function(b){var c=b.relatedTarget;if(c==null){return true;}if(!c){return false;}return(c!=this&&c.prefix!="xul"&&typeOf(this)!="document"&&!this.contains(c));
|
||||||
|
};Element.Events.mouseenter={base:"mouseover",condition:a};Element.Events.mouseleave={base:"mouseout",condition:a};}if(!window.addEventListener){Element.NativeEvents.propertychange=2;
|
||||||
|
Element.Events.change={base:function(){var b=this.type;return(this.get("tag")=="input"&&(b=="radio"||b=="checkbox"))?"propertychange":"change";},condition:function(b){return this.type!="radio"||(b.event.propertyName=="checked"&&this.checked);
|
||||||
|
}};}})();(function(){var c=!!window.addEventListener;Element.NativeEvents.focusin=Element.NativeEvents.focusout=2;var k=function(l,m,n,o,p){while(p&&p!=l){if(m(p,o)){return n.call(p,o,p);
|
||||||
|
}p=document.id(p.parentNode);}};var a={mouseenter:{base:"mouseover"},mouseleave:{base:"mouseout"},focus:{base:"focus"+(c?"":"in"),capture:true},blur:{base:c?"blur":"focusout",capture:true}};
|
||||||
|
var b="$delegation:";var i=function(l){return{base:"focusin",remove:function(m,o){var p=m.retrieve(b+l+"listeners",{})[o];if(p&&p.forms){for(var n=p.forms.length;
|
||||||
|
n--;){p.forms[n].removeEvent(l,p.fns[n]);}}},listen:function(x,r,v,n,t,s){var o=(t.get("tag")=="form")?t:n.target.getParent("form");if(!o){return;}var u=x.retrieve(b+l+"listeners",{}),p=u[s]||{forms:[],fns:[]},m=p.forms,w=p.fns;
|
||||||
|
if(m.indexOf(o)!=-1){return;}m.push(o);var q=function(y){k(x,r,v,y,t);};o.addEvent(l,q);w.push(q);u[s]=p;x.store(b+l+"listeners",u);}};};var d=function(l){return{base:"focusin",listen:function(m,n,p,q,r){var o={blur:function(){this.removeEvents(o);
|
||||||
|
}};o[l]=function(s){k(m,n,p,s,r);};q.target.addEvents(o);}};};if(!c){Object.append(a,{submit:i("submit"),reset:i("reset"),change:d("change"),select:d("select")});
|
||||||
|
}var h=Element.prototype,f=h.addEvent,j=h.removeEvent;var e=function(l,m){return function(r,q,n){if(r.indexOf(":relay")==-1){return l.call(this,r,q,n);
|
||||||
|
}var o=Slick.parse(r).expressions[0][0];if(o.pseudos[0].key!="relay"){return l.call(this,r,q,n);}var p=o.tag;o.pseudos.slice(1).each(function(s){p+=":"+s.key+(s.value?"("+s.value+")":"");
|
||||||
|
});l.call(this,r,q);return m.call(this,p,o.pseudos[0].value,q);};};var g={addEvent:function(v,q,x){var t=this.retrieve("$delegates",{}),r=t[v];if(r){for(var y in r){if(r[y].fn==x&&r[y].match==q){return this;
|
||||||
|
}}}var p=v,u=q,o=x,n=a[v]||{};v=n.base||p;q=function(B){return Slick.match(B,u);};var w=Element.Events[p];if(w&&w.condition){var l=q,m=w.condition;q=function(C,B){return l(C,B)&&m.call(C,B,v);
|
||||||
|
};}var z=this,s=String.uniqueID();var A=n.listen?function(B,C){if(!C&&B&&B.target){C=B.target;}if(C){n.listen(z,q,x,B,C,s);}}:function(B,C){if(!C&&B&&B.target){C=B.target;
|
||||||
|
}if(C){k(z,q,x,B,C);}};if(!r){r={};}r[s]={match:u,fn:o,delegator:A};t[p]=r;return f.call(this,v,A,n.capture);},removeEvent:function(r,n,t,u){var q=this.retrieve("$delegates",{}),p=q[r];
|
||||||
|
if(!p){return this;}if(u){var m=r,w=p[u].delegator,l=a[r]||{};r=l.base||m;if(l.remove){l.remove(this,u);}delete p[u];q[m]=p;return j.call(this,r,w);}var o,v;
|
||||||
|
if(t){for(o in p){v=p[o];if(v.match==n&&v.fn==t){return g.removeEvent.call(this,r,n,t,o);}}}else{for(o in p){v=p[o];if(v.match==n){g.removeEvent.call(this,r,n,v.fn,o);
|
||||||
|
}}}return this;}};[Element,Window,Document].invoke("implement",{addEvent:e(f,g.addEvent),removeEvent:e(j,g.removeEvent)});})();(function(){var h=document.createElement("div"),e=document.createElement("div");
|
||||||
|
h.style.height="0";h.appendChild(e);var d=(e.offsetParent===h);h=e=null;var l=function(m){return k(m,"position")!="static"||a(m);};var i=function(m){return l(m)||(/^(?:table|td|th)$/i).test(m.tagName);
|
||||||
|
};Element.implement({scrollTo:function(m,n){if(a(this)){this.getWindow().scrollTo(m,n);}else{this.scrollLeft=m;this.scrollTop=n;}return this;},getSize:function(){if(a(this)){return this.getWindow().getSize();
|
||||||
|
}return{x:this.offsetWidth,y:this.offsetHeight};},getScrollSize:function(){if(a(this)){return this.getWindow().getScrollSize();}return{x:this.scrollWidth,y:this.scrollHeight};
|
||||||
|
},getScroll:function(){if(a(this)){return this.getWindow().getScroll();}return{x:this.scrollLeft,y:this.scrollTop};},getScrolls:function(){var n=this.parentNode,m={x:0,y:0};
|
||||||
|
while(n&&!a(n)){m.x+=n.scrollLeft;m.y+=n.scrollTop;n=n.parentNode;}return m;},getOffsetParent:d?function(){var m=this;if(a(m)||k(m,"position")=="fixed"){return null;
|
||||||
|
}var n=(k(m,"position")=="static")?i:l;while((m=m.parentNode)){if(n(m)){return m;}}return null;}:function(){var m=this;if(a(m)||k(m,"position")=="fixed"){return null;
|
||||||
|
}try{return m.offsetParent;}catch(n){}return null;},getOffsets:function(){if(this.getBoundingClientRect&&!Browser.Platform.ios){var r=this.getBoundingClientRect(),o=document.id(this.getDocument().documentElement),q=o.getScroll(),t=this.getScrolls(),s=(k(this,"position")=="fixed");
|
||||||
|
return{x:r.left.toInt()+t.x+((s)?0:q.x)-o.clientLeft,y:r.top.toInt()+t.y+((s)?0:q.y)-o.clientTop};}var n=this,m={x:0,y:0};if(a(this)){return m;}while(n&&!a(n)){m.x+=n.offsetLeft;
|
||||||
|
m.y+=n.offsetTop;if(Browser.firefox){if(!c(n)){m.x+=b(n);m.y+=g(n);}var p=n.parentNode;if(p&&k(p,"overflow")!="visible"){m.x+=b(p);m.y+=g(p);}}else{if(n!=this&&Browser.safari){m.x+=b(n);
|
||||||
|
m.y+=g(n);}}n=n.offsetParent;}if(Browser.firefox&&!c(this)){m.x-=b(this);m.y-=g(this);}return m;},getPosition:function(p){var q=this.getOffsets(),n=this.getScrolls();
|
||||||
|
var m={x:q.x-n.x,y:q.y-n.y};if(p&&(p=document.id(p))){var o=p.getPosition();return{x:m.x-o.x-b(p),y:m.y-o.y-g(p)};}return m;},getCoordinates:function(o){if(a(this)){return this.getWindow().getCoordinates();
|
||||||
|
}var m=this.getPosition(o),n=this.getSize();var p={left:m.x,top:m.y,width:n.x,height:n.y};p.right=p.left+p.width;p.bottom=p.top+p.height;return p;},computePosition:function(m){return{left:m.x-j(this,"margin-left"),top:m.y-j(this,"margin-top")};
|
||||||
|
},setPosition:function(m){return this.setStyles(this.computePosition(m));}});[Document,Window].invoke("implement",{getSize:function(){var m=f(this);return{x:m.clientWidth,y:m.clientHeight};
|
||||||
|
},getScroll:function(){var n=this.getWindow(),m=f(this);return{x:n.pageXOffset||m.scrollLeft,y:n.pageYOffset||m.scrollTop};},getScrollSize:function(){var o=f(this),n=this.getSize(),m=this.getDocument().body;
|
||||||
|
return{x:Math.max(o.scrollWidth,m.scrollWidth,n.x),y:Math.max(o.scrollHeight,m.scrollHeight,n.y)};},getPosition:function(){return{x:0,y:0};},getCoordinates:function(){var m=this.getSize();
|
||||||
|
return{top:0,left:0,bottom:m.y,right:m.x,height:m.y,width:m.x};}});var k=Element.getComputedStyle;function j(m,n){return k(m,n).toInt()||0;}function c(m){return k(m,"-moz-box-sizing")=="border-box";
|
||||||
|
}function g(m){return j(m,"border-top-width");}function b(m){return j(m,"border-left-width");}function a(m){return(/^(?:body|html)$/i).test(m.tagName);
|
||||||
|
}function f(m){var n=m.getDocument();return(!n.compatMode||n.compatMode=="CSS1Compat")?n.html:n.body;}})();Element.alias({position:"setPosition"});[Window,Document,Element].invoke("implement",{getHeight:function(){return this.getSize().y;
|
||||||
|
},getWidth:function(){return this.getSize().x;},getScrollTop:function(){return this.getScroll().y;},getScrollLeft:function(){return this.getScroll().x;
|
||||||
|
},getScrollHeight:function(){return this.getScrollSize().y;},getScrollWidth:function(){return this.getScrollSize().x;},getTop:function(){return this.getPosition().y;
|
||||||
|
},getLeft:function(){return this.getPosition().x;}});(function(){var f=this.Fx=new Class({Implements:[Chain,Events,Options],options:{fps:60,unit:false,duration:500,frames:null,frameSkip:true,link:"ignore"},initialize:function(g){this.subject=this.subject||this;
|
||||||
|
this.setOptions(g);},getTransition:function(){return function(g){return -(Math.cos(Math.PI*g)-1)/2;};},step:function(g){if(this.options.frameSkip){var h=(this.time!=null)?(g-this.time):0,i=h/this.frameInterval;
|
||||||
|
this.time=g;this.frame+=i;}else{this.frame++;}if(this.frame<this.frames){var j=this.transition(this.frame/this.frames);this.set(this.compute(this.from,this.to,j));
|
||||||
|
}else{this.frame=this.frames;this.set(this.compute(this.from,this.to,1));this.stop();}},set:function(g){return g;},compute:function(i,h,g){return f.compute(i,h,g);
|
||||||
|
},check:function(){if(!this.isRunning()){return true;}switch(this.options.link){case"cancel":this.cancel();return true;case"chain":this.chain(this.caller.pass(arguments,this));
|
||||||
|
return false;}return false;},start:function(k,j){if(!this.check(k,j)){return this;}this.from=k;this.to=j;this.frame=(this.options.frameSkip)?0:-1;this.time=null;
|
||||||
|
this.transition=this.getTransition();var i=this.options.frames,h=this.options.fps,g=this.options.duration;this.duration=f.Durations[g]||g.toInt();this.frameInterval=1000/h;
|
||||||
|
this.frames=i||Math.round(this.duration/this.frameInterval);this.fireEvent("start",this.subject);b.call(this,h);return this;},stop:function(){if(this.isRunning()){this.time=null;
|
||||||
|
d.call(this,this.options.fps);if(this.frames==this.frame){this.fireEvent("complete",this.subject);if(!this.callChain()){this.fireEvent("chainComplete",this.subject);
|
||||||
|
}}else{this.fireEvent("stop",this.subject);}}return this;},cancel:function(){if(this.isRunning()){this.time=null;d.call(this,this.options.fps);this.frame=this.frames;
|
||||||
|
this.fireEvent("cancel",this.subject).clearChain();}return this;},pause:function(){if(this.isRunning()){this.time=null;d.call(this,this.options.fps);}return this;
|
||||||
|
},resume:function(){if((this.frame<this.frames)&&!this.isRunning()){b.call(this,this.options.fps);}return this;},isRunning:function(){var g=e[this.options.fps];
|
||||||
|
return g&&g.contains(this);}});f.compute=function(i,h,g){return(h-i)*g+i;};f.Durations={"short":250,normal:500,"long":1000};var e={},c={};var a=function(){var h=Date.now();
|
||||||
|
for(var j=this.length;j--;){var g=this[j];if(g){g.step(h);}}};var b=function(h){var g=e[h]||(e[h]=[]);g.push(this);if(!c[h]){c[h]=a.periodical(Math.round(1000/h),g);
|
||||||
|
}};var d=function(h){var g=e[h];if(g){g.erase(this);if(!g.length&&c[h]){delete e[h];c[h]=clearInterval(c[h]);}}};})();Fx.CSS=new Class({Extends:Fx,prepare:function(b,e,a){a=Array.from(a);
|
||||||
|
var h=a[0],g=a[1];if(g==null){g=h;h=b.getStyle(e);var c=this.options.unit;if(c&&h.slice(-c.length)!=c&&parseFloat(h)!=0){b.setStyle(e,g+c);var d=b.getComputedStyle(e);
|
||||||
|
if(!(/px$/.test(d))){d=b.style[("pixel-"+e).camelCase()];if(d==null){var f=b.style.left;b.style.left=g+c;d=b.style.pixelLeft;b.style.left=f;}}h=(g||1)/(parseFloat(d)||1)*(parseFloat(h)||0);
|
||||||
|
b.setStyle(e,h+c);}}return{from:this.parse(h),to:this.parse(g)};},parse:function(a){a=Function.from(a)();a=(typeof a=="string")?a.split(" "):Array.from(a);
|
||||||
|
return a.map(function(c){c=String(c);var b=false;Object.each(Fx.CSS.Parsers,function(f,e){if(b){return;}var d=f.parse(c);if(d||d===0){b={value:d,parser:f};
|
||||||
|
}});b=b||{value:c,parser:Fx.CSS.Parsers.String};return b;});},compute:function(d,c,b){var a=[];(Math.min(d.length,c.length)).times(function(e){a.push({value:d[e].parser.compute(d[e].value,c[e].value,b),parser:d[e].parser});
|
||||||
|
});a.$family=Function.from("fx:css:value");return a;},serve:function(c,b){if(typeOf(c)!="fx:css:value"){c=this.parse(c);}var a=[];c.each(function(d){a=a.concat(d.parser.serve(d.value,b));
|
||||||
|
});return a;},render:function(a,d,c,b){a.setStyle(d,this.serve(c,b));},search:function(a){if(Fx.CSS.Cache[a]){return Fx.CSS.Cache[a];}var c={},b=new RegExp("^"+a.escapeRegExp()+"$");
|
||||||
|
Array.each(document.styleSheets,function(f,e){var d=f.href;if(d&&d.contains("://")&&!d.contains(document.domain)){return;}var g=f.rules||f.cssRules;Array.each(g,function(k,h){if(!k.style){return;
|
||||||
|
}var j=(k.selectorText)?k.selectorText.replace(/^\w+/,function(i){return i.toLowerCase();}):null;if(!j||!b.test(j)){return;}Object.each(Element.Styles,function(l,i){if(!k.style[i]||Element.ShortStyles[i]){return;
|
||||||
|
}l=String(k.style[i]);c[i]=((/^rgb/).test(l))?l.rgbToHex():l;});});});return Fx.CSS.Cache[a]=c;}});Fx.CSS.Cache={};Fx.CSS.Parsers={Color:{parse:function(a){if(a.match(/^#[0-9a-f]{3,6}$/i)){return a.hexToRgb(true);
|
||||||
|
}return((a=a.match(/(\d+),\s*(\d+),\s*(\d+)/)))?[a[1],a[2],a[3]]:false;},compute:function(c,b,a){return c.map(function(e,d){return Math.round(Fx.compute(c[d],b[d],a));
|
||||||
|
});},serve:function(a){return a.map(Number);}},Number:{parse:parseFloat,compute:Fx.compute,serve:function(b,a){return(a)?b+a:b;}},String:{parse:Function.from(false),compute:function(b,a){return a;
|
||||||
|
},serve:function(a){return a;}}};Fx.Tween=new Class({Extends:Fx.CSS,initialize:function(b,a){this.element=this.subject=document.id(b);this.parent(a);},set:function(b,a){if(arguments.length==1){a=b;
|
||||||
|
b=this.property||this.options.property;}this.render(this.element,b,a,this.options.unit);return this;},start:function(c,e,d){if(!this.check(c,e,d)){return this;
|
||||||
|
}var b=Array.flatten(arguments);this.property=this.options.property||b.shift();var a=this.prepare(this.element,this.property,b);return this.parent(a.from,a.to);
|
||||||
|
}});Element.Properties.tween={set:function(a){this.get("tween").cancel().setOptions(a);return this;},get:function(){var a=this.retrieve("tween");if(!a){a=new Fx.Tween(this,{link:"cancel"});
|
||||||
|
this.store("tween",a);}return a;}};Element.implement({tween:function(a,c,b){this.get("tween").start(a,c,b);return this;},fade:function(d){var e=this.get("tween"),g,c=["opacity"].append(arguments),a;
|
||||||
|
if(c[1]==null){c[1]="toggle";}switch(c[1]){case"in":g="start";c[1]=1;break;case"out":g="start";c[1]=0;break;case"show":g="set";c[1]=1;break;case"hide":g="set";
|
||||||
|
c[1]=0;break;case"toggle":var b=this.retrieve("fade:flag",this.getStyle("opacity")==1);g="start";c[1]=b?0:1;this.store("fade:flag",!b);a=true;break;default:g="start";
|
||||||
|
}if(!a){this.eliminate("fade:flag");}e[g].apply(e,c);var f=c[c.length-1];if(g=="set"||f!=0){this.setStyle("visibility",f==0?"hidden":"visible");}else{e.chain(function(){this.element.setStyle("visibility","hidden");
|
||||||
|
this.callChain();});}return this;},highlight:function(c,a){if(!a){a=this.retrieve("highlight:original",this.getStyle("background-color"));a=(a=="transparent")?"#fff":a;
|
||||||
|
}var b=this.get("tween");b.start("background-color",c||"#ffff88",a).chain(function(){this.setStyle("background-color",this.retrieve("highlight:original"));
|
||||||
|
b.callChain();}.bind(this));return this;}});Fx.Morph=new Class({Extends:Fx.CSS,initialize:function(b,a){this.element=this.subject=document.id(b);this.parent(a);
|
||||||
|
},set:function(a){if(typeof a=="string"){a=this.search(a);}for(var b in a){this.render(this.element,b,a[b],this.options.unit);}return this;},compute:function(e,d,c){var a={};
|
||||||
|
for(var b in e){a[b]=this.parent(e[b],d[b],c);}return a;},start:function(b){if(!this.check(b)){return this;}if(typeof b=="string"){b=this.search(b);}var e={},d={};
|
||||||
|
for(var c in b){var a=this.prepare(this.element,c,b[c]);e[c]=a.from;d[c]=a.to;}return this.parent(e,d);}});Element.Properties.morph={set:function(a){this.get("morph").cancel().setOptions(a);
|
||||||
|
return this;},get:function(){var a=this.retrieve("morph");if(!a){a=new Fx.Morph(this,{link:"cancel"});this.store("morph",a);}return a;}};Element.implement({morph:function(a){this.get("morph").start(a);
|
||||||
|
return this;}});Fx.implement({getTransition:function(){var a=this.options.transition||Fx.Transitions.Sine.easeInOut;if(typeof a=="string"){var b=a.split(":");
|
||||||
|
a=Fx.Transitions;a=a[b[0]]||a[b[0].capitalize()];if(b[1]){a=a["ease"+b[1].capitalize()+(b[2]?b[2].capitalize():"")];}}return a;}});Fx.Transition=function(c,b){b=Array.from(b);
|
||||||
|
var a=function(d){return c(d,b);};return Object.append(a,{easeIn:a,easeOut:function(d){return 1-c(1-d,b);},easeInOut:function(d){return(d<=0.5?c(2*d,b):(2-c(2*(1-d),b)))/2;
|
||||||
|
}});};Fx.Transitions={linear:function(a){return a;}};Fx.Transitions.extend=function(a){for(var b in a){Fx.Transitions[b]=new Fx.Transition(a[b]);}};Fx.Transitions.extend({Pow:function(b,a){return Math.pow(b,a&&a[0]||6);
|
||||||
|
},Expo:function(a){return Math.pow(2,8*(a-1));},Circ:function(a){return 1-Math.sin(Math.acos(a));},Sine:function(a){return 1-Math.cos(a*Math.PI/2);},Back:function(b,a){a=a&&a[0]||1.618;
|
||||||
|
return Math.pow(b,2)*((a+1)*b-a);},Bounce:function(f){var e;for(var d=0,c=1;1;d+=c,c/=2){if(f>=(7-4*d)/11){e=c*c-Math.pow((11-6*d-11*f)/4,2);break;}}return e;
|
||||||
|
},Elastic:function(b,a){return Math.pow(2,10*--b)*Math.cos(20*b*Math.PI*(a&&a[0]||1)/3);}});["Quad","Cubic","Quart","Quint"].each(function(b,a){Fx.Transitions[b]=new Fx.Transition(function(c){return Math.pow(c,a+2);
|
||||||
|
});});(function(){var d=function(){},a=("onprogress" in new Browser.Request);var c=this.Request=new Class({Implements:[Chain,Events,Options],options:{url:"",data:"",headers:{"X-Requested-With":"XMLHttpRequest",Accept:"text/javascript, text/html, application/xml, text/xml, */*"},async:true,format:false,method:"post",link:"ignore",isSuccess:null,emulation:true,urlEncoded:true,encoding:"utf-8",evalScripts:false,evalResponse:false,timeout:0,noCache:false},initialize:function(e){this.xhr=new Browser.Request();
|
||||||
|
this.setOptions(e);this.headers=this.options.headers;},onStateChange:function(){var e=this.xhr;if(e.readyState!=4||!this.running){return;}this.running=false;
|
||||||
|
this.status=0;Function.attempt(function(){var f=e.status;this.status=(f==1223)?204:f;}.bind(this));e.onreadystatechange=d;if(a){e.onprogress=e.onloadstart=d;
|
||||||
|
}clearTimeout(this.timer);this.response={text:this.xhr.responseText||"",xml:this.xhr.responseXML};if(this.options.isSuccess.call(this,this.status)){this.success(this.response.text,this.response.xml);
|
||||||
|
}else{this.failure();}},isSuccess:function(){var e=this.status;return(e>=200&&e<300);},isRunning:function(){return !!this.running;},processScripts:function(e){if(this.options.evalResponse||(/(ecma|java)script/).test(this.getHeader("Content-type"))){return Browser.exec(e);
|
||||||
|
}return e.stripScripts(this.options.evalScripts);},success:function(f,e){this.onSuccess(this.processScripts(f),e);},onSuccess:function(){this.fireEvent("complete",arguments).fireEvent("success",arguments).callChain();
|
||||||
|
},failure:function(){this.onFailure();},onFailure:function(){this.fireEvent("complete").fireEvent("failure",this.xhr);},loadstart:function(e){this.fireEvent("loadstart",[e,this.xhr]);
|
||||||
|
},progress:function(e){this.fireEvent("progress",[e,this.xhr]);},timeout:function(){this.fireEvent("timeout",this.xhr);},setHeader:function(e,f){this.headers[e]=f;
|
||||||
|
return this;},getHeader:function(e){return Function.attempt(function(){return this.xhr.getResponseHeader(e);}.bind(this));},check:function(){if(!this.running){return true;
|
||||||
|
}switch(this.options.link){case"cancel":this.cancel();return true;case"chain":this.chain(this.caller.pass(arguments,this));return false;}return false;},send:function(o){if(!this.check(o)){return this;
|
||||||
|
}this.options.isSuccess=this.options.isSuccess||this.isSuccess;this.running=true;var l=typeOf(o);if(l=="string"||l=="element"){o={data:o};}var h=this.options;
|
||||||
|
o=Object.append({data:h.data,url:h.url,method:h.method},o);var j=o.data,f=String(o.url),e=o.method.toLowerCase();switch(typeOf(j)){case"element":j=document.id(j).toQueryString();
|
||||||
|
break;case"object":case"hash":j=Object.toQueryString(j);}if(this.options.format){var m="format="+this.options.format;j=(j)?m+"&"+j:m;}if(this.options.emulation&&!["get","post"].contains(e)){var k="_method="+e;
|
||||||
|
j=(j)?k+"&"+j:k;e="post";}if(this.options.urlEncoded&&["post","put"].contains(e)){var g=(this.options.encoding)?"; charset="+this.options.encoding:"";this.headers["Content-type"]="application/x-www-form-urlencoded"+g;
|
||||||
|
}if(!f){f=document.location.pathname;}var i=f.lastIndexOf("/");if(i>-1&&(i=f.indexOf("#"))>-1){f=f.substr(0,i);}if(this.options.noCache){f+=(f.contains("?")?"&":"?")+String.uniqueID();
|
||||||
|
}if(j&&e=="get"){f+=(f.contains("?")?"&":"?")+j;j=null;}var n=this.xhr;if(a){n.onloadstart=this.loadstart.bind(this);n.onprogress=this.progress.bind(this);
|
||||||
|
}n.open(e.toUpperCase(),f,this.options.async,this.options.user,this.options.password);if(this.options.user&&"withCredentials" in n){n.withCredentials=true;
|
||||||
|
}n.onreadystatechange=this.onStateChange.bind(this);Object.each(this.headers,function(q,p){try{n.setRequestHeader(p,q);}catch(r){this.fireEvent("exception",[p,q]);
|
||||||
|
}},this);this.fireEvent("request");n.send(j);if(!this.options.async){this.onStateChange();}else{if(this.options.timeout){this.timer=this.timeout.delay(this.options.timeout,this);
|
||||||
|
}}return this;},cancel:function(){if(!this.running){return this;}this.running=false;var e=this.xhr;e.abort();clearTimeout(this.timer);e.onreadystatechange=d;
|
||||||
|
if(a){e.onprogress=e.onloadstart=d;}this.xhr=new Browser.Request();this.fireEvent("cancel");return this;}});var b={};["get","post","put","delete","GET","POST","PUT","DELETE"].each(function(e){b[e]=function(g){var f={method:e};
|
||||||
|
if(g!=null){f.data=g;}return this.send(f);};});c.implement(b);Element.Properties.send={set:function(e){var f=this.get("send").cancel();f.setOptions(e);
|
||||||
|
return this;},get:function(){var e=this.retrieve("send");if(!e){e=new c({data:this,link:"cancel",method:this.get("method")||"post",url:this.get("action")});
|
||||||
|
this.store("send",e);}return e;}};Element.implement({send:function(e){var f=this.get("send");f.send({data:this,url:e||f.options.url});return this;}});})();
|
||||||
|
Request.HTML=new Class({Extends:Request,options:{update:false,append:false,evalScripts:true,filter:false,headers:{Accept:"text/html, application/xml, text/xml, */*"}},success:function(f){var e=this.options,c=this.response;
|
||||||
|
c.html=f.stripScripts(function(h){c.javascript=h;});var d=c.html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);if(d){c.html=d[1];}var b=new Element("div").set("html",c.html);
|
||||||
|
c.tree=b.childNodes;c.elements=b.getElements(e.filter||"*");if(e.filter){c.tree=c.elements;}if(e.update){var g=document.id(e.update).empty();if(e.filter){g.adopt(c.elements);
|
||||||
|
}else{g.set("html",c.html);}}else{if(e.append){var a=document.id(e.append);if(e.filter){c.elements.reverse().inject(a);}else{a.adopt(b.getChildren());}}}if(e.evalScripts){Browser.exec(c.javascript);
|
||||||
|
}this.onSuccess(c.tree,c.elements,c.html,c.javascript);}});Element.Properties.load={set:function(a){var b=this.get("load").cancel();b.setOptions(a);return this;
|
||||||
|
},get:function(){var a=this.retrieve("load");if(!a){a=new Request.HTML({data:this,link:"cancel",update:this,method:"get"});this.store("load",a);}return a;
|
||||||
|
}};Element.implement({load:function(){this.get("load").send(Array.link(arguments,{data:Type.isObject,url:Type.isString}));return this;}});if(typeof JSON=="undefined"){this.JSON={};
|
||||||
|
}(function(){var special={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"};var escape=function(chr){return special[chr]||"\\u"+("0000"+chr.charCodeAt(0).toString(16)).slice(-4);
|
||||||
|
};JSON.validate=function(string){string=string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"");
|
||||||
|
return(/^[\],:{}\s]*$/).test(string);};JSON.encode=JSON.stringify?function(obj){return JSON.stringify(obj);}:function(obj){if(obj&&obj.toJSON){obj=obj.toJSON();
|
||||||
|
}switch(typeOf(obj)){case"string":return'"'+obj.replace(/[\x00-\x1f\\"]/g,escape)+'"';case"array":return"["+obj.map(JSON.encode).clean()+"]";case"object":case"hash":var string=[];
|
||||||
|
Object.each(obj,function(value,key){var json=JSON.encode(value);if(json){string.push(JSON.encode(key)+":"+json);}});return"{"+string+"}";case"number":case"boolean":return""+obj;
|
||||||
|
case"null":return"null";}return null;};JSON.decode=function(string,secure){if(!string||typeOf(string)!="string"){return null;}if(secure||JSON.secure){if(JSON.parse){return JSON.parse(string);
|
||||||
|
}if(!JSON.validate(string)){throw new Error("JSON could not decode the input; security is enabled and the value is not secure.");}}return eval("("+string+")");
|
||||||
|
};})();Request.JSON=new Class({Extends:Request,options:{secure:true},initialize:function(a){this.parent(a);Object.append(this.headers,{Accept:"application/json","X-Request":"JSON"});
|
||||||
|
},success:function(c){var b;try{b=this.response.json=JSON.decode(c,this.options.secure);}catch(a){this.fireEvent("error",[c,a]);return;}if(b==null){this.onFailure();
|
||||||
|
}else{this.onSuccess(b,c);}}});var Cookie=new Class({Implements:Options,options:{path:"/",domain:false,duration:false,secure:false,document:document,encode:true},initialize:function(b,a){this.key=b;
|
||||||
|
this.setOptions(a);},write:function(b){if(this.options.encode){b=encodeURIComponent(b);}if(this.options.domain){b+="; domain="+this.options.domain;}if(this.options.path){b+="; path="+this.options.path;
|
||||||
|
}if(this.options.duration){var a=new Date();a.setTime(a.getTime()+this.options.duration*24*60*60*1000);b+="; expires="+a.toGMTString();}if(this.options.secure){b+="; secure";
|
||||||
|
}this.options.document.cookie=this.key+"="+b;return this;},read:function(){var a=this.options.document.cookie.match("(?:^|;)\\s*"+this.key.escapeRegExp()+"=([^;]*)");
|
||||||
|
return(a)?decodeURIComponent(a[1]):null;},dispose:function(){new Cookie(this.key,Object.merge({},this.options,{duration:-1})).write("");return this;}});
|
||||||
|
Cookie.write=function(b,c,a){return new Cookie(b,a).write(c);};Cookie.read=function(a){return new Cookie(a).read();};Cookie.dispose=function(b,a){return new Cookie(b,a).dispose();
|
||||||
|
};(function(i,k){var l,f,e=[],c,b,d=k.createElement("div");var g=function(){clearTimeout(b);if(l){return;}Browser.loaded=l=true;k.removeListener("DOMContentLoaded",g).removeListener("readystatechange",a);
|
||||||
|
k.fireEvent("domready");i.fireEvent("domready");};var a=function(){for(var m=e.length;m--;){if(e[m]()){g();return true;}}return false;};var j=function(){clearTimeout(b);
|
||||||
|
if(!a()){b=setTimeout(j,10);}};k.addListener("DOMContentLoaded",g);var h=function(){try{d.doScroll();return true;}catch(m){}return false;};if(d.doScroll&&!h()){e.push(h);
|
||||||
|
c=true;}if(k.readyState){e.push(function(){var m=k.readyState;return(m=="loaded"||m=="complete");});}if("onreadystatechange" in k){k.addListener("readystatechange",a);
|
||||||
|
}else{c=true;}if(c){j();}Element.Events.domready={onAdd:function(m){if(l){m.call(this);}}};Element.Events.load={base:"load",onAdd:function(m){if(f&&this==i){m.call(this);
|
||||||
|
}},condition:function(){if(this==i){g();delete Element.Events.load;}return true;}};i.addEvent("load",function(){f=true;});})(window,document);(function(){var Swiff=this.Swiff=new Class({Implements:Options,options:{id:null,height:1,width:1,container:null,properties:{},params:{quality:"high",allowScriptAccess:"always",wMode:"window",swLiveConnect:true},callBacks:{},vars:{}},toElement:function(){return this.object;
|
||||||
|
},initialize:function(path,options){this.instance="Swiff_"+String.uniqueID();this.setOptions(options);options=this.options;var id=this.id=options.id||this.instance;
|
||||||
|
var container=document.id(options.container);Swiff.CallBacks[this.instance]={};var params=options.params,vars=options.vars,callBacks=options.callBacks;
|
||||||
|
var properties=Object.append({height:options.height,width:options.width},options.properties);var self=this;for(var callBack in callBacks){Swiff.CallBacks[this.instance][callBack]=(function(option){return function(){return option.apply(self.object,arguments);
|
||||||
|
};})(callBacks[callBack]);vars[callBack]="Swiff.CallBacks."+this.instance+"."+callBack;}params.flashVars=Object.toQueryString(vars);if(Browser.ie){properties.classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
|
||||||
|
params.movie=path;}else{properties.type="application/x-shockwave-flash";}properties.data=path;var build='<object id="'+id+'"';for(var property in properties){build+=" "+property+'="'+properties[property]+'"';
|
||||||
|
}build+=">";for(var param in params){if(params[param]){build+='<param name="'+param+'" value="'+params[param]+'" />';}}build+="</object>";this.object=((container)?container.empty():new Element("div")).set("html",build).firstChild;
|
||||||
|
},replaces:function(element){element=document.id(element,true);element.parentNode.replaceChild(this.toElement(),element);return this;},inject:function(element){document.id(element,true).appendChild(this.toElement());
|
||||||
|
return this;},remote:function(){return Swiff.remote.apply(Swiff,[this.toElement()].append(arguments));}});Swiff.CallBacks={};Swiff.remote=function(obj,fn){var rs=obj.CallFunction('<invoke name="'+fn+'" returntype="javascript">'+__flash__argumentsToXML(arguments,2)+"</invoke>");
|
||||||
|
return eval(rs);};})();
|
|
@ -0,0 +1,45 @@
|
||||||
|
if(searx.autocompleter) {
|
||||||
|
window.addEvent('domready', function() {
|
||||||
|
new Autocompleter.Request.JSON('q', '/autocompleter', {
|
||||||
|
postVar:'q',
|
||||||
|
postData:{
|
||||||
|
'format': 'json'
|
||||||
|
},
|
||||||
|
ajaxOptions:{
|
||||||
|
timeout: 5 // Correct option?
|
||||||
|
},
|
||||||
|
'minLength': 4,
|
||||||
|
// 'selectMode': 'type-ahead',
|
||||||
|
cache: true,
|
||||||
|
delay: 300
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
(function (w, d) {
|
||||||
|
'use strict';
|
||||||
|
function addListener(el, type, fn) {
|
||||||
|
if (el.addEventListener) {
|
||||||
|
el.addEventListener(type, fn, false);
|
||||||
|
} else {
|
||||||
|
el.attachEvent('on' + type, fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function placeCursorAtEnd() {
|
||||||
|
if (this.setSelectionRange) {
|
||||||
|
var len = this.value.length * 2;
|
||||||
|
this.setSelectionRange(len, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addListener(w, 'load', function () {
|
||||||
|
var qinput = d.getElementById('q');
|
||||||
|
if (qinput !== null && qinput.value === "") {
|
||||||
|
addListener(qinput, 'focus', placeCursorAtEnd);
|
||||||
|
qinput.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
})(window, document);
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
html{font-family:sans-serif;font-size:.9em;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-moz-text-size-adjust:100%;text-size-adjust:100%;color:#444;padding:0;margin:0}
|
||||||
|
body,#container{padding:0;margin:0}
|
||||||
|
#container{width:100%;position:absolute;top:0}
|
||||||
|
.search{padding:0;margin:0}.search .checkbox_container label{font-size:.9em;border-bottom:2px solid #e8e7e6}
|
||||||
|
.search .checkbox_container label:hover{border-bottom:2px solid #3498db}
|
||||||
|
.search .checkbox_container input[type="checkbox"]:checked+label{border-bottom:2px solid #2980b9}
|
||||||
|
#search_wrapper{position:relative;width:50em;padding:10px}
|
||||||
|
.center #search_wrapper{margin-left:auto;margin-right:auto}
|
||||||
|
.q{background:none repeat scroll 0 0 #fff;border:1px solid #3498db;color:#222;font-size:16px;height:28px;margin:0;outline:medium none;padding:2px;padding-left:8px;padding-right:0 !important;width:100%;z-index:2}
|
||||||
|
#search_submit{position:absolute;top:13px;right:1px;padding:0;border:0;background:url('../img/search-icon.png') no-repeat;background-size:24px 24px;opacity:.8;width:24px;height:30px;font-size:0}
|
||||||
|
@media screen and (max-width:50em){#search_wrapper{width:90%;clear:both;overflow:hidden}}ul.autocompleter-choices{position:absolute;margin:0;padding:0;list-style:none;border:1px solid #3498db;border-left-color:#3498db;border-right-color:#3498db;border-bottom-color:#3498db;text-align:left;font-family:Verdana,Geneva,Arial,Helvetica,sans-serif;z-index:50;background-color:#fff;color:#444}ul.autocompleter-choices li{position:relative;margin:-2px 0 0 0;padding:.2em 1.5em .2em 1em;display:block;float:none !important;cursor:pointer;font-weight:normal;white-space:nowrap;font-size:1em;line-height:1.5em}ul.autocompleter-choices li.autocompleter-selected{background-color:#444;color:#fff}ul.autocompleter-choices li.autocompleter-selected span.autocompleter-queried{color:#9fcfff}
|
||||||
|
ul.autocompleter-choices span.autocompleter-queried{display:inline;float:none;font-weight:bold;margin:0;padding:0}
|
||||||
|
.row{max-width:800px;margin:20px auto;text-align:justify}.row h1{font-size:3em;margin-top:50px}
|
||||||
|
.row p{padding:0 10px;max-width:700px}
|
||||||
|
.row h3,.row ul{margin:4px 8px}
|
||||||
|
.hmarg{margin:0 20px;border:1px solid #3498db;padding:4px 10px}
|
||||||
|
a:link.hmarg{color:#3498db}
|
||||||
|
a:visited.hmarg{color:#3498db}
|
||||||
|
a:active.hmarg{color:#3498db}
|
||||||
|
a:hover.hmarg{color:#3498db}
|
||||||
|
.top_margin{margin-top:60px}
|
||||||
|
.center{text-align:center}
|
||||||
|
h1{font-size:5em}
|
||||||
|
div.title{background:url('../img/searx.png') no-repeat;width:100%;min-height:80px;background-position:center}div.title h1{visibility:hidden}
|
||||||
|
input[type="submit"]{padding:2px 6px;margin:2px 4px;display:inline-block;background:#3498db;color:#fff;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;border:0;cursor:pointer}
|
||||||
|
input[type="checkbox"]{visibility:hidden}
|
||||||
|
fieldset{margin:8px;border:1px solid #3498db}
|
||||||
|
#categories{margin:0 10px;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
|
||||||
|
.checkbox_container{display:inline-block;position:relative;margin:0 3px;padding:0}.checkbox_container input{display:none}
|
||||||
|
.checkbox_container label,.engine_checkbox label{cursor:pointer;padding:4px 10px;margin:0;display:block;text-transform:capitalize;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
|
||||||
|
.checkbox_container input[type="checkbox"]:checked+label{background:#3498db;color:#fff}
|
||||||
|
.engine_checkbox{padding:4px}
|
||||||
|
label.allow{background:#e74c3c;padding:4px 8px;color:#fff;display:none}
|
||||||
|
label.deny{background:#2ecc71;padding:4px 8px;color:#444;display:inline}
|
||||||
|
.engine_checkbox input[type="checkbox"]:checked+label:nth-child(2)+label{display:none}
|
||||||
|
.engine_checkbox input[type="checkbox"]:checked+label.allow{display:inline}
|
||||||
|
a{text-decoration:none;color:#1a11be}a:visited{color:#8e44ad}
|
||||||
|
.result{margin:19px 0 18px 0;padding:0;clear:both}
|
||||||
|
.result_title{margin-bottom:0}.result_title a{color:#2980b9;font-weight:normal;font-size:1.1em}.result_title a:hover{text-decoration:underline}
|
||||||
|
.result_title a:visited{color:#8e44ad}
|
||||||
|
.cache_link{font-size:10px !important}
|
||||||
|
.result h3{font-size:1em;word-wrap:break-word;margin:5px 0 1px 0;padding:0}
|
||||||
|
.result .content{font-size:.8em;margin:0;padding:0;max-width:54em;word-wrap:break-word;line-height:1.24}.result .content img{float:left;margin-right:5px;max-width:200px;max-height:100px}
|
||||||
|
.result .content br.last{clear:both}
|
||||||
|
.result .url{font-size:.8em;margin:0 0 3px 0;padding:0;max-width:54em;word-wrap:break-word;color:#c0392b}
|
||||||
|
.result .published_date{font-size:.8em;color:#888;Margin:5px 20px}
|
||||||
|
.result .thumbnail{width:400px}
|
||||||
|
.engines{color:#888}
|
||||||
|
.small_font{font-size:.8em}
|
||||||
|
.small p{margin:2px 0}
|
||||||
|
.right{float:right}
|
||||||
|
.invisible{display:none}
|
||||||
|
.left{float:left}
|
||||||
|
.highlight{color:#094089}
|
||||||
|
.content .highlight{color:#000}
|
||||||
|
.image_result{display:inline-block;margin:10px 10px;position:relative;max-height:160px}.image_result img{border:0;max-height:160px}
|
||||||
|
.image_result p{margin:0;padding:0}.image_result p span a{display:none;color:#fff}
|
||||||
|
.image_result p:hover span a{display:block;position:absolute;bottom:0;right:0;padding:4px;background-color:rgba(0,0,0,0.6);font-size:.7em}
|
||||||
|
.torrent_result{border-left:10px solid #d3d3d3;padding-left:3px}.torrent_result p{margin:3px;font-size:.8em}
|
||||||
|
.definition_result{border-left:10px solid #808080;padding-left:3px}
|
||||||
|
.percentage{position:relative;width:300px}.percentage div{background:#444}
|
||||||
|
table{width:100%}
|
||||||
|
td{padding:0 4px}
|
||||||
|
tr:hover{background:#ddd}
|
||||||
|
#results{margin:auto;padding:0;width:50em;margin-bottom:20px}
|
||||||
|
#sidebar{position:fixed;bottom:10px;left:10px;margin:0 2px 5px 5px;padding:0 2px 2px 2px;width:14em}#sidebar input{padding:0;margin:3px;font-size:.8em;display:inline-block;background:transparent;color:#444;cursor:pointer}
|
||||||
|
#sidebar input[type="submit"]{text-decoration:underline}
|
||||||
|
#suggestions form{display:inline}
|
||||||
|
#suggestions,#answers{margin-top:20px;max-width:45em}
|
||||||
|
#suggestions input,#answers input,#infoboxes input{padding:0;margin:3px;font-size:.8em;display:inline-block;background:transparent;color:#444;cursor:pointer}
|
||||||
|
#suggestions input[type="submit"],#answers input[type="submit"],#infoboxes input[type="submit"]{text-decoration:underline}
|
||||||
|
#answers form,#infoboxes form{min-width:210px}
|
||||||
|
#infoboxes{position:absolute;top:100px;right:20px;margin:0 2px 5px 5px;padding:0 2px 2px;max-width:21em}#infoboxes .infobox{margin:10px 0 10px;border:1px solid #ddd;padding:5px;font-size:.8em;}#infoboxes .infobox img{max-width:20em;max-heigt:12em;display:block;margin:5px;padding:5px}
|
||||||
|
#infoboxes .infobox h2{margin:0}
|
||||||
|
#infoboxes .infobox table{width:auto}#infoboxes .infobox table td{vertical-align:top}
|
||||||
|
#infoboxes .infobox input{font-size:1em}
|
||||||
|
#infoboxes .infobox br{clear:both}
|
||||||
|
#search_url{margin-top:8px}#search_url input{border:1px solid #888;padding:4px;color:#444;width:14em;display:block;margin:4px;font-size:.8em}
|
||||||
|
#preferences{top:10px;padding:0;border:0;background:url('../img/preference-icon.png') no-repeat;background-size:28px 28px;opacity:.8;width:28px;height:30px;display:block}#preferences *{display:none}
|
||||||
|
#pagination{clear:both}#pagination br{clear:both}
|
||||||
|
#apis{margin-top:8px;clear:both}
|
||||||
|
#categories_container{position:relative}
|
||||||
|
@media screen and (max-width:50em){#results{margin:auto;padding:0;width:90%} .github{display:none} .checkbox_container{display:block;width:90%}.checkbox_container label{border-bottom:0} .preferences_container{display:none;postion:fixed !important;top:100px;right:0}}@media screen and (max-width:75em){div.title h1{font-size:1em} html.touch #categories{width:95%;height:30px;text-align:left;overflow-x:scroll;overflow-y:hidden;-webkit-overflow-scrolling:touch}html.touch #categories #categories_container{width:1000px;width:-moz-max-content;width:-webkit-max-content;width:max-content}html.touch #categories #categories_container .checkbox_container{display:inline-block;width:auto} #categories{font-size:90%;clear:both}#categories .checkbox_container{margin-top:2px;margin:auto} #suggestions,#answers{margin-top:5px} #infoboxes{position:inherit;max-width:inherit}#infoboxes .infobox{clear:both}#infoboxes .infobox img{float:left;max-width:10em} #categories{font-size:90%;clear:both}#categories .checkbox_container{margin-top:2px;margin:auto} #sidebar{position:static;max-width:50em;margin:0 0 2px 0;padding:0;float:none;border:none;width:auto}#sidebar input{border:0} #apis{display:none} #search_url{display:none} .result{border-top:1px solid #e8e7e6;margin:8px 0 8px 0}.result .thumbnail{max-width:98%} .image_result{max-width:98%}.image_result img{max-width:98%}}.favicon{float:left;margin-right:4px;margin-top:2px}
|
||||||
|
.preferences_back{background:none repeat scroll 0 0 #3498db;border:0 none;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;cursor:pointer;display:inline-block;margin:2px 4px;padding:4px 6px}.preferences_back a{color:#fff}
|
||||||
|
.hidden{opacity:0;overflow:hidden;font-size:.8em;position:absolute;bottom:-20px;width:100%;text-position:center;background:#fff;transition:opacity 1s ease}
|
||||||
|
#categories_container:hover .hidden{transition:opacity 1s ease;opacity:.8}
|