├── .gitignore ├── LICENSE ├── deploy ├── alias_v2_ik.sh ├── mapping_ik.sh ├── mongo2es.py ├── nginx_ninja.conf ├── uwsgi_sock.sh └── uwsgi_tcp.sh ├── index.py ├── index.sh ├── readme.md ├── requirements.txt ├── search ├── __init__.py └── search.py ├── settings.py ├── static ├── css │ ├── index.css │ ├── jquery-ui.min.css │ ├── result.css │ └── result_small.css ├── favicon.ico ├── image │ ├── ninja.png │ └── ninja.svg └── js │ ├── index.js │ └── jquery-ui.min.js ├── templates ├── index.html ├── result.html └── result_base.html └── util ├── __init__.py ├── ago.py └── page.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | eggs/ 15 | lib/ 16 | lib64/ 17 | parts/ 18 | sdist/ 19 | var/ 20 | *.egg-info/ 21 | .installed.cfg 22 | *.egg 23 | 24 | # Installer logs 25 | pip-log.txt 26 | pip-delete-this-directory.txt 27 | 28 | # Unit test / coverage reports 29 | htmlcov/ 30 | .tox/ 31 | .coverage 32 | .cache 33 | nosetests.xml 34 | coverage.xml 35 | 36 | # Translations 37 | *.mo 38 | *.pot 39 | 40 | # Django stuff: 41 | *.log 42 | 43 | # Sphinx documentation 44 | docs/_build/ 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 dbbbit 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /deploy/alias_v2_ik.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # v2 --> ik 3 | # [why use alias?] http://www.elasticsearch.org/blog/changing-mapping-with-zero-downtime/ 4 | # [Doc] http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-aliases.html 5 | 6 | curl -XPOST localhost:9200/_aliases -d ' 7 | { 8 | "actions": [ 9 | { "add": { 10 | "alias": "v2", 11 | "index": "ik" 12 | }} 13 | ] 14 | } 15 | ' 16 | -------------------------------------------------------------------------------- /deploy/mapping_ik.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | curl -XPUT "http://localhost:9200/ik" 3 | curl -XPUT "http://localhost:9200/ik/topic/_mapping" -d ' 4 | { 5 | "topic": { 6 | "_all": { 7 | "indexAnalyzer": "ik", 8 | "searchAnalyzer": "ik", 9 | "term_vector": "no", 10 | "store": "false" 11 | }, 12 | "properties": { 13 | "content": { 14 | "type": "string", 15 | "term_vector": "with_positions_offsets", 16 | "indexAnalyzer": "ik", 17 | "searchAnalyzer": "ik", 18 | "include_in_all": "true" 19 | }, 20 | "content_rendered": { 21 | "type": "string", "index":"no" 22 | }, 23 | "created": { 24 | "type": "long", "store":"yes" 25 | }, 26 | "last_modified": { 27 | "type": "long" 28 | }, 29 | "last_touched": { 30 | "type": "long" 31 | }, 32 | "member": { 33 | "properties": { 34 | "avatar_large": { 35 | "type": "string", "index":"no" 36 | }, 37 | "avatar_mini": { 38 | "type": "string", "index":"no" 39 | }, 40 | "avatar_normal": { 41 | "type": "string", "index":"no" 42 | }, 43 | "id": { 44 | "type": "long" 45 | }, 46 | "tagline": { 47 | "type": "string", "index":"no" 48 | }, 49 | "username": { 50 | "type": "string", "index":"no" 51 | } 52 | } 53 | }, 54 | "node": { 55 | "properties": { 56 | "avatar_large": { 57 | "type": "string", "index":"no" 58 | }, 59 | "avatar_mini": { 60 | "type": "string", "index":"no" 61 | }, 62 | "avatar_normal": { 63 | "type": "string", "index":"no" 64 | }, 65 | "id": { 66 | "type": "long" 67 | }, 68 | "name": { 69 | "type": "string", "index":"no" 70 | }, 71 | "title": { 72 | "type": "string", 73 | "index":"no" 74 | }, 75 | "topics": { 76 | "type": "long" 77 | }, 78 | "url": { 79 | "type": "string", "index":"no" 80 | } 81 | } 82 | }, 83 | "replies": { 84 | "type": "long", "store":"yes" 85 | }, 86 | "rcontent": { 87 | "type": "string", 88 | "term_vector": "with_positions_offsets", 89 | "indexAnalyzer": "ik", 90 | "searchAnalyzer": "ik", 91 | "include_in_all": "true", 92 | "boost":0.7 93 | }, 94 | "title": { 95 | "type": "string", 96 | "term_vector": "with_positions_offsets", 97 | "indexAnalyzer": "ik", 98 | "searchAnalyzer": "ik", 99 | "include_in_all": "true", 100 | "boost":1.5 101 | }, 102 | "url": { 103 | "type": "string", "index":"no" 104 | } 105 | } 106 | } 107 | 108 | } 109 | ' 110 | 111 | 112 | -------------------------------------------------------------------------------- /deploy/mongo2es.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import time 3 | import sys 4 | from elasticsearch import Elasticsearch 5 | from datetime import datetime 6 | from pymongo import MongoClient 7 | import requests 8 | 9 | es = Elasticsearch() 10 | client = MongoClient('mongodb://localhost:27017/') 11 | db = client['v2ex']['topic'] 12 | 13 | 14 | def max_id(): 15 | 16 | #: get max document id 17 | return db.find().sort("_id", -1).limit(1)[0]['_id'] 18 | 19 | 20 | def index(index_name, start_id=None, end_id=None): 21 | 22 | if not end_id: 23 | end_id = max_id() 24 | if not start_id: 25 | start_id = end_id - 200 26 | 27 | #: select * from topic where _id < end_id order by _id desc limit (end_id - start_id) 28 | cursor = db.find({"_id":{"$lt": end_id}}) \ 29 | .sort([('_id',-1)]) \ 30 | .batch_size(10) \ 31 | .limit(end_id - start_id) 32 | 33 | for item in cursor: 34 | item['created'] = item['created'] * 1000 35 | item['last_modified'] = item['last_modified'] * 1000 36 | item['last_touched'] = item['last_touched'] * 1000 37 | 38 | #: merge repies to topic 39 | item['rcontent'] = get_replies(item['_id']) 40 | try: 41 | a = time.time() 42 | es.index(index=index_name, doc_type="topic", id=item['_id'], body=item) 43 | b = time.time() 44 | 45 | except Exception, e: 46 | print(e) 47 | time.sleep(5) 48 | continue 49 | 50 | info = str(datetime.now()) + "cost %d ms: topic %d indexed."%((b-a)*1000, item['_id']) 51 | print(info) 52 | 53 | 54 | def get_replies(topic_id): 55 | 56 | """ 57 | get repies of topic_id 58 | return 59 | string: 60 | 'username created content username created content ...' 61 | """ 62 | 63 | rcontent = u"" 64 | db = client['v2ex']['reply'] 65 | 66 | for r in db.find({"topic_id":topic_id}): 67 | rcontent += r['member']['username'] 68 | rcontent += " " + datetime.fromtimestamp(r['created']).strftime('%Y-%m-%d') 69 | rcontent += " " + r['content'] + " " 70 | 71 | return rcontent 72 | 73 | 74 | if __name__ == '__main__': 75 | 76 | """ 77 | usage: 78 | 1 ./deploy/mongo2es.sh 79 | 2 ./deploy/mongo2es.sh start_id=1 end_id=3 80 | 81 | """ 82 | args = {} 83 | 84 | for i in range(len(sys.argv)): 85 | kv = sys.argv[i].split('=') 86 | if len(kv) == 2: 87 | args[kv[0]]= int(kv[1]) 88 | 89 | index('ik', **args) 90 | 91 | -------------------------------------------------------------------------------- /deploy/nginx_ninja.conf: -------------------------------------------------------------------------------- 1 | # config file for nginx 2 | server { 3 | 4 | listen 80; 5 | 6 | location / { try_files $uri @ninja-search-sock; } 7 | 8 | location @ninja-search-sock { 9 | include uwsgi_params; 10 | uwsgi_pass unix:/tmp/uwsgi.sock; 11 | } 12 | 13 | location @ninja-search-tcp { 14 | include uwsgi_params; 15 | uwsgi_pass 127.0.0.1:9090; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /deploy/uwsgi_sock.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sudo mkdir -p /var/log/ninja-search 3 | sudo uwsgi -s /tmp/uwsgi.sock --module index --callable app --master --processes 4 \ 4 | --daemonize /var/log/ninja-search/uwsgi_server.log 5 | -------------------------------------------------------------------------------- /deploy/uwsgi_tcp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | uwsgi --http :9090 --wsgi-file index.py --callable app --master --processes 4 --stats 127.0.0.1:9191 3 | -------------------------------------------------------------------------------- /index.py: -------------------------------------------------------------------------------- 1 | # coding:utf8 2 | import json 3 | from time import time 4 | from datetime import datetime 5 | from search import Search 6 | from settings import handler, DEBUG 7 | from util import gen_pages, pretty_date 8 | from flask import Flask, request, render_template, jsonify 9 | 10 | app = Flask(__name__) 11 | app.debug = DEBUG 12 | app.logger.addHandler(handler) 13 | 14 | 15 | @app.route("/", methods=['GET']) 16 | @app.route("/api", methods=['GET']) 17 | def index(): 18 | 19 | try: 20 | _from = int(request.args.get('from', '0')) 21 | limit = int(request.args.get('limit', '10')) 22 | q = unicode(request.args.get('q', '')) 23 | s = request.args.get('s', 'sumup') 24 | raw = bool(request.args.get('raw', '')) 25 | 26 | except Exception, e: 27 | result = { 28 | "status_code": 400, 29 | "error": u"(T▽T) params error " 30 | } 31 | return jsonify(**result) 32 | 33 | if len(q) == 0 and request.path == '/': 34 | return render_template("index.html") 35 | 36 | #: build search 37 | search = Search(index='v2', doc_type='topic') 38 | search['body']['query'] = \ 39 | { 40 | "multi_match": 41 | { 42 | "query": "%s" % q, 43 | "fields": ["title", "content", "rcontent"] 44 | } 45 | } 46 | search['size'] = limit 47 | search['from_'] = _from 48 | if request.path == '/api': 49 | #: field exclude for api 50 | search['_source_exclude'] = ['content_rendered', 'rcontent'] 51 | 52 | #: choose a sort method from ['sumup','replies','created','match'] 53 | if s == 'match': 54 | search['sort'] = '_score' 55 | 56 | if s in ["replies", "created"]: 57 | search['sort'] = "%s:desc" % s 58 | 59 | if s == 'sumup': 60 | if search['sort']: 61 | del search['sort'] 62 | search['body']['sort'] = { 63 | "_script": { 64 | "script": "(doc['created'].value-1272124800000) * \ 65 | log10(doc['replies'].value+1)* log10(doc.score)", 66 | "type": "number", 67 | "params": { 68 | "factor": 0 69 | }, 70 | "order": "desc" 71 | } 72 | } 73 | 74 | #: do search in es 75 | try: 76 | time0 = time() 77 | result = search.exe() 78 | time1 = time() 79 | except Exception, e: 80 | msg = str(datetime.now()) + '\n' + \ 81 | unicode(request.url) + '\n' + str(e) + '\r\n' 82 | app.logger.error(msg) 83 | result = { 84 | "status_code": 500, 85 | "error": "Sorry,server error T_T" 86 | } 87 | return jsonify(**result) 88 | 89 | # [return] raw json 90 | if raw and app.debug: 91 | return jsonify(**result) 92 | 93 | # [return] api request 94 | if request.path == '/api': 95 | # remove some keys from result 96 | del_key = ['_shards', 'timed_out', 'took'] 97 | for k in del_key: 98 | if k in result: 99 | del result[k] 100 | result['cost_ms'] = int((time1 - time0) * 1000) 101 | 102 | return jsonify(**result) 103 | 104 | # data for template 105 | total = result['hits']['total'] 106 | current = _from / 10 107 | max_page = total / 10 108 | pages = gen_pages(current, max_page) 109 | 110 | sort_by = { 111 | "sumup": u"综合", 112 | "match": u"精确匹配", 113 | "created": u"创建时间", 114 | "replies": u"回复数" 115 | } 116 | 117 | return render_template( 118 | 'result.html', res=result, pages=pages, 119 | current=current, q=q, s=s, cost=time1-time0, pretty_date=pretty_date, 120 | enumerate=enumerate, int=int, sort_by=sort_by, route="") 121 | 122 | 123 | if __name__ == "__main__": 124 | app.run(host="0.0.0.0", port=8000) 125 | -------------------------------------------------------------------------------- /index.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #:定时更新索引 3 | now() 4 | { 5 | date "+%Y-%m-%d" 6 | } 7 | 8 | t=`now` 9 | 10 | cd `dirname $0` 11 | /usr/bin/python ./deploy/mongo2es.py 12 | 13 | 14 | # crontab -e 15 | # add next line to the file 16 | # */35 * * * * sh /home/ubuntu/Repo/ninja-search/index.sh > /tmp/cron_index.log 2>&1 17 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ninja-search 2 | === 3 | 4 | wiki 5 | ----- 6 | * [Home Page](https://github.com/dbbbit/ninja-search/wiki) 7 | 8 | * [Search Api](https://github.com/dbbbit/ninja-search/wiki/Search-Api) 9 | 10 | require: 11 | -------- 12 | 13 | * flask 14 | * mongodb 15 | * [elasticsearch](http://www.elasticsearch.org/overview/elasticsearch/) + [ik分词插件](https://github.com/medcl/elasticsearch-analysis-ik) 16 | * [v2ex_scrapy](https://github.com/dbbbit/v2ex_scrapy) 17 | 18 | python package 19 | -------------- 20 | 21 | sudo pip install -r requirements.txt 22 | 23 | 24 | elasticsearch 配置 25 | ------------------- 26 | 27 | * [安装 ik 分词](https://github.com/medcl/elasticsearch-analysis-ik) 28 | 29 | * 在 esroot /config /elasticsearch.yml 添加以下内容: 30 | 31 | #: 启用 ES 动态脚本,以提供综合排序 32 | script.disable_dynamic: false 33 | 34 | 35 | 爬取数据 36 | -------- 37 | 38 | * [见 v2ex_scrapy 说明](https://github.com/dbbbit/v2ex_scrapy) 39 | 40 | 41 | 索引 42 | -------- 43 | 44 | * ElasticSearch Scheme Mapping 45 | 46 | sh ninja-search/deploy/mapping_ik.sh 47 | 48 | * 手动创建 Mongo 索引 49 | 50 | db.reply.createIndex({topic_id:1}) 51 | 52 | * 索引数据 53 | 54 | deploy/mongo2es.py 55 | 56 | * 将线上索引指向新的索引 57 | 58 | sh deploy/alias_v2_ik.sh 59 | 60 | 61 | Run 62 | ---- 63 | 64 | sudo python index.py 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | elasticsearch==1.2.0 2 | pymongo==2.7 3 | Flask 4 | -------------------------------------------------------------------------------- /search/__init__.py: -------------------------------------------------------------------------------- 1 | from search import Search 2 | 3 | -------------------------------------------------------------------------------- /search/search.py: -------------------------------------------------------------------------------- 1 | from elasticsearch import Elasticsearch 2 | import json 3 | 4 | 5 | class Search: 6 | """ API Doc for Python Elasticsearch Client 7 | http://elasticsearch-py.readthedocs.org/en/master/ 8 | """ 9 | es = Elasticsearch() 10 | params = {} 11 | 12 | def __init__(self, **kargs): 13 | 14 | #: highlight some result field 15 | self.params['body'] = { 16 | "highlight": { 17 | "fields": { 18 | "content": {}, 19 | "title": {}, 20 | "rcontent": {}, #: replies 21 | } 22 | }, 23 | } 24 | self.params.update(kargs) 25 | 26 | def __setitem__(self, key, item): 27 | self.params[key] = item 28 | 29 | def __getitem__(self, key): 30 | if key in self.params: 31 | return self.params[key] 32 | 33 | def __delitem__(self, key): 34 | if key in self.params: 35 | del self.params[key] 36 | 37 | def exe(self): 38 | try: 39 | result = self.es.search(**self.params) 40 | except Exception, e: 41 | raise e 42 | 43 | return result 44 | 45 | if __name__ == "__main__": 46 | 47 | s = Search(index="v2", doc_type="topic") 48 | s['size'] = 10 49 | s['q'] = 'content: hello' 50 | s['_source_include'] = ['content', 'title'] 51 | print(s.exe()) 52 | -------------------------------------------------------------------------------- /settings.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from logging.handlers import RotatingFileHandler 3 | 4 | #: logging config 5 | LOGGING_PATH = '/var/log/ninja-search/search_error.log' 6 | 7 | handler = RotatingFileHandler(LOGGING_PATH, maxBytes=1000000) 8 | handler.setLevel(logging.INFO) 9 | 10 | #: debug? 11 | DEBUG = False 12 | -------------------------------------------------------------------------------- /static/css/index.css: -------------------------------------------------------------------------------- 1 | .ninja { 2 | margin: 20% 0 20px; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /static/css/jquery-ui.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.11.1 - 2014-09-13 2 | * http://jqueryui.com 3 | * Includes: core.css, autocomplete.css, menu.css, theme.css 4 | * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS%2CTahoma%2CVerdana%2CArial%2Csans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=gloss_wave&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=highlight_soft&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=glass&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=glass&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=highlight_soft&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=diagonals_thick&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=diagonals_thick&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=flat&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px 5 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 6 | 7 | .ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:none}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{position:relative;margin:0;padding:3px 1em 3px .4em;cursor:pointer;min-height:0;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-widget{font-family:Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #ddd;background:#eee url("images/ui-bg_highlight-soft_100_eeeeee_1x100.png") 50% top repeat-x;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #e78f08;background:#f6a828 url("images/ui-bg_gloss-wave_35_f6a828_500x100.png") 50% 50% repeat-x;color:#fff;font-weight:bold}.ui-widget-header a{color:#fff}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #ccc;background:#f6f6f6 url("images/ui-bg_glass_100_f6f6f6_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#1c94c4}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#1c94c4;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #fbcb09;background:#fdf5ce url("images/ui-bg_glass_100_fdf5ce_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#c77405}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited{color:#c77405;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #fbd850;background:#fff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#eb8f00}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#eb8f00;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fed22f;background:#ffe45c url("images/ui-bg_highlight-soft_75_ffe45c_1x100.png") 50% top repeat-x;color:#363636}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#b81900 url("images/ui-bg_diagonals-thick_18_b81900_40x40.png") 50% 50% repeat;color:#fff}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#fff}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#fff}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-state-default .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-active .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-highlight .ui-icon{background-image:url("images/ui-icons_228ef1_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_ffd27a_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:4px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:4px}.ui-widget-overlay{background:#666 url("images/ui-bg_diagonals-thick_20_666666_40x40.png") 50% 50% repeat;opacity:.5;filter:Alpha(Opacity=50)}.ui-widget-shadow{margin:-5px 0 0 -5px;padding:5px;background:#000 url("images/ui-bg_flat_10_000000_40x100.png") 50% 50% repeat-x;opacity:.2;filter:Alpha(Opacity=20);border-radius:5px} -------------------------------------------------------------------------------- /static/css/result.css: -------------------------------------------------------------------------------- 1 | em { 2 | color: rgb(201, 24, 24); 3 | font-style: normal; 4 | } 5 | 6 | a { 7 | color: #065FB4; 8 | } 9 | 10 | #search-form { 11 | margin-top: 3em; 12 | } 13 | 14 | #cost { 15 | float: left; 16 | margin-right: 2em; 17 | } 18 | 19 | #item { 20 | width: 50%; 21 | padding-bottom: 1em; 22 | } 23 | 24 | #item-title { 25 | overflow: hidden; 26 | text-overflow: ellipsis; 27 | white-space: nowrap; 28 | line-height: normal; 29 | } 30 | 31 | #item-content { 32 | font-color: #545454; 33 | word-wrap: break-word; 34 | } 35 | 36 | #item-info { 37 | color: #888; 38 | font-size: x-small; 39 | } 40 | 41 | #item-extra { 42 | display: block; 43 | color: #888; 44 | font-weight: bold; 45 | } 46 | 47 | #item-avatar { 48 | float: left; 49 | margin-right: 1.5em; 50 | width: 28px; 51 | } 52 | 53 | #item-info> p { 54 | margin-bottom: 0.3em; 55 | } 56 | 57 | #search-container { 58 | width: 102%; 59 | background-color: #fefefe; 60 | padding-top: 2em; 61 | margin-left: -9em; 62 | } 63 | 64 | .container { 65 | margin-left: 8em; 66 | } 67 | 68 | .pagination { 69 | margin-left: -1em; 70 | } 71 | 72 | #ninja { 73 | float: left; 74 | margin-right: -1em; 75 | } 76 | 77 | .pagination .active a { 78 | background-color: #000; 79 | border-color: #000; 80 | } 81 | .pagination .active a:hover { 82 | background-color: red; 83 | border-color: red; 84 | } 85 | .pagination> li> a { 86 | color: #000; 87 | } 88 | .pagination> li> a:hover { 89 | color: red; 90 | } 91 | 92 | /* stats */ 93 | #histats_counter { 94 | display: none; 95 | } 96 | -------------------------------------------------------------------------------- /static/css/result_small.css: -------------------------------------------------------------------------------- 1 | em { 2 | color: rgb(201, 24, 24); 3 | font-style: normal; 4 | } 5 | 6 | #ninja { 7 | margin: 1em 0em 1em 6em; 8 | } 9 | 10 | #cost { 11 | margin-left: 1em; 12 | float: left; 13 | } 14 | 15 | #sort-by { 16 | display: none; 17 | } 18 | 19 | #sort-bar { 20 | margin-left: 1.5em; 21 | } 22 | 23 | #item { 24 | margin-left: 1em; 25 | border-top: 1px solid #ebebeb; 26 | } 27 | 28 | #item-title { 29 | font-size: 1.2em; 30 | overflow: hidden; 31 | text-overflow: ellipsis; 32 | white-space: nowrap; 33 | margin-right: 1em; 34 | } 35 | #item-content { 36 | display: none; 37 | } 38 | 39 | #item-avatar { 40 | float: left; 41 | margin-right: 1em; 42 | width: 35px; 43 | } 44 | 45 | #item-info-main { 46 | margin-top: 0em; 47 | margin-right: 2em; 48 | } 49 | 50 | #item-extra { 51 | font-size: x-small; 52 | padding-left: 1em; 53 | } 54 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbbbit/ninja-search/395b265d22064316b187a8accea92c95a0023838/static/favicon.ico -------------------------------------------------------------------------------- /static/image/ninja.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbbbit/ninja-search/395b265d22064316b187a8accea92c95a0023838/static/image/ninja.png -------------------------------------------------------------------------------- /static/image/ninja.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ninja 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /static/js/index.js: -------------------------------------------------------------------------------- 1 | $(function ($) { 2 | // http://api.jqueryui.com/autocomplete/ 3 | var q = $('#keyword'), 4 | max_suggest = 10, //读本地词库时的最大提示数 5 | cache = {}, 6 | local_default = [ 7 | "Project Babel", "V2EX", "OLIVIDA", "音乐", "电影", "地球", "BFBC2", "iPhone", "iPad", "MacBook Pro", "Linux", "问与答", "iDev", "Google App Engine", "Twitter", "分享发现", "分享创造", "上海", "北京", "广州", "深圳", "Mac OS X", "Kevin Kelly", "数学", "丽江", "杭州", "摄影", "Project Picky", "桂林", "成都", "重庆", "NDS", "PlayStation 3", "Xbox 360", "PSP", "Wii", "Kindle", "东京", "Android", "iMac", "昆明", "贵阳", "酷工作", "天津", "Resident Evil 5", "iCode", "Redis", "剧集", "Steam", "New York", "NoSQL", "硬件", "自言自语", "Adobe", "游戏", "互联网", "C/C++/Obj-C", "Photoshop", "随想", "HTML", "MySQL", "PHP", "Java", "YouTube", "Google", "3G", "美酒与美食", "iAd", "二手交易", "iLife", "iWork", "VPN", "字体排印", "Mac Pro", "UNIQLO", "Levi's", "G-Star", "Converse", "服务器", "武汉", "Ruby on Rails", "NGINX", "Mac mini", "MacBook", "Medal of Honor", "Vimeo", "WordPress", "无印良品", "foursquare", "Python", "Firefox", "Chrome", "Safari", "ACG", "植物", "反馈", "Opera", "StarCraft 2", "分享邀请码", "OpenStack", "动物", "git", "Blogger", "云计算", "bzr", "Baby", "糗事分享", "宽带症候群", "搜索引擎技术研究", "OAuth", "阅读", "翻译", "香港", "澳门", "湾区", "Wikipedia", "Berlin", "文学", "五子棋", "Digg", "囧", "创造者", "Perl", "Scala", "经济", "教育", "io", "Apache", "CouchDB", "桌游", "iMarketing", "街机游戏", "晒晒更健康", "中文", "HTTP", "Toruk", "正则表达式", "台北", "CSS", "New Balance", "Nike", "Adidas", "宅", "Dell", "Livid", "JavaScript", "Lua", "无要点", "树洞", "滕州", "厦门", "呼和浩特", "BlackBerry", "南京", "西安", "大庆", "强迫症", "80 天环游地球", "沉默的螺旋", "Diesel", "像素", "Hadoop", "MapReduce", "三亚", "海口", "珠海", "cocos2d", "iPod", "Apple TV", "Matrix", "投资", "iGame", "配件", "Erlang", "大连", "汽车", "直升机", "Linkin Park", "贵阳一中", "云南师大附中", "旅行", "OpenSolaris", "MoinMoin", "Apple", "Blu-ray", "汇编", "梦", "Inception", "SSH", "外包", "BMW", "Camino", "MobileMe", "Pink Floyd", "U2", "Nirvana", "Rammstein", "Flash", "DNS", "London", "Cray", "行程控", "无锡", "Xen", "信用卡", "郑州", "Riak", "Linode", "福州", "济南", "兰州", "青岛", "苏州", "扬州", "设计", "京都", "硅谷", "Podcast", "Lacrimosa", "Ideology", "TechCrunch", "Quora", "REWORK", "Inbox", "Audi", "AutoCAD", "Diablo III", "日本語", "English", "风水", "Iconfactory", "Nokia", "他他", "她她", "24 小时", "绿色低碳", "Seattle", "GQ", "Sydney", "长沙", "生活方式", "Paris", "Dubai", "Singapore", "Mozilla", "Call of Duty", "STOP", "武汉大学", "vim", "Lamy", "Moleskine", "北京大学", "清华大学", "Emacs", "上海交通大学", "中山大学", "画画", "Final Cut Pro", "Motion", "网络安全", "村上春树", "The Beatles", "熵", "亚丁湾", "指环王", "2012", "汇丰银行", "丰田", "webOS", "渣打银行", "花旗银行", "荷兰银行", "德意志银行", "瑞士银行", "Lisp", "WIRED", "能源", "Condé Nast", "Samsonite", "Dior", "OMEGA", "Need for Speed", "EF", "Textie", "物物交换", "天黑以后", "Dropbox", "自行车", "宜家", "Alienware", "Facebook", "SQLite", "团购", "SONY", "Oslo", "Stockholm", "Portland", "Game Dev Story", "Amazon Web Services", "程序员", "设计师", "变形金刚", "Cartier", "Davidoff", "Gap", "日记", "API", "非诚勿扰", "天使投资", "商业模式", "MongoDB", "新手求助", "TED", "水深火热", "标准", "饭否 API", "Volkswagen", "科幻", "乐活", "不靠谱茶话会", "528491", "小小大星球", "3DS", "Battlefield 3", "cURL", "MacBook Air", "Origin", "Asana", "asdf", "1Q84", "AdSense", "AdWords", "最终幻想", "Cut the Rope", "H&M", "Killzone", "站长", "黑魔法", "Yippee Arts", "Minecraft", "MUSE", "莱卡", "佳能", "尼康", "Bento", "Instapaper", "Dribbble", "EC", ".NET", "本体论", "Zakka", "L'Oréal", "Alexa", "GarageBand", "3ds Max", "LANCÔME", "日本", "寻人", "AdMob", "Windows", "Tornado", "Path", "Guild Wars 2", "Chamber", "PostgreSQL", "使用指南", "Ruby", "早睡早起身体好俱乐部", "Portal series", "罗技", "Amazon", "Fusion-io", "Blog", "iRobot", "Go", "Starbucks", "仙剑奇侠传", "World of Warcraft", "Creative Commons", "水", "新余", "岳阳", "大理", "长春", "遵义", "西宁", "哈尔滨", "南昌", "保定", "Crysis", "Windows Phone", "Arch", "铜仁", "Getting Things Done", "开源软件", "GCC", "LLVM", "AMD", "Intel", "NVIDIA", "Maya", "Shade", "Bitcoin", "Porsche", "Project Stormwind", "reddit", "Oracle", "Bing", "Pixelmator", "Jekyll", "Y Combinator", "Arc", "编程", "操作系统", "版本控制系统", "编程框架", "软件", "计算机", "游戏主机", "BASIC", "数据库", "浏览器", "Mercurial", "Cobra", "jQuery", "WWDC", "PlayStation Vita", "EA", "Nintendo", "iCloud", "Django", "中二病", "1990", "Celery", "Instagram", "node.js", "DotCloud", "Google Wave Protocol", "ifttt", "媒体", "LUMIX", "GoPro", "iTunes", "App Store", "科技", "RAGE", "Chicago", "OpenGL", "DirectX", "DUST 514", "Gnome", "DotA", "CDN", "英雄联盟", "Scrum", "CentOS", "游戏开发", "Unreal Development Kit", "VMware", "GlassFish", "Jinja", "Monocle", "UNITY", "Blender", "3D", "Project Zeppelin", "Flask", "I Am A", "Razer", "NetBeans", "跑步", "Clojure", "ColdFusion", "WhatsApp", "Backbone.js", "TextMate", "编辑器", "吉林大学", "500px", "Pinterest", "Sina App Engine", "Subversion", "海外留学", "空气", "LEGO", "Project Asteroid", "公司运营", "支付宝", "Xcode", "上古卷轴 V", "Metal Gear Solid", "Homebrew", "RabbitMQ", "业界八卦", "Grand Theft Auto", "星球大战", "InDesign", "Illustrator", "分享萌物", "Fling", "Pogo", "FreeBSD", "视频制作", "Trello", "Lighttpd", "Arduino", "求职", "Gran Turismo", "宠物", "Charles", "EVE", "Wacom", "Markdown", "Zope", "iBook", "微博", "Project Galaxy", "Project Museum", "奇思妙想", "中国科学技术大学", "关闭交易", "骑行", "0x10c", "蘑菇", "Paper", "Homme", "域名", "Femme", "TechStars", "Meteor", "Haskell", "SQLAlchemy", "Storm", "绿茵场", "Sublime Text", "Ubuntu", "Fedora", "Gentoo", "Los Angeles", "Evernote", "唐茶", "沙盒", "Down Voted", "信息处理中心", "吉他", "时间", "签证", "Dev", "Lightroom", "Aperture", "免费赠送", "Stripe", "咖啡", "G-WAN", "Squid", "Velocity", "生活", "欧洲", "英国", "美国", "瑞典", "德国", "OpenBSD", "HAProxy", "MECE", "C3Edge", "Snort", "SSL", "RRDtool", "XenServer", "Puppet", "Doit.im", "CloudStack", "SimCity", "Varnish", "RQ", "KDE", "Hawken", "调查", "iOS", "Windows Azure", "IIS", "New Relic", "Startup Visa", "make", "O'Reilly", "Delphi", "宁波", "4G", "MemSQL", "OpenVZ", "NBA", "EdgeCast", "Fitbit", "Couchbase", "Cassandra", "HBase", "Lucene", "ERP", "Cisco", "Solr", "硅谷", "OpenShift", "新单位", "ORCA", "魅族", "不是问题的问题", "Computer vision", "Pasadena", "奥运会", "Stack Overflow", "Nexus", "每个月都会出现的那种主题", "App.net", "elasticsearch", "Santa Monica", "Phusion Passenger", "Anno", "Chocolat", "San Francisco", "Big Data", "DevOps", "VirtualBox", "Cherokee", "Ubersmith", "Chef", "Battle for Wesnoth", "IRC", "Munin", "Solaris", "Heroku", "Ingress", "Las Vegas", "NetBSD", "OpenNebula", "Juniper", "Ceph", "Swift", "Splunk", "stunnel", "California", "SmartOS", "ZFS", "Atlassian", "SDN", "Unix", "Clash of Clans", "shadowsocks", "微信", "Raspberry Pi", "Square", "openSUSE", "VPS", "LXC", "Confluence", "Wiki", "Plone", "Sphinx", "reStructuredText", "HUBOT", "Vagrant", "Ansible", "Traffic Server", "加拿大", "中国", "墨西哥", "韩国", "亚洲", "Discourse", "Fluentd", "Debian", "Alfred", "Resident Evil 6", "Cloudera", "PlayStation 4", "我叫 MT", "Eucalyptus", "机器学习", "台湾", "PayPal", "OpenResty", "Stash", "XeHost", "VoltDB", "ELINKVPS", "Battlefield 4", "Smartisan OS", "SSD", "AeroFS", "MIUI", "前端优化", "Sailfish", "机械键盘", "LINE", "Spark", "Google Glass", "发烧友", "EMC", "微软", "健康", "WebRTC", "Braun", "SOHO", "4K", "重口味问与答", "German", "Duolingo", "Core", "LVM", "JIRA", "Bash", "Oculus VR", "BTSync", "iTransfer", "MapR", "Docker", "Hyperloop", "Bootstrap", "ZooKeeper", "IE", "PowerDNS", "Dyn", "Salt Stack", "SAP", "纪录片", "淘宝", "Dogma", "Diamond Bar", "Rowland Heights", "Walnut", "Borderlands", "Ghost", "来往", "Nissan", "Chevrolet", "海外运营", "Hearthstone", "Heroes of the Storm", "项目管理", "剑灵", "FoundationDB", "Dart", "Xbox One", "Herman Miller", "Ripple", "BOINC", "优惠信息", "Wii U", "Linux Mint", "外汇交易", "魔兽争霸", "MODO", "Lynda", "Mudbox", "情感问题", "Titanfall", "Percona", "KVM", "Sentry", "Rust", "Serf", "Boston", "CoreOS", "体育运动", "OUYA", "OpenCL", "WebP", "Wagas", "MBTI", "职场话题", "iBeacon", "GitHub", "Atom", "Bose", "Sputnik", "PowerShell", "搜索引擎优化", "Ace", "SolarCity", "SpaceX", "Hack", "Avocado", "Sketch", "RFC", "Medium", "Samsung", "Tarsnap", "沈阳", "SSDB", "Rust", "前端开发", "Servo", "Phabricator", "bong", "AVOS Cloud", "ThinkPad", "太阳能", "日志处理", "雅思", "工作假期签证", "Syslog", "Uber", "Twitch", "买买买", "Notes", "OpenWrt", "福特", "奔驰", "Battlefield Hardline", "模拟驾驶", "OneAPM", "草稿箱", "抑郁症", "AngularJS", "Tesla", "Destiny", "Waze", "Factorio", " WATCH", "Corvette" 8 | ] 9 | 10 | var filter = function (array, searching_str) { 11 | return $.grep(array, function (value) { 12 | //关键字满足开头才返回,忽略大小写 13 | return 0 === value.toLowerCase().indexOf(searching_str.toLowerCase()) 14 | }).slice(0, max_suggest) 15 | } 16 | 17 | q.autocomplete({ 18 | source: function (rq, rsp) { 19 | var term = rq.term 20 | if (term in cache) { 21 | rsp(cache[term]) 22 | } 23 | else { 24 | $.getJSON('http://107.155.97.232/suggest', rq) 25 | //远程获取 26 | .done(function (data) { 27 | console.info('done') 28 | if(data['data']){ 29 | cache[term] = data['data'] //缓存每次远程结果 30 | rsp(data['data']) 31 | } 32 | }) 33 | 34 | //远程失败则从本地获取 35 | .fail(function (jq) { 36 | console.info('fail') 37 | rsp(filter(local_default, term)) 38 | }) 39 | } 40 | } 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /static/js/jquery-ui.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.11.1 - 2014-09-12 2 | * http://jqueryui.com 3 | * Includes: core.js, widget.js, position.js, autocomplete.js, menu.js 4 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 5 | 6 | (function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e(jQuery)})(function(e){function t(t,s){var n,a,o,r=t.nodeName.toLowerCase();return"area"===r?(n=t.parentNode,a=n.name,t.href&&a&&"map"===n.nodeName.toLowerCase()?(o=e("img[usemap='#"+a+"']")[0],!!o&&i(o)):!1):(/input|select|textarea|button|object/.test(r)?!t.disabled:"a"===r?t.href||s:s)&&i(t)}function i(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack().filter(function(){return"hidden"===e.css(this,"visibility")}).length}e.ui=e.ui||{},e.extend(e.ui,{version:"1.11.1",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({scrollParent:function(t){var i=this.css("position"),s="absolute"===i,n=t?/(auto|scroll|hidden)/:/(auto|scroll)/,a=this.parents().filter(function(){var t=e(this);return s&&"static"===t.css("position")?!1:n.test(t.css("overflow")+t.css("overflow-y")+t.css("overflow-x"))}).eq(0);return"fixed"!==i&&a.length?a:e(this[0].ownerDocument||document)},uniqueId:function(){var e=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++e)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&e(this).removeAttr("id")})}}),e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(i){return!!e.data(i,t)}}):function(t,i,s){return!!e.data(t,s[3])},focusable:function(i){return t(i,!isNaN(e.attr(i,"tabindex")))},tabbable:function(i){var s=e.attr(i,"tabindex"),n=isNaN(s);return(n||s>=0)&&t(i,!n)}}),e("").outerWidth(1).jquery||e.each(["Width","Height"],function(t,i){function s(t,i,s,a){return e.each(n,function(){i-=parseFloat(e.css(t,"padding"+this))||0,s&&(i-=parseFloat(e.css(t,"border"+this+"Width"))||0),a&&(i-=parseFloat(e.css(t,"margin"+this))||0)}),i}var n="Width"===i?["Left","Right"]:["Top","Bottom"],a=i.toLowerCase(),o={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+i]=function(t){return void 0===t?o["inner"+i].call(this):this.each(function(){e(this).css(a,s(this,t)+"px")})},e.fn["outer"+i]=function(t,n){return"number"!=typeof t?o["outer"+i].call(this,t):this.each(function(){e(this).css(a,s(this,t,!0,n)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e("").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(i){return arguments.length?t.call(this,e.camelCase(i)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.fn.extend({focus:function(t){return function(i,s){return"number"==typeof i?this.each(function(){var t=this;setTimeout(function(){e(t).focus(),s&&s.call(t)},i)}):t.apply(this,arguments)}}(e.fn.focus),disableSelection:function(){var e="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.bind(e+".ui-disableSelection",function(e){e.preventDefault()})}}(),enableSelection:function(){return this.unbind(".ui-disableSelection")},zIndex:function(t){if(void 0!==t)return this.css("zIndex",t);if(this.length)for(var i,s,n=e(this[0]);n.length&&n[0]!==document;){if(i=n.css("position"),("absolute"===i||"relative"===i||"fixed"===i)&&(s=parseInt(n.css("zIndex"),10),!isNaN(s)&&0!==s))return s;n=n.parent()}return 0}}),e.ui.plugin={add:function(t,i,s){var n,a=e.ui[t].prototype;for(n in s)a.plugins[n]=a.plugins[n]||[],a.plugins[n].push([i,s[n]])},call:function(e,t,i,s){var n,a=e.plugins[t];if(a&&(s||e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType))for(n=0;a.length>n;n++)e.options[a[n][0]]&&a[n][1].apply(e.element,i)}};var s=0,n=Array.prototype.slice;e.cleanData=function(t){return function(i){var s,n,a;for(a=0;null!=(n=i[a]);a++)try{s=e._data(n,"events"),s&&s.remove&&e(n).triggerHandler("remove")}catch(o){}t(i)}}(e.cleanData),e.widget=function(t,i,s){var n,a,o,r,h={},l=t.split(".")[0];return t=t.split(".")[1],n=l+"-"+t,s||(s=i,i=e.Widget),e.expr[":"][n.toLowerCase()]=function(t){return!!e.data(t,n)},e[l]=e[l]||{},a=e[l][t],o=e[l][t]=function(e,t){return this._createWidget?(arguments.length&&this._createWidget(e,t),void 0):new o(e,t)},e.extend(o,a,{version:s.version,_proto:e.extend({},s),_childConstructors:[]}),r=new i,r.options=e.widget.extend({},r.options),e.each(s,function(t,s){return e.isFunction(s)?(h[t]=function(){var e=function(){return i.prototype[t].apply(this,arguments)},n=function(e){return i.prototype[t].apply(this,e)};return function(){var t,i=this._super,a=this._superApply;return this._super=e,this._superApply=n,t=s.apply(this,arguments),this._super=i,this._superApply=a,t}}(),void 0):(h[t]=s,void 0)}),o.prototype=e.widget.extend(r,{widgetEventPrefix:a?r.widgetEventPrefix||t:t},h,{constructor:o,namespace:l,widgetName:t,widgetFullName:n}),a?(e.each(a._childConstructors,function(t,i){var s=i.prototype;e.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete a._childConstructors):i._childConstructors.push(o),e.widget.bridge(t,o),o},e.widget.extend=function(t){for(var i,s,a=n.call(arguments,1),o=0,r=a.length;r>o;o++)for(i in a[o])s=a[o][i],a[o].hasOwnProperty(i)&&void 0!==s&&(t[i]=e.isPlainObject(s)?e.isPlainObject(t[i])?e.widget.extend({},t[i],s):e.widget.extend({},s):s);return t},e.widget.bridge=function(t,i){var s=i.prototype.widgetFullName||t;e.fn[t]=function(a){var o="string"==typeof a,r=n.call(arguments,1),h=this;return a=!o&&r.length?e.widget.extend.apply(null,[a].concat(r)):a,o?this.each(function(){var i,n=e.data(this,s);return"instance"===a?(h=n,!1):n?e.isFunction(n[a])&&"_"!==a.charAt(0)?(i=n[a].apply(n,r),i!==n&&void 0!==i?(h=i&&i.jquery?h.pushStack(i.get()):i,!1):void 0):e.error("no such method '"+a+"' for "+t+" widget instance"):e.error("cannot call methods on "+t+" prior to initialization; "+"attempted to call method '"+a+"'")}):this.each(function(){var t=e.data(this,s);t?(t.option(a||{}),t._init&&t._init()):e.data(this,s,new i(a,this))}),h}},e.Widget=function(){},e.Widget._childConstructors=[],e.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
",options:{disabled:!1,create:null},_createWidget:function(t,i){i=e(i||this.defaultElement||this)[0],this.element=e(i),this.uuid=s++,this.eventNamespace="."+this.widgetName+this.uuid,this.options=e.widget.extend({},this.options,this._getCreateOptions(),t),this.bindings=e(),this.hoverable=e(),this.focusable=e(),i!==this&&(e.data(i,this.widgetFullName,this),this._on(!0,this.element,{remove:function(e){e.target===i&&this.destroy()}}),this.document=e(i.style?i.ownerDocument:i.document||i),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:e.noop,_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetFullName).removeData(e.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:e.noop,widget:function(){return this.element},option:function(t,i){var s,n,a,o=t;if(0===arguments.length)return e.widget.extend({},this.options);if("string"==typeof t)if(o={},s=t.split("."),t=s.shift(),s.length){for(n=o[t]=e.widget.extend({},this.options[t]),a=0;s.length-1>a;a++)n[s[a]]=n[s[a]]||{},n=n[s[a]];if(t=s.pop(),1===arguments.length)return void 0===n[t]?null:n[t];n[t]=i}else{if(1===arguments.length)return void 0===this.options[t]?null:this.options[t];o[t]=i}return this._setOptions(o),this},_setOptions:function(e){var t;for(t in e)this._setOption(t,e[t]);return this},_setOption:function(e,t){return this.options[e]=t,"disabled"===e&&(this.widget().toggleClass(this.widgetFullName+"-disabled",!!t),t&&(this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus"))),this},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_on:function(t,i,s){var n,a=this;"boolean"!=typeof t&&(s=i,i=t,t=!1),s?(i=n=e(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),e.each(s,function(s,o){function r(){return t||a.options.disabled!==!0&&!e(this).hasClass("ui-state-disabled")?("string"==typeof o?a[o]:o).apply(a,arguments):void 0}"string"!=typeof o&&(r.guid=o.guid=o.guid||r.guid||e.guid++);var h=s.match(/^([\w:-]*)\s*(.*)$/),l=h[1]+a.eventNamespace,u=h[2];u?n.delegate(u,l,r):i.bind(l,r)})},_off:function(e,t){t=(t||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.unbind(t).undelegate(t)},_delay:function(e,t){function i(){return("string"==typeof e?s[e]:e).apply(s,arguments)}var s=this;return setTimeout(i,t||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){e(t.currentTarget).addClass("ui-state-hover")},mouseleave:function(t){e(t.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){e(t.currentTarget).addClass("ui-state-focus")},focusout:function(t){e(t.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(t,i,s){var n,a,o=this.options[t];if(s=s||{},i=e.Event(i),i.type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),i.target=this.element[0],a=i.originalEvent)for(n in a)n in i||(i[n]=a[n]);return this.element.trigger(i,s),!(e.isFunction(o)&&o.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},e.each({show:"fadeIn",hide:"fadeOut"},function(t,i){e.Widget.prototype["_"+t]=function(s,n,a){"string"==typeof n&&(n={effect:n});var o,r=n?n===!0||"number"==typeof n?i:n.effect||i:t;n=n||{},"number"==typeof n&&(n={duration:n}),o=!e.isEmptyObject(n),n.complete=a,n.delay&&s.delay(n.delay),o&&e.effects&&e.effects.effect[r]?s[t](n):r!==t&&s[r]?s[r](n.duration,n.easing,a):s.queue(function(i){e(this)[t](),a&&a.call(s[0]),i()})}}),e.widget,function(){function t(e,t,i){return[parseFloat(e[0])*(p.test(e[0])?t/100:1),parseFloat(e[1])*(p.test(e[1])?i/100:1)]}function i(t,i){return parseInt(e.css(t,i),10)||0}function s(t){var i=t[0];return 9===i.nodeType?{width:t.width(),height:t.height(),offset:{top:0,left:0}}:e.isWindow(i)?{width:t.width(),height:t.height(),offset:{top:t.scrollTop(),left:t.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:t.outerWidth(),height:t.outerHeight(),offset:t.offset()}}e.ui=e.ui||{};var n,a,o=Math.max,r=Math.abs,h=Math.round,l=/left|center|right/,u=/top|center|bottom/,d=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,p=/%$/,f=e.fn.position;e.position={scrollbarWidth:function(){if(void 0!==n)return n;var t,i,s=e("
"),a=s.children()[0];return e("body").append(s),t=a.offsetWidth,s.css("overflow","scroll"),i=a.offsetWidth,t===i&&(i=s[0].clientWidth),s.remove(),n=t-i},getScrollInfo:function(t){var i=t.isWindow||t.isDocument?"":t.element.css("overflow-x"),s=t.isWindow||t.isDocument?"":t.element.css("overflow-y"),n="scroll"===i||"auto"===i&&t.widthi?"left":t>0?"right":"center",vertical:0>a?"top":s>0?"bottom":"middle"};d>m&&m>r(t+i)&&(h.horizontal="center"),c>g&&g>r(s+a)&&(h.vertical="middle"),h.important=o(r(t),r(i))>o(r(s),r(a))?"horizontal":"vertical",n.using.call(this,e,h)}),u.offset(e.extend(N,{using:l}))})},e.ui.position={fit:{left:function(e,t){var i,s=t.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=e.left-t.collisionPosition.marginLeft,h=n-r,l=r+t.collisionWidth-a-n;t.collisionWidth>a?h>0&&0>=l?(i=e.left+h+t.collisionWidth-a-n,e.left+=h-i):e.left=l>0&&0>=h?n:h>l?n+a-t.collisionWidth:n:h>0?e.left+=h:l>0?e.left-=l:e.left=o(e.left-r,e.left)},top:function(e,t){var i,s=t.within,n=s.isWindow?s.scrollTop:s.offset.top,a=t.within.height,r=e.top-t.collisionPosition.marginTop,h=n-r,l=r+t.collisionHeight-a-n;t.collisionHeight>a?h>0&&0>=l?(i=e.top+h+t.collisionHeight-a-n,e.top+=h-i):e.top=l>0&&0>=h?n:h>l?n+a-t.collisionHeight:n:h>0?e.top+=h:l>0?e.top-=l:e.top=o(e.top-r,e.top)}},flip:{left:function(e,t){var i,s,n=t.within,a=n.offset.left+n.scrollLeft,o=n.width,h=n.isWindow?n.scrollLeft:n.offset.left,l=e.left-t.collisionPosition.marginLeft,u=l-h,d=l+t.collisionWidth-o-h,c="left"===t.my[0]?-t.elemWidth:"right"===t.my[0]?t.elemWidth:0,p="left"===t.at[0]?t.targetWidth:"right"===t.at[0]?-t.targetWidth:0,f=-2*t.offset[0];0>u?(i=e.left+c+p+f+t.collisionWidth-o-a,(0>i||r(u)>i)&&(e.left+=c+p+f)):d>0&&(s=e.left-t.collisionPosition.marginLeft+c+p+f-h,(s>0||d>r(s))&&(e.left+=c+p+f))},top:function(e,t){var i,s,n=t.within,a=n.offset.top+n.scrollTop,o=n.height,h=n.isWindow?n.scrollTop:n.offset.top,l=e.top-t.collisionPosition.marginTop,u=l-h,d=l+t.collisionHeight-o-h,c="top"===t.my[1],p=c?-t.elemHeight:"bottom"===t.my[1]?t.elemHeight:0,f="top"===t.at[1]?t.targetHeight:"bottom"===t.at[1]?-t.targetHeight:0,m=-2*t.offset[1];0>u?(s=e.top+p+f+m+t.collisionHeight-o-a,e.top+p+f+m>u&&(0>s||r(u)>s)&&(e.top+=p+f+m)):d>0&&(i=e.top-t.collisionPosition.marginTop+p+f+m-h,e.top+p+f+m>d&&(i>0||d>r(i))&&(e.top+=p+f+m))}},flipfit:{left:function(){e.ui.position.flip.left.apply(this,arguments),e.ui.position.fit.left.apply(this,arguments)},top:function(){e.ui.position.flip.top.apply(this,arguments),e.ui.position.fit.top.apply(this,arguments)}}},function(){var t,i,s,n,o,r=document.getElementsByTagName("body")[0],h=document.createElement("div");t=document.createElement(r?"div":"body"),s={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},r&&e.extend(s,{position:"absolute",left:"-1000px",top:"-1000px"});for(o in s)t.style[o]=s[o];t.appendChild(h),i=r||document.documentElement,i.insertBefore(t,i.firstChild),h.style.cssText="position: absolute; left: 10.7432222px;",n=e(h).offset().left,a=n>10&&11>n,t.innerHTML="",i.removeChild(t)}()}(),e.ui.position,e.widget("ui.menu",{version:"1.11.1",defaultElement:"