├── dev.env
├── memcachier_algebra
├── __init__.py
├── static
│ ├── .gitkeep
│ ├── favicon.ico
│ ├── memcachier-small.png
│ ├── main.js
│ ├── html5shiv.js
│ ├── bootstrap-responsive.min.css
│ ├── bootstrap.js
│ └── jquery-1.7.2.js
├── urls.py
├── views.py
├── wsgi.py
├── templates
│ └── index.html
└── settings.py
├── runtime.txt
├── .gitignore
├── Procfile.dev
├── Procfile
├── requirements.txt
├── manage.py
├── app.json
├── pylibmc_test.py
├── LICENSE
└── README.md
/dev.env:
--------------------------------------------------------------------------------
1 | DEVELOPMENT=1
2 |
--------------------------------------------------------------------------------
/memcachier_algebra/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/runtime.txt:
--------------------------------------------------------------------------------
1 | python-2.7.12
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | venv
2 | *.pyc
3 | .env
4 |
--------------------------------------------------------------------------------
/memcachier_algebra/static/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Procfile.dev:
--------------------------------------------------------------------------------
1 | web: python manage.py runserver
2 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: gunicorn memcachier_algebra.wsgi --log-file -
2 |
--------------------------------------------------------------------------------
/memcachier_algebra/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/memcachier/examples-django/HEAD/memcachier_algebra/static/favicon.ico
--------------------------------------------------------------------------------
/memcachier_algebra/static/memcachier-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/memcachier/examples-django/HEAD/memcachier_algebra/static/memcachier-small.png
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | Django==1.8.16
2 | dj-database-url==0.4.1
3 | django-pylibmc==0.6.1
4 | gunicorn==19.6.0
5 | psycopg2==2.6.2
6 | pylibmc==1.5.1
7 | wsgiref==0.1.2
8 |
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "memcachier_algebra.settings")
7 |
8 | from django.core.management import execute_from_command_line
9 |
10 | execute_from_command_line(sys.argv)
11 |
--------------------------------------------------------------------------------
/memcachier_algebra/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import patterns, include, url
2 | from django.contrib.staticfiles.urls import staticfiles_urlpatterns
3 | from memcachier_algebra import views
4 |
5 | urlpatterns = patterns('',
6 | url(r'^compute$', views.compute),
7 | url(r'^$', views.home),
8 | )
9 |
10 | urlpatterns += staticfiles_urlpatterns()
11 |
--------------------------------------------------------------------------------
/memcachier_algebra/static/main.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 | $("input[data-compute]").live("click", function() {
3 | var answer = $("#answer");
4 | answer.html("Working ...");
5 | $.get(
6 | "/compute?a=" + $("#a").val() + "&b=" + $("#b").val() + "&op=" + $("#op").val(),
7 | function(data) {
8 | answer.html(data);
9 | }
10 | );
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "MemCachier Django Example App",
3 | "description": "Django app illustrating how to use MemCachier to cache computations",
4 | "keywords": ["python", "django", "memcache", "memcachier", "caching" ],
5 | "keywords": ["python", "django", "memcache", "memcachier", "caching", "performance", "pylibmc" ],
6 | "logo": "https://www.memcachier.com/assets/logo-large.png",
7 | "image": "heroku/python",
8 | "website": "https://www.memcachier.com",
9 | "repository": "https://github.com/memcachier/examples-django",
10 | "addons": [ "memcachier" ]
11 | }
12 |
--------------------------------------------------------------------------------
/memcachier_algebra/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render_to_response
2 | from django.http import HttpResponse
3 | from django.core.cache import cache
4 | import sys
5 |
6 | def home(request):
7 | return render_to_response('index.html', {})
8 |
9 | def compute(request):
10 | try:
11 | a = int(request.GET["a"])
12 | b = int(request.GET["b"])
13 | op = request.GET["op"]
14 | expr = "%d%s%d" % (a, op, b)
15 |
16 | in_cache = cache.get(expr)
17 | if in_cache:
18 | result = in_cache
19 | message = "hit"
20 | else:
21 | if op == "plus":
22 | result = str(a + b)
23 | else:
24 | result = str(a - b)
25 | message = "miss"
26 | cache.set(expr, result)
27 |
28 | return HttpResponse("%s cache: %s" % (result, message))
29 | except:
30 | print sys.exc_info()
31 | return HttpResponse("give us two values to add or subtract")
32 |
--------------------------------------------------------------------------------
/pylibmc_test.py:
--------------------------------------------------------------------------------
1 | import os
2 | import pylibmc
3 | import sys
4 | import time
5 |
6 | mc_srvs = os.environ.get('MEMCACHIER_SERVERS', '').split(',')
7 | mc_user = os.environ.get('MEMCACHIER_USERNAME', '')
8 | mc_pass = os.environ.get('MEMCACHIER_PASSWORD', '')
9 |
10 | mc = pylibmc.Client(
11 | mc_srvs,
12 | username=mc_user,
13 | password=mc_pass,
14 | binary=True,
15 | behaviors={
16 | # Enable faster IO
17 | 'tcp_nodelay': True,
18 | 'tcp_keepalive': True,
19 |
20 | # Timeout settings
21 | 'connect_timeout': 2000, # ms
22 | 'send_timeout': 750 * 1000, # us
23 | 'receive_timeout': 750 * 1000, # us
24 | '_poll_timeout': 2000, # ms
25 |
26 | # Better failover
27 | 'ketama': True,
28 | 'remove_failed': 1,
29 | 'retry_timeout': 2,
30 | 'dead_timeout': 30,
31 | }
32 | )
33 |
34 | print mc.behaviors
35 |
36 | mc["get_key"] = "value"
37 |
38 | while True:
39 | try:
40 | time.sleep(0.5)
41 | print mc["get_key"]
42 | except KeyboardInterrupt:
43 | print "Exit..."
44 | break
45 | except:
46 | print "Unexpected error:", sys.exc_info()[0]
47 |
48 |
--------------------------------------------------------------------------------
/memcachier_algebra/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for memcachier_algebra project.
3 |
4 | This module contains the WSGI application used by Django's development server
5 | and any production WSGI deployments. It should expose a module-level variable
6 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
7 | this application via the ``WSGI_APPLICATION`` setting.
8 |
9 | Usually you will have the standard Django WSGI application here, but it also
10 | might make sense to replace the whole Django WSGI application with a custom one
11 | that later delegates to the Django one. For example, you could introduce WSGI
12 | middleware here, or combine a Django application with an application of another
13 | framework.
14 |
15 | """
16 | import os
17 |
18 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "memcachier_algebra.settings")
19 |
20 | # Fix django closing connection to MemCachier after every request (#11331)
21 | from django.core.cache.backends.memcached import BaseMemcachedCache
22 | BaseMemcachedCache.close = lambda self, **kwargs: None
23 |
24 | # This application object is used by any WSGI server configured to use this
25 | # file. This includes Django's development server, if the WSGI_APPLICATION
26 | # setting points here.
27 | from django.core.wsgi import get_wsgi_application
28 | application = get_wsgi_application()
29 |
30 | # Apply WSGI middleware here.
31 | # from helloworld.wsgi import HelloWorldApplication
32 | # application = HelloWorldApplication(application)
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016, MemCachier Inc.
2 |
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions
7 | are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright
13 | notice, this list of conditions and the following disclaimer in the
14 | documentation and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the author nor the names of his contributors
17 | may be used to endorse or promote products derived from this software
18 | without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
21 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
24 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 | POSSIBILITY OF SUCH DAMAGE.
31 |
--------------------------------------------------------------------------------
/memcachier_algebra/static/html5shiv.js:
--------------------------------------------------------------------------------
1 | /*
2 | HTML5 Shiv v3.6.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
3 | */
4 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag();
5 | a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/\w+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x";
6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode||
7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup main mark meter nav output progress section summary time video",version:"3.6.2",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);if(g)return a.createDocumentFragment();
8 | for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d
2 |
3 |
4 |
5 | Django caching example
6 |
7 |
8 |
9 |
10 |
11 |
64 |
65 |
66 |
67 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | Django caching example
82 |
83 |
84 |
Create an algebraic equation. When you submit the form, we'll check
85 | the cache to see if the answer has been cached. If we get a hit, we'll return
86 | the results from the cache. If not, we'll do the computation.
87 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/memcachier_algebra/settings.py:
--------------------------------------------------------------------------------
1 | # Django settings for MemCachier django example.
2 | import os
3 |
4 | ## MemCachier Settings
5 | ## ===================
6 | # Docs: http://sendapatch.se/projects/pylibmc/behaviors.html
7 | # http://docs.libmemcached.org/memcached_behavior.html
8 | # https://github.com/django-pylibmc/django-pylibmc
9 | # https://docs.djangoproject.com/en/1.8/topics/cache
10 | if os.environ.get('DEVELOPMENT', None):
11 | CACHES = {
12 | 'default': {
13 | 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'
14 | }
15 | }
16 | else:
17 | # Configure server credentials
18 | os.environ['MEMCACHE_SERVERS'] = os.environ.get('MEMCACHIER_SERVERS', '').replace(',', ';')
19 | os.environ['MEMCACHE_USERNAME'] = os.environ.get('MEMCACHIER_USERNAME', '')
20 | os.environ['MEMCACHE_PASSWORD'] = os.environ.get('MEMCACHIER_PASSWORD', '')
21 |
22 | # Configure cache settings
23 | CACHES = {
24 | 'default': {
25 | # Use pylibmc
26 | 'BACKEND': 'django_pylibmc.memcached.PyLibMCCache',
27 |
28 | # Use binary memcache protocol (needed for authentication)
29 | 'BINARY': True,
30 |
31 | # TIMEOUT is not the connection timeout! It's the default expiration
32 | # timeout that should be applied to keys! Setting it to `None`
33 | # disables expiration.
34 | 'TIMEOUT': None,
35 |
36 | 'OPTIONS': {
37 | # Enable faster IO
38 | 'tcp_nodelay': True,
39 |
40 | # Keep connection alive
41 | 'tcp_keepalive': True,
42 |
43 | # Timeout settings
44 | 'connect_timeout': 2000, # ms
45 | 'send_timeout': 750 * 1000, # us
46 | 'receive_timeout': 750 * 1000, # us
47 | '_poll_timeout': 2000, # ms
48 |
49 | # Better failover
50 | 'ketama': True,
51 | 'remove_failed': 1,
52 | 'retry_timeout': 2,
53 | 'dead_timeout': 30,
54 | }
55 | }
56 | }
57 |
58 | ## ======================================================================
59 | ## EVERYTHING BELOW IS CONFIGURATION UNRELATED TO THIS MEMCACHIER EXAMPLE
60 | ## ======================================================================
61 |
62 | ## Database Settings (none)
63 | ## ========================
64 | DATABASES = {
65 | 'default': {
66 | 'ENGINE': 'django.db.backends.',
67 | 'NAME': '',
68 | 'USER': '',
69 | 'PASSWORD': '',
70 | 'HOST': '',
71 | 'PORT': '',
72 | }
73 | }
74 |
75 | ## Static Assets & Heroku
76 | ## ======================
77 | PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
78 | STATIC_ROOT = os.path.join(PROJECT_ROOT, 'staticfiles')
79 | STATIC_URL = '/static/'
80 | STATICFILES_DIRS = [
81 | os.path.join(PROJECT_ROOT, 'static')
82 | ]
83 | STATICFILES_FINDERS = (
84 | 'django.contrib.staticfiles.finders.FileSystemFinder',
85 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
86 | )
87 |
88 | ## Templates
89 | ## =========
90 |
91 | TEMPLATE_LOADERS = (
92 | 'django.template.loaders.filesystem.Loader',
93 | 'django.template.loaders.app_directories.Loader',
94 | )
95 | TEMPLATE_DIRS = (
96 | os.path.join(PROJECT_ROOT, 'templates'),
97 | )
98 |
99 | ## Other Settings
100 | ## ==============
101 | DEBUG = True
102 | ADMINS = ()
103 | MANAGERS = ADMINS
104 | TIME_ZONE = 'America/Los_Angeles'
105 | LANGUAGE_CODE = 'en-us'
106 | SITE_ID = 1
107 | USE_I18N = True
108 | USE_L10N = True
109 | USE_TZ = True
110 | MEDIA_ROOT = ''
111 | MEDIA_URL = ''
112 |
113 | # Allow all host headers
114 | ALLOWED_HOSTS = ['*']
115 |
116 | SECRET_KEY = 'l&nd6u%i-s)2c)s5=^i2#v*4)%i9j-g^yo=)z#(#+5pe)o_=%v'
117 |
118 | MIDDLEWARE_CLASSES = (
119 | 'django.middleware.common.CommonMiddleware',
120 | 'django.contrib.sessions.middleware.SessionMiddleware',
121 | 'django.middleware.csrf.CsrfViewMiddleware',
122 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
123 | 'django.contrib.messages.middleware.MessageMiddleware',
124 | )
125 |
126 | ROOT_URLCONF = 'memcachier_algebra.urls'
127 |
128 | WSGI_APPLICATION = 'memcachier_algebra.wsgi.application'
129 |
130 | INSTALLED_APPS = (
131 | 'django.contrib.auth',
132 | 'django.contrib.contenttypes',
133 | 'django.contrib.sessions',
134 | 'django.contrib.sites',
135 | 'django.contrib.messages',
136 | 'django.contrib.staticfiles',
137 | )
138 |
139 | LOGGING = {
140 | 'version': 1,
141 | 'disable_existing_loggers': False,
142 | 'filters': {
143 | 'require_debug_false': {
144 | '()': 'django.utils.log.RequireDebugFalse'
145 | }
146 | },
147 | 'handlers': {
148 | 'mail_admins': {
149 | 'level': 'ERROR',
150 | 'filters': ['require_debug_false'],
151 | 'class': 'django.utils.log.AdminEmailHandler'
152 | }
153 | },
154 | 'loggers': {
155 | 'django.request': {
156 | 'handlers': ['mail_admins'],
157 | 'level': 'ERROR',
158 | 'propagate': True,
159 | },
160 | }
161 | }
162 |
163 | TEST_RUNNER = 'django.test.runner.DiscoverRunner'
164 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MemCachier Django Example App
2 |
3 | This is an example Django app that uses
4 | [MemCachier](http://www.memcachier.com) to cache algebraic
5 | computations.
6 |
7 | **This example is written with Django 1.8.16. Unless you specifically
8 | need an old Django version you should check out a newer
9 | [example](https://github.com/memcachier/examples-django-tasklist).**
10 |
11 | You can view a working version of this app
12 | [here](http://memcachier-examples-django.herokuapp.com) that uses
13 | [MemCachier on Heroku](https://addons.heroku.com/memcachier).
14 | Running this app on your local machine in development will work as
15 | well, although then you won't be using MemCachier -- you'll be using a
16 | local dummy cache. MemCachier is currently only available with various
17 | cloud providers.
18 |
19 | Setting up MemCachier to work in Django is very easy. You need to
20 | make changes to requirements.txt, settings.py, and any app code that
21 | you want cached. These changes are covered in detail below.
22 |
23 | ## Deploy to Heroku
24 |
25 | You can deploy this app yourself to Heroku to play with.
26 |
27 | [](https://heroku.com/deploy)
28 |
29 | ## Building
30 |
31 | It is best to use the python `virtualenv` tool to build locally:
32 |
33 | ```sh
34 | $ virtualenv-2.7 venv
35 | $ source venv/bin/activate
36 | $ pip install -r requirements.txt
37 | $ DEVELOPMENT=1 python manage.py runserver
38 | ```
39 |
40 | Then visit `http://localhost:8000` to view the app. Alternatively you
41 | can use foreman and gunicorn to run the server locally (after copying
42 | `dev.env` to `.env`):
43 |
44 | ```sh
45 | $ foreman start
46 | ```
47 |
48 | ## Deploy to Heroku
49 |
50 | Run the following commands to deploy the app to Heroku:
51 |
52 | ```sh
53 | $ git clone https://github.com/memcachier/examples-django.git
54 | $ cd examples-django
55 | $ heroku create
56 | $ heroku addons:add memcachier:dev
57 | $ git push heroku master:master
58 | $ heroku open
59 | ```
60 |
61 | ## requirements.txt
62 |
63 | MemCachier has been tested with the pylibmc memcache client, but the
64 | default client doesn't support SASL authentication. Run the following
65 | commands to install the necessary pips:
66 |
67 | ```sh
68 | $ sudo brew install libmemcached
69 | $ pip install django-pylibmc pylibmc
70 | ```
71 |
72 | Don't forget to update your requirements.txt file with these new pips.
73 | requirements.txt should have the following two lines:
74 |
75 | ```
76 | django-pylibmc==0.6.1
77 | pylibmc==1.5.1
78 | ```
79 |
80 | ## Configuring MemCachier (settings.py)
81 |
82 | To configure Django to use pylibmc with SASL authentication. You'll also need
83 | to setup your environment, because pylibmc expects different environment
84 | variables than MemCachier provides. Somewhere in your `settings.py` file you
85 | should have the following lines:
86 |
87 | ```python
88 | os.environ['MEMCACHE_SERVERS'] = os.environ.get('MEMCACHIER_SERVERS', '').replace(',', ';')
89 | os.environ['MEMCACHE_USERNAME'] = os.environ.get('MEMCACHIER_USERNAME', '')
90 | os.environ['MEMCACHE_PASSWORD'] = os.environ.get('MEMCACHIER_PASSWORD', '')
91 |
92 | CACHES = {
93 | 'default': {
94 | # Use pylibmc
95 | 'BACKEND': 'django_pylibmc.memcached.PyLibMCCache',
96 |
97 | # Use binary memcache protocol (needed for authentication)
98 | 'BINARY': True,
99 |
100 | # TIMEOUT is not the connection timeout! It's the default expiration
101 | # timeout that should be applied to keys! Setting it to `None`
102 | # disables expiration.
103 | 'TIMEOUT': None,
104 | 'OPTIONS': {
105 | # Enable faster IO
106 | 'tcp_nodelay': True,
107 |
108 | # Keep connection alive
109 | 'tcp_keepalive': True,
110 |
111 | # Timeout settings
112 | 'connect_timeout': 2000, # ms
113 | 'send_timeout': 750 * 1000, # us
114 | 'receive_timeout': 750 * 1000, # us
115 | '_poll_timeout': 2000, # ms
116 |
117 | # Better failover
118 | 'ketama': True,
119 | 'remove_failed': 1,
120 | 'retry_timeout': 2,
121 | 'dead_timeout': 30,
122 | }
123 | }
124 | }
125 | ```
126 |
127 | ## Persistent Connections
128 |
129 | By default, Django doesn't use persistent connections with memcached. This is a
130 | huge performance problem, especially when using SASL authentication as the
131 | connection setup is even more expensive than normal.
132 |
133 | You can fix this by putting the following code in your `wsgi.py` file:
134 |
135 | ```python
136 | # Fix django closing connection to MemCachier after every request (#11331)
137 | from django.core.cache.backends.memcached import BaseMemcachedCache
138 | BaseMemcachedCache.close = lambda self, **kwargs: None
139 | ```
140 |
141 | There is a bug file against Django for this issue
142 | ([#11331](https://code.djangoproject.com/ticket/11331)).
143 |
144 | ## Application Code
145 |
146 | In your application, use django.core.cache methods to access
147 | MemCachier. A description of the low-level caching API can be found
148 | [here](https://docs.djangoproject.com/en/1.8/topics/cache/#the-low-level-cache-api).
149 | All the built-in Django caching tools will work, too.
150 |
151 | Take a look at
152 | [memcachier_algebra/views.py](https://github.com/memcachier/examples-django/blob/master/memcachier_algebra/views.py)
153 | in this repository for an example.
154 |
155 | ## Get involved!
156 |
157 | We are happy to receive bug reports, fixes, documentation enhancements,
158 | and other improvements.
159 |
160 | Please report bugs via the
161 | [github issue tracker](http://github.com/memcachier/examples-django/issues).
162 |
163 | Master [git repository](http://github.com/memcachier/examples-django):
164 |
165 | * `git clone git://github.com/memcachier/examples-django.git`
166 |
167 | ## Licensing
168 |
169 | This library is BSD-licensed.
170 |
171 |
--------------------------------------------------------------------------------
/memcachier_algebra/static/bootstrap-responsive.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Responsive v2.2.1
3 | *
4 | * Copyright 2012 Twitter, Inc
5 | * Licensed under the Apache License v2.0
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Designed and built with all the love in the world @twitter by @mdo and @fat.
9 | */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .dropdown-menu a:hover{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:hover{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}}
10 |
--------------------------------------------------------------------------------
/memcachier_algebra/static/bootstrap.js:
--------------------------------------------------------------------------------
1 | /* ===================================================
2 | * bootstrap-transition.js v2.0.3
3 | * http://twitter.github.com/bootstrap/javascript.html#transitions
4 | * ===================================================
5 | * Copyright 2012 Twitter, Inc.
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ========================================================== */
19 |
20 |
21 | !function ($) {
22 |
23 | $(function () {
24 |
25 | "use strict"; // jshint ;_;
26 |
27 |
28 | /* CSS TRANSITION SUPPORT (http://www.modernizr.com/)
29 | * ======================================================= */
30 |
31 | $.support.transition = (function () {
32 |
33 | var transitionEnd = (function () {
34 |
35 | var el = document.createElement('bootstrap')
36 | , transEndEventNames = {
37 | 'WebkitTransition' : 'webkitTransitionEnd'
38 | , 'MozTransition' : 'transitionend'
39 | , 'OTransition' : 'oTransitionEnd'
40 | , 'msTransition' : 'MSTransitionEnd'
41 | , 'transition' : 'transitionend'
42 | }
43 | , name
44 |
45 | for (name in transEndEventNames){
46 | if (el.style[name] !== undefined) {
47 | return transEndEventNames[name]
48 | }
49 | }
50 |
51 | }())
52 |
53 | return transitionEnd && {
54 | end: transitionEnd
55 | }
56 |
57 | })()
58 |
59 | })
60 |
61 | }(window.jQuery);/* ==========================================================
62 | * bootstrap-alert.js v2.0.3
63 | * http://twitter.github.com/bootstrap/javascript.html#alerts
64 | * ==========================================================
65 | * Copyright 2012 Twitter, Inc.
66 | *
67 | * Licensed under the Apache License, Version 2.0 (the "License");
68 | * you may not use this file except in compliance with the License.
69 | * You may obtain a copy of the License at
70 | *
71 | * http://www.apache.org/licenses/LICENSE-2.0
72 | *
73 | * Unless required by applicable law or agreed to in writing, software
74 | * distributed under the License is distributed on an "AS IS" BASIS,
75 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
76 | * See the License for the specific language governing permissions and
77 | * limitations under the License.
78 | * ========================================================== */
79 |
80 |
81 | !function ($) {
82 |
83 | "use strict"; // jshint ;_;
84 |
85 |
86 | /* ALERT CLASS DEFINITION
87 | * ====================== */
88 |
89 | var dismiss = '[data-dismiss="alert"]'
90 | , Alert = function (el) {
91 | $(el).on('click', dismiss, this.close)
92 | }
93 |
94 | Alert.prototype.close = function (e) {
95 | var $this = $(this)
96 | , selector = $this.attr('data-target')
97 | , $parent
98 |
99 | if (!selector) {
100 | selector = $this.attr('href')
101 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
102 | }
103 |
104 | $parent = $(selector)
105 |
106 | e && e.preventDefault()
107 |
108 | $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent())
109 |
110 | $parent.trigger(e = $.Event('close'))
111 |
112 | if (e.isDefaultPrevented()) return
113 |
114 | $parent.removeClass('in')
115 |
116 | function removeElement() {
117 | $parent
118 | .trigger('closed')
119 | .remove()
120 | }
121 |
122 | $.support.transition && $parent.hasClass('fade') ?
123 | $parent.on($.support.transition.end, removeElement) :
124 | removeElement()
125 | }
126 |
127 |
128 | /* ALERT PLUGIN DEFINITION
129 | * ======================= */
130 |
131 | $.fn.alert = function (option) {
132 | return this.each(function () {
133 | var $this = $(this)
134 | , data = $this.data('alert')
135 | if (!data) $this.data('alert', (data = new Alert(this)))
136 | if (typeof option == 'string') data[option].call($this)
137 | })
138 | }
139 |
140 | $.fn.alert.Constructor = Alert
141 |
142 |
143 | /* ALERT DATA-API
144 | * ============== */
145 |
146 | $(function () {
147 | $('body').on('click.alert.data-api', dismiss, Alert.prototype.close)
148 | })
149 |
150 | }(window.jQuery);/* ============================================================
151 | * bootstrap-button.js v2.0.3
152 | * http://twitter.github.com/bootstrap/javascript.html#buttons
153 | * ============================================================
154 | * Copyright 2012 Twitter, Inc.
155 | *
156 | * Licensed under the Apache License, Version 2.0 (the "License");
157 | * you may not use this file except in compliance with the License.
158 | * You may obtain a copy of the License at
159 | *
160 | * http://www.apache.org/licenses/LICENSE-2.0
161 | *
162 | * Unless required by applicable law or agreed to in writing, software
163 | * distributed under the License is distributed on an "AS IS" BASIS,
164 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
165 | * See the License for the specific language governing permissions and
166 | * limitations under the License.
167 | * ============================================================ */
168 |
169 |
170 | !function ($) {
171 |
172 | "use strict"; // jshint ;_;
173 |
174 |
175 | /* BUTTON PUBLIC CLASS DEFINITION
176 | * ============================== */
177 |
178 | var Button = function (element, options) {
179 | this.$element = $(element)
180 | this.options = $.extend({}, $.fn.button.defaults, options)
181 | }
182 |
183 | Button.prototype.setState = function (state) {
184 | var d = 'disabled'
185 | , $el = this.$element
186 | , data = $el.data()
187 | , val = $el.is('input') ? 'val' : 'html'
188 |
189 | state = state + 'Text'
190 | data.resetText || $el.data('resetText', $el[val]())
191 |
192 | $el[val](data[state] || this.options[state])
193 |
194 | // push to event loop to allow forms to submit
195 | setTimeout(function () {
196 | state == 'loadingText' ?
197 | $el.addClass(d).attr(d, d) :
198 | $el.removeClass(d).removeAttr(d)
199 | }, 0)
200 | }
201 |
202 | Button.prototype.toggle = function () {
203 | var $parent = this.$element.parent('[data-toggle="buttons-radio"]')
204 |
205 | $parent && $parent
206 | .find('.active')
207 | .removeClass('active')
208 |
209 | this.$element.toggleClass('active')
210 | }
211 |
212 |
213 | /* BUTTON PLUGIN DEFINITION
214 | * ======================== */
215 |
216 | $.fn.button = function (option) {
217 | return this.each(function () {
218 | var $this = $(this)
219 | , data = $this.data('button')
220 | , options = typeof option == 'object' && option
221 | if (!data) $this.data('button', (data = new Button(this, options)))
222 | if (option == 'toggle') data.toggle()
223 | else if (option) data.setState(option)
224 | })
225 | }
226 |
227 | $.fn.button.defaults = {
228 | loadingText: 'loading...'
229 | }
230 |
231 | $.fn.button.Constructor = Button
232 |
233 |
234 | /* BUTTON DATA-API
235 | * =============== */
236 |
237 | $(function () {
238 | $('body').on('click.button.data-api', '[data-toggle^=button]', function ( e ) {
239 | var $btn = $(e.target)
240 | if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
241 | $btn.button('toggle')
242 | })
243 | })
244 |
245 | }(window.jQuery);/* ==========================================================
246 | * bootstrap-carousel.js v2.0.3
247 | * http://twitter.github.com/bootstrap/javascript.html#carousel
248 | * ==========================================================
249 | * Copyright 2012 Twitter, Inc.
250 | *
251 | * Licensed under the Apache License, Version 2.0 (the "License");
252 | * you may not use this file except in compliance with the License.
253 | * You may obtain a copy of the License at
254 | *
255 | * http://www.apache.org/licenses/LICENSE-2.0
256 | *
257 | * Unless required by applicable law or agreed to in writing, software
258 | * distributed under the License is distributed on an "AS IS" BASIS,
259 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
260 | * See the License for the specific language governing permissions and
261 | * limitations under the License.
262 | * ========================================================== */
263 |
264 |
265 | !function ($) {
266 |
267 | "use strict"; // jshint ;_;
268 |
269 |
270 | /* CAROUSEL CLASS DEFINITION
271 | * ========================= */
272 |
273 | var Carousel = function (element, options) {
274 | this.$element = $(element)
275 | this.options = options
276 | this.options.slide && this.slide(this.options.slide)
277 | this.options.pause == 'hover' && this.$element
278 | .on('mouseenter', $.proxy(this.pause, this))
279 | .on('mouseleave', $.proxy(this.cycle, this))
280 | }
281 |
282 | Carousel.prototype = {
283 |
284 | cycle: function (e) {
285 | if (!e) this.paused = false
286 | this.options.interval
287 | && !this.paused
288 | && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
289 | return this
290 | }
291 |
292 | , to: function (pos) {
293 | var $active = this.$element.find('.active')
294 | , children = $active.parent().children()
295 | , activePos = children.index($active)
296 | , that = this
297 |
298 | if (pos > (children.length - 1) || pos < 0) return
299 |
300 | if (this.sliding) {
301 | return this.$element.one('slid', function () {
302 | that.to(pos)
303 | })
304 | }
305 |
306 | if (activePos == pos) {
307 | return this.pause().cycle()
308 | }
309 |
310 | return this.slide(pos > activePos ? 'next' : 'prev', $(children[pos]))
311 | }
312 |
313 | , pause: function (e) {
314 | if (!e) this.paused = true
315 | clearInterval(this.interval)
316 | this.interval = null
317 | return this
318 | }
319 |
320 | , next: function () {
321 | if (this.sliding) return
322 | return this.slide('next')
323 | }
324 |
325 | , prev: function () {
326 | if (this.sliding) return
327 | return this.slide('prev')
328 | }
329 |
330 | , slide: function (type, next) {
331 | var $active = this.$element.find('.active')
332 | , $next = next || $active[type]()
333 | , isCycling = this.interval
334 | , direction = type == 'next' ? 'left' : 'right'
335 | , fallback = type == 'next' ? 'first' : 'last'
336 | , that = this
337 | , e = $.Event('slide')
338 |
339 | this.sliding = true
340 |
341 | isCycling && this.pause()
342 |
343 | $next = $next.length ? $next : this.$element.find('.item')[fallback]()
344 |
345 | if ($next.hasClass('active')) return
346 |
347 | if ($.support.transition && this.$element.hasClass('slide')) {
348 | this.$element.trigger(e)
349 | if (e.isDefaultPrevented()) return
350 | $next.addClass(type)
351 | $next[0].offsetWidth // force reflow
352 | $active.addClass(direction)
353 | $next.addClass(direction)
354 | this.$element.one($.support.transition.end, function () {
355 | $next.removeClass([type, direction].join(' ')).addClass('active')
356 | $active.removeClass(['active', direction].join(' '))
357 | that.sliding = false
358 | setTimeout(function () { that.$element.trigger('slid') }, 0)
359 | })
360 | } else {
361 | this.$element.trigger(e)
362 | if (e.isDefaultPrevented()) return
363 | $active.removeClass('active')
364 | $next.addClass('active')
365 | this.sliding = false
366 | this.$element.trigger('slid')
367 | }
368 |
369 | isCycling && this.cycle()
370 |
371 | return this
372 | }
373 |
374 | }
375 |
376 |
377 | /* CAROUSEL PLUGIN DEFINITION
378 | * ========================== */
379 |
380 | $.fn.carousel = function (option) {
381 | return this.each(function () {
382 | var $this = $(this)
383 | , data = $this.data('carousel')
384 | , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option)
385 | if (!data) $this.data('carousel', (data = new Carousel(this, options)))
386 | if (typeof option == 'number') data.to(option)
387 | else if (typeof option == 'string' || (option = options.slide)) data[option]()
388 | else if (options.interval) data.cycle()
389 | })
390 | }
391 |
392 | $.fn.carousel.defaults = {
393 | interval: 5000
394 | , pause: 'hover'
395 | }
396 |
397 | $.fn.carousel.Constructor = Carousel
398 |
399 |
400 | /* CAROUSEL DATA-API
401 | * ================= */
402 |
403 | $(function () {
404 | $('body').on('click.carousel.data-api', '[data-slide]', function ( e ) {
405 | var $this = $(this), href
406 | , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
407 | , options = !$target.data('modal') && $.extend({}, $target.data(), $this.data())
408 | $target.carousel(options)
409 | e.preventDefault()
410 | })
411 | })
412 |
413 | }(window.jQuery);/* =============================================================
414 | * bootstrap-collapse.js v2.0.3
415 | * http://twitter.github.com/bootstrap/javascript.html#collapse
416 | * =============================================================
417 | * Copyright 2012 Twitter, Inc.
418 | *
419 | * Licensed under the Apache License, Version 2.0 (the "License");
420 | * you may not use this file except in compliance with the License.
421 | * You may obtain a copy of the License at
422 | *
423 | * http://www.apache.org/licenses/LICENSE-2.0
424 | *
425 | * Unless required by applicable law or agreed to in writing, software
426 | * distributed under the License is distributed on an "AS IS" BASIS,
427 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
428 | * See the License for the specific language governing permissions and
429 | * limitations under the License.
430 | * ============================================================ */
431 |
432 |
433 | !function ($) {
434 |
435 | "use strict"; // jshint ;_;
436 |
437 |
438 | /* COLLAPSE PUBLIC CLASS DEFINITION
439 | * ================================ */
440 |
441 | var Collapse = function (element, options) {
442 | this.$element = $(element)
443 | this.options = $.extend({}, $.fn.collapse.defaults, options)
444 |
445 | if (this.options.parent) {
446 | this.$parent = $(this.options.parent)
447 | }
448 |
449 | this.options.toggle && this.toggle()
450 | }
451 |
452 | Collapse.prototype = {
453 |
454 | constructor: Collapse
455 |
456 | , dimension: function () {
457 | var hasWidth = this.$element.hasClass('width')
458 | return hasWidth ? 'width' : 'height'
459 | }
460 |
461 | , show: function () {
462 | var dimension
463 | , scroll
464 | , actives
465 | , hasData
466 |
467 | if (this.transitioning) return
468 |
469 | dimension = this.dimension()
470 | scroll = $.camelCase(['scroll', dimension].join('-'))
471 | actives = this.$parent && this.$parent.find('> .accordion-group > .in')
472 |
473 | if (actives && actives.length) {
474 | hasData = actives.data('collapse')
475 | if (hasData && hasData.transitioning) return
476 | actives.collapse('hide')
477 | hasData || actives.data('collapse', null)
478 | }
479 |
480 | this.$element[dimension](0)
481 | this.transition('addClass', $.Event('show'), 'shown')
482 | this.$element[dimension](this.$element[0][scroll])
483 | }
484 |
485 | , hide: function () {
486 | var dimension
487 | if (this.transitioning) return
488 | dimension = this.dimension()
489 | this.reset(this.$element[dimension]())
490 | this.transition('removeClass', $.Event('hide'), 'hidden')
491 | this.$element[dimension](0)
492 | }
493 |
494 | , reset: function (size) {
495 | var dimension = this.dimension()
496 |
497 | this.$element
498 | .removeClass('collapse')
499 | [dimension](size || 'auto')
500 | [0].offsetWidth
501 |
502 | this.$element[size !== null ? 'addClass' : 'removeClass']('collapse')
503 |
504 | return this
505 | }
506 |
507 | , transition: function (method, startEvent, completeEvent) {
508 | var that = this
509 | , complete = function () {
510 | if (startEvent.type == 'show') that.reset()
511 | that.transitioning = 0
512 | that.$element.trigger(completeEvent)
513 | }
514 |
515 | this.$element.trigger(startEvent)
516 |
517 | if (startEvent.isDefaultPrevented()) return
518 |
519 | this.transitioning = 1
520 |
521 | this.$element[method]('in')
522 |
523 | $.support.transition && this.$element.hasClass('collapse') ?
524 | this.$element.one($.support.transition.end, complete) :
525 | complete()
526 | }
527 |
528 | , toggle: function () {
529 | this[this.$element.hasClass('in') ? 'hide' : 'show']()
530 | }
531 |
532 | }
533 |
534 |
535 | /* COLLAPSIBLE PLUGIN DEFINITION
536 | * ============================== */
537 |
538 | $.fn.collapse = function (option) {
539 | return this.each(function () {
540 | var $this = $(this)
541 | , data = $this.data('collapse')
542 | , options = typeof option == 'object' && option
543 | if (!data) $this.data('collapse', (data = new Collapse(this, options)))
544 | if (typeof option == 'string') data[option]()
545 | })
546 | }
547 |
548 | $.fn.collapse.defaults = {
549 | toggle: true
550 | }
551 |
552 | $.fn.collapse.Constructor = Collapse
553 |
554 |
555 | /* COLLAPSIBLE DATA-API
556 | * ==================== */
557 |
558 | $(function () {
559 | $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) {
560 | var $this = $(this), href
561 | , target = $this.attr('data-target')
562 | || e.preventDefault()
563 | || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
564 | , option = $(target).data('collapse') ? 'toggle' : $this.data()
565 | $(target).collapse(option)
566 | })
567 | })
568 |
569 | }(window.jQuery);/* ============================================================
570 | * bootstrap-dropdown.js v2.0.3
571 | * http://twitter.github.com/bootstrap/javascript.html#dropdowns
572 | * ============================================================
573 | * Copyright 2012 Twitter, Inc.
574 | *
575 | * Licensed under the Apache License, Version 2.0 (the "License");
576 | * you may not use this file except in compliance with the License.
577 | * You may obtain a copy of the License at
578 | *
579 | * http://www.apache.org/licenses/LICENSE-2.0
580 | *
581 | * Unless required by applicable law or agreed to in writing, software
582 | * distributed under the License is distributed on an "AS IS" BASIS,
583 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
584 | * See the License for the specific language governing permissions and
585 | * limitations under the License.
586 | * ============================================================ */
587 |
588 |
589 | !function ($) {
590 |
591 | "use strict"; // jshint ;_;
592 |
593 |
594 | /* DROPDOWN CLASS DEFINITION
595 | * ========================= */
596 |
597 | var toggle = '[data-toggle="dropdown"]'
598 | , Dropdown = function (element) {
599 | var $el = $(element).on('click.dropdown.data-api', this.toggle)
600 | $('html').on('click.dropdown.data-api', function () {
601 | $el.parent().removeClass('open')
602 | })
603 | }
604 |
605 | Dropdown.prototype = {
606 |
607 | constructor: Dropdown
608 |
609 | , toggle: function (e) {
610 | var $this = $(this)
611 | , $parent
612 | , selector
613 | , isActive
614 |
615 | if ($this.is('.disabled, :disabled')) return
616 |
617 | selector = $this.attr('data-target')
618 |
619 | if (!selector) {
620 | selector = $this.attr('href')
621 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
622 | }
623 |
624 | $parent = $(selector)
625 | $parent.length || ($parent = $this.parent())
626 |
627 | isActive = $parent.hasClass('open')
628 |
629 | clearMenus()
630 |
631 | if (!isActive) $parent.toggleClass('open')
632 |
633 | return false
634 | }
635 |
636 | }
637 |
638 | function clearMenus() {
639 | $(toggle).parent().removeClass('open')
640 | }
641 |
642 |
643 | /* DROPDOWN PLUGIN DEFINITION
644 | * ========================== */
645 |
646 | $.fn.dropdown = function (option) {
647 | return this.each(function () {
648 | var $this = $(this)
649 | , data = $this.data('dropdown')
650 | if (!data) $this.data('dropdown', (data = new Dropdown(this)))
651 | if (typeof option == 'string') data[option].call($this)
652 | })
653 | }
654 |
655 | $.fn.dropdown.Constructor = Dropdown
656 |
657 |
658 | /* APPLY TO STANDARD DROPDOWN ELEMENTS
659 | * =================================== */
660 |
661 | $(function () {
662 | $('html').on('click.dropdown.data-api', clearMenus)
663 | $('body')
664 | .on('click.dropdown', '.dropdown form', function (e) { e.stopPropagation() })
665 | .on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle)
666 | })
667 |
668 | }(window.jQuery);/* =========================================================
669 | * bootstrap-modal.js v2.0.3
670 | * http://twitter.github.com/bootstrap/javascript.html#modals
671 | * =========================================================
672 | * Copyright 2012 Twitter, Inc.
673 | *
674 | * Licensed under the Apache License, Version 2.0 (the "License");
675 | * you may not use this file except in compliance with the License.
676 | * You may obtain a copy of the License at
677 | *
678 | * http://www.apache.org/licenses/LICENSE-2.0
679 | *
680 | * Unless required by applicable law or agreed to in writing, software
681 | * distributed under the License is distributed on an "AS IS" BASIS,
682 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
683 | * See the License for the specific language governing permissions and
684 | * limitations under the License.
685 | * ========================================================= */
686 |
687 |
688 | !function ($) {
689 |
690 | "use strict"; // jshint ;_;
691 |
692 |
693 | /* MODAL CLASS DEFINITION
694 | * ====================== */
695 |
696 | var Modal = function (content, options) {
697 | this.options = options
698 | this.$element = $(content)
699 | .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
700 | }
701 |
702 | Modal.prototype = {
703 |
704 | constructor: Modal
705 |
706 | , toggle: function () {
707 | return this[!this.isShown ? 'show' : 'hide']()
708 | }
709 |
710 | , show: function () {
711 | var that = this
712 | , e = $.Event('show')
713 |
714 | this.$element.trigger(e)
715 |
716 | if (this.isShown || e.isDefaultPrevented()) return
717 |
718 | $('body').addClass('modal-open')
719 |
720 | this.isShown = true
721 |
722 | escape.call(this)
723 | backdrop.call(this, function () {
724 | var transition = $.support.transition && that.$element.hasClass('fade')
725 |
726 | if (!that.$element.parent().length) {
727 | that.$element.appendTo(document.body) //don't move modals dom position
728 | }
729 |
730 | that.$element
731 | .show()
732 |
733 | if (transition) {
734 | that.$element[0].offsetWidth // force reflow
735 | }
736 |
737 | that.$element.addClass('in')
738 |
739 | transition ?
740 | that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) :
741 | that.$element.trigger('shown')
742 |
743 | })
744 | }
745 |
746 | , hide: function (e) {
747 | e && e.preventDefault()
748 |
749 | var that = this
750 |
751 | e = $.Event('hide')
752 |
753 | this.$element.trigger(e)
754 |
755 | if (!this.isShown || e.isDefaultPrevented()) return
756 |
757 | this.isShown = false
758 |
759 | $('body').removeClass('modal-open')
760 |
761 | escape.call(this)
762 |
763 | this.$element.removeClass('in')
764 |
765 | $.support.transition && this.$element.hasClass('fade') ?
766 | hideWithTransition.call(this) :
767 | hideModal.call(this)
768 | }
769 |
770 | }
771 |
772 |
773 | /* MODAL PRIVATE METHODS
774 | * ===================== */
775 |
776 | function hideWithTransition() {
777 | var that = this
778 | , timeout = setTimeout(function () {
779 | that.$element.off($.support.transition.end)
780 | hideModal.call(that)
781 | }, 500)
782 |
783 | this.$element.one($.support.transition.end, function () {
784 | clearTimeout(timeout)
785 | hideModal.call(that)
786 | })
787 | }
788 |
789 | function hideModal(that) {
790 | this.$element
791 | .hide()
792 | .trigger('hidden')
793 |
794 | backdrop.call(this)
795 | }
796 |
797 | function backdrop(callback) {
798 | var that = this
799 | , animate = this.$element.hasClass('fade') ? 'fade' : ''
800 |
801 | if (this.isShown && this.options.backdrop) {
802 | var doAnimate = $.support.transition && animate
803 |
804 | this.$backdrop = $('')
805 | .appendTo(document.body)
806 |
807 | if (this.options.backdrop != 'static') {
808 | this.$backdrop.click($.proxy(this.hide, this))
809 | }
810 |
811 | if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
812 |
813 | this.$backdrop.addClass('in')
814 |
815 | doAnimate ?
816 | this.$backdrop.one($.support.transition.end, callback) :
817 | callback()
818 |
819 | } else if (!this.isShown && this.$backdrop) {
820 | this.$backdrop.removeClass('in')
821 |
822 | $.support.transition && this.$element.hasClass('fade')?
823 | this.$backdrop.one($.support.transition.end, $.proxy(removeBackdrop, this)) :
824 | removeBackdrop.call(this)
825 |
826 | } else if (callback) {
827 | callback()
828 | }
829 | }
830 |
831 | function removeBackdrop() {
832 | this.$backdrop.remove()
833 | this.$backdrop = null
834 | }
835 |
836 | function escape() {
837 | var that = this
838 | if (this.isShown && this.options.keyboard) {
839 | $(document).on('keyup.dismiss.modal', function ( e ) {
840 | e.which == 27 && that.hide()
841 | })
842 | } else if (!this.isShown) {
843 | $(document).off('keyup.dismiss.modal')
844 | }
845 | }
846 |
847 |
848 | /* MODAL PLUGIN DEFINITION
849 | * ======================= */
850 |
851 | $.fn.modal = function (option) {
852 | return this.each(function () {
853 | var $this = $(this)
854 | , data = $this.data('modal')
855 | , options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
856 | if (!data) $this.data('modal', (data = new Modal(this, options)))
857 | if (typeof option == 'string') data[option]()
858 | else if (options.show) data.show()
859 | })
860 | }
861 |
862 | $.fn.modal.defaults = {
863 | backdrop: true
864 | , keyboard: true
865 | , show: true
866 | }
867 |
868 | $.fn.modal.Constructor = Modal
869 |
870 |
871 | /* MODAL DATA-API
872 | * ============== */
873 |
874 | $(function () {
875 | $('body').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
876 | var $this = $(this), href
877 | , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
878 | , option = $target.data('modal') ? 'toggle' : $.extend({}, $target.data(), $this.data())
879 |
880 | e.preventDefault()
881 | $target.modal(option)
882 | })
883 | })
884 |
885 | }(window.jQuery);/* ===========================================================
886 | * bootstrap-tooltip.js v2.0.3
887 | * http://twitter.github.com/bootstrap/javascript.html#tooltips
888 | * Inspired by the original jQuery.tipsy by Jason Frame
889 | * ===========================================================
890 | * Copyright 2012 Twitter, Inc.
891 | *
892 | * Licensed under the Apache License, Version 2.0 (the "License");
893 | * you may not use this file except in compliance with the License.
894 | * You may obtain a copy of the License at
895 | *
896 | * http://www.apache.org/licenses/LICENSE-2.0
897 | *
898 | * Unless required by applicable law or agreed to in writing, software
899 | * distributed under the License is distributed on an "AS IS" BASIS,
900 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
901 | * See the License for the specific language governing permissions and
902 | * limitations under the License.
903 | * ========================================================== */
904 |
905 |
906 | !function ($) {
907 |
908 | "use strict"; // jshint ;_;
909 |
910 |
911 | /* TOOLTIP PUBLIC CLASS DEFINITION
912 | * =============================== */
913 |
914 | var Tooltip = function (element, options) {
915 | this.init('tooltip', element, options)
916 | }
917 |
918 | Tooltip.prototype = {
919 |
920 | constructor: Tooltip
921 |
922 | , init: function (type, element, options) {
923 | var eventIn
924 | , eventOut
925 |
926 | this.type = type
927 | this.$element = $(element)
928 | this.options = this.getOptions(options)
929 | this.enabled = true
930 |
931 | if (this.options.trigger != 'manual') {
932 | eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
933 | eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur'
934 | this.$element.on(eventIn, this.options.selector, $.proxy(this.enter, this))
935 | this.$element.on(eventOut, this.options.selector, $.proxy(this.leave, this))
936 | }
937 |
938 | this.options.selector ?
939 | (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
940 | this.fixTitle()
941 | }
942 |
943 | , getOptions: function (options) {
944 | options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data())
945 |
946 | if (options.delay && typeof options.delay == 'number') {
947 | options.delay = {
948 | show: options.delay
949 | , hide: options.delay
950 | }
951 | }
952 |
953 | return options
954 | }
955 |
956 | , enter: function (e) {
957 | var self = $(e.currentTarget)[this.type](this._options).data(this.type)
958 |
959 | if (!self.options.delay || !self.options.delay.show) return self.show()
960 |
961 | clearTimeout(this.timeout)
962 | self.hoverState = 'in'
963 | this.timeout = setTimeout(function() {
964 | if (self.hoverState == 'in') self.show()
965 | }, self.options.delay.show)
966 | }
967 |
968 | , leave: function (e) {
969 | var self = $(e.currentTarget)[this.type](this._options).data(this.type)
970 |
971 | if (!self.options.delay || !self.options.delay.hide) return self.hide()
972 |
973 | clearTimeout(this.timeout)
974 | self.hoverState = 'out'
975 | this.timeout = setTimeout(function() {
976 | if (self.hoverState == 'out') self.hide()
977 | }, self.options.delay.hide)
978 | }
979 |
980 | , show: function () {
981 | var $tip
982 | , inside
983 | , pos
984 | , actualWidth
985 | , actualHeight
986 | , placement
987 | , tp
988 |
989 | if (this.hasContent() && this.enabled) {
990 | $tip = this.tip()
991 | this.setContent()
992 |
993 | if (this.options.animation) {
994 | $tip.addClass('fade')
995 | }
996 |
997 | placement = typeof this.options.placement == 'function' ?
998 | this.options.placement.call(this, $tip[0], this.$element[0]) :
999 | this.options.placement
1000 |
1001 | inside = /in/.test(placement)
1002 |
1003 | $tip
1004 | .remove()
1005 | .css({ top: 0, left: 0, display: 'block' })
1006 | .appendTo(inside ? this.$element : document.body)
1007 |
1008 | pos = this.getPosition(inside)
1009 |
1010 | actualWidth = $tip[0].offsetWidth
1011 | actualHeight = $tip[0].offsetHeight
1012 |
1013 | switch (inside ? placement.split(' ')[1] : placement) {
1014 | case 'bottom':
1015 | tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
1016 | break
1017 | case 'top':
1018 | tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
1019 | break
1020 | case 'left':
1021 | tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
1022 | break
1023 | case 'right':
1024 | tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
1025 | break
1026 | }
1027 |
1028 | $tip
1029 | .css(tp)
1030 | .addClass(placement)
1031 | .addClass('in')
1032 | }
1033 | }
1034 |
1035 | , isHTML: function(text) {
1036 | // html string detection logic adapted from jQuery
1037 | return typeof text != 'string'
1038 | || ( text.charAt(0) === "<"
1039 | && text.charAt( text.length - 1 ) === ">"
1040 | && text.length >= 3
1041 | ) || /^(?:[^<]*<[\w\W]+>[^>]*$)/.exec(text)
1042 | }
1043 |
1044 | , setContent: function () {
1045 | var $tip = this.tip()
1046 | , title = this.getTitle()
1047 |
1048 | $tip.find('.tooltip-inner')[this.isHTML(title) ? 'html' : 'text'](title)
1049 | $tip.removeClass('fade in top bottom left right')
1050 | }
1051 |
1052 | , hide: function () {
1053 | var that = this
1054 | , $tip = this.tip()
1055 |
1056 | $tip.removeClass('in')
1057 |
1058 | function removeWithAnimation() {
1059 | var timeout = setTimeout(function () {
1060 | $tip.off($.support.transition.end).remove()
1061 | }, 500)
1062 |
1063 | $tip.one($.support.transition.end, function () {
1064 | clearTimeout(timeout)
1065 | $tip.remove()
1066 | })
1067 | }
1068 |
1069 | $.support.transition && this.$tip.hasClass('fade') ?
1070 | removeWithAnimation() :
1071 | $tip.remove()
1072 | }
1073 |
1074 | , fixTitle: function () {
1075 | var $e = this.$element
1076 | if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
1077 | $e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
1078 | }
1079 | }
1080 |
1081 | , hasContent: function () {
1082 | return this.getTitle()
1083 | }
1084 |
1085 | , getPosition: function (inside) {
1086 | return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), {
1087 | width: this.$element[0].offsetWidth
1088 | , height: this.$element[0].offsetHeight
1089 | })
1090 | }
1091 |
1092 | , getTitle: function () {
1093 | var title
1094 | , $e = this.$element
1095 | , o = this.options
1096 |
1097 | title = $e.attr('data-original-title')
1098 | || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
1099 |
1100 | return title
1101 | }
1102 |
1103 | , tip: function () {
1104 | return this.$tip = this.$tip || $(this.options.template)
1105 | }
1106 |
1107 | , validate: function () {
1108 | if (!this.$element[0].parentNode) {
1109 | this.hide()
1110 | this.$element = null
1111 | this.options = null
1112 | }
1113 | }
1114 |
1115 | , enable: function () {
1116 | this.enabled = true
1117 | }
1118 |
1119 | , disable: function () {
1120 | this.enabled = false
1121 | }
1122 |
1123 | , toggleEnabled: function () {
1124 | this.enabled = !this.enabled
1125 | }
1126 |
1127 | , toggle: function () {
1128 | this[this.tip().hasClass('in') ? 'hide' : 'show']()
1129 | }
1130 |
1131 | }
1132 |
1133 |
1134 | /* TOOLTIP PLUGIN DEFINITION
1135 | * ========================= */
1136 |
1137 | $.fn.tooltip = function ( option ) {
1138 | return this.each(function () {
1139 | var $this = $(this)
1140 | , data = $this.data('tooltip')
1141 | , options = typeof option == 'object' && option
1142 | if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
1143 | if (typeof option == 'string') data[option]()
1144 | })
1145 | }
1146 |
1147 | $.fn.tooltip.Constructor = Tooltip
1148 |
1149 | $.fn.tooltip.defaults = {
1150 | animation: true
1151 | , placement: 'top'
1152 | , selector: false
1153 | , template: '
'
1154 | , trigger: 'hover'
1155 | , title: ''
1156 | , delay: 0
1157 | }
1158 |
1159 | }(window.jQuery);/* ===========================================================
1160 | * bootstrap-popover.js v2.0.3
1161 | * http://twitter.github.com/bootstrap/javascript.html#popovers
1162 | * ===========================================================
1163 | * Copyright 2012 Twitter, Inc.
1164 | *
1165 | * Licensed under the Apache License, Version 2.0 (the "License");
1166 | * you may not use this file except in compliance with the License.
1167 | * You may obtain a copy of the License at
1168 | *
1169 | * http://www.apache.org/licenses/LICENSE-2.0
1170 | *
1171 | * Unless required by applicable law or agreed to in writing, software
1172 | * distributed under the License is distributed on an "AS IS" BASIS,
1173 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1174 | * See the License for the specific language governing permissions and
1175 | * limitations under the License.
1176 | * =========================================================== */
1177 |
1178 |
1179 | !function ($) {
1180 |
1181 | "use strict"; // jshint ;_;
1182 |
1183 |
1184 | /* POPOVER PUBLIC CLASS DEFINITION
1185 | * =============================== */
1186 |
1187 | var Popover = function ( element, options ) {
1188 | this.init('popover', element, options)
1189 | }
1190 |
1191 |
1192 | /* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js
1193 | ========================================== */
1194 |
1195 | Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, {
1196 |
1197 | constructor: Popover
1198 |
1199 | , setContent: function () {
1200 | var $tip = this.tip()
1201 | , title = this.getTitle()
1202 | , content = this.getContent()
1203 |
1204 | $tip.find('.popover-title')[this.isHTML(title) ? 'html' : 'text'](title)
1205 | $tip.find('.popover-content > *')[this.isHTML(content) ? 'html' : 'text'](content)
1206 |
1207 | $tip.removeClass('fade top bottom left right in')
1208 | }
1209 |
1210 | , hasContent: function () {
1211 | return this.getTitle() || this.getContent()
1212 | }
1213 |
1214 | , getContent: function () {
1215 | var content
1216 | , $e = this.$element
1217 | , o = this.options
1218 |
1219 | content = $e.attr('data-content')
1220 | || (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)
1221 |
1222 | return content
1223 | }
1224 |
1225 | , tip: function () {
1226 | if (!this.$tip) {
1227 | this.$tip = $(this.options.template)
1228 | }
1229 | return this.$tip
1230 | }
1231 |
1232 | })
1233 |
1234 |
1235 | /* POPOVER PLUGIN DEFINITION
1236 | * ======================= */
1237 |
1238 | $.fn.popover = function (option) {
1239 | return this.each(function () {
1240 | var $this = $(this)
1241 | , data = $this.data('popover')
1242 | , options = typeof option == 'object' && option
1243 | if (!data) $this.data('popover', (data = new Popover(this, options)))
1244 | if (typeof option == 'string') data[option]()
1245 | })
1246 | }
1247 |
1248 | $.fn.popover.Constructor = Popover
1249 |
1250 | $.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, {
1251 | placement: 'right'
1252 | , content: ''
1253 | , template: '
'
1254 | })
1255 |
1256 | }(window.jQuery);/* =============================================================
1257 | * bootstrap-scrollspy.js v2.0.3
1258 | * http://twitter.github.com/bootstrap/javascript.html#scrollspy
1259 | * =============================================================
1260 | * Copyright 2012 Twitter, Inc.
1261 | *
1262 | * Licensed under the Apache License, Version 2.0 (the "License");
1263 | * you may not use this file except in compliance with the License.
1264 | * You may obtain a copy of the License at
1265 | *
1266 | * http://www.apache.org/licenses/LICENSE-2.0
1267 | *
1268 | * Unless required by applicable law or agreed to in writing, software
1269 | * distributed under the License is distributed on an "AS IS" BASIS,
1270 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1271 | * See the License for the specific language governing permissions and
1272 | * limitations under the License.
1273 | * ============================================================== */
1274 |
1275 |
1276 | !function ($) {
1277 |
1278 | "use strict"; // jshint ;_;
1279 |
1280 |
1281 | /* SCROLLSPY CLASS DEFINITION
1282 | * ========================== */
1283 |
1284 | function ScrollSpy( element, options) {
1285 | var process = $.proxy(this.process, this)
1286 | , $element = $(element).is('body') ? $(window) : $(element)
1287 | , href
1288 | this.options = $.extend({}, $.fn.scrollspy.defaults, options)
1289 | this.$scrollElement = $element.on('scroll.scroll.data-api', process)
1290 | this.selector = (this.options.target
1291 | || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
1292 | || '') + ' .nav li > a'
1293 | this.$body = $('body')
1294 | this.refresh()
1295 | this.process()
1296 | }
1297 |
1298 | ScrollSpy.prototype = {
1299 |
1300 | constructor: ScrollSpy
1301 |
1302 | , refresh: function () {
1303 | var self = this
1304 | , $targets
1305 |
1306 | this.offsets = $([])
1307 | this.targets = $([])
1308 |
1309 | $targets = this.$body
1310 | .find(this.selector)
1311 | .map(function () {
1312 | var $el = $(this)
1313 | , href = $el.data('target') || $el.attr('href')
1314 | , $href = /^#\w/.test(href) && $(href)
1315 | return ( $href
1316 | && href.length
1317 | && [[ $href.position().top, href ]] ) || null
1318 | })
1319 | .sort(function (a, b) { return a[0] - b[0] })
1320 | .each(function () {
1321 | self.offsets.push(this[0])
1322 | self.targets.push(this[1])
1323 | })
1324 | }
1325 |
1326 | , process: function () {
1327 | var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
1328 | , scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
1329 | , maxScroll = scrollHeight - this.$scrollElement.height()
1330 | , offsets = this.offsets
1331 | , targets = this.targets
1332 | , activeTarget = this.activeTarget
1333 | , i
1334 |
1335 | if (scrollTop >= maxScroll) {
1336 | return activeTarget != (i = targets.last()[0])
1337 | && this.activate ( i )
1338 | }
1339 |
1340 | for (i = offsets.length; i--;) {
1341 | activeTarget != targets[i]
1342 | && scrollTop >= offsets[i]
1343 | && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
1344 | && this.activate( targets[i] )
1345 | }
1346 | }
1347 |
1348 | , activate: function (target) {
1349 | var active
1350 | , selector
1351 |
1352 | this.activeTarget = target
1353 |
1354 | $(this.selector)
1355 | .parent('.active')
1356 | .removeClass('active')
1357 |
1358 | selector = this.selector
1359 | + '[data-target="' + target + '"],'
1360 | + this.selector + '[href="' + target + '"]'
1361 |
1362 | active = $(selector)
1363 | .parent('li')
1364 | .addClass('active')
1365 |
1366 | if (active.parent('.dropdown-menu')) {
1367 | active = active.closest('li.dropdown').addClass('active')
1368 | }
1369 |
1370 | active.trigger('activate')
1371 | }
1372 |
1373 | }
1374 |
1375 |
1376 | /* SCROLLSPY PLUGIN DEFINITION
1377 | * =========================== */
1378 |
1379 | $.fn.scrollspy = function ( option ) {
1380 | return this.each(function () {
1381 | var $this = $(this)
1382 | , data = $this.data('scrollspy')
1383 | , options = typeof option == 'object' && option
1384 | if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options)))
1385 | if (typeof option == 'string') data[option]()
1386 | })
1387 | }
1388 |
1389 | $.fn.scrollspy.Constructor = ScrollSpy
1390 |
1391 | $.fn.scrollspy.defaults = {
1392 | offset: 10
1393 | }
1394 |
1395 |
1396 | /* SCROLLSPY DATA-API
1397 | * ================== */
1398 |
1399 | $(function () {
1400 | $('[data-spy="scroll"]').each(function () {
1401 | var $spy = $(this)
1402 | $spy.scrollspy($spy.data())
1403 | })
1404 | })
1405 |
1406 | }(window.jQuery);/* ========================================================
1407 | * bootstrap-tab.js v2.0.3
1408 | * http://twitter.github.com/bootstrap/javascript.html#tabs
1409 | * ========================================================
1410 | * Copyright 2012 Twitter, Inc.
1411 | *
1412 | * Licensed under the Apache License, Version 2.0 (the "License");
1413 | * you may not use this file except in compliance with the License.
1414 | * You may obtain a copy of the License at
1415 | *
1416 | * http://www.apache.org/licenses/LICENSE-2.0
1417 | *
1418 | * Unless required by applicable law or agreed to in writing, software
1419 | * distributed under the License is distributed on an "AS IS" BASIS,
1420 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1421 | * See the License for the specific language governing permissions and
1422 | * limitations under the License.
1423 | * ======================================================== */
1424 |
1425 |
1426 | !function ($) {
1427 |
1428 | "use strict"; // jshint ;_;
1429 |
1430 |
1431 | /* TAB CLASS DEFINITION
1432 | * ==================== */
1433 |
1434 | var Tab = function ( element ) {
1435 | this.element = $(element)
1436 | }
1437 |
1438 | Tab.prototype = {
1439 |
1440 | constructor: Tab
1441 |
1442 | , show: function () {
1443 | var $this = this.element
1444 | , $ul = $this.closest('ul:not(.dropdown-menu)')
1445 | , selector = $this.attr('data-target')
1446 | , previous
1447 | , $target
1448 | , e
1449 |
1450 | if (!selector) {
1451 | selector = $this.attr('href')
1452 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
1453 | }
1454 |
1455 | if ( $this.parent('li').hasClass('active') ) return
1456 |
1457 | previous = $ul.find('.active a').last()[0]
1458 |
1459 | e = $.Event('show', {
1460 | relatedTarget: previous
1461 | })
1462 |
1463 | $this.trigger(e)
1464 |
1465 | if (e.isDefaultPrevented()) return
1466 |
1467 | $target = $(selector)
1468 |
1469 | this.activate($this.parent('li'), $ul)
1470 | this.activate($target, $target.parent(), function () {
1471 | $this.trigger({
1472 | type: 'shown'
1473 | , relatedTarget: previous
1474 | })
1475 | })
1476 | }
1477 |
1478 | , activate: function ( element, container, callback) {
1479 | var $active = container.find('> .active')
1480 | , transition = callback
1481 | && $.support.transition
1482 | && $active.hasClass('fade')
1483 |
1484 | function next() {
1485 | $active
1486 | .removeClass('active')
1487 | .find('> .dropdown-menu > .active')
1488 | .removeClass('active')
1489 |
1490 | element.addClass('active')
1491 |
1492 | if (transition) {
1493 | element[0].offsetWidth // reflow for transition
1494 | element.addClass('in')
1495 | } else {
1496 | element.removeClass('fade')
1497 | }
1498 |
1499 | if ( element.parent('.dropdown-menu') ) {
1500 | element.closest('li.dropdown').addClass('active')
1501 | }
1502 |
1503 | callback && callback()
1504 | }
1505 |
1506 | transition ?
1507 | $active.one($.support.transition.end, next) :
1508 | next()
1509 |
1510 | $active.removeClass('in')
1511 | }
1512 | }
1513 |
1514 |
1515 | /* TAB PLUGIN DEFINITION
1516 | * ===================== */
1517 |
1518 | $.fn.tab = function ( option ) {
1519 | return this.each(function () {
1520 | var $this = $(this)
1521 | , data = $this.data('tab')
1522 | if (!data) $this.data('tab', (data = new Tab(this)))
1523 | if (typeof option == 'string') data[option]()
1524 | })
1525 | }
1526 |
1527 | $.fn.tab.Constructor = Tab
1528 |
1529 |
1530 | /* TAB DATA-API
1531 | * ============ */
1532 |
1533 | $(function () {
1534 | $('body').on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
1535 | e.preventDefault()
1536 | $(this).tab('show')
1537 | })
1538 | })
1539 |
1540 | }(window.jQuery);/* =============================================================
1541 | * bootstrap-typeahead.js v2.0.3
1542 | * http://twitter.github.com/bootstrap/javascript.html#typeahead
1543 | * =============================================================
1544 | * Copyright 2012 Twitter, Inc.
1545 | *
1546 | * Licensed under the Apache License, Version 2.0 (the "License");
1547 | * you may not use this file except in compliance with the License.
1548 | * You may obtain a copy of the License at
1549 | *
1550 | * http://www.apache.org/licenses/LICENSE-2.0
1551 | *
1552 | * Unless required by applicable law or agreed to in writing, software
1553 | * distributed under the License is distributed on an "AS IS" BASIS,
1554 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1555 | * See the License for the specific language governing permissions and
1556 | * limitations under the License.
1557 | * ============================================================ */
1558 |
1559 |
1560 | !function($){
1561 |
1562 | "use strict"; // jshint ;_;
1563 |
1564 |
1565 | /* TYPEAHEAD PUBLIC CLASS DEFINITION
1566 | * ================================= */
1567 |
1568 | var Typeahead = function (element, options) {
1569 | this.$element = $(element)
1570 | this.options = $.extend({}, $.fn.typeahead.defaults, options)
1571 | this.matcher = this.options.matcher || this.matcher
1572 | this.sorter = this.options.sorter || this.sorter
1573 | this.highlighter = this.options.highlighter || this.highlighter
1574 | this.updater = this.options.updater || this.updater
1575 | this.$menu = $(this.options.menu).appendTo('body')
1576 | this.source = this.options.source
1577 | this.shown = false
1578 | this.listen()
1579 | }
1580 |
1581 | Typeahead.prototype = {
1582 |
1583 | constructor: Typeahead
1584 |
1585 | , select: function () {
1586 | var val = this.$menu.find('.active').attr('data-value')
1587 | this.$element
1588 | .val(this.updater(val))
1589 | .change()
1590 | return this.hide()
1591 | }
1592 |
1593 | , updater: function (item) {
1594 | return item
1595 | }
1596 |
1597 | , show: function () {
1598 | var pos = $.extend({}, this.$element.offset(), {
1599 | height: this.$element[0].offsetHeight
1600 | })
1601 |
1602 | this.$menu.css({
1603 | top: pos.top + pos.height
1604 | , left: pos.left
1605 | })
1606 |
1607 | this.$menu.show()
1608 | this.shown = true
1609 | return this
1610 | }
1611 |
1612 | , hide: function () {
1613 | this.$menu.hide()
1614 | this.shown = false
1615 | return this
1616 | }
1617 |
1618 | , lookup: function (event) {
1619 | var that = this
1620 | , items
1621 | , q
1622 |
1623 | this.query = this.$element.val()
1624 |
1625 | if (!this.query) {
1626 | return this.shown ? this.hide() : this
1627 | }
1628 |
1629 | items = $.grep(this.source, function (item) {
1630 | return that.matcher(item)
1631 | })
1632 |
1633 | items = this.sorter(items)
1634 |
1635 | if (!items.length) {
1636 | return this.shown ? this.hide() : this
1637 | }
1638 |
1639 | return this.render(items.slice(0, this.options.items)).show()
1640 | }
1641 |
1642 | , matcher: function (item) {
1643 | return ~item.toLowerCase().indexOf(this.query.toLowerCase())
1644 | }
1645 |
1646 | , sorter: function (items) {
1647 | var beginswith = []
1648 | , caseSensitive = []
1649 | , caseInsensitive = []
1650 | , item
1651 |
1652 | while (item = items.shift()) {
1653 | if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
1654 | else if (~item.indexOf(this.query)) caseSensitive.push(item)
1655 | else caseInsensitive.push(item)
1656 | }
1657 |
1658 | return beginswith.concat(caseSensitive, caseInsensitive)
1659 | }
1660 |
1661 | , highlighter: function (item) {
1662 | var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
1663 | return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
1664 | return '' + match + ''
1665 | })
1666 | }
1667 |
1668 | , render: function (items) {
1669 | var that = this
1670 |
1671 | items = $(items).map(function (i, item) {
1672 | i = $(that.options.item).attr('data-value', item)
1673 | i.find('a').html(that.highlighter(item))
1674 | return i[0]
1675 | })
1676 |
1677 | items.first().addClass('active')
1678 | this.$menu.html(items)
1679 | return this
1680 | }
1681 |
1682 | , next: function (event) {
1683 | var active = this.$menu.find('.active').removeClass('active')
1684 | , next = active.next()
1685 |
1686 | if (!next.length) {
1687 | next = $(this.$menu.find('li')[0])
1688 | }
1689 |
1690 | next.addClass('active')
1691 | }
1692 |
1693 | , prev: function (event) {
1694 | var active = this.$menu.find('.active').removeClass('active')
1695 | , prev = active.prev()
1696 |
1697 | if (!prev.length) {
1698 | prev = this.$menu.find('li').last()
1699 | }
1700 |
1701 | prev.addClass('active')
1702 | }
1703 |
1704 | , listen: function () {
1705 | this.$element
1706 | .on('blur', $.proxy(this.blur, this))
1707 | .on('keypress', $.proxy(this.keypress, this))
1708 | .on('keyup', $.proxy(this.keyup, this))
1709 |
1710 | if ($.browser.webkit || $.browser.msie) {
1711 | this.$element.on('keydown', $.proxy(this.keypress, this))
1712 | }
1713 |
1714 | this.$menu
1715 | .on('click', $.proxy(this.click, this))
1716 | .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
1717 | }
1718 |
1719 | , keyup: function (e) {
1720 | switch(e.keyCode) {
1721 | case 40: // down arrow
1722 | case 38: // up arrow
1723 | break
1724 |
1725 | case 9: // tab
1726 | case 13: // enter
1727 | if (!this.shown) return
1728 | this.select()
1729 | break
1730 |
1731 | case 27: // escape
1732 | if (!this.shown) return
1733 | this.hide()
1734 | break
1735 |
1736 | default:
1737 | this.lookup()
1738 | }
1739 |
1740 | e.stopPropagation()
1741 | e.preventDefault()
1742 | }
1743 |
1744 | , keypress: function (e) {
1745 | if (!this.shown) return
1746 |
1747 | switch(e.keyCode) {
1748 | case 9: // tab
1749 | case 13: // enter
1750 | case 27: // escape
1751 | e.preventDefault()
1752 | break
1753 |
1754 | case 38: // up arrow
1755 | if (e.type != 'keydown') break
1756 | e.preventDefault()
1757 | this.prev()
1758 | break
1759 |
1760 | case 40: // down arrow
1761 | if (e.type != 'keydown') break
1762 | e.preventDefault()
1763 | this.next()
1764 | break
1765 | }
1766 |
1767 | e.stopPropagation()
1768 | }
1769 |
1770 | , blur: function (e) {
1771 | var that = this
1772 | setTimeout(function () { that.hide() }, 150)
1773 | }
1774 |
1775 | , click: function (e) {
1776 | e.stopPropagation()
1777 | e.preventDefault()
1778 | this.select()
1779 | }
1780 |
1781 | , mouseenter: function (e) {
1782 | this.$menu.find('.active').removeClass('active')
1783 | $(e.currentTarget).addClass('active')
1784 | }
1785 |
1786 | }
1787 |
1788 |
1789 | /* TYPEAHEAD PLUGIN DEFINITION
1790 | * =========================== */
1791 |
1792 | $.fn.typeahead = function (option) {
1793 | return this.each(function () {
1794 | var $this = $(this)
1795 | , data = $this.data('typeahead')
1796 | , options = typeof option == 'object' && option
1797 | if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
1798 | if (typeof option == 'string') data[option]()
1799 | })
1800 | }
1801 |
1802 | $.fn.typeahead.defaults = {
1803 | source: []
1804 | , items: 8
1805 | , menu: '