'''
85 | m_values = client.hgetall(fullkey)
86 | alt = False
87 | for key, value in m_values.items():
88 | if len(value) > 200:
89 | value = 'data(len:%s)' % len(value)
90 | alt_str = alt and 'class="alt"' or ''
91 | out += '''
%(key)s
%(value)s
92 |
93 |
94 |
95 |
96 | ''' % ({'value': value, 'key': key, 'sid': sid, 'db': db, 'fullkey': fullkey, 'alt_str': alt_str,
97 | 'media_prefix': media_prefix})
98 | alt = not alt
99 | out += '
'''
107 | m_values = client.lrange(fullkey, 0, -1)
108 | alt = False
109 | index = 0
110 | for value in m_values:
111 | alt_str = alt and 'class="alt"' or ''
112 | out += '''
%(index)s
%(value)s
113 |
114 |
115 |
116 |
117 | ''' % ({'value': value.replace('"', '\\\''), 'index': index, 'sid': sid, 'db': db, 'fullkey': fullkey,
118 | 'alt_str': alt_str, 'media_prefix': media_prefix})
119 | alt = not alt
120 | index += 1
121 | out += '
'''
129 | m_values = client.smembers(fullkey)
130 | alt = False
131 | for value in m_values:
132 | alt_str = alt and 'class="alt"' or ''
133 | out += '''
%(value)s
134 |
135 |
136 |
137 |
138 | ''' % (
139 | {'value': value, 'sid': sid, 'db': db, 'fullkey': fullkey, 'alt_str': alt_str, 'media_prefix': media_prefix})
140 | alt = not alt
141 | out += '
'''
149 | m_values = client.zrange(fullkey, 0, -1)
150 | alt = False
151 | for value in m_values:
152 | score = client.zscore(fullkey, value)
153 | alt_str = alt and 'class="alt"' or ''
154 | out += '''
%(score)s
%(value)s
155 |
156 |
157 |
158 |
159 | ''' % ({'value': value, 'score': score, 'sid': sid, 'db': db, 'fullkey': fullkey, 'alt_str': alt_str,
160 | 'media_prefix': media_prefix})
161 | alt = not alt
162 | out += '
'
163 | return out
164 |
--------------------------------------------------------------------------------
/mole/route.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import re
4 |
5 |
6 | from common import HTTPError,MoleException
7 | from structs import lazy_attribute
8 |
9 |
10 | class RouteError(MoleException):
11 | """ This is a base class for all routing related exceptions """
12 |
13 | class RouteSyntaxError(RouteError):
14 | """ The route parser found something not supported by this router """
15 |
16 | class RouteBuildError(RouteError):
17 | """ The route could not been built """
18 |
19 | class Router(object):
20 | '''
21 | A route is defined by a path-rule and a HTTP method.
22 | wildcards consume characters up to the next slash (`/`).
23 | The path-rule is either a static path or a dynamic path
24 |
25 | The path-rule is either a static path (e.g. `/contact`) or a dynamic
26 | path that contains wildcards (e.g. `/wiki/:page`). By default, wildcards
27 | consume characters up to the next slash (`/`). To change that, you may
28 | add a regular expression pattern (e.g. `/wiki/:page#[a-z]+#`).
29 |
30 | For performance reasons, static routes (rules without wildcards) are
31 | checked first. Dynamic routes are tested in order and the first
32 | matching rule returns. Try to avoid ambiguous or overlapping rules.
33 |
34 | The HTTP method string matches only on equality, with two exceptions:
35 | * ´GET´ routes also match ´HEAD´ requests if there is no appropriate
36 | ´HEAD´ route installed.
37 | * ´ANY´ routes do match if there is no other suitable route installed.
38 | '''
39 | default = '[^/]+'
40 |
41 | @lazy_attribute
42 | def syntax(cls):
43 | return re.compile(r'(?(rule, build_info) mapping
49 | self.static = {} # Cache for static routes: {path: {method: target}}
50 | self.dynamic = [] # Cache for dynamic routes. See _compile()
51 |
52 | def add(self, rule, method, target, name=None):
53 | ''' Add a new route or overwrite an existing target. '''
54 | if rule in self.routes:#self.routes.has_key(rule)
55 | self.routes[rule][method.upper()] = target
56 | else:
57 | self.routes[rule] = {method.upper(): target}
58 | self.rules.append(rule)
59 | if self.static or self.dynamic: # Clear precompiler cache.
60 | self.static, self.dynamic = {}, {}
61 | if name:
62 | self.named[name] = (rule, None)
63 |
64 | def delete(self, rule, method=None):
65 | ''' Delete an existing route. Omit `method` to delete all targets. '''
66 | if rule not in self.routes and rule in self.named:
67 | rule = self.named[rule][0]
68 | if rule in self.routes:
69 | if method: del self.routes[rule][method]
70 | else: self.routes[rule].clear() #清空字典
71 | if not self.routes[rule]:
72 | del self.routes[rule]
73 | self.rules.remove(rule)
74 |
75 | def build(self, _name, *anon, **args):
76 | ''' Return a string that matches a named route. Use keyword arguments
77 | to fill out named wildcards. Remaining arguments are appended as a
78 | query string. Raises RouteBuildError or KeyError.'''
79 | from urllib import urlencode
80 | if _name not in self.named:
81 | raise RouteBuildError("No route with that name.", _name)
82 | rule, pairs = self.named[_name]
83 | if not pairs:
84 | token = self.syntax.split(rule)
85 | parts = [p.replace('\\:',':') for p in token[::3]]
86 | names = token[1::3]
87 | if len(parts) > len(names): names.append(None)
88 | pairs = zip(parts, names)
89 | self.named[_name] = (rule, pairs)
90 | try:
91 | anon = list(anon) # * 参数的处理
92 | url = [s if k is None
93 | else s+str(args.pop(k)) if k else s+str(anon.pop())
94 | for s, k in pairs]
95 | except IndexError:
96 | msg = "Not enough arguments to fill out anonymous wildcards.缺少参数匹配"
97 | raise RouteBuildError(msg)
98 | except KeyError, e:
99 | raise RouteBuildError(*e.args)
100 |
101 | if args: url += ['?', urlencode(args)]
102 | return ''.join(url)
103 |
104 | def match(self, environ):
105 | ''' Return a (target, url_agrs) tuple or raise HTTPError(404/405). '''
106 | targets, urlargs = self._match_path(environ)
107 | if not targets:
108 | raise HTTPError(404, "Not found: " + environ.get('PATH_INFO',''))
109 | environ['router.url_args'] = urlargs
110 | method = environ['REQUEST_METHOD'].upper()
111 | if method in targets:
112 | return targets[method], urlargs
113 | if method == 'HEAD' and 'GET' in targets:
114 | return targets['GET'], urlargs
115 | if 'ANY' in targets:
116 | return targets['ANY'], urlargs
117 | allowed = [verb for verb in targets if verb != 'ANY']
118 | if 'GET' in allowed and 'HEAD' not in allowed:
119 | allowed.append('HEAD')
120 | raise HTTPError(405, "Method not allowed.",
121 | header=[('Allow',",".join(allowed))])
122 |
123 | def _match_path(self, environ):
124 | ''' Optimized PATH_INFO matcher. '''
125 | path = environ['PATH_INFO'] or '/'
126 | # Assume we are in a warm state. Search compiled rules first.
127 | match = self.static.get(path)
128 | if match: return match, {}
129 | for combined, rules in self.dynamic:
130 | match = combined.match(path)
131 | if not match: continue
132 | gpat, match = rules[match.lastindex - 1]
133 | return match, gpat.match(path).groupdict() if gpat else {}
134 | # Lazy-check if we are really in a warm state. If yes, stop here.
135 | if self.static or self.dynamic or not self.routes: return None, {}
136 | # Cold state: We have not compiled any rules yet. Do so and try again.
137 | if not environ.get('wsgi.run_once'):
138 | self._compile()
139 | return self._match_path(environ)
140 | # For run_once (CGI) environments, don't compile. Just check one by one.
141 | epath = path.replace(':','\\:') # Turn path into its own static rule.
142 | match = self.routes.get(epath) # This returns static rule only.
143 | if match: return match, {}
144 | for rule in self.rules:
145 | #: Skip static routes to reduce re.compile() calls.
146 | if rule.count(':') < rule.count('\\:'): continue
147 | match = self._compile_pattern(rule).match(path)
148 | if match: return self.routes[rule], match.groupdict()
149 | return None, {}
150 |
151 | def _compile(self):
152 | ''' Prepare static and dynamic search structures. '''
153 | self.static = {}
154 | self.dynamic = []
155 | def fpat_sub(m):
156 | return m.group(0) if len(m.group(1)) % 2 else m.group(1) + '(?:'
157 | for rule in self.rules:
158 | target = self.routes[rule]
159 | if not self.syntax.search(rule):
160 | self.static[rule.replace('\\:',':')] = target
161 | continue
162 | gpat = self._compile_pattern(rule)
163 | fpat = re.sub(r'(\\*)(\(\?P<[^>]*>|\((?!\?))', fpat_sub, gpat.pattern)
164 | gpat = gpat if gpat.groupindex else None
165 | try:
166 | combined = '%s|(%s)' % (self.dynamic[-1][0].pattern, fpat)
167 | self.dynamic[-1] = (re.compile(combined), self.dynamic[-1][1])
168 | self.dynamic[-1][1].append((gpat, target))
169 | except (AssertionError, IndexError), e: # AssertionError: Too many groups
170 | self.dynamic.append((re.compile('(^%s$)'%fpat),
171 | [(gpat, target)]))
172 | except re.error, e:
173 | raise RouteSyntaxError("Could not add Route: %s (%s)" % (rule, e))
174 |
175 | def _compile_pattern(self, rule):
176 | ''' Return a regular expression with named groups for each wildcard. '''
177 | out = ''
178 | for i, part in enumerate(self.syntax.split(rule)):
179 | if i%3 == 0: out += re.escape(part.replace('\\:',':'))
180 | elif i%3 == 1: out += '(?P<%s>' % part if part else '(?:'
181 | else: out += '%s)' % (part or '[^/]+')
182 | return re.compile('^%s$'%out)
183 |
--------------------------------------------------------------------------------
/mole/server.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Server
4 |
5 | a list Server Adapter for wsgi
6 | """
7 | import sys
8 | import os
9 |
10 | class ServerAdapter(object):
11 | quiet = False # 是否禁用标准输出和错误输出
12 | def __init__(self, host='127.0.0.1', port=8080, **config):
13 | self.options = config
14 | self.host = host
15 | self.port = int(port)
16 |
17 | def run(self, handler): # pragma: no cover
18 | pass
19 |
20 | def __repr__(self):
21 | ''' 返回一个对象的字符串表示 '''
22 | args = ', '.join(['%s=%s'%(k,repr(v)) for k, v in self.options.items()])
23 | return "%s(%s)" % (self.__class__.__name__, args)
24 |
25 |
26 | class BjoernServer(ServerAdapter):
27 | """ Screamingly fast server written in C: https://github.com/jonashaag/bjoern """
28 | def run(self, handler):
29 | from bjoern import run
30 | run(handler, self.host, self.port)
31 |
32 | class UVWebServer(ServerAdapter):
33 | def run(self, handler):
34 | from uvweb import run
35 | run(handler, self.host, self.port)
36 |
37 | class CGIServer(ServerAdapter):
38 | quiet = True
39 | def run(self, handler): # pragma: no cover
40 | from wsgiref.handlers import CGIHandler
41 | CGIHandler().run(handler) # Just ignore host and port here
42 |
43 |
44 | class FlupFCGIServer(ServerAdapter):
45 | def run(self, handler): # pragma: no cover
46 | import flup.server.fcgi
47 | kwargs = {'bindAddress':(self.host, self.port)}
48 | kwargs.update(self.options) # allow to override bindAddress and others
49 | flup.server.fcgi.WSGIServer(handler, **kwargs).run()
50 |
51 |
52 | class WSGIRefServer(ServerAdapter):
53 | def run(self, handler): # pragma: no cover
54 | __import__('BaseHTTPServer').BaseHTTPRequestHandler.address_string = lambda x:x.client_address[0]
55 | from wsgiref.simple_server import make_server, WSGIRequestHandler
56 | if self.quiet:
57 | class QuietHandler(WSGIRequestHandler):
58 | def log_request(*args, **kw): pass
59 | self.options['handler_class'] = QuietHandler
60 | srv = make_server(self.host, self.port, handler, **self.options)
61 | srv.serve_forever()
62 |
63 |
64 | class CherryPyServer(ServerAdapter):
65 | def run(self, handler): # pragma: no cover
66 | from cherrypy import wsgiserver
67 | server = wsgiserver.CherryPyWSGIServer((self.host, self.port), handler)
68 | server.start()
69 |
70 |
71 | class PasteServer(ServerAdapter):
72 | def run(self, handler): # pragma: no cover
73 | from paste import httpserver
74 | if not self.quiet:
75 | from paste.translogger import TransLogger
76 | handler = TransLogger(handler)
77 | httpserver.serve(handler, host=self.host, port=str(self.port),
78 | **self.options)
79 |
80 | class MeinheldServer(ServerAdapter):
81 | def run(self, handler):
82 | from meinheld import server
83 | server.listen((self.host, self.port))
84 | server.run(handler)
85 |
86 | class FapwsServer(ServerAdapter):
87 | """ Extremely fast webserver using libev. See http://www.fapws.org/ """
88 | def run(self, handler): # pragma: no cover
89 | import fapws._evwsgi as evwsgi
90 | from fapws import base, config
91 | port = self.port
92 | if float(config.SERVER_IDENT[-2:]) > 0.4:
93 | # fapws3 silently changed its API in 0.5
94 | port = str(port)
95 | evwsgi.start(self.host, port)
96 | # fapws3 never releases the GIL. Complain upstream. I tried. No luck.
97 | if 'MOLE_CHILD' in os.environ and not self.quiet:
98 | print "WARNING: Auto-reloading does not work with Fapws3."
99 | print " (Fapws3 breaks python thread support)"
100 | evwsgi.set_base_module(base)
101 | def app(environ, start_response):
102 | environ['wsgi.multiprocess'] = False
103 | return handler(environ, start_response)
104 | evwsgi.wsgi_cb(('', app))
105 | evwsgi.run()
106 |
107 |
108 | class TornadoServer(ServerAdapter):
109 | """ The super hyped asynchronous server by facebook. Untested. """
110 | def run(self, handler): # pragma: no cover
111 | import tornado.wsgi
112 | import tornado.httpserver
113 | import tornado.ioloop
114 | container = tornado.wsgi.WSGIContainer(handler)
115 | server = tornado.httpserver.HTTPServer(container)
116 | server.listen(port=self.port)
117 | tornado.ioloop.IOLoop.instance().start()
118 |
119 |
120 | class AppEngineServer(ServerAdapter):
121 | """ Adapter for Google App Engine. """
122 | quiet = True
123 | def run(self, handler):
124 | from google.appengine.ext.webapp import util
125 | # A main() function in the handler script enables 'App Caching'.
126 | # Lets makes sure it is there. This _really_ improves performance.
127 | module = sys.modules.get('__main__')
128 | if module and not hasattr(module, 'main'):
129 | module.main = lambda: util.run_wsgi_app(handler)
130 | util.run_wsgi_app(handler)
131 |
132 |
133 | class TwistedServer(ServerAdapter):
134 | """ Untested. """
135 | def run(self, handler):
136 | from twisted.web import server, wsgi
137 | from twisted.python.threadpool import ThreadPool
138 | from twisted.internet import reactor
139 | thread_pool = ThreadPool()
140 | thread_pool.start()
141 | reactor.addSystemEventTrigger('after', 'shutdown', thread_pool.stop)
142 | factory = server.Site(wsgi.WSGIResource(reactor, thread_pool, handler))
143 | reactor.listenTCP(self.port, factory, interface=self.host)
144 | reactor.run()
145 |
146 |
147 | class DieselServer(ServerAdapter):
148 | """ Untested. """
149 | def run(self, handler):
150 | from diesel.protocols.wsgi import WSGIApplication
151 | app = WSGIApplication(handler, port=self.port)
152 | app.run()
153 |
154 |
155 | class GeventServer(ServerAdapter):
156 | """ Untested. """
157 | def run(self, handler):
158 | from gevent import wsgi
159 | #from gevent.hub import getcurrent
160 | #self.set_context_ident(getcurrent, weakref=True) # see contextlocal
161 | wsgi.WSGIServer((self.host, self.port), handler).serve_forever()
162 |
163 |
164 | class GeventWebSocketServer(ServerAdapter):
165 | def run(self, handler):
166 | from gevent import pywsgi
167 | from geventwebsocket.handler import WebSocketHandler
168 | pywsgi.WSGIServer((self.host, self.port), handler, handler_class=WebSocketHandler).serve_forever()
169 |
170 |
171 | class GunicornServer(ServerAdapter):
172 | """ Untested. """
173 | def run(self, handler):
174 | from gunicorn.arbiter import Arbiter
175 | from gunicorn.config import Config
176 | handler.cfg = Config({'bind': "%s:%d" % (self.host, self.port), 'workers': 4})
177 | arbiter = Arbiter(handler)
178 | arbiter.run()
179 |
180 |
181 | class EventletServer(ServerAdapter):
182 | """ Untested """
183 | def run(self, handler):
184 | from eventlet import wsgi, listen
185 | wsgi.server(listen((self.host, self.port)), handler)
186 |
187 |
188 | class RocketServer(ServerAdapter):
189 | """ Untested. As requested in issue 63
190 | https://github.com/defnull/mole/issues/#issue/63 """
191 | def run(self, handler):
192 | from rocket import Rocket
193 | server = Rocket((self.host, self.port), 'wsgi', { 'wsgi_app' : handler })
194 | server.start()
195 |
196 | class AutoServer(ServerAdapter):
197 | """ Untested. """
198 | adapters = [PasteServer, CherryPyServer, TwistedServer, WSGIRefServer]
199 | def run(self, handler):
200 | for sa in self.adapters:
201 | try:
202 | return sa(self.host, self.port, **self.options).run(handler)
203 | except ImportError:
204 | pass
205 |
206 |
207 | server_names = {
208 | 'cgi': CGIServer,
209 | 'flup': FlupFCGIServer,
210 | 'wsgiref': WSGIRefServer,
211 | 'cherrypy': CherryPyServer,
212 | 'paste': PasteServer,
213 | 'fapws3': FapwsServer,
214 | 'tornado': TornadoServer,
215 | 'gae': AppEngineServer,
216 | 'twisted': TwistedServer,
217 | 'diesel': DieselServer,
218 | 'meinheld': MeinheldServer,
219 | 'gunicorn': GunicornServer,
220 | 'eventlet': EventletServer,
221 | 'gevent': GeventServer,
222 | 'geventws': GeventWebSocketServer,
223 | 'rocket': RocketServer,
224 | 'bjoern' : BjoernServer,
225 | 'uvweb' : UVWebServer,
226 | 'auto': AutoServer,
227 | }
228 |
--------------------------------------------------------------------------------
/routes.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 |
3 | from mole import route, run, static_file, error, get, post, put, delete, Mole # 均来自Mole类
4 | from mole.template import template, Jinja2Template
5 | from mole import request
6 | from mole import response
7 | from mole.mole import json_dumps
8 | from mole import redirect
9 | from mole.sessions import get_current_session, authenticator
10 |
11 | from config import media_prefix
12 | import config
13 | import i18n
14 |
15 | auth_required = authenticator(login_url='/auth/login')
16 |
17 |
18 | @route('/%s/:file#.*#' % media_prefix)
19 | def media(file):
20 | return static_file(file, root='./media')
21 |
22 |
23 | @route('/db_tree')
24 | @auth_required()
25 | def db_tree():
26 | from over_view import get_all_trees
27 | import config
28 | try:
29 | cur_server_index = int(request.GET.get('s', '0'))
30 | cur_db_index = int(request.GET.get('db', '0'))
31 | cur_scan_cursor = int(request.GET.get('cursor', '0'))
32 | except:
33 | cur_server_index = 0
34 | cur_db_index = 0
35 | cur_scan_cursor = 0
36 | key = request.GET.get('k', '*')
37 | all_trees = get_all_trees(cur_server_index, key, db=cur_db_index, cursor=cur_scan_cursor)
38 | if type(all_trees) == list:
39 | next_scan_cursor, count = all_trees.pop()
40 | all_trees_json = json_dumps(all_trees)
41 | error_msg = ''
42 | else:
43 | next_scan_cursor, count = 0, 0
44 | all_trees_json = []
45 | error_msg = all_trees
46 | m_config = config.base
47 | return template('db_tree',
48 | all_trees=all_trees_json,
49 | config=m_config,
50 | cur_server_index=cur_server_index,
51 | cur_db_index=cur_db_index,
52 | cur_scan_cursor=next_scan_cursor,
53 | pre_scan_cursor=cur_scan_cursor,
54 | cur_search_key=(key != '*' and key or ''),
55 | count=count,
56 | error_msg=error_msg,
57 | media_prefix=media_prefix
58 | )
59 |
60 |
61 | @route('/db_view')
62 | @auth_required()
63 | def db_view():
64 | try:
65 | cur_server_index = int(request.GET.get('s', 'server0').replace('server', ''))
66 | cur_db_index = int(request.GET.get('db', 'db0').replace('db', ''))
67 | except:
68 | cur_server_index = 0
69 | cur_db_index = 0
70 | key = request.GET.get('k', '*')
71 | return template("db_view", media_prefix=media_prefix, cur_server_index=cur_server_index, cur_db_index=cur_db_index,
72 | keyword=key)
73 |
74 |
75 | @route('/server_tree')
76 | @auth_required()
77 | def server_tree():
78 | from over_view import get_db_trees
79 | all_trees = get_db_trees()
80 | return template("server_tree", all_trees=json_dumps(all_trees), media_prefix=media_prefix)
81 |
82 |
83 | @route('/')
84 | @auth_required()
85 | def server_view():
86 | return template("main", media_prefix=media_prefix)
87 |
88 |
89 | @route('/overview')
90 | @auth_required()
91 | def overview():
92 | from over_view import get_redis_info
93 | return template('overview', redis_info=get_redis_info(), media_prefix=media_prefix)
94 |
95 |
96 | @route('/view')
97 | @auth_required()
98 | def view():
99 | from data_view import general_html, title_html
100 | fullkey = request.GET.get('key', '')
101 | refmodel = request.GET.get('refmodel', None)
102 |
103 | cl, cur_server_index, cur_db_index = get_cl()
104 | if cl.exists(fullkey):
105 | title_html = title_html(fullkey, cur_server_index, cur_db_index)
106 | general_html = general_html(fullkey, cur_server_index, cur_db_index, cl)
107 | out_html = title_html + general_html
108 | if refmodel:
109 | return out_html
110 | else:
111 | return template('view', out_html=out_html, media_prefix=media_prefix)
112 | else:
113 | return ' This key does not exist.'
114 |
115 |
116 | @route('/edit')
117 | @auth_required()
118 | def edit():
119 | from data_change import edit_value
120 | key = request.GET.get('key', None)
121 | value = request.GET.get('value', None)
122 | type = request.GET.get('type', None)
123 | new = request.GET.get('new', None)
124 | score = request.GET.get('score', None)
125 | cl, cur_server_index, cur_db_index = get_cl()
126 | edit_value(key, value, new, score, type, cl)
127 | if new:
128 | return ''
129 | else:
130 | return ''
132 |
133 |
134 | @route('/add')
135 | @auth_required()
136 | def add():
137 | from data_change import add_value
138 | key = request.GET.get('key', None)
139 | value = request.GET.get('value', None)
140 | type = request.GET.get('type', None)
141 | name = request.GET.get('name', None)
142 | score = request.GET.get('score', None)
143 | cl, cur_server_index, cur_db_index = get_cl()
144 | add_value(key, value, name, score, type, cl)
145 | return ''
146 |
147 |
148 | def get_cl():
149 | from config import base
150 | from redis_api import get_client
151 | try:
152 | cur_server_index = int(request.GET.get('s', '0'))
153 | cur_db_index = int(request.GET.get('db', '0'))
154 | except:
155 | cur_server_index = 0
156 | cur_db_index = 0
157 | server = base['servers'][cur_server_index]
158 | cl = get_client(host=server['host'], port=server['port'], db=cur_db_index,
159 | password=server.has_key('password') and server['password'] or None)
160 | return cl, cur_server_index, cur_db_index
161 |
162 |
163 | @route('/delete')
164 | @auth_required()
165 | def delete():
166 | from data_change import delete_key, delete_value
167 | key = request.GET.get('key', '')
168 | value = request.GET.get('value', None)
169 | type = request.GET.get('type', None)
170 | cur_scan_cursor = request.GET.get('cursor', None)
171 | cl, cur_server_index, cur_db_index = get_cl()
172 | if value:
173 | delete_value(key, value, type, cl)
174 | else:
175 | delete_key(key, cl, cursor=cur_scan_cursor)
176 | return ''
177 | return ''
178 |
179 |
180 | @route('/ttl')
181 | @auth_required()
182 | def ttl():
183 | from data_change import change_ttl
184 | cl, cur_server_index, cur_db_index = get_cl()
185 | key = request.GET.get('key', None)
186 | new = request.GET.get('new', None)
187 | if new:
188 | change_ttl(key, int(new), cl)
189 | return ''
190 |
191 |
192 | @route('/rename')
193 | @auth_required()
194 | def rename():
195 | from data_change import rename_key
196 | cl, cur_server_index, cur_db_index = get_cl()
197 | key = request.GET.get('key', None)
198 | new = request.GET.get('new', None)
199 | rename_key(key, new, cl)
200 | return ''
201 |
202 |
203 | @route('/export')
204 | def export():
205 | return 'Still in developme. You can see it in next version.'
206 |
207 |
208 | @route('/import')
209 | def iimport():
210 | return 'Still in developme. You can see it in next version.'
211 |
212 |
213 | @route('/save')
214 | @auth_required()
215 | def save():
216 | cl, cur_server_index, cur_db_index = get_cl()
217 | cl.bgsave()
218 | return ''
219 |
220 |
221 | @route('/auth/login', method=['GET', 'POST'])
222 | def login():
223 | if request.method == 'POST':
224 | username = request.POST.get("username", '')
225 | password = request.POST.get("password", '')
226 | if password == config.admin_pwd and username == config.admin_user:
227 | session = get_current_session()
228 | session['username'] = username
229 | return {'code': 0, 'msg': 'OK'}
230 | else:
231 | return {'code': -1, 'msg': '用户名或密码错误'}
232 | else:
233 | return template('auth/login.html', config=config, media_prefix=media_prefix)
234 |
235 |
236 | @route('/auth/logout')
237 | def logout():
238 | session = get_current_session()
239 | del session['username']
240 | return redirect(request.params.get('next') or '/')
241 |
242 |
243 | if __name__ == "__main__":
244 | from mole.mole import default_app
245 | from mole.sessions import SessionMiddleware
246 |
247 | app = SessionMiddleware(app=default_app(), cookie_key="457rxK3w54tkKiqkfqwfoiQS@kaJSFOo8h", no_datastore=True)
248 | run(app=app, host=config.host, port=config.port, reloader=config.debug)
249 |
--------------------------------------------------------------------------------
/media/jquery-ztree/3.5.12/js/jquery.ztree.excheck-3.5.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | * JQuery zTree excheck 3.5.12
3 | * http://zTree.me/
4 | *
5 | * Copyright (c) 2010 Hunter.z
6 | *
7 | * Licensed same as jquery - MIT License
8 | * http://www.opensource.org/licenses/mit-license.php
9 | *
10 | * email: hunter.z@263.net
11 | * Date: 2013-03-11
12 | */
13 | (function(m){var p,q,r,n={event:{CHECK:"ztree_check"},id:{CHECK:"_check"},checkbox:{STYLE:"checkbox",DEFAULT:"chk",DISABLED:"disable",FALSE:"false",TRUE:"true",FULL:"full",PART:"part",FOCUS:"focus"},radio:{STYLE:"radio",TYPE_ALL:"all",TYPE_LEVEL:"level"}},v={check:{enable:!1,autoCheckTrigger:!1,chkStyle:n.checkbox.STYLE,nocheckInherit:!1,chkDisabledInherit:!1,radioType:n.radio.TYPE_LEVEL,chkboxType:{Y:"ps",N:"ps"}},data:{key:{checked:"checked"}},callback:{beforeCheck:null,onCheck:null}};p=function(c,
14 | a){if(a.chkDisabled===!0)return!1;var b=g.getSetting(c.data.treeId),d=b.data.key.checked;if(l.apply(b.callback.beforeCheck,[b.treeId,a],!0)==!1)return!0;a[d]=!a[d];e.checkNodeRelation(b,a);d=m("#"+a.tId+i.id.CHECK);e.setChkClass(b,d,a);e.repairParentChkClassWithSelf(b,a);b.treeObj.trigger(i.event.CHECK,[c,b.treeId,a]);return!0};q=function(c,a){if(a.chkDisabled===!0)return!1;var b=g.getSetting(c.data.treeId),d=m("#"+a.tId+i.id.CHECK);a.check_Focus=!0;e.setChkClass(b,d,a);return!0};r=function(c,a){if(a.chkDisabled===
15 | !0)return!1;var b=g.getSetting(c.data.treeId),d=m("#"+a.tId+i.id.CHECK);a.check_Focus=!1;e.setChkClass(b,d,a);return!0};m.extend(!0,m.fn.zTree.consts,n);m.extend(!0,m.fn.zTree._z,{tools:{},view:{checkNodeRelation:function(c,a){var b,d,h,k=c.data.key.children,j=c.data.key.checked;b=i.radio;if(c.check.chkStyle==b.STYLE){var f=g.getRadioCheckedList(c);if(a[j])if(c.check.radioType==b.TYPE_ALL){for(d=f.length-1;d>=0;d--)b=f[d],b[j]=!1,f.splice(d,1),e.setChkClass(c,m("#"+b.tId+i.id.CHECK),b),b.parentTId!=
16 | a.parentTId&&e.repairParentChkClassWithSelf(c,b);f.push(a)}else{f=a.parentTId?a.getParentNode():g.getRoot(c);for(d=0,h=f[k].length;d-1)&&e.setSonNodeCheckBox(c,a,!0),!a[j]&&(!a[k]||a[k].length==0||c.check.chkboxType.N.indexOf("s")>-1)&&e.setSonNodeCheckBox(c,
17 | a,!1),a[j]&&c.check.chkboxType.Y.indexOf("p")>-1&&e.setParentNodeCheckBox(c,a,!0),!a[j]&&c.check.chkboxType.N.indexOf("p")>-1&&e.setParentNodeCheckBox(c,a,!1)},makeChkClass:function(c,a){var b=c.data.key.checked,d=i.checkbox,h=i.radio,k="",k=a.chkDisabled===!0?d.DISABLED:a.halfCheck?d.PART:c.check.chkStyle==h.STYLE?a.check_Child_State<1?d.FULL:d.PART:a[b]?a.check_Child_State===2||a.check_Child_State===-1?d.FULL:d.PART:a.check_Child_State<1?d.FULL:d.PART,b=c.check.chkStyle+"_"+(a[b]?d.TRUE:d.FALSE)+
18 | "_"+k,b=a.check_Focus&&a.chkDisabled!==!0?b+"_"+d.FOCUS:b;return i.className.BUTTON+" "+d.DEFAULT+" "+b},repairAllChk:function(c,a){if(c.check.enable&&c.check.chkStyle===i.checkbox.STYLE)for(var b=c.data.key.checked,d=c.data.key.children,h=g.getRoot(c),k=0,j=h[d].length;k0?e.repairParentChkClass(c,a[b][0]):e.repairParentChkClass(c,a)}},repairSonChkDisabled:function(c,a,b,d){if(a){var h=c.data.key.children;if(a.chkDisabled!=b)a.chkDisabled=b;e.repairChkClass(c,a);if(a[h]&&d)for(var k=0,j=a[h].length;k0){j=!1;break}j&&e.setParentNodeCheckBox(c,a.getParentNode(),b,d)}},setSonNodeCheckBox:function(c,a,b,d){if(a){var h=c.data.key.children,k=c.data.key.checked,j=m("#"+a.tId+i.id.CHECK);d||(d=a);var f=!1;if(a[h])for(var o=0,l=a[h].length;o0?b?2:0:-1}else a.check_Child_State=-1;e.setChkClass(c,j,a);c.check.autoCheckTrigger&&a!=d&&a.nocheck!==!0&&a.chkDisabled!==!0&&c.treeObj.trigger(i.event.CHECK,[null,c.treeId,a])}}}},event:{},data:{getRadioCheckedList:function(c){for(var a=g.getRoot(c).radioCheckedList,b=0,d=
23 | a.length;b-1&&a.check_Child_State<2:a.check_Child_State>0}},getTreeCheckedNodes:function(c,a,b,d){if(!a)return[];for(var h=c.data.key.children,k=c.data.key.checked,e=b&&c.check.chkStyle==i.radio.STYLE&&
24 | c.check.radioType==i.radio.TYPE_ALL,d=!d?[]:d,f=0,o=a.length;f0)break}return d},getTreeChangeCheckedNodes:function(c,a,b){if(!a)return[];for(var d=c.data.key.children,h=c.data.key.checked,b=!b?[]:b,k=0,e=a.length;k0?2:0,g==2){h=2;break}else g==0&&(h=0);else if(c.check.chkStyle==i.checkbox.STYLE)if(g=f.nocheck===!0||f.chkDisabled===!0?f.check_Child_State:f.halfCheck===!0?1:f[d]?f.check_Child_State===-1||f.check_Child_State===2?2:1:f.check_Child_State>
26 | 0?1:0,g===1){h=1;break}else if(g===2&&h>-1&&e>0&&g!==h){h=1;break}else if(h===2&&g>-1&&g<2){h=1;break}else g>-1&&(h=g)}a.check_Child_State=h}}}});var n=m.fn.zTree,l=n._z.tools,i=n.consts,e=n._z.view,g=n._z.data;g.exSetting(v);g.addInitBind(function(c){c.treeObj.bind(i.event.CHECK,function(a,b,d,h){l.apply(c.callback.onCheck,[b?b:a,d,h])})});g.addInitUnBind(function(c){c.treeObj.unbind(i.event.CHECK)});g.addInitCache(function(){});g.addInitNode(function(c,a,b,d){if(b){a=c.data.key.checked;typeof b[a]==
27 | "string"&&(b[a]=l.eqs(b[a],"true"));b[a]=!!b[a];b.checkedOld=b[a];if(typeof b.nocheck=="string")b.nocheck=l.eqs(b.nocheck,"true");b.nocheck=!!b.nocheck||c.check.nocheckInherit&&d&&!!d.nocheck;if(typeof b.chkDisabled=="string")b.chkDisabled=l.eqs(b.chkDisabled,"true");b.chkDisabled=!!b.chkDisabled||c.check.chkDisabledInherit&&d&&!!d.chkDisabled;if(typeof b.halfCheck=="string")b.halfCheck=l.eqs(b.halfCheck,"true");b.halfCheck=!!b.halfCheck;b.check_Child_State=-1;b.check_Focus=!1;b.getCheckStatus=function(){return g.getCheckStatus(c,
28 | b)}}});g.addInitProxy(function(c){var a=c.target,b=g.getSetting(c.data.treeId),d="",h=null,e="",j=null;if(l.eqs(c.type,"mouseover")){if(b.check.enable&&l.eqs(a.tagName,"span")&&a.getAttribute("treeNode"+i.id.CHECK)!==null)d=a.parentNode.id,e="mouseoverCheck"}else if(l.eqs(c.type,"mouseout")){if(b.check.enable&&l.eqs(a.tagName,"span")&&a.getAttribute("treeNode"+i.id.CHECK)!==null)d=a.parentNode.id,e="mouseoutCheck"}else if(l.eqs(c.type,"click")&&b.check.enable&&l.eqs(a.tagName,"span")&&a.getAttribute("treeNode"+
29 | i.id.CHECK)!==null)d=a.parentNode.id,e="checkNode";if(d.length>0)switch(h=g.getNodeCache(b,d),e){case "checkNode":j=p;break;case "mouseoverCheck":j=q;break;case "mouseoutCheck":j=r}return{stop:!1,node:h,nodeEventType:e,nodeEventCallback:j,treeEventType:"",treeEventCallback:null}});g.addInitRoot(function(c){g.getRoot(c).radioCheckedList=[]});g.addBeforeA(function(c,a,b){var d=c.data.key.checked;c.check.enable&&(g.makeChkFlag(c,a),c.check.chkStyle==i.radio.STYLE&&c.check.radioType==i.radio.TYPE_ALL&&
30 | a[d]&&g.getRoot(c).radioCheckedList.push(a),b.push(""))});g.addZTreeTools(function(c,a){a.checkNode=function(a,b,g,j){var f=this.setting.data.key.checked;if(a.chkDisabled!==!0&&(b!==!0&&b!==!1&&(b=!a[f]),j=!!j,(a[f]!==b||g)&&!(j&&l.apply(this.setting.callback.beforeCheck,[this.setting.treeId,a],!0)==!1)&&l.uCanDo(this.setting)&&this.setting.check.enable&&a.nocheck!==
31 | !0))a[f]=b,b=m("#"+a.tId+i.id.CHECK),(g||this.setting.check.chkStyle===i.radio.STYLE)&&e.checkNodeRelation(this.setting,a),e.setChkClass(this.setting,b,a),e.repairParentChkClassWithSelf(this.setting,a),j&&c.treeObj.trigger(i.event.CHECK,[null,c.treeId,a])};a.checkAllNodes=function(a){e.repairAllChk(this.setting,!!a)};a.getCheckedNodes=function(a){var b=this.setting.data.key.children;return g.getTreeCheckedNodes(this.setting,g.getRoot(c)[b],a!==!1)};a.getChangeCheckedNodes=function(){var a=this.setting.data.key.children;
32 | return g.getTreeChangeCheckedNodes(this.setting,g.getRoot(c)[a])};a.setChkDisabled=function(a,b,c,g){b=!!b;c=!!c;e.repairSonChkDisabled(this.setting,a,b,!!g);e.repairParentChkDisabled(this.setting,a.getParentNode(),b,c)};var b=a.updateNode;a.updateNode=function(c,h){b&&b.apply(a,arguments);if(c&&this.setting.check.enable&&m("#"+c.tId).get(0)&&l.uCanDo(this.setting)){var g=m("#"+c.tId+i.id.CHECK);(h==!0||this.setting.check.chkStyle===i.radio.STYLE)&&e.checkNodeRelation(this.setting,c);e.setChkClass(this.setting,
33 | g,c);e.repairParentChkClassWithSelf(this.setting,c)}}});var s=e.createNodes;e.createNodes=function(c,a,b,d){s&&s.apply(e,arguments);b&&e.repairParentChkClassWithSelf(c,d)};var t=e.removeNode;e.removeNode=function(c,a){var b=a.getParentNode();t&&t.apply(e,arguments);a&&b&&(e.repairChkClass(c,b),e.repairParentChkClass(c,b))};var u=e.appendNodes;e.appendNodes=function(c,a,b,d,h,i){var j="";u&&(j=u.apply(e,arguments));d&&g.makeChkFlag(c,d);return j}})(jQuery);
34 |
--------------------------------------------------------------------------------
/redis/lock.py:
--------------------------------------------------------------------------------
1 | import threading
2 | import time as mod_time
3 | import uuid
4 | from redis.exceptions import LockError, WatchError
5 | from redis.utils import dummy
6 | from redis._compat import b
7 |
8 |
9 | class Lock(object):
10 | """
11 | A shared, distributed Lock. Using Redis for locking allows the Lock
12 | to be shared across processes and/or machines.
13 |
14 | It's left to the user to resolve deadlock issues and make sure
15 | multiple clients play nicely together.
16 | """
17 | def __init__(self, redis, name, timeout=None, sleep=0.1,
18 | blocking=True, blocking_timeout=None, thread_local=True):
19 | """
20 | Create a new Lock instance named ``name`` using the Redis client
21 | supplied by ``redis``.
22 |
23 | ``timeout`` indicates a maximum life for the lock.
24 | By default, it will remain locked until release() is called.
25 | ``timeout`` can be specified as a float or integer, both representing
26 | the number of seconds to wait.
27 |
28 | ``sleep`` indicates the amount of time to sleep per loop iteration
29 | when the lock is in blocking mode and another client is currently
30 | holding the lock.
31 |
32 | ``blocking`` indicates whether calling ``acquire`` should block until
33 | the lock has been acquired or to fail immediately, causing ``acquire``
34 | to return False and the lock not being acquired. Defaults to True.
35 | Note this value can be overridden by passing a ``blocking``
36 | argument to ``acquire``.
37 |
38 | ``blocking_timeout`` indicates the maximum amount of time in seconds to
39 | spend trying to acquire the lock. A value of ``None`` indicates
40 | continue trying forever. ``blocking_timeout`` can be specified as a
41 | float or integer, both representing the number of seconds to wait.
42 |
43 | ``thread_local`` indicates whether the lock token is placed in
44 | thread-local storage. By default, the token is placed in thread local
45 | storage so that a thread only sees its token, not a token set by
46 | another thread. Consider the following timeline:
47 |
48 | time: 0, thread-1 acquires `my-lock`, with a timeout of 5 seconds.
49 | thread-1 sets the token to "abc"
50 | time: 1, thread-2 blocks trying to acquire `my-lock` using the
51 | Lock instance.
52 | time: 5, thread-1 has not yet completed. redis expires the lock
53 | key.
54 | time: 5, thread-2 acquired `my-lock` now that it's available.
55 | thread-2 sets the token to "xyz"
56 | time: 6, thread-1 finishes its work and calls release(). if the
57 | token is *not* stored in thread local storage, then
58 | thread-1 would see the token value as "xyz" and would be
59 | able to successfully release the thread-2's lock.
60 |
61 | In some use cases it's necessary to disable thread local storage. For
62 | example, if you have code where one thread acquires a lock and passes
63 | that lock instance to a worker thread to release later. If thread
64 | local storage isn't disabled in this case, the worker thread won't see
65 | the token set by the thread that acquired the lock. Our assumption
66 | is that these cases aren't common and as such default to using
67 | thread local storage.
68 | """
69 | self.redis = redis
70 | self.name = name
71 | self.timeout = timeout
72 | self.sleep = sleep
73 | self.blocking = blocking
74 | self.blocking_timeout = blocking_timeout
75 | self.thread_local = bool(thread_local)
76 | self.local = threading.local() if self.thread_local else dummy()
77 | self.local.token = None
78 | if self.timeout and self.sleep > self.timeout:
79 | raise LockError("'sleep' must be less than 'timeout'")
80 |
81 | def __enter__(self):
82 | # force blocking, as otherwise the user would have to check whether
83 | # the lock was actually acquired or not.
84 | self.acquire(blocking=True)
85 | return self
86 |
87 | def __exit__(self, exc_type, exc_value, traceback):
88 | self.release()
89 |
90 | def acquire(self, blocking=None, blocking_timeout=None):
91 | """
92 | Use Redis to hold a shared, distributed lock named ``name``.
93 | Returns True once the lock is acquired.
94 |
95 | If ``blocking`` is False, always return immediately. If the lock
96 | was acquired, return True, otherwise return False.
97 |
98 | ``blocking_timeout`` specifies the maximum number of seconds to
99 | wait trying to acquire the lock.
100 | """
101 | sleep = self.sleep
102 | token = b(uuid.uuid1().hex)
103 | if blocking is None:
104 | blocking = self.blocking
105 | if blocking_timeout is None:
106 | blocking_timeout = self.blocking_timeout
107 | stop_trying_at = None
108 | if blocking_timeout is not None:
109 | stop_trying_at = mod_time.time() + blocking_timeout
110 | while 1:
111 | if self.do_acquire(token):
112 | self.local.token = token
113 | return True
114 | if not blocking:
115 | return False
116 | if stop_trying_at is not None and mod_time.time() > stop_trying_at:
117 | return False
118 | mod_time.sleep(sleep)
119 |
120 | def do_acquire(self, token):
121 | if self.redis.setnx(self.name, token):
122 | if self.timeout:
123 | # convert to milliseconds
124 | timeout = int(self.timeout * 1000)
125 | self.redis.pexpire(self.name, timeout)
126 | return True
127 | return False
128 |
129 | def release(self):
130 | "Releases the already acquired lock"
131 | expected_token = self.local.token
132 | if expected_token is None:
133 | raise LockError("Cannot release an unlocked lock")
134 | self.local.token = None
135 | self.do_release(expected_token)
136 |
137 | def do_release(self, expected_token):
138 | name = self.name
139 |
140 | def execute_release(pipe):
141 | lock_value = pipe.get(name)
142 | if lock_value != expected_token:
143 | raise LockError("Cannot release a lock that's no longer owned")
144 | pipe.delete(name)
145 |
146 | self.redis.transaction(execute_release, name)
147 |
148 | def extend(self, additional_time):
149 | """
150 | Adds more time to an already acquired lock.
151 |
152 | ``additional_time`` can be specified as an integer or a float, both
153 | representing the number of seconds to add.
154 | """
155 | if self.local.token is None:
156 | raise LockError("Cannot extend an unlocked lock")
157 | if self.timeout is None:
158 | raise LockError("Cannot extend a lock with no timeout")
159 | return self.do_extend(additional_time)
160 |
161 | def do_extend(self, additional_time):
162 | pipe = self.redis.pipeline()
163 | pipe.watch(self.name)
164 | lock_value = pipe.get(self.name)
165 | if lock_value != self.local.token:
166 | raise LockError("Cannot extend a lock that's no longer owned")
167 | expiration = pipe.pttl(self.name)
168 | if expiration is None or expiration < 0:
169 | # Redis evicted the lock key between the previous get() and now
170 | # we'll handle this when we call pexpire()
171 | expiration = 0
172 | pipe.multi()
173 | pipe.pexpire(self.name, expiration + int(additional_time * 1000))
174 |
175 | try:
176 | response = pipe.execute()
177 | except WatchError:
178 | # someone else acquired the lock
179 | raise LockError("Cannot extend a lock that's no longer owned")
180 | if not response[0]:
181 | # pexpire returns False if the key doesn't exist
182 | raise LockError("Cannot extend a lock that's no longer owned")
183 | return True
184 |
185 |
186 | class LuaLock(Lock):
187 | """
188 | A lock implementation that uses Lua scripts rather than pipelines
189 | and watches.
190 | """
191 | lua_acquire = None
192 | lua_release = None
193 | lua_extend = None
194 |
195 | # KEYS[1] - lock name
196 | # ARGV[1] - token
197 | # ARGV[2] - timeout in milliseconds
198 | # return 1 if lock was acquired, otherwise 0
199 | LUA_ACQUIRE_SCRIPT = """
200 | if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then
201 | if ARGV[2] ~= '' then
202 | redis.call('pexpire', KEYS[1], ARGV[2])
203 | end
204 | return 1
205 | end
206 | return 0
207 | """
208 |
209 | # KEYS[1] - lock name
210 | # ARGS[1] - token
211 | # return 1 if the lock was released, otherwise 0
212 | LUA_RELEASE_SCRIPT = """
213 | local token = redis.call('get', KEYS[1])
214 | if not token or token ~= ARGV[1] then
215 | return 0
216 | end
217 | redis.call('del', KEYS[1])
218 | return 1
219 | """
220 |
221 | # KEYS[1] - lock name
222 | # ARGS[1] - token
223 | # ARGS[2] - additional milliseconds
224 | # return 1 if the locks time was extended, otherwise 0
225 | LUA_EXTEND_SCRIPT = """
226 | local token = redis.call('get', KEYS[1])
227 | if not token or token ~= ARGV[1] then
228 | return 0
229 | end
230 | local expiration = redis.call('pttl', KEYS[1])
231 | if not expiration then
232 | expiration = 0
233 | end
234 | if expiration < 0 then
235 | return 0
236 | end
237 | redis.call('pexpire', KEYS[1], expiration + ARGV[2])
238 | return 1
239 | """
240 |
241 | def __init__(self, *args, **kwargs):
242 | super(LuaLock, self).__init__(*args, **kwargs)
243 | LuaLock.register_scripts(self.redis)
244 |
245 | @classmethod
246 | def register_scripts(cls, redis):
247 | if cls.lua_acquire is None:
248 | cls.lua_acquire = redis.register_script(cls.LUA_ACQUIRE_SCRIPT)
249 | if cls.lua_release is None:
250 | cls.lua_release = redis.register_script(cls.LUA_RELEASE_SCRIPT)
251 | if cls.lua_extend is None:
252 | cls.lua_extend = redis.register_script(cls.LUA_EXTEND_SCRIPT)
253 |
254 | def do_acquire(self, token):
255 | timeout = self.timeout and int(self.timeout * 1000) or ''
256 | return bool(self.lua_acquire(keys=[self.name],
257 | args=[token, timeout],
258 | client=self.redis))
259 |
260 | def do_release(self, expected_token):
261 | if not bool(self.lua_release(keys=[self.name],
262 | args=[expected_token],
263 | client=self.redis)):
264 | raise LockError("Cannot release a lock that's no longer owned")
265 |
266 | def do_extend(self, additional_time):
267 | additional_time = int(additional_time * 1000)
268 | if not bool(self.lua_extend(keys=[self.name],
269 | args=[self.local.token, additional_time],
270 | client=self.redis)):
271 | raise LockError("Cannot extend a lock that's no longer owned")
272 | return True
273 |
--------------------------------------------------------------------------------
/media/jquery-ztree/3.5.12/js/jquery.ztree.exhide-3.5.js:
--------------------------------------------------------------------------------
1 | /*
2 | * JQuery zTree exHideNodes 3.5.12
3 | * http://zTree.me/
4 | *
5 | * Copyright (c) 2010 Hunter.z
6 | *
7 | * Licensed same as jquery - MIT License
8 | * http://www.opensource.org/licenses/mit-license.php
9 | *
10 | * email: hunter.z@263.net
11 | * Date: 2013-03-11
12 | */
13 | (function($){
14 | //default init node of exLib
15 | var _initNode = function(setting, level, n, parentNode, isFirstNode, isLastNode, openFlag) {
16 | if (typeof n.isHidden == "string") n.isHidden = tools.eqs(n.isHidden, "true");
17 | n.isHidden = !!n.isHidden;
18 | data.initHideForExCheck(setting, n);
19 | },
20 | //add dom for check
21 | _beforeA = function(setting, node, html) {},
22 | //update zTreeObj, add method of exLib
23 | _zTreeTools = function(setting, zTreeTools) {
24 | zTreeTools.showNodes = function(nodes, options) {
25 | view.showNodes(setting, nodes, options);
26 | }
27 | zTreeTools.showNode = function(node, options) {
28 | if (!node) {
29 | return;
30 | }
31 | view.showNodes(setting, [node], options);
32 | }
33 | zTreeTools.hideNodes = function(nodes, options) {
34 | view.hideNodes(setting, nodes, options);
35 | }
36 | zTreeTools.hideNode = function(node, options) {
37 | if (!node) {
38 | return;
39 | }
40 | view.hideNodes(setting, [node], options);
41 | }
42 |
43 | var _checkNode = zTreeTools.checkNode;
44 | if (_checkNode) {
45 | zTreeTools.checkNode = function(node, checked, checkTypeFlag, callbackFlag) {
46 | if (!!node && !!node.isHidden) {
47 | return;
48 | }
49 | _checkNode.apply(zTreeTools, arguments);
50 | }
51 | }
52 | },
53 | //method of operate data
54 | _data = {
55 | initHideForExCheck: function(setting, n) {
56 | if (n.isHidden && setting.check && setting.check.enable) {
57 | if(typeof n._nocheck == "undefined") {
58 | n._nocheck = !!n.nocheck
59 | n.nocheck = true;
60 | }
61 | n.check_Child_State = -1;
62 | if (view.repairParentChkClassWithSelf) {
63 | view.repairParentChkClassWithSelf(setting, n);
64 | }
65 | }
66 | },
67 | initShowForExCheck: function(setting, n) {
68 | if (!n.isHidden && setting.check && setting.check.enable) {
69 | if(typeof n._nocheck != "undefined") {
70 | n.nocheck = n._nocheck;
71 | delete n._nocheck;
72 | }
73 | if (view.setChkClass) {
74 | var checkObj = $("#" + n.tId + consts.id.CHECK);
75 | view.setChkClass(setting, checkObj, n);
76 | }
77 | if (view.repairParentChkClassWithSelf) {
78 | view.repairParentChkClassWithSelf(setting, n);
79 | }
80 | }
81 | }
82 | },
83 | //method of operate ztree dom
84 | _view = {
85 | clearOldFirstNode: function(setting, node) {
86 | var n = node.getNextNode();
87 | while(!!n){
88 | if (n.isFirstNode) {
89 | n.isFirstNode = false;
90 | view.setNodeLineIcos(setting, n);
91 | break;
92 | }
93 | if (n.isLastNode) {
94 | break;
95 | }
96 | n = n.getNextNode();
97 | }
98 | },
99 | clearOldLastNode: function(setting, node) {
100 | var n = node.getPreNode();
101 | while(!!n){
102 | if (n.isLastNode) {
103 | n.isLastNode = false;
104 | view.setNodeLineIcos(setting, n);
105 | break;
106 | }
107 | if (n.isFirstNode) {
108 | break;
109 | }
110 | n = n.getPreNode();
111 | }
112 | },
113 | makeDOMNodeMainBefore: function(html, setting, node) {
114 | html.push("
");
115 | },
116 | showNode: function(setting, node, options) {
117 | node.isHidden = false;
118 | data.initShowForExCheck(setting, node);
119 | $("#" + node.tId).show();
120 | },
121 | showNodes: function(setting, nodes, options) {
122 | if (!nodes || nodes.length == 0) {
123 | return;
124 | }
125 | var pList = {}, i, j;
126 | for (i=0, j=nodes.length; i 0 && !parentNode[childKey][0].isHidden) {
170 | parentNode[childKey][0].isFirstNode = true;
171 | } else if (childLength > 0) {
172 | view.setFirstNodeForHide(setting, parentNode[childKey]);
173 | }
174 | },
175 | setLastNode: function(setting, parentNode) {
176 | var childKey = setting.data.key.children, childLength = parentNode[childKey].length;
177 | if (childLength > 0 && !parentNode[childKey][0].isHidden) {
178 | parentNode[childKey][childLength - 1].isLastNode = true;
179 | } else if (childLength > 0) {
180 | view.setLastNodeForHide(setting, parentNode[childKey]);
181 | }
182 | },
183 | setFirstNodeForHide: function(setting, nodes) {
184 | var n,i,j;
185 | for (i=0, j=nodes.length; i=0; i--) {
225 | n = nodes[i];
226 | if (n.isLastNode) {
227 | break;
228 | }
229 | if (!n.isHidden && !n.isLastNode) {
230 | n.isLastNode = true;
231 | view.setNodeLineIcos(setting, n);
232 | break;
233 | } else {
234 | n = null;
235 | }
236 | }
237 | return n;
238 | },
239 | setLastNodeForShow: function(setting, nodes) {
240 | var n,i,j, last, old;
241 | for (i=nodes.length-1; i>=0; i--) {
242 | n = nodes[i];
243 | if (!last && !n.isHidden && n.isLastNode) {
244 | last = n;
245 | break;
246 | } else if (!last && !n.isHidden && !n.isLastNode) {
247 | n.isLastNode = true;
248 | last = n;
249 | view.setNodeLineIcos(setting, n);
250 | } else if (last && n.isLastNode) {
251 | n.isLastNode = false;
252 | old = n;
253 | view.setNodeLineIcos(setting, n);
254 | break;
255 | } else {
256 | n = null;
257 | }
258 | }
259 | return {"new":last, "old":old};
260 | }
261 | },
262 |
263 | _z = {
264 | view: _view,
265 | data: _data
266 | };
267 | $.extend(true, $.fn.zTree._z, _z);
268 |
269 | var zt = $.fn.zTree,
270 | tools = zt._z.tools,
271 | consts = zt.consts,
272 | view = zt._z.view,
273 | data = zt._z.data,
274 | event = zt._z.event;
275 |
276 | data.addInitNode(_initNode);
277 | data.addBeforeA(_beforeA);
278 | data.addZTreeTools(_zTreeTools);
279 |
280 | // Override method in core
281 | var _dInitNode = data.initNode;
282 | data.tmpHideParent = -1;
283 | data.initNode = function(setting, level, node, parentNode, isFirstNode, isLastNode, openFlag) {
284 | if (data.tmpHideParent !== parentNode) {
285 | data.tmpHideParent = parentNode;
286 | var tmpPNode = (parentNode) ? parentNode: data.getRoot(setting),
287 | children = tmpPNode[setting.data.key.children];
288 | data.tmpHideFirstNode = view.setFirstNodeForHide(setting, children);
289 | data.tmpHideLastNode = view.setLastNodeForHide(setting, children);
290 | view.setNodeLineIcos(setting, data.tmpHideFirstNode);
291 | view.setNodeLineIcos(setting, data.tmpHideLastNode);
292 | }
293 | isFirstNode = (data.tmpHideFirstNode === node);
294 | isLastNode = (data.tmpHideLastNode === node);
295 | if (_dInitNode) _dInitNode.apply(data, arguments);
296 | if (isLastNode) {
297 | view.clearOldLastNode(setting, node);
298 | }
299 | }
300 |
301 | var _makeChkFlag = data.makeChkFlag;
302 | if (!!_makeChkFlag) {
303 | data.makeChkFlag = function(setting, node) {
304 | if (!!node && !!node.isHidden) {
305 | return;
306 | }
307 | _makeChkFlag.apply(data, arguments);
308 | }
309 | }
310 |
311 | var _getTreeCheckedNodes = data.getTreeCheckedNodes;
312 | if (!!_getTreeCheckedNodes) {
313 | data.getTreeCheckedNodes = function(setting, nodes, checked, results) {
314 | if (!!nodes && nodes.length > 0) {
315 | var p = nodes[0].getParentNode();
316 | if (!!p && !!p.isHidden) {
317 | return [];
318 | }
319 | }
320 | return _getTreeCheckedNodes.apply(data, arguments);
321 | }
322 | }
323 |
324 | var _getTreeChangeCheckedNodes = data.getTreeChangeCheckedNodes;
325 | if (!!_getTreeChangeCheckedNodes) {
326 | data.getTreeChangeCheckedNodes = function(setting, nodes, results) {
327 | if (!!nodes && nodes.length > 0) {
328 | var p = nodes[0].getParentNode();
329 | if (!!p && !!p.isHidden) {
330 | return [];
331 | }
332 | }
333 | return _getTreeChangeCheckedNodes.apply(data, arguments);
334 | }
335 | }
336 |
337 | var _expandCollapseSonNode = view.expandCollapseSonNode;
338 | if (!!_expandCollapseSonNode) {
339 | view.expandCollapseSonNode = function(setting, node, expandFlag, animateFlag, callback) {
340 | if (!!node && !!node.isHidden) {
341 | return;
342 | }
343 | _expandCollapseSonNode.apply(view, arguments);
344 | }
345 | }
346 |
347 | var _setSonNodeCheckBox = view.setSonNodeCheckBox;
348 | if (!!_setSonNodeCheckBox) {
349 | view.setSonNodeCheckBox = function(setting, node, value, srcNode) {
350 | if (!!node && !!node.isHidden) {
351 | return;
352 | }
353 | _setSonNodeCheckBox.apply(view, arguments);
354 | }
355 | }
356 |
357 | var _repairParentChkClassWithSelf = view.repairParentChkClassWithSelf;
358 | if (!!_repairParentChkClassWithSelf) {
359 | view.repairParentChkClassWithSelf = function(setting, node) {
360 | if (!!node && !!node.isHidden) {
361 | return;
362 | }
363 | _repairParentChkClassWithSelf.apply(view, arguments);
364 | }
365 | }
366 | })(jQuery);
--------------------------------------------------------------------------------
/redis/sentinel.py:
--------------------------------------------------------------------------------
1 | import os
2 | import random
3 | import weakref
4 |
5 | from redis.client import StrictRedis
6 | from redis.connection import ConnectionPool, Connection
7 | from redis.exceptions import ConnectionError, ResponseError, ReadOnlyError
8 | from redis._compat import iteritems, nativestr, xrange
9 |
10 |
11 | class MasterNotFoundError(ConnectionError):
12 | pass
13 |
14 |
15 | class SlaveNotFoundError(ConnectionError):
16 | pass
17 |
18 |
19 | class SentinelManagedConnection(Connection):
20 | def __init__(self, **kwargs):
21 | self.connection_pool = kwargs.pop('connection_pool')
22 | super(SentinelManagedConnection, self).__init__(**kwargs)
23 |
24 | def __repr__(self):
25 | pool = self.connection_pool
26 | s = '%s' % (type(self).__name__, pool.service_name)
27 | if self.host:
28 | host_info = ',host=%s,port=%s' % (self.host, self.port)
29 | s = s % host_info
30 | return s
31 |
32 | def connect_to(self, address):
33 | self.host, self.port = address
34 | super(SentinelManagedConnection, self).connect()
35 | if self.connection_pool.check_connection:
36 | self.send_command('PING')
37 | if nativestr(self.read_response()) != 'PONG':
38 | raise ConnectionError('PING failed')
39 |
40 | def connect(self):
41 | if self._sock:
42 | return # already connected
43 | if self.connection_pool.is_master:
44 | self.connect_to(self.connection_pool.get_master_address())
45 | else:
46 | for slave in self.connection_pool.rotate_slaves():
47 | try:
48 | return self.connect_to(slave)
49 | except ConnectionError:
50 | continue
51 | raise SlaveNotFoundError # Never be here
52 |
53 | def read_response(self):
54 | try:
55 | return super(SentinelManagedConnection, self).read_response()
56 | except ReadOnlyError:
57 | if self.connection_pool.is_master:
58 | # When talking to a master, a ReadOnlyError when likely
59 | # indicates that the previous master that we're still connected
60 | # to has been demoted to a slave and there's a new master.
61 | # calling disconnect will force the connection to re-query
62 | # sentinel during the next connect() attempt.
63 | self.disconnect()
64 | raise ConnectionError('The previous master is now a slave')
65 | raise
66 |
67 |
68 | class SentinelConnectionPool(ConnectionPool):
69 | """
70 | Sentinel backed connection pool.
71 |
72 | If ``check_connection`` flag is set to True, SentinelManagedConnection
73 | sends a PING command right after establishing the connection.
74 | """
75 |
76 | def __init__(self, service_name, sentinel_manager, **kwargs):
77 | kwargs['connection_class'] = kwargs.get(
78 | 'connection_class', SentinelManagedConnection)
79 | self.is_master = kwargs.pop('is_master', True)
80 | self.check_connection = kwargs.pop('check_connection', False)
81 | super(SentinelConnectionPool, self).__init__(**kwargs)
82 | self.connection_kwargs['connection_pool'] = weakref.proxy(self)
83 | self.service_name = service_name
84 | self.sentinel_manager = sentinel_manager
85 |
86 | def __repr__(self):
87 | return "%s>> from redis.sentinel import Sentinel
142 | >>> sentinel = Sentinel([('localhost', 26379)], socket_timeout=0.1)
143 | >>> master = sentinel.master_for('mymaster', socket_timeout=0.1)
144 | >>> master.set('foo', 'bar')
145 | >>> slave = sentinel.slave_for('mymaster', socket_timeout=0.1)
146 | >>> slave.get('foo')
147 | 'bar'
148 |
149 | ``sentinels`` is a list of sentinel nodes. Each node is represented by
150 | a pair (hostname, port).
151 |
152 | ``min_other_sentinels`` defined a minimum number of peers for a sentinel.
153 | When querying a sentinel, if it doesn't meet this threshold, responses
154 | from that sentinel won't be considered valid.
155 |
156 | ``sentinel_kwargs`` is a dictionary of connection arguments used when
157 | connecting to sentinel instances. Any argument that can be passed to
158 | a normal Redis connection can be specified here. If ``sentinel_kwargs`` is
159 | not specified, any socket_timeout and socket_keepalive options specified
160 | in ``connection_kwargs`` will be used.
161 |
162 | ``connection_kwargs`` are keyword arguments that will be used when
163 | establishing a connection to a Redis server.
164 | """
165 |
166 | def __init__(self, sentinels, min_other_sentinels=0, sentinel_kwargs=None,
167 | **connection_kwargs):
168 | # if sentinel_kwargs isn't defined, use the socket_* options from
169 | # connection_kwargs
170 | if sentinel_kwargs is None:
171 | sentinel_kwargs = dict([(k, v)
172 | for k, v in iteritems(connection_kwargs)
173 | if k.startswith('socket_')
174 | ])
175 | self.sentinel_kwargs = sentinel_kwargs
176 |
177 | self.sentinels = [StrictRedis(hostname, port, **self.sentinel_kwargs)
178 | for hostname, port in sentinels]
179 | self.min_other_sentinels = min_other_sentinels
180 | self.connection_kwargs = connection_kwargs
181 |
182 | def __repr__(self):
183 | sentinel_addresses = []
184 | for sentinel in self.sentinels:
185 | sentinel_addresses.append('%s:%s' % (
186 | sentinel.connection_pool.connection_kwargs['host'],
187 | sentinel.connection_pool.connection_kwargs['port'],
188 | ))
189 | return '%s' % (
190 | type(self).__name__,
191 | ','.join(sentinel_addresses))
192 |
193 | def check_master_state(self, state, service_name):
194 | if not state['is_master'] or state['is_sdown'] or state['is_odown']:
195 | return False
196 | # Check if our sentinel doesn't see other nodes
197 | if state['num-other-sentinels'] < self.min_other_sentinels:
198 | return False
199 | return True
200 |
201 | def discover_master(self, service_name):
202 | """
203 | Asks sentinel servers for the Redis master's address corresponding
204 | to the service labeled ``service_name``.
205 |
206 | Returns a pair (address, port) or raises MasterNotFoundError if no
207 | master is found.
208 | """
209 | for sentinel_no, sentinel in enumerate(self.sentinels):
210 | try:
211 | masters = sentinel.sentinel_masters()
212 | except ConnectionError:
213 | continue
214 | state = masters.get(service_name)
215 | if state and self.check_master_state(state, service_name):
216 | # Put this sentinel at the top of the list
217 | self.sentinels[0], self.sentinels[sentinel_no] = (
218 | sentinel, self.sentinels[0])
219 | return state['ip'], state['port']
220 | raise MasterNotFoundError("No master found for %r" % (service_name,))
221 |
222 | def filter_slaves(self, slaves):
223 | "Remove slaves that are in an ODOWN or SDOWN state"
224 | slaves_alive = []
225 | for slave in slaves:
226 | if slave['is_odown'] or slave['is_sdown']:
227 | continue
228 | slaves_alive.append((slave['ip'], slave['port']))
229 | return slaves_alive
230 |
231 | def discover_slaves(self, service_name):
232 | "Returns a list of alive slaves for service ``service_name``"
233 | for sentinel in self.sentinels:
234 | try:
235 | slaves = sentinel.sentinel_slaves(service_name)
236 | except (ConnectionError, ResponseError):
237 | continue
238 | slaves = self.filter_slaves(slaves)
239 | if slaves:
240 | return slaves
241 | return []
242 |
243 | def master_for(self, service_name, redis_class=StrictRedis,
244 | connection_pool_class=SentinelConnectionPool, **kwargs):
245 | """
246 | Returns a redis client instance for the ``service_name`` master.
247 |
248 | A SentinelConnectionPool class is used to retrive the master's
249 | address before establishing a new connection.
250 |
251 | NOTE: If the master's address has changed, any cached connections to
252 | the old master are closed.
253 |
254 | By default clients will be a redis.StrictRedis instance. Specify a
255 | different class to the ``redis_class`` argument if you desire
256 | something different.
257 |
258 | The ``connection_pool_class`` specifies the connection pool to use.
259 | The SentinelConnectionPool will be used by default.
260 |
261 | All other keyword arguments are merged with any connection_kwargs
262 | passed to this class and passed to the connection pool as keyword
263 | arguments to be used to initialize Redis connections.
264 | """
265 | kwargs['is_master'] = True
266 | connection_kwargs = dict(self.connection_kwargs)
267 | connection_kwargs.update(kwargs)
268 | return redis_class(connection_pool=connection_pool_class(
269 | service_name, self, **connection_kwargs))
270 |
271 | def slave_for(self, service_name, redis_class=StrictRedis,
272 | connection_pool_class=SentinelConnectionPool, **kwargs):
273 | """
274 | Returns redis client instance for the ``service_name`` slave(s).
275 |
276 | A SentinelConnectionPool class is used to retrive the slave's
277 | address before establishing a new connection.
278 |
279 | By default clients will be a redis.StrictRedis instance. Specify a
280 | different class to the ``redis_class`` argument if you desire
281 | something different.
282 |
283 | The ``connection_pool_class`` specifies the connection pool to use.
284 | The SentinelConnectionPool will be used by default.
285 |
286 | All other keyword arguments are merged with any connection_kwargs
287 | passed to this class and passed to the connection pool as keyword
288 | arguments to be used to initialize Redis connections.
289 | """
290 | kwargs['is_master'] = False
291 | connection_kwargs = dict(self.connection_kwargs)
292 | connection_kwargs.update(kwargs)
293 | return redis_class(connection_pool=connection_pool_class(
294 | service_name, self, **connection_kwargs))
295 |
--------------------------------------------------------------------------------
/mole/request.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Request
4 |
5 | Request class for wsgi
6 | """
7 | import sys
8 | import cgi
9 | import threading
10 | import base64
11 | import tempfile
12 | from urlparse import urlunsplit
13 | from urllib import quote as urlquote
14 | try: from collections import MutableMapping as DictMixin
15 | except ImportError: # pragma: no cover
16 | from UserDict import DictMixin
17 |
18 | try: from urlparse import parse_qs
19 | except ImportError: # pragma: no cover
20 | from cgi import parse_qs
21 | from StringIO import StringIO as BytesIO
22 | from tempfile import TemporaryFile
23 | from Cookie import SimpleCookie
24 |
25 | if sys.version_info >= (3,0,0): # pragma: no cover
26 | # See Request.POST
27 | from io import TextIOWrapper
28 | class NCTextIOWrapper(TextIOWrapper):
29 | ''' Garbage collecting an io.TextIOWrapper(buffer) instance closes the
30 | wrapped buffer. This subclass keeps it open. '''
31 | def close(self): pass
32 | else:
33 | NCTextIOWrapper = None
34 |
35 | import utils
36 | from cookie import cookie_decode
37 | from structs import DictProperty,MultiDict
38 | import const
39 |
40 | def parse_auth(header):
41 | """ Parse rfc2617 HTTP authentication header string (basic) and return (user,pass) tuple or None"""
42 | try:
43 | method, data = header.split(None, 1)
44 | if method.lower() == 'basic':
45 | name, pwd = base64.b64decode(data).split(':', 1)
46 | return name, pwd
47 | except (KeyError, ValueError, TypeError):
48 | return None
49 |
50 | class WSGIHeaderDict(DictMixin):
51 | ''' This dict-like class wraps a WSGI environ dict and provides convenient
52 | access to HTTP_* fields. Keys and values are native strings
53 | (2.x bytes or 3.x unicode) and keys are case-insensitive. If the WSGI
54 | environment contains non-native string values, these are de- or encoded
55 | using a lossless 'latin1' character set.
56 |
57 | The API will remain stable even on changes to the relevant PEPs.
58 | Currently PEP 333, 444 and 3333 are supported. (PEP 444 is the only one
59 | that uses non-native strings.)
60 | '''
61 |
62 | def __init__(self, environ):
63 | self.environ = environ
64 |
65 | def _ekey(self, key): # Translate header field name to environ key.
66 | return 'HTTP_' + key.replace('-','_').upper()
67 |
68 | def raw(self, key, default=None):
69 | ''' Return the header value as is (may be bytes or unicode). '''
70 | return self.environ.get(self._ekey(key), default)
71 |
72 | def __getitem__(self, key):
73 | return utils.tonat(self.environ[self._ekey(key)], 'latin1')
74 |
75 | def __setitem__(self, key, value):
76 | raise TypeError("%s is read-only." % self.__class__)
77 |
78 | def __delitem__(self, key):
79 | raise TypeError("%s is read-only." % self.__class__)
80 |
81 | def __iter__(self):
82 | for key in self.environ:
83 | if key[:5] == 'HTTP_':
84 | yield key[5:].replace('_', '-').title()
85 |
86 | def keys(self): return list(self)
87 | def __len__(self): return len(list(self))
88 | def __contains__(self, key): return self._ekey(key) in self.environ
89 |
90 | class Request(threading.local, DictMixin):
91 | """ Represents a single HTTP request using thread-local attributes.
92 | The Request object wraps a WSGI environment and can be used as such.
93 | """
94 | def __init__(self, environ=None):
95 | """ Create a new Request instance.
96 |
97 | You usually don't do this but use the global `mole.request`
98 | instance instead.
99 | """
100 | self.bind(environ or {},)
101 |
102 | def bind(self, environ):
103 | """ Bind a new WSGI environment.
104 |
105 | This is done automatically for the global `mole.request`
106 | instance on every request.
107 | """
108 | self.environ = environ
109 | # These attributes are used anyway, so it is ok to compute them here
110 | self.path = '/' + environ.get('PATH_INFO', '/').lstrip('/')
111 | self.method = environ.get('REQUEST_METHOD', 'GET').upper()
112 |
113 | @property
114 | def _environ(self):
115 | utils.depr("Request._environ renamed to Request.environ")
116 | return self.environ
117 |
118 | def copy(self):
119 | ''' Returns a copy of self '''
120 | return Request(self.environ.copy())
121 |
122 | def path_shift(self, shift=1):
123 | ''' Shift path fragments from PATH_INFO to SCRIPT_NAME and vice versa.
124 |
125 | :param shift: The number of path fragments to shift. May be negative
126 | to change the shift direction. (default: 1)
127 | '''
128 | script_name = self.environ.get('SCRIPT_NAME','/')
129 | self['SCRIPT_NAME'], self.path = path_shift(script_name, self.path, shift)
130 | self['PATH_INFO'] = self.path
131 |
132 | def __getitem__(self, key): return self.environ[key]
133 | def __delitem__(self, key): self[key] = ""; del(self.environ[key])
134 | def __iter__(self): return iter(self.environ)
135 | def __len__(self): return len(self.environ)
136 | def keys(self): return self.environ.keys()
137 | def __setitem__(self, key, value):
138 | """ Shortcut for Request.environ.__setitem__ """
139 | self.environ[key] = value
140 | todelete = []
141 | if key in ('PATH_INFO','REQUEST_METHOD'):
142 | self.bind(self.environ)
143 | elif key == 'wsgi.input': todelete = ('body','forms','files','params')
144 | elif key == 'QUERY_STRING': todelete = ('get','params')
145 | elif key.startswith('HTTP_'): todelete = ('headers', 'cookies')
146 | for key in todelete:
147 | if 'mole.' + key in self.environ:
148 | del self.environ['mole.' + key]
149 |
150 | @property
151 | def query_string(self):
152 | """ The part of the URL following the '?'. """
153 | return self.environ.get('QUERY_STRING', '')
154 |
155 | @property
156 | def fullpath(self):
157 | """ Request path including SCRIPT_NAME (if present). """
158 | return self.environ.get('SCRIPT_NAME', '').rstrip('/') + self.path
159 |
160 | @property
161 | def url(self):
162 | """ Full URL as requested by the client (computed).
163 |
164 | This value is constructed out of different environment variables
165 | and includes scheme, host, port, scriptname, path and query string.
166 | """
167 | scheme = self.environ.get('wsgi.url_scheme', 'http')
168 | host = self.environ.get('HTTP_X_FORWARDED_HOST')
169 | host = host or self.environ.get('HTTP_HOST', None)
170 | if not host:
171 | host = self.environ.get('SERVER_NAME')
172 | port = self.environ.get('SERVER_PORT', '80')
173 | if (scheme, port) not in (('https','443'), ('http','80')):
174 | host += ':' + port
175 | parts = (scheme, host, urlquote(self.fullpath), self.query_string, '')
176 | return urlunsplit(parts)
177 |
178 | @property
179 | def content_length(self):
180 | """ Content-Length header as an integer, -1 if not specified """
181 | return int(self.environ.get('CONTENT_LENGTH', '') or -1)
182 |
183 | @property
184 | def header(self):
185 | utils.depr("The Request.header property was renamed to Request.headers")
186 | return self.headers
187 |
188 | @DictProperty('environ', 'mole.headers', read_only=True)
189 | def headers(self):
190 | ''' Request HTTP Headers stored in a :cls:`HeaderDict`. '''
191 | return WSGIHeaderDict(self.environ)
192 |
193 | @DictProperty('environ', 'mole.get', read_only=True)
194 | def GET(self):
195 | """ The QUERY_STRING parsed into an instance of :class:`MultiDict`. """
196 | data = parse_qs(self.query_string, keep_blank_values=True)
197 | get = self.environ['mole.get'] = MultiDict()
198 | for key, values in data.iteritems():
199 | for value in values:
200 | get[key] = value
201 | return get
202 |
203 | @DictProperty('environ', 'mole.post', read_only=True)
204 | def POST(self):
205 | """ The combined values from :attr:`forms` and :attr:`files`. Values are
206 | either strings (form values) or instances of
207 | :class:`cgi.FieldStorage` (file uploads).
208 | """
209 | post = MultiDict()
210 | safe_env = {'QUERY_STRING':''} # Build a safe environment for cgi
211 | for key in ('REQUEST_METHOD', 'CONTENT_TYPE', 'CONTENT_LENGTH'):
212 | if key in self.environ: safe_env[key] = self.environ[key]
213 | if NCTextIOWrapper:
214 | fb = NCTextIOWrapper(self.body, encoding='ISO-8859-1', newline='\n')
215 | else:
216 | fb = self.body
217 | data = cgi.FieldStorage(fp=fb, environ=safe_env, keep_blank_values=True)
218 | for item in data.list or []:
219 | post[item.name] = item if item.filename else item.value
220 | return post
221 |
222 | @DictProperty('environ', 'mole.forms', read_only=True)
223 | def forms(self):
224 | """ POST form values parsed into an instance of :class:`MultiDict`.
225 |
226 | This property contains form values parsed from an `url-encoded`
227 | or `multipart/form-data` encoded POST request bidy. The values are
228 | native strings.
229 | """
230 | forms = MultiDict()
231 | for name, item in self.POST.iterallitems():
232 | if not hasattr(item, 'filename'):
233 | forms[name] = item
234 | return forms
235 |
236 | @DictProperty('environ', 'mole.files', read_only=True)
237 | def files(self):
238 | """ File uploads parsed into an instance of :class:`MultiDict`.
239 |
240 | This property contains file uploads parsed from an
241 | `multipart/form-data` encoded POST request body. The values are
242 | instances of :class:`cgi.FieldStorage`.
243 | """
244 | files = MultiDict()
245 | for name, item in self.POST.iterallitems():
246 | if hasattr(item, 'filename'):
247 | files[name] = item
248 | return files
249 |
250 | @DictProperty('environ', 'mole.params', read_only=True)
251 | def params(self):
252 | """ A combined :class:`MultiDict` with values from :attr:`forms` and
253 | :attr:`GET`. File-uploads are not included. """
254 | params = MultiDict(self.GET)
255 | for key, value in self.forms.iterallitems():
256 | params[key] = value
257 | return params
258 |
259 | @DictProperty('environ', 'mole.req', read_only=True)
260 | def REQUEST(self):
261 | """ A combined :class:`MultiDict` with values from :attr:`forms` and
262 | :attr:`GET`. File-uploads are not included. """
263 | req = MultiDict(self.GET)
264 | for key, value in self.forms.iterallitems():
265 | req[key] = value
266 | return req
267 |
268 | @DictProperty('environ', 'mole.body', read_only=True)
269 | def _body(self):
270 | """ The HTTP request body as a seekable file-like object.
271 |
272 | This property returns a copy of the `wsgi.input` stream and should
273 | be used instead of `environ['wsgi.input']`.
274 | """
275 | maxread = max(0, self.content_length)
276 | stream = self.environ['wsgi.input']
277 | body = BytesIO() if maxread < const.MEMFILE_MAX else TemporaryFile(mode='w+b')
278 | while maxread > 0:
279 | part = stream.read(min(maxread, const.MEMFILE_MAX))
280 | if not part: break
281 | body.write(part)
282 | maxread -= len(part)
283 | self.environ['wsgi.input'] = body
284 | body.seek(0)
285 | return body
286 |
287 | @property
288 | def body(self):
289 | self._body.seek(0)
290 | return self._body
291 |
292 | @property
293 | def auth(self): #TODO: Tests and docs. Add support for digest. namedtuple?
294 | """ HTTP authorization data as a (user, passwd) tuple. (experimental)
295 |
296 | This implementation currently only supports basic auth and returns
297 | None on errors.
298 | """
299 | return parse_auth(self.headers.get('Authorization',''))
300 |
301 | @DictProperty('environ', 'mole.cookies', read_only=True)
302 | def COOKIES(self):
303 | """ Cookies parsed into a dictionary. Secure cookies are NOT decoded
304 | automatically. See :meth:`get_cookie` for details.
305 | """
306 | raw_dict = SimpleCookie(self.headers.get('Cookie',''))
307 | cookies = {}
308 | for cookie in raw_dict.itervalues():
309 | cookies[cookie.key] = cookie.value
310 | return cookies
311 |
312 | def get_cookie(self, key, secret=None):
313 | """ Return the content of a cookie. To read a `Secure Cookies`, use the
314 | same `secret` as used to create the cookie (see
315 | :meth:`Response.set_cookie`). If anything goes wrong, None is
316 | returned.
317 | """
318 | value = self.COOKIES.get(key)
319 | if secret and value:
320 | dec = cookie_decode(value, secret) # (key, value) tuple or None
321 | return dec[1] if dec and dec[0] == key else None
322 | return value or None
323 |
324 | @property
325 | def is_ajax(self):
326 | ''' True if the request was generated using XMLHttpRequest '''
327 | #TODO: write tests
328 | return self.header.get('X-Requested-With') == 'XMLHttpRequest'
--------------------------------------------------------------------------------
/mole/structs.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from types import GeneratorType
3 |
4 | import functools
5 | try: from collections import MutableMapping as DictMixin
6 | except ImportError: # pragma: no cover
7 | from UserDict import DictMixin
8 |
9 |
10 | class MultiDict(DictMixin):
11 | """ A dict that remembers old values for each key """
12 | # collections.MutableMapping would be better for Python >= 2.6
13 | def __init__(self, *a, **k):
14 | self.dict = dict()
15 | for k, v in dict(*a, **k).iteritems():
16 | self[k] = v
17 |
18 | def __len__(self): return len(self.dict)
19 | def __iter__(self): return iter(self.dict)
20 | def __contains__(self, key): return key in self.dict
21 | def __delitem__(self, key): del self.dict[key]
22 | def keys(self): return self.dict.keys()
23 | def __getitem__(self, key): return self.get(key, KeyError, -1)
24 | def __setitem__(self, key, value): self.append(key, value)
25 |
26 | def append(self, key, value): self.dict.setdefault(key, []).append(value)
27 | def replace(self, key, value): self.dict[key] = [value]
28 | def getall(self, key): return self.dict.get(key) or []
29 |
30 | def get(self, key, default=None, index=-1):
31 | if key not in self.dict and default != KeyError:
32 | return [default][index]
33 | return self.dict[key][index]
34 |
35 | def iterallitems(self):
36 | for key, values in self.dict.iteritems():
37 | for value in values:
38 | yield key, value
39 |
40 | def has_key(self, key):
41 | return key in self.dict
42 |
43 | class DictProperty(object):
44 | ''' Property that maps to a key in a local dict-like attribute. '''
45 | def __init__(self, attr, key=None, read_only=False):
46 | self.attr, self.key, self.read_only = attr, key, read_only
47 |
48 | def __call__(self, func):
49 | functools.update_wrapper(self, func, updated=[])
50 | self.getter, self.key = func, self.key or func.__name__
51 | return self
52 |
53 | def __get__(self, obj, cls):
54 | if not obj: return self
55 | key, storage = self.key, getattr(obj, self.attr)
56 | if key not in storage: storage[key] = self.getter(obj)
57 | return storage[key]
58 |
59 | def __set__(self, obj, value):
60 | if self.read_only: raise AttributeError("Read-Only property.")
61 | getattr(obj, self.attr)[self.key] = value
62 |
63 | def __delete__(self, obj):
64 | if self.read_only: raise AttributeError("Read-Only property.")
65 | del getattr(obj, self.attr)[self.key]
66 |
67 | def cached_property(func):
68 | ''' A property that, if accessed, replaces itself with the computed
69 | value. Subsequent accesses won't call the getter again. '''
70 | return DictProperty('__dict__')(func)
71 |
72 | class lazy_attribute(object): # Does not need configuration -> lower-case name
73 | ''' A property that caches itself to the class object. '''
74 | def __init__(self, func):
75 | functools.update_wrapper(self, func, updated=[])
76 | self.getter = func
77 |
78 | def __get__(self, obj, cls):
79 | value = self.getter(cls)
80 | setattr(cls, self.__name__, value)
81 | return value
82 |
83 | class SortedDict(dict):
84 | """
85 | A dictionary that keeps its keys in the order in which they're inserted.
86 | """
87 | def __new__(cls, *args, **kwargs):
88 | instance = super(SortedDict, cls).__new__(cls, *args, **kwargs)
89 | instance.keyOrder = []
90 | return instance
91 |
92 | def __init__(self, data=None):
93 | if data is None:
94 | data = {}
95 | elif isinstance(data, GeneratorType):
96 | # Unfortunately we need to be able to read a generator twice. Once
97 | # to get the data into self with our super().__init__ call and a
98 | # second time to setup keyOrder correctly
99 | data = list(data)
100 | super(SortedDict, self).__init__(data)
101 | if isinstance(data, dict):
102 | self.keyOrder = data.keys()
103 | else:
104 | self.keyOrder = []
105 | for key, value in data:
106 | if key not in self.keyOrder:
107 | self.keyOrder.append(key)
108 |
109 | def __deepcopy__(self, memo):
110 | return self.__class__([(key, deepcopy(value, memo))
111 | for key, value in self.iteritems()])
112 |
113 | def __setitem__(self, key, value):
114 | if key not in self:
115 | self.keyOrder.append(key)
116 | super(SortedDict, self).__setitem__(key, value)
117 |
118 | def __delitem__(self, key):
119 | super(SortedDict, self).__delitem__(key)
120 | self.keyOrder.remove(key)
121 |
122 | def __iter__(self):
123 | return iter(self.keyOrder)
124 |
125 | def pop(self, k, *args):
126 | result = super(SortedDict, self).pop(k, *args)
127 | try:
128 | self.keyOrder.remove(k)
129 | except ValueError:
130 | # Key wasn't in the dictionary in the first place. No problem.
131 | pass
132 | return result
133 |
134 | def popitem(self):
135 | result = super(SortedDict, self).popitem()
136 | self.keyOrder.remove(result[0])
137 | return result
138 |
139 | def items(self):
140 | return zip(self.keyOrder, self.values())
141 |
142 | def iteritems(self):
143 | for key in self.keyOrder:
144 | yield key, self[key]
145 |
146 | def keys(self):
147 | return self.keyOrder[:]
148 |
149 | def iterkeys(self):
150 | return iter(self.keyOrder)
151 |
152 | def values(self):
153 | return map(self.__getitem__, self.keyOrder)
154 |
155 | def itervalues(self):
156 | for key in self.keyOrder:
157 | yield self[key]
158 |
159 | def update(self, dict_):
160 | for k, v in dict_.iteritems():
161 | self[k] = v
162 |
163 | def setdefault(self, key, default):
164 | if key not in self:
165 | self.keyOrder.append(key)
166 | return super(SortedDict, self).setdefault(key, default)
167 |
168 | def value_for_index(self, index):
169 | """Returns the value of the item at the given zero-based index."""
170 | return self[self.keyOrder[index]]
171 |
172 | def insert(self, index, key, value):
173 | """Inserts the key, value pair before the item with the given index."""
174 | if key in self.keyOrder:
175 | n = self.keyOrder.index(key)
176 | del self.keyOrder[n]
177 | if n < index:
178 | index -= 1
179 | self.keyOrder.insert(index, key)
180 | super(SortedDict, self).__setitem__(key, value)
181 |
182 | def copy(self):
183 | """Returns a copy of this object."""
184 | # This way of initializing the copy means it works for subclasses, too.
185 | obj = self.__class__(self)
186 | obj.keyOrder = self.keyOrder[:]
187 | return obj
188 |
189 | def __repr__(self):
190 | """
191 | Replaces the normal dict.__repr__ with a version that returns the keys
192 | in their sorted order.
193 | """
194 | return '{%s}' % ', '.join(['%r: %r' % (k, v) for k, v in self.items()])
195 |
196 | def clear(self):
197 | super(SortedDict, self).clear()
198 | self.keyOrder = []
199 |
200 |
201 | class MultiValueDict(dict):
202 | """
203 | A subclass of dictionary customized to handle multiple values for the
204 | same key.
205 |
206 | >>> d = MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer']})
207 | >>> d['name']
208 | 'Simon'
209 | >>> d.getlist('name')
210 | ['Adrian', 'Simon']
211 | >>> d.get('lastname', 'nonexistent')
212 | 'nonexistent'
213 | >>> d.setlist('lastname', ['Holovaty', 'Willison'])
214 |
215 | This class exists to solve the irritating problem raised by cgi.parse_qs,
216 | which returns a list for every key, even though most Web forms submit
217 | single name-value pairs.
218 | """
219 | def __init__(self, key_to_list_mapping=()):
220 | super(MultiValueDict, self).__init__(key_to_list_mapping)
221 |
222 | def __repr__(self):
223 | return "<%s: %s>" % (self.__class__.__name__,
224 | super(MultiValueDict, self).__repr__())
225 |
226 | def __getitem__(self, key):
227 | """
228 | Returns the last data value for this key, or [] if it's an empty list;
229 | raises KeyError if not found.
230 | """
231 | try:
232 | list_ = super(MultiValueDict, self).__getitem__(key)
233 | except KeyError:
234 | raise MultiValueDictKeyError("Key %r not found in %r" % (key, self))
235 | try:
236 | return list_[-1]
237 | except IndexError:
238 | return []
239 |
240 | def __setitem__(self, key, value):
241 | super(MultiValueDict, self).__setitem__(key, [value])
242 |
243 | def __copy__(self):
244 | return self.__class__(super(MultiValueDict, self).items())
245 |
246 | def __deepcopy__(self, memo=None):
247 | import django.utils.copycompat as copy
248 | if memo is None:
249 | memo = {}
250 | result = self.__class__()
251 | memo[id(self)] = result
252 | for key, value in dict.items(self):
253 | dict.__setitem__(result, copy.deepcopy(key, memo),
254 | copy.deepcopy(value, memo))
255 | return result
256 |
257 | def __getstate__(self):
258 | obj_dict = self.__dict__.copy()
259 | obj_dict['_data'] = dict([(k, self.getlist(k)) for k in self])
260 | return obj_dict
261 |
262 | def __setstate__(self, obj_dict):
263 | data = obj_dict.pop('_data', {})
264 | for k, v in data.items():
265 | self.setlist(k, v)
266 | self.__dict__.update(obj_dict)
267 |
268 | def get(self, key, default=None):
269 | """
270 | Returns the last data value for the passed key. If key doesn't exist
271 | or value is an empty list, then default is returned.
272 | """
273 | try:
274 | val = self[key]
275 | except KeyError:
276 | return default
277 | if val == []:
278 | return default
279 | return val
280 |
281 | def getlist(self, key):
282 | """
283 | Returns the list of values for the passed key. If key doesn't exist,
284 | then an empty list is returned.
285 | """
286 | try:
287 | return super(MultiValueDict, self).__getitem__(key)
288 | except KeyError:
289 | return []
290 |
291 | def setlist(self, key, list_):
292 | super(MultiValueDict, self).__setitem__(key, list_)
293 |
294 | def setdefault(self, key, default=None):
295 | if key not in self:
296 | self[key] = default
297 | return self[key]
298 |
299 | def setlistdefault(self, key, default_list=()):
300 | if key not in self:
301 | self.setlist(key, default_list)
302 | return self.getlist(key)
303 |
304 | def appendlist(self, key, value):
305 | """Appends an item to the internal list associated with key."""
306 | self.setlistdefault(key, [])
307 | super(MultiValueDict, self).__setitem__(key, self.getlist(key) + [value])
308 |
309 | def items(self):
310 | """
311 | Returns a list of (key, value) pairs, where value is the last item in
312 | the list associated with the key.
313 | """
314 | return [(key, self[key]) for key in self.keys()]
315 |
316 | def iteritems(self):
317 | """
318 | Yields (key, value) pairs, where value is the last item in the list
319 | associated with the key.
320 | """
321 | for key in self.keys():
322 | yield (key, self[key])
323 |
324 | def lists(self):
325 | """Returns a list of (key, list) pairs."""
326 | return super(MultiValueDict, self).items()
327 |
328 | def iterlists(self):
329 | """Yields (key, list) pairs."""
330 | return super(MultiValueDict, self).iteritems()
331 |
332 | def values(self):
333 | """Returns a list of the last value on every key list."""
334 | return [self[key] for key in self.keys()]
335 |
336 | def itervalues(self):
337 | """Yield the last value on every key list."""
338 | for key in self.iterkeys():
339 | yield self[key]
340 |
341 | def copy(self):
342 | """Returns a copy of this object."""
343 | return self.__deepcopy__()
344 |
345 | def update(self, *args, **kwargs):
346 | """
347 | update() extends rather than replaces existing key lists.
348 | Also accepts keyword args.
349 | """
350 | if len(args) > 1:
351 | raise TypeError("update expected at most 1 arguments, got %d" % len(args))
352 | if args:
353 | other_dict = args[0]
354 | if isinstance(other_dict, MultiValueDict):
355 | for key, value_list in other_dict.lists():
356 | self.setlistdefault(key, []).extend(value_list)
357 | else:
358 | try:
359 | for key, value in other_dict.items():
360 | self.setlistdefault(key, []).append(value)
361 | except TypeError:
362 | raise ValueError("MultiValueDict.update() takes either a MultiValueDict or dictionary")
363 | for key, value in kwargs.iteritems():
364 | self.setlistdefault(key, []).append(value)
365 |
366 | class MergeDict(object):
367 | """
368 | A simple class for creating new "virtual" dictionaries that actually look
369 | up values in more than one dictionary, passed in the constructor.
370 |
371 | If a key appears in more than one of the given dictionaries, only the
372 | first occurrence will be used.
373 | """
374 | def __init__(self, *dicts):
375 | self.dicts = dicts
376 |
377 | def __getitem__(self, key):
378 | for dict_ in self.dicts:
379 | try:
380 | return dict_[key]
381 | except KeyError:
382 | pass
383 | raise KeyError
384 |
385 | def __copy__(self):
386 | return self.__class__(*self.dicts)
387 |
388 | def get(self, key, default=None):
389 | try:
390 | return self[key]
391 | except KeyError:
392 | return default
393 |
394 | def getlist(self, key):
395 | for dict_ in self.dicts:
396 | if key in dict_.keys():
397 | return dict_.getlist(key)
398 | return []
399 |
400 | def iteritems(self):
401 | seen = set()
402 | for dict_ in self.dicts:
403 | for item in dict_.iteritems():
404 | k, v = item
405 | if k in seen:
406 | continue
407 | seen.add(k)
408 | yield item
409 |
410 | def iterkeys(self):
411 | for k, v in self.iteritems():
412 | yield k
413 |
414 | def itervalues(self):
415 | for k, v in self.iteritems():
416 | yield v
417 |
418 | def items(self):
419 | return list(self.iteritems())
420 |
421 | def keys(self):
422 | return list(self.iterkeys())
423 |
424 | def values(self):
425 | return list(self.itervalues())
426 |
427 | def has_key(self, key):
428 | for dict_ in self.dicts:
429 | if key in dict_:
430 | return True
431 | return False
432 |
433 | __contains__ = has_key
434 | __iter__ = iterkeys
435 |
436 | def copy(self):
437 | """Returns a copy of this object."""
438 | return self.__copy__()
439 |
--------------------------------------------------------------------------------
/media/bootstrap/bsie/js/bootstrap-ie.old.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 | $.eb = $.eb || {};
3 |
4 | // $.eb.ie = function (min,max) {
5 | // // return true;
6 | // if ($.browser.msie) {
7 | // var v = Math.floor($.browser.version);
8 | // if (v >= min && v <= max) {
9 | // return true;
10 | // }
11 | // }
12 | // return false;
13 | // }
14 | $.eb.ie6 = function () {
15 | return navigator.userAgent.toLowerCase().indexOf('msie 6.0') > -1;
16 | // alert(navigator.userAgent.toLowerCase().indexOf('msie 6.0'));
17 | }
18 |
19 |
20 | $.eb.color = function () {
21 | var pad = function(num, totalChars) {
22 | var pad = '0';
23 | num = num + '';
24 | while (num.length < totalChars) {
25 | num = pad + num;
26 | }
27 | return num;
28 | };
29 |
30 | // Ratio is between 0 and 1
31 | this.changeColor = function(color, ratio, darker) {
32 | // Trim trailing/leading whitespace
33 | color = color.replace(/^\s*|\s*$/, '');
34 |
35 | // Expand three-digit hex
36 | color = color.replace(
37 | /^#?([a-f0-9])([a-f0-9])([a-f0-9])$/i,
38 | '#$1$1$2$2$3$3'
39 | );
40 |
41 | // Calculate ratio
42 | var difference = Math.round(ratio * 256) * (darker ? -1 : 1),
43 | // Determine if input is RGB(A)
44 | rgb = color.match(new RegExp('^rgba?\\(\\s*' +
45 | '(\\d|[1-9]\\d|1\\d{2}|2[0-4][0-9]|25[0-5])' +
46 | '\\s*,\\s*' +
47 | '(\\d|[1-9]\\d|1\\d{2}|2[0-4][0-9]|25[0-5])' +
48 | '\\s*,\\s*' +
49 | '(\\d|[1-9]\\d|1\\d{2}|2[0-4][0-9]|25[0-5])' +
50 | '(?:\\s*,\\s*' +
51 | '(0|1|0?\\.\\d+))?' +
52 | '\\s*\\)$'
53 | , 'i')),
54 | alpha = !!rgb && rgb[4] != null ? rgb[4] : null,
55 |
56 | // Convert hex to decimal
57 | decimal = !!rgb? [rgb[1], rgb[2], rgb[3]] : color.replace(
58 | /^#?([a-f0-9][a-f0-9])([a-f0-9][a-f0-9])([a-f0-9][a-f0-9])/i,
59 | function() {
60 | return parseInt(arguments[1], 16) + ',' +
61 | parseInt(arguments[2], 16) + ',' +
62 | parseInt(arguments[3], 16);
63 | }
64 | ).split(/,/),
65 | returnValue;
66 |
67 | // Return RGB(A)
68 | return !!rgb ?
69 | 'rgb' + (alpha !== null ? 'a' : '') + '(' +
70 | Math[darker ? 'max' : 'min'](
71 | parseInt(decimal[0], 10) + difference, darker ? 0 : 255
72 | ) + ', ' +
73 | Math[darker ? 'max' : 'min'](
74 | parseInt(decimal[1], 10) + difference, darker ? 0 : 255
75 | ) + ', ' +
76 | Math[darker ? 'max' : 'min'](
77 | parseInt(decimal[2], 10) + difference, darker ? 0 : 255
78 | ) +
79 | (alpha !== null ? ', ' + alpha : '') +
80 | ')' :
81 | // Return hex
82 | [
83 | '#',
84 | pad(Math[darker ? 'max' : 'min'](
85 | parseInt(decimal[0], 10) + difference, darker ? 0 : 255
86 | ).toString(16), 2),
87 | pad(Math[darker ? 'max' : 'min'](
88 | parseInt(decimal[1], 10) + difference, darker ? 0 : 255
89 | ).toString(16), 2),
90 | pad(Math[darker ? 'max' : 'min'](
91 | parseInt(decimal[2], 10) + difference, darker ? 0 : 255
92 | ).toString(16), 2)
93 | ].join('');
94 | };
95 | this.lighten = function(color, ratio) {
96 | return changeColor(color, ratio, false);
97 | };
98 | this.darken = function(color, ratio) {
99 | return changeColor(color, ratio, true);
100 | };
101 | return this;
102 | }();
103 |
104 |
105 | function bootstrapIE6(el) {
106 | var dropdownWidthFix = function (el) {
107 | el.each(function () {
108 | var w = 0;
109 | $(this).children('li').each(function() {
110 | var aw = $(this).outerWidth();
111 | if (aw > w) w = aw;
112 | });
113 |
114 | $(this).width(w);
115 | });
116 | }
117 |
118 | if ($.eb.ie6()) {
119 | el = el || $('html');
120 |
121 | //-------------
122 | // GRID
123 | //-------------
124 | $('.row-fluid [class*="span"]:first-child, .row [class*="span"]:first-child').addClass('span-first-child');
125 |
126 | //-------------
127 | // dropdown
128 | //-------------
129 | // fix for IE6 not support li:hover
130 | var lis = ['dropdown-submenu'];
131 | for (var i in lis) {
132 | var child = 'li.' + lis[i];
133 | var hover = lis[i] + '-hover';
134 | $('ul', el).on('mouseenter', child, function () {
135 | $(this).addClass(hover);
136 | }).on('mouseleave', child, function () {
137 | $(this).removeClass(hover);
138 | });
139 | }
140 |
141 | /// fix :after selector -- dropdown-submenu > a:after
142 | $('.dropdown-submenu > a', el).after('');
143 |
144 | /// fix multi class selector -- .dropdown-submenu.pull-left
145 | $('.dropdown-submenu.pull-left', el).removeClass('pull-left').addClass('dropdown-submenu-pull-left');
146 | // $('.navbar .nav.pull-right').removeClass('pull-right').addClass('nav-pull-right');
147 |
148 | /// fix ul li 100% width bug, set ul width to max width of it's sub li
149 | dropdownWidthFix($('.dropdown-menu:visible', el));
150 |
151 |
152 | //-------------
153 | // buttons
154 | //-------------
155 | var btnColorCls = ['btn-primary','btn-warning','btn-danger','btn-success','btn-info','btn-inverse'];
156 | var btnSizeCls = ['btn-mini','btn-small','btn-large'];
157 | $('.btn-group', el).parent().find('.btn-group:eq(0)').addClass('btn-group-first');
158 | $('.btn', el).parent().find('.btn:eq(0)').addClass('btn-first');
159 |
160 | // fix button:hover
161 | $('body', el).on('mouseenter', '.btn', function () {
162 | var btn = $(this);
163 | var hover = 'btn-hover';
164 | btn.data('ie6hover',hover);
165 | $.each(btnColorCls, function (k,v) {
166 | if (btn.hasClass(v)) {
167 | hover = v + '-hover';
168 | btn.data('ie6hover',hover);
169 | return false;
170 | }
171 | });
172 | btn.addClass(hover);
173 | }).on('mouseleave', '.btn', function () {
174 | var btn = $(this);
175 | var hover = btn.data('ie6hover');
176 | btn.removeData('ie6hover');
177 | if (hover) btn.removeClass(hover);
178 | });
179 |
180 | // fix .btn.dropdown-toggle, .btn-primary.dropdown-toggle ...
181 | // fix .btn.dropdown-toggle, .btn-small.dropdown-toggle ...
182 | $('.btn.dropdown-toggle', el).each(function () {
183 | var btn = $(this);
184 | var ddt = 'btn-dropdown-toggle';
185 | btn.addClass(ddt);
186 |
187 | ddt = null;
188 | $.each(btnColorCls, function (k,v) {
189 | if (btn.hasClass(v)) {
190 | ddt = v + '-dropdown-toggle';
191 | return false;
192 | }
193 | });
194 | if (ddt) btn.addClass(ddt);
195 |
196 | ddt = null;
197 | $.each(btnSizeCls, function (k,v) {
198 | if (btn.hasClass(v)) {
199 | ddt = v + '-dropdown-toggle';
200 | return false;
201 | }
202 | });
203 | if (ddt) btn.addClass(ddt);
204 | });
205 |
206 | // fix split button dropdown toggle background color
207 | $('.btn + .btn.dropdown-toggle', el).each(function () {
208 | var btn = $(this);
209 | var c = btn.css('background-color');
210 | // alert($.eb.color.darken(c, .2));
211 | btn.css('background-color', $.eb.color.darken(c, .1));
212 | });
213 |
214 | // fix .btn-group.open
215 | var dropdownPropertyChange = function(e) {
216 | var self = $(this);
217 | var cls = e.data.cls;
218 |
219 | /// fix ul li 100% width bug, set ul width to max width of it's sub li
220 | var el = $('.dropdown-menu:visible', this);
221 | if (el.length) dropdownWidthFix(el);
222 |
223 | if (self.hasClass('open') && !self.hasClass(cls+'-open')) {
224 | self.addClass(cls+'-open');
225 | }
226 | else if (!self.hasClass('open') && self.hasClass(cls+'-open')) {
227 | self.removeClass(cls+'-open');
228 | }
229 |
230 | self.one('propertychange', {cls:cls}, dropdownPropertyChange);
231 | };
232 | $.each(['btn-group', 'dropdown'], function (k,cls) {
233 | $('.'+cls, el).one('propertychange', {cls:cls}, dropdownPropertyChange);
234 | });
235 |
236 | // fix .btn.disabled selector
237 | $('.btn.disabled', el).addClass('btn-disabled');
238 |
239 | var btnPropertyChange = function (e) {
240 | var self = $(this);
241 | var cls = e.data.cls;
242 |
243 | if (self.hasClass('disabled') && !self.hasClass(cls+'-disabled')) {
244 | self.addClass(cls+'-disabled');
245 | }
246 | else if (!self.hasClass('disabled') && self.hasClass(cls+'-disabled')) {
247 | self.removeClass(cls+'-disabled');
248 | }
249 |
250 | self.one('propertychange', {cls:cls}, btnPropertyChange);
251 | }
252 | $.each(['btn'], function (k,cls) {
253 | $('.'+cls, el).one('propertychange', {cls:cls}, btnPropertyChange);
254 | });
255 |
256 |
257 | //-------------
258 | // table
259 | //-------------
260 |
261 | // fix table-hover effect
262 | $('table.table-hover', el).on('mouseenter', 'tr', function () {
263 | $(this).addClass('tr-hover');
264 | }).on('mouseleave', 'tr', function () {
265 | $(this).removeClass('tr-hover');
266 | });
267 |
268 | //-------------
269 | // form
270 | //-------------
271 |
272 | // fix input[type=xxx] selector
273 | $('input[type="file"], input[type="image"], input[type="submit"], input[type="reset"], input[type="button"], input[type="radio"], input[type="checkbox"], input[type="text"], input[type="password"], input[type="datetime"], input[type="datetime-local"], input[type="date"], input[type="month"], input[type="time"], input[type="week"], input[type="number"], input[type="email"], input[type="url"], input[type="search"], input[type="tel"], input[type="color"]', el).each(function () {
274 | var input = $(this);
275 | input.addClass('input-'+input.attr('type'));
276 | });
277 |
278 | // fix form-horizontal controls margin-left
279 | $('.form-horizontal .controls:first-child', el).addClass('controls-first-child');
280 |
281 | // fix .checkbox.inline
282 | $('.checkbox.inline', el).addClass('checkbox-inline');
283 | $('.radio.inline', el).addClass('radio-inline');
284 |
285 | // fix select[multiple], select[size]
286 | $('select[multiple]', el).addClass('select-multiple');
287 | $('select[size]', el).addClass('select-size');
288 |
289 | // fix tag[disabled]
290 | $('input[disabled], select[disabled], textarea[disabled]', el).each(function () {
291 | var self = $(this);
292 | self.addClass(self[0].tagName.toLowerCase()+'-disabled');
293 | });
294 |
295 | // $('input,select,textarea', el).on('propertychange', function() {
296 | // var self = $(this);
297 | // if (self.data('chgDisabled')) return;
298 |
299 | // var cls = self[0].tagName.toLowerCase();
300 | // // alert(self.attr('disabled'));
301 | // if (self.attr('disabled') && !self.hasClass(cls+'-disabled')) {
302 | // // alert('abc');
303 | // self.addClass(cls+'-disabled');
304 | // self.data('chgDisabled', true);
305 | // }
306 | // else if (!self.attr('disabled') && self.hasClass(cls+'-disabled')) {
307 | // self.removeClass(cls+'-disabled');
308 | // self.data('chgDisabled', true);
309 | // }
310 | // });
311 |
312 | // $('input,select,textarea', el).on('propertychange', function() {
313 | // var self = $(this);
314 | // if (self.data('chgReadonly')) return;
315 |
316 | // var cls = self[0].tagName.toLowerCase();
317 |
318 | // if (self.attr('readonly') && !self.hasClass(cls+'-readonly')) {
319 | // self.addClass(cls+'-readonly');
320 | // self.data('chgReadonly', true);
321 | // }
322 | // else if (typeof self.attr('readonly') == 'undefined' && self.hasClass(cls+'-readonly')) {
323 | // self.removeClass(cls+'-readonly');
324 | // self.data('chgReadonly', true);
325 | // }
326 | // });
327 |
328 | // fix tag[readonly]
329 | $('input[readonly], select[readonly], textarea[readonly]', el).each(function () {
330 | var self = $(this);
331 | self.addClass(self[0].tagName.toLowerCase()+'-readonly');
332 | });
333 |
334 | // fix input[type=xxx][disabled]
335 | $('input[type="radio"][disabled], input[type="checkbox"][disabled]', el).each(function () {
336 | var self = $(this);
337 | self.addClass(self.attr('type').toLowerCase()+'-disabled');
338 | });
339 |
340 | // fix input[type=xxx][readonly]
341 | $('input[type="radio"][readonly], input[type="checkbox"][readonly]', el).each(function () {
342 | var self = $(this);
343 | self.addClass(self.attr('type').toLowerCase()+'-readonly');
344 | });
345 |
346 | // fix.control-group.warning ...
347 | var ctlGrpTypeCls = ['warning','success','error','info'];
348 | $.each(ctlGrpTypeCls, function (k,v) {
349 | $('.control-group.'+v, el).addClass('control-group-'+v);
350 | });
351 |
352 | var controlGroupPropertyChange = function(e) {
353 | if(e.originalEvent.propertyName.toLowerCase() == 'classname') {
354 | var self = $(this);
355 | $.each(ctlGrpTypeCls, function (k,v) {
356 | var ieCls = 'control-group-'+v;
357 | if (self.hasClass(v)) {
358 | if (!self.hasClass(ieCls)) {
359 | self.addClass(ieCls);
360 | }
361 | }
362 | else {
363 | if (self.hasClass(ieCls)) {
364 | self.removeClass(ieCls);
365 | }
366 | }
367 | });
368 | }
369 | $(this).one('propertychange', controlGroupPropertyChange);
370 | };
371 | $('.control-group', el).one('propertychange', controlGroupPropertyChange);
372 |
373 | //-------------
374 | // popover
375 | //-------------
376 | // $('.popover .arrow', el).after('');
377 |
378 | //-------------
379 | // pagination
380 | //-------------
381 | $('.pagination ul li:first-child', el).addClass('first-child');
382 |
383 |
384 | //-------------
385 | // icons
386 | //-------------
387 | $('[class^="icon-"],[class*=" icon-"]').each(function () {
388 | var self = $(this);
389 | if (!self.hasClass('icon-xxx')) {
390 | self.addClass('icon-xxx');
391 | self.css('background-position-y',
392 | (parseInt(self.css('background-position-y')) + 1)+'px');
393 | }
394 | });
395 |
396 | //-------------
397 | // carousel
398 | //-------------
399 | $('.carousel-control.left', el).removeClass('left').addClass('carousel-control-left');
400 | $('.carousel-control.right', el).removeClass('right').addClass('carousel-control-right');
401 | $('.carousel-caption').each(function() {
402 | var self = $(this);
403 | var padding = self.outerWidth() - self.width();
404 | self.width(self.parents('.carousel-inner .item').width() - padding);
405 | });
406 |
407 |
408 | }
409 | }
410 | $.bootstrapIE6 = bootstrapIE6;
411 |
412 |
413 | $(document).ready(function () {
414 | bootstrapIE6();
415 | });
416 |
417 | })(jQuery);
--------------------------------------------------------------------------------