├── main ├── __init__.py ├── migrations │ ├── __init__.py │ └── 0001_initial.py ├── static │ └── main │ │ ├── favicon.ico │ │ ├── favicon.png │ │ ├── images │ │ ├── joy.png │ │ ├── ten.png │ │ ├── hands.png │ │ ├── tongue.png │ │ ├── example1.png │ │ ├── exhibit1.png │ │ ├── exhibit2.png │ │ ├── exhibit2.psd │ │ └── bg_nature.jpg │ │ ├── simplepolllogo.png │ │ ├── simplepolllogo-colors.png │ │ └── startbootstrap │ │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ │ ├── README.md │ │ ├── css │ │ └── one-page-wonder.css │ │ ├── LICENSE │ │ └── js │ │ ├── bootstrap.min.js │ │ └── bootstrap.js ├── templates │ └── main │ │ ├── oauthcallback.html │ │ ├── privacy-policy.html │ │ └── index.html ├── tests.py ├── models.py └── views.py ├── runtime.txt ├── simpleslackpoll ├── __init__.py ├── wsgi.py ├── urls.py └── settings.py ├── .gitignore ├── Procfile ├── requirements.txt ├── manage.py └── README.md /main/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-2.7.9 2 | -------------------------------------------------------------------------------- /simpleslackpoll/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | *.pyc 3 | staticfiles 4 | .env 5 | *.sqlite3 -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: newrelic-admin run-program gunicorn simpleslackpoll.wsgi --log-file - -------------------------------------------------------------------------------- /main/static/main/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wilhelmklopp/simple-poll/HEAD/main/static/main/favicon.ico -------------------------------------------------------------------------------- /main/static/main/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wilhelmklopp/simple-poll/HEAD/main/static/main/favicon.png -------------------------------------------------------------------------------- /main/static/main/images/joy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wilhelmklopp/simple-poll/HEAD/main/static/main/images/joy.png -------------------------------------------------------------------------------- /main/static/main/images/ten.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wilhelmklopp/simple-poll/HEAD/main/static/main/images/ten.png -------------------------------------------------------------------------------- /main/static/main/images/hands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wilhelmklopp/simple-poll/HEAD/main/static/main/images/hands.png -------------------------------------------------------------------------------- /main/static/main/images/tongue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wilhelmklopp/simple-poll/HEAD/main/static/main/images/tongue.png -------------------------------------------------------------------------------- /main/static/main/images/example1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wilhelmklopp/simple-poll/HEAD/main/static/main/images/example1.png -------------------------------------------------------------------------------- /main/static/main/images/exhibit1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wilhelmklopp/simple-poll/HEAD/main/static/main/images/exhibit1.png -------------------------------------------------------------------------------- /main/static/main/images/exhibit2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wilhelmklopp/simple-poll/HEAD/main/static/main/images/exhibit2.png -------------------------------------------------------------------------------- /main/static/main/images/exhibit2.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wilhelmklopp/simple-poll/HEAD/main/static/main/images/exhibit2.psd -------------------------------------------------------------------------------- /main/static/main/simplepolllogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wilhelmklopp/simple-poll/HEAD/main/static/main/simplepolllogo.png -------------------------------------------------------------------------------- /main/static/main/images/bg_nature.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wilhelmklopp/simple-poll/HEAD/main/static/main/images/bg_nature.jpg -------------------------------------------------------------------------------- /main/static/main/simplepolllogo-colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wilhelmklopp/simple-poll/HEAD/main/static/main/simplepolllogo-colors.png -------------------------------------------------------------------------------- /main/templates/main/oauthcallback.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

{{status}}

6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django==1.8.6 2 | gunicorn==19.1.1 3 | wsgiref==0.1.2 4 | requests==2.4.3 5 | dj-database-url==0.3.0 6 | psycopg2==2.5.4 7 | whitenoise==2.0.6 8 | newrelic -------------------------------------------------------------------------------- /main/static/main/startbootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wilhelmklopp/simple-poll/HEAD/main/static/main/startbootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /main/static/main/startbootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wilhelmklopp/simple-poll/HEAD/main/static/main/startbootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /main/static/main/startbootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wilhelmklopp/simple-poll/HEAD/main/static/main/startbootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /main/static/main/startbootstrap/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wilhelmklopp/simple-poll/HEAD/main/static/main/startbootstrap/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /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", "simpleslackpoll.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /main/templates/main/privacy-policy.html: -------------------------------------------------------------------------------- 1 | 2 |

The whole premise of Simple Poll is not to store user data, and to keep most of the User Experience within Slack. The absolute minimum of data is stored for the purpose of interacting with the Slack API. This includes the access token, the team name, the team id, and the time at which the app was added to Slack. For more details check https://github.com/xoneco/simple-poll/.

-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple Poll 2 | 3 | ![No longer maintained](https://img.shields.io/badge/maintained%3F-no-red.svg) 4 | The code here is no longer up to date, but feel free to continue to report issues and track progress. :smile: 5 | 6 | Create native, simple polls in Slack. 7 | 8 | Get it here: [https://simplepoll.rocks](https://simplepoll.rocks) 9 | 10 | ![Demo Gif](https://simplepoll.rocks/static/main/images/demogif.gif) 11 | -------------------------------------------------------------------------------- /main/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | 5 | 6 | class MainViewsTestCase(TestCase): 7 | def test_index(self): 8 | resp = self.client.get("/") 9 | self.assertEqual(resp.status_code, 200) 10 | self.assertTrue("state" in resp.context) 11 | 12 | def test_poll(self): 13 | resp = self.client.get("/poll/") 14 | self.assertEqual(resp.status_code, 400) 15 | 16 | resp = self.client.post("/poll/") 17 | self.assertEqual(resp.status_code, 400) 18 | -------------------------------------------------------------------------------- /simpleslackpoll/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for simpleslackpoll project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "simpleslackpoll.settings") 15 | 16 | application = get_wsgi_application() 17 | 18 | from whitenoise.django import DjangoWhiteNoise 19 | 20 | application = get_wsgi_application() 21 | application = DjangoWhiteNoise(application) 22 | -------------------------------------------------------------------------------- /main/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | 5 | 6 | class Teams(models.Model): 7 | access_token = models.CharField(max_length=1000) 8 | team_name = models.CharField(max_length=1000) 9 | team_id = models.CharField(primary_key=True, max_length=1000) 10 | incoming_webhook_url = models.CharField(max_length=1000) 11 | incoming_webhook_configuration_url = models.CharField(max_length=1000) 12 | last_changed = models.DateTimeField(auto_now = True, auto_now_add = False) 13 | created = models.DateTimeField(auto_now = False, auto_now_add = True, editable=False) 14 | 15 | def __unicode__(self): 16 | return str(self.unique_uuid) 17 | -------------------------------------------------------------------------------- /main/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='Teams', 15 | fields=[ 16 | ('access_token', models.CharField(max_length=1000)), 17 | ('team_name', models.CharField(max_length=1000)), 18 | ('team_id', models.CharField(max_length=1000, serialize=False, primary_key=True)), 19 | ('incoming_webhook_url', models.CharField(max_length=1000)), 20 | ('incoming_webhook_configuration_url', models.CharField(max_length=1000)), 21 | ('last_changed', models.DateTimeField(auto_now=True)), 22 | ('created', models.DateTimeField(auto_now_add=True)), 23 | ], 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /simpleslackpoll/urls.py: -------------------------------------------------------------------------------- 1 | """simpleslackpoll URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.8/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Add an import: from blog import urls as blog_urls 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls)) 15 | """ 16 | from django.conf.urls import include, url 17 | from django.contrib import admin 18 | from main import views 19 | 20 | urlpatterns = [ 21 | url(r'^$', views.index, name="index"), 22 | url(r'^oauthcallback/', views.oauthcallback, name="oauthcallback"), 23 | url(r'^poll/', views.poll, name="poll"), 24 | url(r'^privacy-policy/', views.privacy_policy, name="privacy-policy"), 25 | ] 26 | -------------------------------------------------------------------------------- /main/static/main/startbootstrap/README.md: -------------------------------------------------------------------------------- 1 | # [Start Bootstrap](http://startbootstrap.com/) - [One Page Wonder](http://startbootstrap.com/template-overviews/one-page-wonder/) 2 | 3 | [One Page Wonder](http://startbootstrap.com/template-overviews/one-page-wonder/) is a basic one page template for [Bootstrap](http://getbootstrap.com/) created by [Start Bootstrap](http://startbootstrap.com/). 4 | 5 | ## Getting Started 6 | 7 | To use this template, choose one of the following options to get started: 8 | * Download the latest release on Start Bootstrap 9 | * Fork this repository on GitHub 10 | 11 | ## Bugs and Issues 12 | 13 | Have a bug or an issue with this template? [Open a new issue](https://github.com/IronSummitMedia/startbootstrap-one-page-wonder/issues) here on GitHub or leave a comment on the [template overview page at Start Bootstrap](http://startbootstrap.com/template-overviews/one-page-wonder/). 14 | 15 | ## Creator 16 | 17 | Start Bootstrap was created by and is maintained by **David Miller**, Managing Parter at [Iron Summit Media Strategies](http://www.ironsummitmedia.com/). 18 | 19 | * https://twitter.com/davidmillerskt 20 | * https://github.com/davidtmiller 21 | 22 | Start Bootstrap is based on the [Bootstrap](http://getbootstrap.com/) framework created by [Mark Otto](https://twitter.com/mdo) and [Jacob Thorton](https://twitter.com/fat). 23 | 24 | ## Copyright and License 25 | 26 | Copyright 2013-2015 Iron Summit Media Strategies, LLC. Code released under the [Apache 2.0](https://github.com/IronSummitMedia/startbootstrap-one-page-wonder/blob/gh-pages/LICENSE) license. -------------------------------------------------------------------------------- /simpleslackpoll/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for simpleslackpoll project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.8.6. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.8/ref/settings/ 11 | """ 12 | 13 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 14 | import os 15 | 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = os.environ.get("DJANGO_SECRET_KEY", "") 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = False 27 | 28 | ALLOWED_HOSTS = ["simplepoll.rocks"] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = ( 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'main', 41 | ) 42 | 43 | MIDDLEWARE_CLASSES = ( 44 | 'django.contrib.sessions.middleware.SessionMiddleware', 45 | 'django.middleware.common.CommonMiddleware', 46 | 'django.middleware.csrf.CsrfViewMiddleware', 47 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 48 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 49 | 'django.contrib.messages.middleware.MessageMiddleware', 50 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 51 | 'django.middleware.security.SecurityMiddleware', 52 | ) 53 | 54 | ROOT_URLCONF = 'simpleslackpoll.urls' 55 | 56 | TEMPLATES = [ 57 | { 58 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 59 | 'DIRS': [], 60 | 'APP_DIRS': True, 61 | 'OPTIONS': { 62 | 'context_processors': [ 63 | 'django.template.context_processors.debug', 64 | 'django.template.context_processors.request', 65 | 'django.contrib.auth.context_processors.auth', 66 | 'django.contrib.messages.context_processors.messages', 67 | ], 68 | }, 69 | }, 70 | ] 71 | 72 | WSGI_APPLICATION = 'simpleslackpoll.wsgi.application' 73 | 74 | 75 | # Database 76 | # https://docs.djangoproject.com/en/1.8/ref/settings/#databases 77 | 78 | DATABASES = { 79 | 'default': { 80 | 'ENGINE': 'django.db.backends.sqlite3', 81 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 82 | } 83 | } 84 | 85 | 86 | # Internationalization 87 | # https://docs.djangoproject.com/en/1.8/topics/i18n/ 88 | 89 | LANGUAGE_CODE = 'en-us' 90 | 91 | TIME_ZONE = 'UTC' 92 | 93 | USE_I18N = True 94 | 95 | USE_L10N = True 96 | 97 | USE_TZ = True 98 | 99 | 100 | # Static files (CSS, JavaScript, Images) 101 | # https://docs.djangoproject.com/en/1.8/howto/static-files/ 102 | 103 | STATIC_URL = '/static/' 104 | 105 | STATIC_ROOT = 'staticfiles' 106 | 107 | # Simplified static file serving. 108 | # https://warehouse.python.org/project/whitenoise/ 109 | 110 | STATICFILES_STORAGE = 'whitenoise.django.GzipManifestStaticFilesStorage' 111 | 112 | 113 | # Parse database configuration from $DATABASE_URL 114 | import dj_database_url 115 | DATABASES['default'] = dj_database_url.config() 116 | 117 | # Honor the 'X-Forwarded-Proto' header for request.is_secure() 118 | SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') 119 | -------------------------------------------------------------------------------- /main/static/main/startbootstrap/css/one-page-wonder.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Start Bootstrap - One Page Wonder HTML Template (http://startbootstrap.com) 3 | * Code licensed under the Apache License v2.0. 4 | * For details, see http://www.apache.org/licenses/LICENSE-2.0. 5 | */ 6 | 7 | 8 | *{ 9 | font-family: Roboto; 10 | } 11 | body { 12 | margin-top: 50px; /* Required padding for .navbar-fixed-top. Remove if using .navbar-static-top. Change if height of navigation changes. */ 13 | } 14 | 15 | .explain { 16 | margin-top: 50px; 17 | } 18 | 19 | .logo { 20 | position: relative; 21 | width: 18%; 22 | float: left; 23 | left: 5%; 24 | } 25 | .header-image { 26 | display: block; 27 | width: 100%; 28 | text-align: center; 29 | background: url('../../images/bg_nature.jpg') no-repeat scroll; 30 | -webkit-background-size: cover; 31 | -moz-background-size: cover; 32 | background-size: cover; 33 | -o-background-size: cover; 34 | background-position: 0 75%; 35 | } 36 | 37 | .headline { 38 | padding: 120px 0; 39 | } 40 | 41 | .greeting-text { 42 | padding: 0 0 20px 0; 43 | color: #fff; 44 | } 45 | 46 | .headline h1 { 47 | font-size: 130px; 48 | text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black; 49 | } 50 | 51 | .headline h2 { 52 | font-size: 77px; 53 | } 54 | .headline h6 { 55 | font-size: 20px; 56 | } 57 | 58 | .featurette-divider { 59 | margin: 80px 0; 60 | } 61 | 62 | .featurette { 63 | overflow: hidden; 64 | } 65 | 66 | .featurette-image.pull-left { 67 | margin-right: 40px; 68 | } 69 | 70 | .featurette-image.pull-right { 71 | margin-left: 40px; 72 | } 73 | 74 | .featurette-heading { 75 | font-size: 50px; 76 | } 77 | 78 | footer { 79 | margin: 50px 0; 80 | } 81 | 82 | @media(max-width:1200px) { 83 | .headline h1 { 84 | font-size: 140px; 85 | } 86 | 87 | .headline h2 { 88 | font-size: 63px; 89 | } 90 | 91 | .featurette-divider { 92 | margin: 50px 0; 93 | } 94 | 95 | .featurette-image.pull-left { 96 | margin-right: 20px; 97 | } 98 | 99 | .featurette-image.pull-right { 100 | margin-left: 20px; 101 | } 102 | 103 | .featurette-heading { 104 | font-size: 35px; 105 | } 106 | } 107 | 108 | @media(max-width:991px) { 109 | .headline h1 { 110 | font-size: 105px; 111 | } 112 | 113 | .headline h2 { 114 | font-size: 50px; 115 | } 116 | 117 | .featurette-divider { 118 | margin: 40px 0; 119 | } 120 | 121 | .featurette-image { 122 | max-width: 50%; 123 | } 124 | 125 | .featurette-image.pull-left { 126 | margin-right: 10px; 127 | } 128 | 129 | .featurette-image.pull-right { 130 | margin-left: 10px; 131 | } 132 | 133 | .featurette-heading { 134 | font-size: 30px; 135 | } 136 | } 137 | 138 | @media(max-width:768px) { 139 | .container { 140 | margin: 0 15px; 141 | } 142 | 143 | .featurette-divider { 144 | margin: 40px 0; 145 | } 146 | 147 | .featurette-heading { 148 | font-size: 25px; 149 | } 150 | } 151 | 152 | @media(max-width:668px) { 153 | .headline h1 { 154 | font-size: 70px; 155 | } 156 | 157 | .headline h2 { 158 | font-size: 32px; 159 | } 160 | 161 | .featurette-divider { 162 | margin: 30px 0; 163 | } 164 | } 165 | 166 | @media(max-width:640px) { 167 | .headline { 168 | padding: 75px 0 25px 0; 169 | } 170 | 171 | .headline h1 { 172 | font-size: 60px; 173 | } 174 | 175 | .headline h2 { 176 | font-size: 30px; 177 | } 178 | } 179 | 180 | @media(max-width:375px) { 181 | .featurette-divider { 182 | margin: 10px 0; 183 | } 184 | 185 | .featurette-image { 186 | max-width: 100%; 187 | } 188 | 189 | .featurette-image.pull-left { 190 | margin-right: 0; 191 | margin-bottom: 10px; 192 | } 193 | 194 | .featurette-image.pull-right { 195 | margin-bottom: 10px; 196 | margin-left: 0; 197 | } 198 | } -------------------------------------------------------------------------------- /main/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django.http import HttpResponse, HttpResponseBadRequest 3 | import requests 4 | from main.models import Teams 5 | from django.core.exceptions import ObjectDoesNotExist 6 | from django.utils import timezone 7 | import string 8 | import random 9 | from django.views.decorators.csrf import csrf_exempt 10 | import os 11 | 12 | # Create your views here. 13 | 14 | 15 | def index(request): 16 | state = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(36)) 17 | context = {"state": state} 18 | # TODO: track and verify state in cookie 19 | return render(request, "main/index.html", context) 20 | 21 | 22 | client_id = "16839103392.17540259856" 23 | client_secret = os.environ.get("SLACK_CLIENT_SECRET", "") 24 | 25 | 26 | def oauthcallback(request): 27 | if "error" in request.GET: 28 | status = "Oauth authentication failed. You aborted the Authentication process. Redirecting back to the homepage..." 29 | context = {"status": status} 30 | return render(request, "main/oauthcallback.html", context) 31 | 32 | code = request.GET["code"] 33 | 34 | url = "https://slack.com/api/oauth.access" 35 | data = { 36 | "client_id": client_id, 37 | "client_secret": client_secret, 38 | "code": code, 39 | } 40 | 41 | r = requests.get(url, params=data) 42 | query_result = r.json() 43 | if query_result["ok"]: 44 | access_token = query_result["access_token"] 45 | team_name = query_result["team_name"] 46 | team_id = query_result["team_id"] 47 | 48 | try: 49 | team = Teams.objects.get(team_id=team_id) 50 | except ObjectDoesNotExist: 51 | # new Team (yay!) 52 | new_team = Teams(access_token=access_token, team_name=team_name, team_id=team_id, last_changed=timezone.now()) 53 | new_team.save() 54 | else: 55 | team.access_token = access_token 56 | team.team_name = team_name 57 | team.save() 58 | 59 | else: 60 | status = "Oauth authentication failed. Redirecting back to the homepage..." 61 | context = {"status": status} 62 | return render(request, "main/oauthcallback.html", context) 63 | 64 | status = "Oauth authentication successful! You can now start using /poll. Redirecting back to the homepage..." 65 | context = {"status": status} 66 | return render(request, "main/oauthcallback.html", context) 67 | 68 | 69 | @csrf_exempt 70 | def poll(request): 71 | verifier = os.environ.get("SLACK_POLL_VERIFIER", "") 72 | if request.method != "POST": 73 | return HttpResponseBadRequest("400 Request should be of type POST.") 74 | try: 75 | sent_token = request.POST["token"] 76 | except KeyError: 77 | return HttpResponseBadRequest("400 Request is not signed!") 78 | else: 79 | if verifier != sent_token: 80 | return HttpResponseBadRequest("400 Request is not signed correctly!") 81 | data = request.POST["text"] 82 | 83 | data = data.replace(u'\u201C', '"') 84 | data = data.replace(u'\u201D', '"') 85 | 86 | items = data.split('"') 87 | 88 | question = items[1] 89 | options = [] 90 | for i in range(1, len(items)+1): 91 | if i % 2 == 0 and i > 2: 92 | options.append(items[i-1]) 93 | # all data ready for initial message at this point 94 | 95 | numbers = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "keycap_ten"] 96 | 97 | def sendPollMessage(): 98 | 99 | text = "" 100 | text = "*" + question + "*\n\n" 101 | for option in range(0, len(options)): 102 | toAdd = ":" + numbers[option] + ": " + options[option] + "\n" 103 | text += unicode(toAdd) 104 | 105 | postMessage_url = "https://slack.com/api/chat.postMessage" 106 | postMessage_params = { 107 | "token": Teams.objects.get(team_id=request.POST["team_id"]).access_token, 108 | "text": text, 109 | "channel": request.POST["channel_id"], 110 | "username": "Simple Poll", 111 | "icon_url": "https://simplepoll.rocks/static/main/simplepolllogo-colors.png", 112 | } 113 | text_response = requests.post(postMessage_url, params=postMessage_params) 114 | 115 | return text_response.json()["ts"] # return message timestamp 116 | 117 | class ChannelDoesNotExist(Exception): 118 | def __init__(self, *args, **kwargs): 119 | Exception.__init__(self, *args, **kwargs) 120 | 121 | try: 122 | timestamp = sendPollMessage() 123 | except ChannelDoesNotExist: 124 | return HttpResponse("We cannot add reactions to the channel you posted to. You will have to add your own. Sorry!. :(") 125 | 126 | def addNumberReaction(number): 127 | 128 | reactions_url = "https://slack.com/api/reactions.add" 129 | 130 | reactions_params = { 131 | "token": Teams.objects.get(team_id=request.POST["team_id"]).access_token, 132 | "name": number, 133 | "channel": request.POST["channel_id"], 134 | "timestamp": timestamp 135 | } 136 | 137 | add_reaction = requests.get(reactions_url, params=reactions_params) 138 | 139 | for option in range(0, len(options)): 140 | addNumberReaction(numbers[option]) 141 | 142 | return HttpResponse() # Empty 200 HTTP response, to not display any additional content in Slack 143 | 144 | 145 | def privacy_policy(request): 146 | return render(request, "main/privacy-policy.html") 147 | -------------------------------------------------------------------------------- /main/templates/main/index.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Simple Poll 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 67 | 68 | 69 |
70 |
71 |
72 | 75 |

Simple Poll

76 | Add to Slack 77 |
78 |
79 |
80 | 81 | 82 |
83 | 84 | 85 |
86 |
87 |
88 |

Simple Poll™ is the easiest way to create polls in Slack. It's quick, and more importantly it's native. Everything happens right within Slack. We do not store your poll data anywhere and you do not have to go to our website to see the results.

89 |
90 |
91 |
92 |
93 |

Just type: /poll "Poll question" "Option1" "Option2" "Option3"

94 |

Example: /poll "Which design is best?" "Mike's design" "Anna's design" "My design"

95 |
96 |
97 | 98 | 99 |
100 | 101 |

Simple, native polls right within Slack.

102 |

See that awesome simple poll? You can make your own one just like it by typing:
/poll "Where should we go for lunch?" "Subway" "That burrito place" "The micro kitchen"
And boooom everybody can vote on what place would be best for lunch today!

103 |
104 | 105 |
106 | 107 | 108 |
109 | 110 |

Emoji powered

111 |

Simple Poll is powered by emoji, because who doesn't love emoji?!?
112 | With Simple Poll you can have up to different options!

113 |
114 | 115 |
116 | 117 | 118 |
119 |

Watch the video.

120 | 121 |
122 | 123 |
124 | 125 | 126 | 133 | 134 |
135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /main/static/main/startbootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2013-2015 Iron Summit Media Strategies, LLC 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /main/static/main/startbootstrap/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.6 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under the MIT license 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>2)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 3")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.6",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.6",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.6",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.6",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.6",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.6",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.6",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.6",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); -------------------------------------------------------------------------------- /main/static/main/startbootstrap/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.6 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under the MIT license 5 | */ 6 | 7 | if (typeof jQuery === 'undefined') { 8 | throw new Error('Bootstrap\'s JavaScript requires jQuery') 9 | } 10 | 11 | +function ($) { 12 | 'use strict'; 13 | var version = $.fn.jquery.split(' ')[0].split('.') 14 | if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 2)) { 15 | throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 3') 16 | } 17 | }(jQuery); 18 | 19 | /* ======================================================================== 20 | * Bootstrap: transition.js v3.3.6 21 | * http://getbootstrap.com/javascript/#transitions 22 | * ======================================================================== 23 | * Copyright 2011-2015 Twitter, Inc. 24 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 25 | * ======================================================================== */ 26 | 27 | 28 | +function ($) { 29 | 'use strict'; 30 | 31 | // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) 32 | // ============================================================ 33 | 34 | function transitionEnd() { 35 | var el = document.createElement('bootstrap') 36 | 37 | var transEndEventNames = { 38 | WebkitTransition : 'webkitTransitionEnd', 39 | MozTransition : 'transitionend', 40 | OTransition : 'oTransitionEnd otransitionend', 41 | transition : 'transitionend' 42 | } 43 | 44 | for (var name in transEndEventNames) { 45 | if (el.style[name] !== undefined) { 46 | return { end: transEndEventNames[name] } 47 | } 48 | } 49 | 50 | return false // explicit for ie8 ( ._.) 51 | } 52 | 53 | // http://blog.alexmaccaw.com/css-transitions 54 | $.fn.emulateTransitionEnd = function (duration) { 55 | var called = false 56 | var $el = this 57 | $(this).one('bsTransitionEnd', function () { called = true }) 58 | var callback = function () { if (!called) $($el).trigger($.support.transition.end) } 59 | setTimeout(callback, duration) 60 | return this 61 | } 62 | 63 | $(function () { 64 | $.support.transition = transitionEnd() 65 | 66 | if (!$.support.transition) return 67 | 68 | $.event.special.bsTransitionEnd = { 69 | bindType: $.support.transition.end, 70 | delegateType: $.support.transition.end, 71 | handle: function (e) { 72 | if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) 73 | } 74 | } 75 | }) 76 | 77 | }(jQuery); 78 | 79 | /* ======================================================================== 80 | * Bootstrap: alert.js v3.3.6 81 | * http://getbootstrap.com/javascript/#alerts 82 | * ======================================================================== 83 | * Copyright 2011-2015 Twitter, Inc. 84 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 85 | * ======================================================================== */ 86 | 87 | 88 | +function ($) { 89 | 'use strict'; 90 | 91 | // ALERT CLASS DEFINITION 92 | // ====================== 93 | 94 | var dismiss = '[data-dismiss="alert"]' 95 | var Alert = function (el) { 96 | $(el).on('click', dismiss, this.close) 97 | } 98 | 99 | Alert.VERSION = '3.3.6' 100 | 101 | Alert.TRANSITION_DURATION = 150 102 | 103 | Alert.prototype.close = function (e) { 104 | var $this = $(this) 105 | var selector = $this.attr('data-target') 106 | 107 | if (!selector) { 108 | selector = $this.attr('href') 109 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 110 | } 111 | 112 | var $parent = $(selector) 113 | 114 | if (e) e.preventDefault() 115 | 116 | if (!$parent.length) { 117 | $parent = $this.closest('.alert') 118 | } 119 | 120 | $parent.trigger(e = $.Event('close.bs.alert')) 121 | 122 | if (e.isDefaultPrevented()) return 123 | 124 | $parent.removeClass('in') 125 | 126 | function removeElement() { 127 | // detach from parent, fire event then clean up data 128 | $parent.detach().trigger('closed.bs.alert').remove() 129 | } 130 | 131 | $.support.transition && $parent.hasClass('fade') ? 132 | $parent 133 | .one('bsTransitionEnd', removeElement) 134 | .emulateTransitionEnd(Alert.TRANSITION_DURATION) : 135 | removeElement() 136 | } 137 | 138 | 139 | // ALERT PLUGIN DEFINITION 140 | // ======================= 141 | 142 | function Plugin(option) { 143 | return this.each(function () { 144 | var $this = $(this) 145 | var data = $this.data('bs.alert') 146 | 147 | if (!data) $this.data('bs.alert', (data = new Alert(this))) 148 | if (typeof option == 'string') data[option].call($this) 149 | }) 150 | } 151 | 152 | var old = $.fn.alert 153 | 154 | $.fn.alert = Plugin 155 | $.fn.alert.Constructor = Alert 156 | 157 | 158 | // ALERT NO CONFLICT 159 | // ================= 160 | 161 | $.fn.alert.noConflict = function () { 162 | $.fn.alert = old 163 | return this 164 | } 165 | 166 | 167 | // ALERT DATA-API 168 | // ============== 169 | 170 | $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) 171 | 172 | }(jQuery); 173 | 174 | /* ======================================================================== 175 | * Bootstrap: button.js v3.3.6 176 | * http://getbootstrap.com/javascript/#buttons 177 | * ======================================================================== 178 | * Copyright 2011-2015 Twitter, Inc. 179 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 180 | * ======================================================================== */ 181 | 182 | 183 | +function ($) { 184 | 'use strict'; 185 | 186 | // BUTTON PUBLIC CLASS DEFINITION 187 | // ============================== 188 | 189 | var Button = function (element, options) { 190 | this.$element = $(element) 191 | this.options = $.extend({}, Button.DEFAULTS, options) 192 | this.isLoading = false 193 | } 194 | 195 | Button.VERSION = '3.3.6' 196 | 197 | Button.DEFAULTS = { 198 | loadingText: 'loading...' 199 | } 200 | 201 | Button.prototype.setState = function (state) { 202 | var d = 'disabled' 203 | var $el = this.$element 204 | var val = $el.is('input') ? 'val' : 'html' 205 | var data = $el.data() 206 | 207 | state += 'Text' 208 | 209 | if (data.resetText == null) $el.data('resetText', $el[val]()) 210 | 211 | // push to event loop to allow forms to submit 212 | setTimeout($.proxy(function () { 213 | $el[val](data[state] == null ? this.options[state] : data[state]) 214 | 215 | if (state == 'loadingText') { 216 | this.isLoading = true 217 | $el.addClass(d).attr(d, d) 218 | } else if (this.isLoading) { 219 | this.isLoading = false 220 | $el.removeClass(d).removeAttr(d) 221 | } 222 | }, this), 0) 223 | } 224 | 225 | Button.prototype.toggle = function () { 226 | var changed = true 227 | var $parent = this.$element.closest('[data-toggle="buttons"]') 228 | 229 | if ($parent.length) { 230 | var $input = this.$element.find('input') 231 | if ($input.prop('type') == 'radio') { 232 | if ($input.prop('checked')) changed = false 233 | $parent.find('.active').removeClass('active') 234 | this.$element.addClass('active') 235 | } else if ($input.prop('type') == 'checkbox') { 236 | if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false 237 | this.$element.toggleClass('active') 238 | } 239 | $input.prop('checked', this.$element.hasClass('active')) 240 | if (changed) $input.trigger('change') 241 | } else { 242 | this.$element.attr('aria-pressed', !this.$element.hasClass('active')) 243 | this.$element.toggleClass('active') 244 | } 245 | } 246 | 247 | 248 | // BUTTON PLUGIN DEFINITION 249 | // ======================== 250 | 251 | function Plugin(option) { 252 | return this.each(function () { 253 | var $this = $(this) 254 | var data = $this.data('bs.button') 255 | var options = typeof option == 'object' && option 256 | 257 | if (!data) $this.data('bs.button', (data = new Button(this, options))) 258 | 259 | if (option == 'toggle') data.toggle() 260 | else if (option) data.setState(option) 261 | }) 262 | } 263 | 264 | var old = $.fn.button 265 | 266 | $.fn.button = Plugin 267 | $.fn.button.Constructor = Button 268 | 269 | 270 | // BUTTON NO CONFLICT 271 | // ================== 272 | 273 | $.fn.button.noConflict = function () { 274 | $.fn.button = old 275 | return this 276 | } 277 | 278 | 279 | // BUTTON DATA-API 280 | // =============== 281 | 282 | $(document) 283 | .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { 284 | var $btn = $(e.target) 285 | if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') 286 | Plugin.call($btn, 'toggle') 287 | if (!($(e.target).is('input[type="radio"]') || $(e.target).is('input[type="checkbox"]'))) e.preventDefault() 288 | }) 289 | .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { 290 | $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type)) 291 | }) 292 | 293 | }(jQuery); 294 | 295 | /* ======================================================================== 296 | * Bootstrap: carousel.js v3.3.6 297 | * http://getbootstrap.com/javascript/#carousel 298 | * ======================================================================== 299 | * Copyright 2011-2015 Twitter, Inc. 300 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 301 | * ======================================================================== */ 302 | 303 | 304 | +function ($) { 305 | 'use strict'; 306 | 307 | // CAROUSEL CLASS DEFINITION 308 | // ========================= 309 | 310 | var Carousel = function (element, options) { 311 | this.$element = $(element) 312 | this.$indicators = this.$element.find('.carousel-indicators') 313 | this.options = options 314 | this.paused = null 315 | this.sliding = null 316 | this.interval = null 317 | this.$active = null 318 | this.$items = null 319 | 320 | this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) 321 | 322 | this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element 323 | .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) 324 | .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) 325 | } 326 | 327 | Carousel.VERSION = '3.3.6' 328 | 329 | Carousel.TRANSITION_DURATION = 600 330 | 331 | Carousel.DEFAULTS = { 332 | interval: 5000, 333 | pause: 'hover', 334 | wrap: true, 335 | keyboard: true 336 | } 337 | 338 | Carousel.prototype.keydown = function (e) { 339 | if (/input|textarea/i.test(e.target.tagName)) return 340 | switch (e.which) { 341 | case 37: this.prev(); break 342 | case 39: this.next(); break 343 | default: return 344 | } 345 | 346 | e.preventDefault() 347 | } 348 | 349 | Carousel.prototype.cycle = function (e) { 350 | e || (this.paused = false) 351 | 352 | this.interval && clearInterval(this.interval) 353 | 354 | this.options.interval 355 | && !this.paused 356 | && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) 357 | 358 | return this 359 | } 360 | 361 | Carousel.prototype.getItemIndex = function (item) { 362 | this.$items = item.parent().children('.item') 363 | return this.$items.index(item || this.$active) 364 | } 365 | 366 | Carousel.prototype.getItemForDirection = function (direction, active) { 367 | var activeIndex = this.getItemIndex(active) 368 | var willWrap = (direction == 'prev' && activeIndex === 0) 369 | || (direction == 'next' && activeIndex == (this.$items.length - 1)) 370 | if (willWrap && !this.options.wrap) return active 371 | var delta = direction == 'prev' ? -1 : 1 372 | var itemIndex = (activeIndex + delta) % this.$items.length 373 | return this.$items.eq(itemIndex) 374 | } 375 | 376 | Carousel.prototype.to = function (pos) { 377 | var that = this 378 | var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) 379 | 380 | if (pos > (this.$items.length - 1) || pos < 0) return 381 | 382 | if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" 383 | if (activeIndex == pos) return this.pause().cycle() 384 | 385 | return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) 386 | } 387 | 388 | Carousel.prototype.pause = function (e) { 389 | e || (this.paused = true) 390 | 391 | if (this.$element.find('.next, .prev').length && $.support.transition) { 392 | this.$element.trigger($.support.transition.end) 393 | this.cycle(true) 394 | } 395 | 396 | this.interval = clearInterval(this.interval) 397 | 398 | return this 399 | } 400 | 401 | Carousel.prototype.next = function () { 402 | if (this.sliding) return 403 | return this.slide('next') 404 | } 405 | 406 | Carousel.prototype.prev = function () { 407 | if (this.sliding) return 408 | return this.slide('prev') 409 | } 410 | 411 | Carousel.prototype.slide = function (type, next) { 412 | var $active = this.$element.find('.item.active') 413 | var $next = next || this.getItemForDirection(type, $active) 414 | var isCycling = this.interval 415 | var direction = type == 'next' ? 'left' : 'right' 416 | var that = this 417 | 418 | if ($next.hasClass('active')) return (this.sliding = false) 419 | 420 | var relatedTarget = $next[0] 421 | var slideEvent = $.Event('slide.bs.carousel', { 422 | relatedTarget: relatedTarget, 423 | direction: direction 424 | }) 425 | this.$element.trigger(slideEvent) 426 | if (slideEvent.isDefaultPrevented()) return 427 | 428 | this.sliding = true 429 | 430 | isCycling && this.pause() 431 | 432 | if (this.$indicators.length) { 433 | this.$indicators.find('.active').removeClass('active') 434 | var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) 435 | $nextIndicator && $nextIndicator.addClass('active') 436 | } 437 | 438 | var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" 439 | if ($.support.transition && this.$element.hasClass('slide')) { 440 | $next.addClass(type) 441 | $next[0].offsetWidth // force reflow 442 | $active.addClass(direction) 443 | $next.addClass(direction) 444 | $active 445 | .one('bsTransitionEnd', function () { 446 | $next.removeClass([type, direction].join(' ')).addClass('active') 447 | $active.removeClass(['active', direction].join(' ')) 448 | that.sliding = false 449 | setTimeout(function () { 450 | that.$element.trigger(slidEvent) 451 | }, 0) 452 | }) 453 | .emulateTransitionEnd(Carousel.TRANSITION_DURATION) 454 | } else { 455 | $active.removeClass('active') 456 | $next.addClass('active') 457 | this.sliding = false 458 | this.$element.trigger(slidEvent) 459 | } 460 | 461 | isCycling && this.cycle() 462 | 463 | return this 464 | } 465 | 466 | 467 | // CAROUSEL PLUGIN DEFINITION 468 | // ========================== 469 | 470 | function Plugin(option) { 471 | return this.each(function () { 472 | var $this = $(this) 473 | var data = $this.data('bs.carousel') 474 | var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) 475 | var action = typeof option == 'string' ? option : options.slide 476 | 477 | if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) 478 | if (typeof option == 'number') data.to(option) 479 | else if (action) data[action]() 480 | else if (options.interval) data.pause().cycle() 481 | }) 482 | } 483 | 484 | var old = $.fn.carousel 485 | 486 | $.fn.carousel = Plugin 487 | $.fn.carousel.Constructor = Carousel 488 | 489 | 490 | // CAROUSEL NO CONFLICT 491 | // ==================== 492 | 493 | $.fn.carousel.noConflict = function () { 494 | $.fn.carousel = old 495 | return this 496 | } 497 | 498 | 499 | // CAROUSEL DATA-API 500 | // ================= 501 | 502 | var clickHandler = function (e) { 503 | var href 504 | var $this = $(this) 505 | var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 506 | if (!$target.hasClass('carousel')) return 507 | var options = $.extend({}, $target.data(), $this.data()) 508 | var slideIndex = $this.attr('data-slide-to') 509 | if (slideIndex) options.interval = false 510 | 511 | Plugin.call($target, options) 512 | 513 | if (slideIndex) { 514 | $target.data('bs.carousel').to(slideIndex) 515 | } 516 | 517 | e.preventDefault() 518 | } 519 | 520 | $(document) 521 | .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) 522 | .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) 523 | 524 | $(window).on('load', function () { 525 | $('[data-ride="carousel"]').each(function () { 526 | var $carousel = $(this) 527 | Plugin.call($carousel, $carousel.data()) 528 | }) 529 | }) 530 | 531 | }(jQuery); 532 | 533 | /* ======================================================================== 534 | * Bootstrap: collapse.js v3.3.6 535 | * http://getbootstrap.com/javascript/#collapse 536 | * ======================================================================== 537 | * Copyright 2011-2015 Twitter, Inc. 538 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 539 | * ======================================================================== */ 540 | 541 | 542 | +function ($) { 543 | 'use strict'; 544 | 545 | // COLLAPSE PUBLIC CLASS DEFINITION 546 | // ================================ 547 | 548 | var Collapse = function (element, options) { 549 | this.$element = $(element) 550 | this.options = $.extend({}, Collapse.DEFAULTS, options) 551 | this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' + 552 | '[data-toggle="collapse"][data-target="#' + element.id + '"]') 553 | this.transitioning = null 554 | 555 | if (this.options.parent) { 556 | this.$parent = this.getParent() 557 | } else { 558 | this.addAriaAndCollapsedClass(this.$element, this.$trigger) 559 | } 560 | 561 | if (this.options.toggle) this.toggle() 562 | } 563 | 564 | Collapse.VERSION = '3.3.6' 565 | 566 | Collapse.TRANSITION_DURATION = 350 567 | 568 | Collapse.DEFAULTS = { 569 | toggle: true 570 | } 571 | 572 | Collapse.prototype.dimension = function () { 573 | var hasWidth = this.$element.hasClass('width') 574 | return hasWidth ? 'width' : 'height' 575 | } 576 | 577 | Collapse.prototype.show = function () { 578 | if (this.transitioning || this.$element.hasClass('in')) return 579 | 580 | var activesData 581 | var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing') 582 | 583 | if (actives && actives.length) { 584 | activesData = actives.data('bs.collapse') 585 | if (activesData && activesData.transitioning) return 586 | } 587 | 588 | var startEvent = $.Event('show.bs.collapse') 589 | this.$element.trigger(startEvent) 590 | if (startEvent.isDefaultPrevented()) return 591 | 592 | if (actives && actives.length) { 593 | Plugin.call(actives, 'hide') 594 | activesData || actives.data('bs.collapse', null) 595 | } 596 | 597 | var dimension = this.dimension() 598 | 599 | this.$element 600 | .removeClass('collapse') 601 | .addClass('collapsing')[dimension](0) 602 | .attr('aria-expanded', true) 603 | 604 | this.$trigger 605 | .removeClass('collapsed') 606 | .attr('aria-expanded', true) 607 | 608 | this.transitioning = 1 609 | 610 | var complete = function () { 611 | this.$element 612 | .removeClass('collapsing') 613 | .addClass('collapse in')[dimension]('') 614 | this.transitioning = 0 615 | this.$element 616 | .trigger('shown.bs.collapse') 617 | } 618 | 619 | if (!$.support.transition) return complete.call(this) 620 | 621 | var scrollSize = $.camelCase(['scroll', dimension].join('-')) 622 | 623 | this.$element 624 | .one('bsTransitionEnd', $.proxy(complete, this)) 625 | .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) 626 | } 627 | 628 | Collapse.prototype.hide = function () { 629 | if (this.transitioning || !this.$element.hasClass('in')) return 630 | 631 | var startEvent = $.Event('hide.bs.collapse') 632 | this.$element.trigger(startEvent) 633 | if (startEvent.isDefaultPrevented()) return 634 | 635 | var dimension = this.dimension() 636 | 637 | this.$element[dimension](this.$element[dimension]())[0].offsetHeight 638 | 639 | this.$element 640 | .addClass('collapsing') 641 | .removeClass('collapse in') 642 | .attr('aria-expanded', false) 643 | 644 | this.$trigger 645 | .addClass('collapsed') 646 | .attr('aria-expanded', false) 647 | 648 | this.transitioning = 1 649 | 650 | var complete = function () { 651 | this.transitioning = 0 652 | this.$element 653 | .removeClass('collapsing') 654 | .addClass('collapse') 655 | .trigger('hidden.bs.collapse') 656 | } 657 | 658 | if (!$.support.transition) return complete.call(this) 659 | 660 | this.$element 661 | [dimension](0) 662 | .one('bsTransitionEnd', $.proxy(complete, this)) 663 | .emulateTransitionEnd(Collapse.TRANSITION_DURATION) 664 | } 665 | 666 | Collapse.prototype.toggle = function () { 667 | this[this.$element.hasClass('in') ? 'hide' : 'show']() 668 | } 669 | 670 | Collapse.prototype.getParent = function () { 671 | return $(this.options.parent) 672 | .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') 673 | .each($.proxy(function (i, element) { 674 | var $element = $(element) 675 | this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) 676 | }, this)) 677 | .end() 678 | } 679 | 680 | Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { 681 | var isOpen = $element.hasClass('in') 682 | 683 | $element.attr('aria-expanded', isOpen) 684 | $trigger 685 | .toggleClass('collapsed', !isOpen) 686 | .attr('aria-expanded', isOpen) 687 | } 688 | 689 | function getTargetFromTrigger($trigger) { 690 | var href 691 | var target = $trigger.attr('data-target') 692 | || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 693 | 694 | return $(target) 695 | } 696 | 697 | 698 | // COLLAPSE PLUGIN DEFINITION 699 | // ========================== 700 | 701 | function Plugin(option) { 702 | return this.each(function () { 703 | var $this = $(this) 704 | var data = $this.data('bs.collapse') 705 | var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) 706 | 707 | if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false 708 | if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) 709 | if (typeof option == 'string') data[option]() 710 | }) 711 | } 712 | 713 | var old = $.fn.collapse 714 | 715 | $.fn.collapse = Plugin 716 | $.fn.collapse.Constructor = Collapse 717 | 718 | 719 | // COLLAPSE NO CONFLICT 720 | // ==================== 721 | 722 | $.fn.collapse.noConflict = function () { 723 | $.fn.collapse = old 724 | return this 725 | } 726 | 727 | 728 | // COLLAPSE DATA-API 729 | // ================= 730 | 731 | $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { 732 | var $this = $(this) 733 | 734 | if (!$this.attr('data-target')) e.preventDefault() 735 | 736 | var $target = getTargetFromTrigger($this) 737 | var data = $target.data('bs.collapse') 738 | var option = data ? 'toggle' : $this.data() 739 | 740 | Plugin.call($target, option) 741 | }) 742 | 743 | }(jQuery); 744 | 745 | /* ======================================================================== 746 | * Bootstrap: dropdown.js v3.3.6 747 | * http://getbootstrap.com/javascript/#dropdowns 748 | * ======================================================================== 749 | * Copyright 2011-2015 Twitter, Inc. 750 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 751 | * ======================================================================== */ 752 | 753 | 754 | +function ($) { 755 | 'use strict'; 756 | 757 | // DROPDOWN CLASS DEFINITION 758 | // ========================= 759 | 760 | var backdrop = '.dropdown-backdrop' 761 | var toggle = '[data-toggle="dropdown"]' 762 | var Dropdown = function (element) { 763 | $(element).on('click.bs.dropdown', this.toggle) 764 | } 765 | 766 | Dropdown.VERSION = '3.3.6' 767 | 768 | function getParent($this) { 769 | var selector = $this.attr('data-target') 770 | 771 | if (!selector) { 772 | selector = $this.attr('href') 773 | selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 774 | } 775 | 776 | var $parent = selector && $(selector) 777 | 778 | return $parent && $parent.length ? $parent : $this.parent() 779 | } 780 | 781 | function clearMenus(e) { 782 | if (e && e.which === 3) return 783 | $(backdrop).remove() 784 | $(toggle).each(function () { 785 | var $this = $(this) 786 | var $parent = getParent($this) 787 | var relatedTarget = { relatedTarget: this } 788 | 789 | if (!$parent.hasClass('open')) return 790 | 791 | if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return 792 | 793 | $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)) 794 | 795 | if (e.isDefaultPrevented()) return 796 | 797 | $this.attr('aria-expanded', 'false') 798 | $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget)) 799 | }) 800 | } 801 | 802 | Dropdown.prototype.toggle = function (e) { 803 | var $this = $(this) 804 | 805 | if ($this.is('.disabled, :disabled')) return 806 | 807 | var $parent = getParent($this) 808 | var isActive = $parent.hasClass('open') 809 | 810 | clearMenus() 811 | 812 | if (!isActive) { 813 | if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { 814 | // if mobile we use a backdrop because click events don't delegate 815 | $(document.createElement('div')) 816 | .addClass('dropdown-backdrop') 817 | .insertAfter($(this)) 818 | .on('click', clearMenus) 819 | } 820 | 821 | var relatedTarget = { relatedTarget: this } 822 | $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget)) 823 | 824 | if (e.isDefaultPrevented()) return 825 | 826 | $this 827 | .trigger('focus') 828 | .attr('aria-expanded', 'true') 829 | 830 | $parent 831 | .toggleClass('open') 832 | .trigger($.Event('shown.bs.dropdown', relatedTarget)) 833 | } 834 | 835 | return false 836 | } 837 | 838 | Dropdown.prototype.keydown = function (e) { 839 | if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return 840 | 841 | var $this = $(this) 842 | 843 | e.preventDefault() 844 | e.stopPropagation() 845 | 846 | if ($this.is('.disabled, :disabled')) return 847 | 848 | var $parent = getParent($this) 849 | var isActive = $parent.hasClass('open') 850 | 851 | if (!isActive && e.which != 27 || isActive && e.which == 27) { 852 | if (e.which == 27) $parent.find(toggle).trigger('focus') 853 | return $this.trigger('click') 854 | } 855 | 856 | var desc = ' li:not(.disabled):visible a' 857 | var $items = $parent.find('.dropdown-menu' + desc) 858 | 859 | if (!$items.length) return 860 | 861 | var index = $items.index(e.target) 862 | 863 | if (e.which == 38 && index > 0) index-- // up 864 | if (e.which == 40 && index < $items.length - 1) index++ // down 865 | if (!~index) index = 0 866 | 867 | $items.eq(index).trigger('focus') 868 | } 869 | 870 | 871 | // DROPDOWN PLUGIN DEFINITION 872 | // ========================== 873 | 874 | function Plugin(option) { 875 | return this.each(function () { 876 | var $this = $(this) 877 | var data = $this.data('bs.dropdown') 878 | 879 | if (!data) $this.data('bs.dropdown', (data = new Dropdown(this))) 880 | if (typeof option == 'string') data[option].call($this) 881 | }) 882 | } 883 | 884 | var old = $.fn.dropdown 885 | 886 | $.fn.dropdown = Plugin 887 | $.fn.dropdown.Constructor = Dropdown 888 | 889 | 890 | // DROPDOWN NO CONFLICT 891 | // ==================== 892 | 893 | $.fn.dropdown.noConflict = function () { 894 | $.fn.dropdown = old 895 | return this 896 | } 897 | 898 | 899 | // APPLY TO STANDARD DROPDOWN ELEMENTS 900 | // =================================== 901 | 902 | $(document) 903 | .on('click.bs.dropdown.data-api', clearMenus) 904 | .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) 905 | .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle) 906 | .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown) 907 | .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown) 908 | 909 | }(jQuery); 910 | 911 | /* ======================================================================== 912 | * Bootstrap: modal.js v3.3.6 913 | * http://getbootstrap.com/javascript/#modals 914 | * ======================================================================== 915 | * Copyright 2011-2015 Twitter, Inc. 916 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 917 | * ======================================================================== */ 918 | 919 | 920 | +function ($) { 921 | 'use strict'; 922 | 923 | // MODAL CLASS DEFINITION 924 | // ====================== 925 | 926 | var Modal = function (element, options) { 927 | this.options = options 928 | this.$body = $(document.body) 929 | this.$element = $(element) 930 | this.$dialog = this.$element.find('.modal-dialog') 931 | this.$backdrop = null 932 | this.isShown = null 933 | this.originalBodyPad = null 934 | this.scrollbarWidth = 0 935 | this.ignoreBackdropClick = false 936 | 937 | if (this.options.remote) { 938 | this.$element 939 | .find('.modal-content') 940 | .load(this.options.remote, $.proxy(function () { 941 | this.$element.trigger('loaded.bs.modal') 942 | }, this)) 943 | } 944 | } 945 | 946 | Modal.VERSION = '3.3.6' 947 | 948 | Modal.TRANSITION_DURATION = 300 949 | Modal.BACKDROP_TRANSITION_DURATION = 150 950 | 951 | Modal.DEFAULTS = { 952 | backdrop: true, 953 | keyboard: true, 954 | show: true 955 | } 956 | 957 | Modal.prototype.toggle = function (_relatedTarget) { 958 | return this.isShown ? this.hide() : this.show(_relatedTarget) 959 | } 960 | 961 | Modal.prototype.show = function (_relatedTarget) { 962 | var that = this 963 | var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget }) 964 | 965 | this.$element.trigger(e) 966 | 967 | if (this.isShown || e.isDefaultPrevented()) return 968 | 969 | this.isShown = true 970 | 971 | this.checkScrollbar() 972 | this.setScrollbar() 973 | this.$body.addClass('modal-open') 974 | 975 | this.escape() 976 | this.resize() 977 | 978 | this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this)) 979 | 980 | this.$dialog.on('mousedown.dismiss.bs.modal', function () { 981 | that.$element.one('mouseup.dismiss.bs.modal', function (e) { 982 | if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true 983 | }) 984 | }) 985 | 986 | this.backdrop(function () { 987 | var transition = $.support.transition && that.$element.hasClass('fade') 988 | 989 | if (!that.$element.parent().length) { 990 | that.$element.appendTo(that.$body) // don't move modals dom position 991 | } 992 | 993 | that.$element 994 | .show() 995 | .scrollTop(0) 996 | 997 | that.adjustDialog() 998 | 999 | if (transition) { 1000 | that.$element[0].offsetWidth // force reflow 1001 | } 1002 | 1003 | that.$element.addClass('in') 1004 | 1005 | that.enforceFocus() 1006 | 1007 | var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget }) 1008 | 1009 | transition ? 1010 | that.$dialog // wait for modal to slide in 1011 | .one('bsTransitionEnd', function () { 1012 | that.$element.trigger('focus').trigger(e) 1013 | }) 1014 | .emulateTransitionEnd(Modal.TRANSITION_DURATION) : 1015 | that.$element.trigger('focus').trigger(e) 1016 | }) 1017 | } 1018 | 1019 | Modal.prototype.hide = function (e) { 1020 | if (e) e.preventDefault() 1021 | 1022 | e = $.Event('hide.bs.modal') 1023 | 1024 | this.$element.trigger(e) 1025 | 1026 | if (!this.isShown || e.isDefaultPrevented()) return 1027 | 1028 | this.isShown = false 1029 | 1030 | this.escape() 1031 | this.resize() 1032 | 1033 | $(document).off('focusin.bs.modal') 1034 | 1035 | this.$element 1036 | .removeClass('in') 1037 | .off('click.dismiss.bs.modal') 1038 | .off('mouseup.dismiss.bs.modal') 1039 | 1040 | this.$dialog.off('mousedown.dismiss.bs.modal') 1041 | 1042 | $.support.transition && this.$element.hasClass('fade') ? 1043 | this.$element 1044 | .one('bsTransitionEnd', $.proxy(this.hideModal, this)) 1045 | .emulateTransitionEnd(Modal.TRANSITION_DURATION) : 1046 | this.hideModal() 1047 | } 1048 | 1049 | Modal.prototype.enforceFocus = function () { 1050 | $(document) 1051 | .off('focusin.bs.modal') // guard against infinite focus loop 1052 | .on('focusin.bs.modal', $.proxy(function (e) { 1053 | if (this.$element[0] !== e.target && !this.$element.has(e.target).length) { 1054 | this.$element.trigger('focus') 1055 | } 1056 | }, this)) 1057 | } 1058 | 1059 | Modal.prototype.escape = function () { 1060 | if (this.isShown && this.options.keyboard) { 1061 | this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) { 1062 | e.which == 27 && this.hide() 1063 | }, this)) 1064 | } else if (!this.isShown) { 1065 | this.$element.off('keydown.dismiss.bs.modal') 1066 | } 1067 | } 1068 | 1069 | Modal.prototype.resize = function () { 1070 | if (this.isShown) { 1071 | $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this)) 1072 | } else { 1073 | $(window).off('resize.bs.modal') 1074 | } 1075 | } 1076 | 1077 | Modal.prototype.hideModal = function () { 1078 | var that = this 1079 | this.$element.hide() 1080 | this.backdrop(function () { 1081 | that.$body.removeClass('modal-open') 1082 | that.resetAdjustments() 1083 | that.resetScrollbar() 1084 | that.$element.trigger('hidden.bs.modal') 1085 | }) 1086 | } 1087 | 1088 | Modal.prototype.removeBackdrop = function () { 1089 | this.$backdrop && this.$backdrop.remove() 1090 | this.$backdrop = null 1091 | } 1092 | 1093 | Modal.prototype.backdrop = function (callback) { 1094 | var that = this 1095 | var animate = this.$element.hasClass('fade') ? 'fade' : '' 1096 | 1097 | if (this.isShown && this.options.backdrop) { 1098 | var doAnimate = $.support.transition && animate 1099 | 1100 | this.$backdrop = $(document.createElement('div')) 1101 | .addClass('modal-backdrop ' + animate) 1102 | .appendTo(this.$body) 1103 | 1104 | this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) { 1105 | if (this.ignoreBackdropClick) { 1106 | this.ignoreBackdropClick = false 1107 | return 1108 | } 1109 | if (e.target !== e.currentTarget) return 1110 | this.options.backdrop == 'static' 1111 | ? this.$element[0].focus() 1112 | : this.hide() 1113 | }, this)) 1114 | 1115 | if (doAnimate) this.$backdrop[0].offsetWidth // force reflow 1116 | 1117 | this.$backdrop.addClass('in') 1118 | 1119 | if (!callback) return 1120 | 1121 | doAnimate ? 1122 | this.$backdrop 1123 | .one('bsTransitionEnd', callback) 1124 | .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : 1125 | callback() 1126 | 1127 | } else if (!this.isShown && this.$backdrop) { 1128 | this.$backdrop.removeClass('in') 1129 | 1130 | var callbackRemove = function () { 1131 | that.removeBackdrop() 1132 | callback && callback() 1133 | } 1134 | $.support.transition && this.$element.hasClass('fade') ? 1135 | this.$backdrop 1136 | .one('bsTransitionEnd', callbackRemove) 1137 | .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : 1138 | callbackRemove() 1139 | 1140 | } else if (callback) { 1141 | callback() 1142 | } 1143 | } 1144 | 1145 | // these following methods are used to handle overflowing modals 1146 | 1147 | Modal.prototype.handleUpdate = function () { 1148 | this.adjustDialog() 1149 | } 1150 | 1151 | Modal.prototype.adjustDialog = function () { 1152 | var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight 1153 | 1154 | this.$element.css({ 1155 | paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '', 1156 | paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : '' 1157 | }) 1158 | } 1159 | 1160 | Modal.prototype.resetAdjustments = function () { 1161 | this.$element.css({ 1162 | paddingLeft: '', 1163 | paddingRight: '' 1164 | }) 1165 | } 1166 | 1167 | Modal.prototype.checkScrollbar = function () { 1168 | var fullWindowWidth = window.innerWidth 1169 | if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8 1170 | var documentElementRect = document.documentElement.getBoundingClientRect() 1171 | fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left) 1172 | } 1173 | this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth 1174 | this.scrollbarWidth = this.measureScrollbar() 1175 | } 1176 | 1177 | Modal.prototype.setScrollbar = function () { 1178 | var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10) 1179 | this.originalBodyPad = document.body.style.paddingRight || '' 1180 | if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth) 1181 | } 1182 | 1183 | Modal.prototype.resetScrollbar = function () { 1184 | this.$body.css('padding-right', this.originalBodyPad) 1185 | } 1186 | 1187 | Modal.prototype.measureScrollbar = function () { // thx walsh 1188 | var scrollDiv = document.createElement('div') 1189 | scrollDiv.className = 'modal-scrollbar-measure' 1190 | this.$body.append(scrollDiv) 1191 | var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth 1192 | this.$body[0].removeChild(scrollDiv) 1193 | return scrollbarWidth 1194 | } 1195 | 1196 | 1197 | // MODAL PLUGIN DEFINITION 1198 | // ======================= 1199 | 1200 | function Plugin(option, _relatedTarget) { 1201 | return this.each(function () { 1202 | var $this = $(this) 1203 | var data = $this.data('bs.modal') 1204 | var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option) 1205 | 1206 | if (!data) $this.data('bs.modal', (data = new Modal(this, options))) 1207 | if (typeof option == 'string') data[option](_relatedTarget) 1208 | else if (options.show) data.show(_relatedTarget) 1209 | }) 1210 | } 1211 | 1212 | var old = $.fn.modal 1213 | 1214 | $.fn.modal = Plugin 1215 | $.fn.modal.Constructor = Modal 1216 | 1217 | 1218 | // MODAL NO CONFLICT 1219 | // ================= 1220 | 1221 | $.fn.modal.noConflict = function () { 1222 | $.fn.modal = old 1223 | return this 1224 | } 1225 | 1226 | 1227 | // MODAL DATA-API 1228 | // ============== 1229 | 1230 | $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) { 1231 | var $this = $(this) 1232 | var href = $this.attr('href') 1233 | var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7 1234 | var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data()) 1235 | 1236 | if ($this.is('a')) e.preventDefault() 1237 | 1238 | $target.one('show.bs.modal', function (showEvent) { 1239 | if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown 1240 | $target.one('hidden.bs.modal', function () { 1241 | $this.is(':visible') && $this.trigger('focus') 1242 | }) 1243 | }) 1244 | Plugin.call($target, option, this) 1245 | }) 1246 | 1247 | }(jQuery); 1248 | 1249 | /* ======================================================================== 1250 | * Bootstrap: tooltip.js v3.3.6 1251 | * http://getbootstrap.com/javascript/#tooltip 1252 | * Inspired by the original jQuery.tipsy by Jason Frame 1253 | * ======================================================================== 1254 | * Copyright 2011-2015 Twitter, Inc. 1255 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 1256 | * ======================================================================== */ 1257 | 1258 | 1259 | +function ($) { 1260 | 'use strict'; 1261 | 1262 | // TOOLTIP PUBLIC CLASS DEFINITION 1263 | // =============================== 1264 | 1265 | var Tooltip = function (element, options) { 1266 | this.type = null 1267 | this.options = null 1268 | this.enabled = null 1269 | this.timeout = null 1270 | this.hoverState = null 1271 | this.$element = null 1272 | this.inState = null 1273 | 1274 | this.init('tooltip', element, options) 1275 | } 1276 | 1277 | Tooltip.VERSION = '3.3.6' 1278 | 1279 | Tooltip.TRANSITION_DURATION = 150 1280 | 1281 | Tooltip.DEFAULTS = { 1282 | animation: true, 1283 | placement: 'top', 1284 | selector: false, 1285 | template: '', 1286 | trigger: 'hover focus', 1287 | title: '', 1288 | delay: 0, 1289 | html: false, 1290 | container: false, 1291 | viewport: { 1292 | selector: 'body', 1293 | padding: 0 1294 | } 1295 | } 1296 | 1297 | Tooltip.prototype.init = function (type, element, options) { 1298 | this.enabled = true 1299 | this.type = type 1300 | this.$element = $(element) 1301 | this.options = this.getOptions(options) 1302 | this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport)) 1303 | this.inState = { click: false, hover: false, focus: false } 1304 | 1305 | if (this.$element[0] instanceof document.constructor && !this.options.selector) { 1306 | throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!') 1307 | } 1308 | 1309 | var triggers = this.options.trigger.split(' ') 1310 | 1311 | for (var i = triggers.length; i--;) { 1312 | var trigger = triggers[i] 1313 | 1314 | if (trigger == 'click') { 1315 | this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) 1316 | } else if (trigger != 'manual') { 1317 | var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin' 1318 | var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout' 1319 | 1320 | this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) 1321 | this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) 1322 | } 1323 | } 1324 | 1325 | this.options.selector ? 1326 | (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : 1327 | this.fixTitle() 1328 | } 1329 | 1330 | Tooltip.prototype.getDefaults = function () { 1331 | return Tooltip.DEFAULTS 1332 | } 1333 | 1334 | Tooltip.prototype.getOptions = function (options) { 1335 | options = $.extend({}, this.getDefaults(), this.$element.data(), options) 1336 | 1337 | if (options.delay && typeof options.delay == 'number') { 1338 | options.delay = { 1339 | show: options.delay, 1340 | hide: options.delay 1341 | } 1342 | } 1343 | 1344 | return options 1345 | } 1346 | 1347 | Tooltip.prototype.getDelegateOptions = function () { 1348 | var options = {} 1349 | var defaults = this.getDefaults() 1350 | 1351 | this._options && $.each(this._options, function (key, value) { 1352 | if (defaults[key] != value) options[key] = value 1353 | }) 1354 | 1355 | return options 1356 | } 1357 | 1358 | Tooltip.prototype.enter = function (obj) { 1359 | var self = obj instanceof this.constructor ? 1360 | obj : $(obj.currentTarget).data('bs.' + this.type) 1361 | 1362 | if (!self) { 1363 | self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) 1364 | $(obj.currentTarget).data('bs.' + this.type, self) 1365 | } 1366 | 1367 | if (obj instanceof $.Event) { 1368 | self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true 1369 | } 1370 | 1371 | if (self.tip().hasClass('in') || self.hoverState == 'in') { 1372 | self.hoverState = 'in' 1373 | return 1374 | } 1375 | 1376 | clearTimeout(self.timeout) 1377 | 1378 | self.hoverState = 'in' 1379 | 1380 | if (!self.options.delay || !self.options.delay.show) return self.show() 1381 | 1382 | self.timeout = setTimeout(function () { 1383 | if (self.hoverState == 'in') self.show() 1384 | }, self.options.delay.show) 1385 | } 1386 | 1387 | Tooltip.prototype.isInStateTrue = function () { 1388 | for (var key in this.inState) { 1389 | if (this.inState[key]) return true 1390 | } 1391 | 1392 | return false 1393 | } 1394 | 1395 | Tooltip.prototype.leave = function (obj) { 1396 | var self = obj instanceof this.constructor ? 1397 | obj : $(obj.currentTarget).data('bs.' + this.type) 1398 | 1399 | if (!self) { 1400 | self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) 1401 | $(obj.currentTarget).data('bs.' + this.type, self) 1402 | } 1403 | 1404 | if (obj instanceof $.Event) { 1405 | self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false 1406 | } 1407 | 1408 | if (self.isInStateTrue()) return 1409 | 1410 | clearTimeout(self.timeout) 1411 | 1412 | self.hoverState = 'out' 1413 | 1414 | if (!self.options.delay || !self.options.delay.hide) return self.hide() 1415 | 1416 | self.timeout = setTimeout(function () { 1417 | if (self.hoverState == 'out') self.hide() 1418 | }, self.options.delay.hide) 1419 | } 1420 | 1421 | Tooltip.prototype.show = function () { 1422 | var e = $.Event('show.bs.' + this.type) 1423 | 1424 | if (this.hasContent() && this.enabled) { 1425 | this.$element.trigger(e) 1426 | 1427 | var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]) 1428 | if (e.isDefaultPrevented() || !inDom) return 1429 | var that = this 1430 | 1431 | var $tip = this.tip() 1432 | 1433 | var tipId = this.getUID(this.type) 1434 | 1435 | this.setContent() 1436 | $tip.attr('id', tipId) 1437 | this.$element.attr('aria-describedby', tipId) 1438 | 1439 | if (this.options.animation) $tip.addClass('fade') 1440 | 1441 | var placement = typeof this.options.placement == 'function' ? 1442 | this.options.placement.call(this, $tip[0], this.$element[0]) : 1443 | this.options.placement 1444 | 1445 | var autoToken = /\s?auto?\s?/i 1446 | var autoPlace = autoToken.test(placement) 1447 | if (autoPlace) placement = placement.replace(autoToken, '') || 'top' 1448 | 1449 | $tip 1450 | .detach() 1451 | .css({ top: 0, left: 0, display: 'block' }) 1452 | .addClass(placement) 1453 | .data('bs.' + this.type, this) 1454 | 1455 | this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) 1456 | this.$element.trigger('inserted.bs.' + this.type) 1457 | 1458 | var pos = this.getPosition() 1459 | var actualWidth = $tip[0].offsetWidth 1460 | var actualHeight = $tip[0].offsetHeight 1461 | 1462 | if (autoPlace) { 1463 | var orgPlacement = placement 1464 | var viewportDim = this.getPosition(this.$viewport) 1465 | 1466 | placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' : 1467 | placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' : 1468 | placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' : 1469 | placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' : 1470 | placement 1471 | 1472 | $tip 1473 | .removeClass(orgPlacement) 1474 | .addClass(placement) 1475 | } 1476 | 1477 | var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight) 1478 | 1479 | this.applyPlacement(calculatedOffset, placement) 1480 | 1481 | var complete = function () { 1482 | var prevHoverState = that.hoverState 1483 | that.$element.trigger('shown.bs.' + that.type) 1484 | that.hoverState = null 1485 | 1486 | if (prevHoverState == 'out') that.leave(that) 1487 | } 1488 | 1489 | $.support.transition && this.$tip.hasClass('fade') ? 1490 | $tip 1491 | .one('bsTransitionEnd', complete) 1492 | .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : 1493 | complete() 1494 | } 1495 | } 1496 | 1497 | Tooltip.prototype.applyPlacement = function (offset, placement) { 1498 | var $tip = this.tip() 1499 | var width = $tip[0].offsetWidth 1500 | var height = $tip[0].offsetHeight 1501 | 1502 | // manually read margins because getBoundingClientRect includes difference 1503 | var marginTop = parseInt($tip.css('margin-top'), 10) 1504 | var marginLeft = parseInt($tip.css('margin-left'), 10) 1505 | 1506 | // we must check for NaN for ie 8/9 1507 | if (isNaN(marginTop)) marginTop = 0 1508 | if (isNaN(marginLeft)) marginLeft = 0 1509 | 1510 | offset.top += marginTop 1511 | offset.left += marginLeft 1512 | 1513 | // $.fn.offset doesn't round pixel values 1514 | // so we use setOffset directly with our own function B-0 1515 | $.offset.setOffset($tip[0], $.extend({ 1516 | using: function (props) { 1517 | $tip.css({ 1518 | top: Math.round(props.top), 1519 | left: Math.round(props.left) 1520 | }) 1521 | } 1522 | }, offset), 0) 1523 | 1524 | $tip.addClass('in') 1525 | 1526 | // check to see if placing tip in new offset caused the tip to resize itself 1527 | var actualWidth = $tip[0].offsetWidth 1528 | var actualHeight = $tip[0].offsetHeight 1529 | 1530 | if (placement == 'top' && actualHeight != height) { 1531 | offset.top = offset.top + height - actualHeight 1532 | } 1533 | 1534 | var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight) 1535 | 1536 | if (delta.left) offset.left += delta.left 1537 | else offset.top += delta.top 1538 | 1539 | var isVertical = /top|bottom/.test(placement) 1540 | var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight 1541 | var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight' 1542 | 1543 | $tip.offset(offset) 1544 | this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical) 1545 | } 1546 | 1547 | Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) { 1548 | this.arrow() 1549 | .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%') 1550 | .css(isVertical ? 'top' : 'left', '') 1551 | } 1552 | 1553 | Tooltip.prototype.setContent = function () { 1554 | var $tip = this.tip() 1555 | var title = this.getTitle() 1556 | 1557 | $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) 1558 | $tip.removeClass('fade in top bottom left right') 1559 | } 1560 | 1561 | Tooltip.prototype.hide = function (callback) { 1562 | var that = this 1563 | var $tip = $(this.$tip) 1564 | var e = $.Event('hide.bs.' + this.type) 1565 | 1566 | function complete() { 1567 | if (that.hoverState != 'in') $tip.detach() 1568 | that.$element 1569 | .removeAttr('aria-describedby') 1570 | .trigger('hidden.bs.' + that.type) 1571 | callback && callback() 1572 | } 1573 | 1574 | this.$element.trigger(e) 1575 | 1576 | if (e.isDefaultPrevented()) return 1577 | 1578 | $tip.removeClass('in') 1579 | 1580 | $.support.transition && $tip.hasClass('fade') ? 1581 | $tip 1582 | .one('bsTransitionEnd', complete) 1583 | .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : 1584 | complete() 1585 | 1586 | this.hoverState = null 1587 | 1588 | return this 1589 | } 1590 | 1591 | Tooltip.prototype.fixTitle = function () { 1592 | var $e = this.$element 1593 | if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') { 1594 | $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') 1595 | } 1596 | } 1597 | 1598 | Tooltip.prototype.hasContent = function () { 1599 | return this.getTitle() 1600 | } 1601 | 1602 | Tooltip.prototype.getPosition = function ($element) { 1603 | $element = $element || this.$element 1604 | 1605 | var el = $element[0] 1606 | var isBody = el.tagName == 'BODY' 1607 | 1608 | var elRect = el.getBoundingClientRect() 1609 | if (elRect.width == null) { 1610 | // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093 1611 | elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top }) 1612 | } 1613 | var elOffset = isBody ? { top: 0, left: 0 } : $element.offset() 1614 | var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() } 1615 | var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null 1616 | 1617 | return $.extend({}, elRect, scroll, outerDims, elOffset) 1618 | } 1619 | 1620 | Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { 1621 | return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : 1622 | placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : 1623 | placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : 1624 | /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } 1625 | 1626 | } 1627 | 1628 | Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) { 1629 | var delta = { top: 0, left: 0 } 1630 | if (!this.$viewport) return delta 1631 | 1632 | var viewportPadding = this.options.viewport && this.options.viewport.padding || 0 1633 | var viewportDimensions = this.getPosition(this.$viewport) 1634 | 1635 | if (/right|left/.test(placement)) { 1636 | var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll 1637 | var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight 1638 | if (topEdgeOffset < viewportDimensions.top) { // top overflow 1639 | delta.top = viewportDimensions.top - topEdgeOffset 1640 | } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow 1641 | delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset 1642 | } 1643 | } else { 1644 | var leftEdgeOffset = pos.left - viewportPadding 1645 | var rightEdgeOffset = pos.left + viewportPadding + actualWidth 1646 | if (leftEdgeOffset < viewportDimensions.left) { // left overflow 1647 | delta.left = viewportDimensions.left - leftEdgeOffset 1648 | } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow 1649 | delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset 1650 | } 1651 | } 1652 | 1653 | return delta 1654 | } 1655 | 1656 | Tooltip.prototype.getTitle = function () { 1657 | var title 1658 | var $e = this.$element 1659 | var o = this.options 1660 | 1661 | title = $e.attr('data-original-title') 1662 | || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) 1663 | 1664 | return title 1665 | } 1666 | 1667 | Tooltip.prototype.getUID = function (prefix) { 1668 | do prefix += ~~(Math.random() * 1000000) 1669 | while (document.getElementById(prefix)) 1670 | return prefix 1671 | } 1672 | 1673 | Tooltip.prototype.tip = function () { 1674 | if (!this.$tip) { 1675 | this.$tip = $(this.options.template) 1676 | if (this.$tip.length != 1) { 1677 | throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!') 1678 | } 1679 | } 1680 | return this.$tip 1681 | } 1682 | 1683 | Tooltip.prototype.arrow = function () { 1684 | return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')) 1685 | } 1686 | 1687 | Tooltip.prototype.enable = function () { 1688 | this.enabled = true 1689 | } 1690 | 1691 | Tooltip.prototype.disable = function () { 1692 | this.enabled = false 1693 | } 1694 | 1695 | Tooltip.prototype.toggleEnabled = function () { 1696 | this.enabled = !this.enabled 1697 | } 1698 | 1699 | Tooltip.prototype.toggle = function (e) { 1700 | var self = this 1701 | if (e) { 1702 | self = $(e.currentTarget).data('bs.' + this.type) 1703 | if (!self) { 1704 | self = new this.constructor(e.currentTarget, this.getDelegateOptions()) 1705 | $(e.currentTarget).data('bs.' + this.type, self) 1706 | } 1707 | } 1708 | 1709 | if (e) { 1710 | self.inState.click = !self.inState.click 1711 | if (self.isInStateTrue()) self.enter(self) 1712 | else self.leave(self) 1713 | } else { 1714 | self.tip().hasClass('in') ? self.leave(self) : self.enter(self) 1715 | } 1716 | } 1717 | 1718 | Tooltip.prototype.destroy = function () { 1719 | var that = this 1720 | clearTimeout(this.timeout) 1721 | this.hide(function () { 1722 | that.$element.off('.' + that.type).removeData('bs.' + that.type) 1723 | if (that.$tip) { 1724 | that.$tip.detach() 1725 | } 1726 | that.$tip = null 1727 | that.$arrow = null 1728 | that.$viewport = null 1729 | }) 1730 | } 1731 | 1732 | 1733 | // TOOLTIP PLUGIN DEFINITION 1734 | // ========================= 1735 | 1736 | function Plugin(option) { 1737 | return this.each(function () { 1738 | var $this = $(this) 1739 | var data = $this.data('bs.tooltip') 1740 | var options = typeof option == 'object' && option 1741 | 1742 | if (!data && /destroy|hide/.test(option)) return 1743 | if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) 1744 | if (typeof option == 'string') data[option]() 1745 | }) 1746 | } 1747 | 1748 | var old = $.fn.tooltip 1749 | 1750 | $.fn.tooltip = Plugin 1751 | $.fn.tooltip.Constructor = Tooltip 1752 | 1753 | 1754 | // TOOLTIP NO CONFLICT 1755 | // =================== 1756 | 1757 | $.fn.tooltip.noConflict = function () { 1758 | $.fn.tooltip = old 1759 | return this 1760 | } 1761 | 1762 | }(jQuery); 1763 | 1764 | /* ======================================================================== 1765 | * Bootstrap: popover.js v3.3.6 1766 | * http://getbootstrap.com/javascript/#popovers 1767 | * ======================================================================== 1768 | * Copyright 2011-2015 Twitter, Inc. 1769 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 1770 | * ======================================================================== */ 1771 | 1772 | 1773 | +function ($) { 1774 | 'use strict'; 1775 | 1776 | // POPOVER PUBLIC CLASS DEFINITION 1777 | // =============================== 1778 | 1779 | var Popover = function (element, options) { 1780 | this.init('popover', element, options) 1781 | } 1782 | 1783 | if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js') 1784 | 1785 | Popover.VERSION = '3.3.6' 1786 | 1787 | Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, { 1788 | placement: 'right', 1789 | trigger: 'click', 1790 | content: '', 1791 | template: '' 1792 | }) 1793 | 1794 | 1795 | // NOTE: POPOVER EXTENDS tooltip.js 1796 | // ================================ 1797 | 1798 | Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype) 1799 | 1800 | Popover.prototype.constructor = Popover 1801 | 1802 | Popover.prototype.getDefaults = function () { 1803 | return Popover.DEFAULTS 1804 | } 1805 | 1806 | Popover.prototype.setContent = function () { 1807 | var $tip = this.tip() 1808 | var title = this.getTitle() 1809 | var content = this.getContent() 1810 | 1811 | $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) 1812 | $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events 1813 | this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text' 1814 | ](content) 1815 | 1816 | $tip.removeClass('fade top bottom left right in') 1817 | 1818 | // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do 1819 | // this manually by checking the contents. 1820 | if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide() 1821 | } 1822 | 1823 | Popover.prototype.hasContent = function () { 1824 | return this.getTitle() || this.getContent() 1825 | } 1826 | 1827 | Popover.prototype.getContent = function () { 1828 | var $e = this.$element 1829 | var o = this.options 1830 | 1831 | return $e.attr('data-content') 1832 | || (typeof o.content == 'function' ? 1833 | o.content.call($e[0]) : 1834 | o.content) 1835 | } 1836 | 1837 | Popover.prototype.arrow = function () { 1838 | return (this.$arrow = this.$arrow || this.tip().find('.arrow')) 1839 | } 1840 | 1841 | 1842 | // POPOVER PLUGIN DEFINITION 1843 | // ========================= 1844 | 1845 | function Plugin(option) { 1846 | return this.each(function () { 1847 | var $this = $(this) 1848 | var data = $this.data('bs.popover') 1849 | var options = typeof option == 'object' && option 1850 | 1851 | if (!data && /destroy|hide/.test(option)) return 1852 | if (!data) $this.data('bs.popover', (data = new Popover(this, options))) 1853 | if (typeof option == 'string') data[option]() 1854 | }) 1855 | } 1856 | 1857 | var old = $.fn.popover 1858 | 1859 | $.fn.popover = Plugin 1860 | $.fn.popover.Constructor = Popover 1861 | 1862 | 1863 | // POPOVER NO CONFLICT 1864 | // =================== 1865 | 1866 | $.fn.popover.noConflict = function () { 1867 | $.fn.popover = old 1868 | return this 1869 | } 1870 | 1871 | }(jQuery); 1872 | 1873 | /* ======================================================================== 1874 | * Bootstrap: scrollspy.js v3.3.6 1875 | * http://getbootstrap.com/javascript/#scrollspy 1876 | * ======================================================================== 1877 | * Copyright 2011-2015 Twitter, Inc. 1878 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 1879 | * ======================================================================== */ 1880 | 1881 | 1882 | +function ($) { 1883 | 'use strict'; 1884 | 1885 | // SCROLLSPY CLASS DEFINITION 1886 | // ========================== 1887 | 1888 | function ScrollSpy(element, options) { 1889 | this.$body = $(document.body) 1890 | this.$scrollElement = $(element).is(document.body) ? $(window) : $(element) 1891 | this.options = $.extend({}, ScrollSpy.DEFAULTS, options) 1892 | this.selector = (this.options.target || '') + ' .nav li > a' 1893 | this.offsets = [] 1894 | this.targets = [] 1895 | this.activeTarget = null 1896 | this.scrollHeight = 0 1897 | 1898 | this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this)) 1899 | this.refresh() 1900 | this.process() 1901 | } 1902 | 1903 | ScrollSpy.VERSION = '3.3.6' 1904 | 1905 | ScrollSpy.DEFAULTS = { 1906 | offset: 10 1907 | } 1908 | 1909 | ScrollSpy.prototype.getScrollHeight = function () { 1910 | return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight) 1911 | } 1912 | 1913 | ScrollSpy.prototype.refresh = function () { 1914 | var that = this 1915 | var offsetMethod = 'offset' 1916 | var offsetBase = 0 1917 | 1918 | this.offsets = [] 1919 | this.targets = [] 1920 | this.scrollHeight = this.getScrollHeight() 1921 | 1922 | if (!$.isWindow(this.$scrollElement[0])) { 1923 | offsetMethod = 'position' 1924 | offsetBase = this.$scrollElement.scrollTop() 1925 | } 1926 | 1927 | this.$body 1928 | .find(this.selector) 1929 | .map(function () { 1930 | var $el = $(this) 1931 | var href = $el.data('target') || $el.attr('href') 1932 | var $href = /^#./.test(href) && $(href) 1933 | 1934 | return ($href 1935 | && $href.length 1936 | && $href.is(':visible') 1937 | && [[$href[offsetMethod]().top + offsetBase, href]]) || null 1938 | }) 1939 | .sort(function (a, b) { return a[0] - b[0] }) 1940 | .each(function () { 1941 | that.offsets.push(this[0]) 1942 | that.targets.push(this[1]) 1943 | }) 1944 | } 1945 | 1946 | ScrollSpy.prototype.process = function () { 1947 | var scrollTop = this.$scrollElement.scrollTop() + this.options.offset 1948 | var scrollHeight = this.getScrollHeight() 1949 | var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height() 1950 | var offsets = this.offsets 1951 | var targets = this.targets 1952 | var activeTarget = this.activeTarget 1953 | var i 1954 | 1955 | if (this.scrollHeight != scrollHeight) { 1956 | this.refresh() 1957 | } 1958 | 1959 | if (scrollTop >= maxScroll) { 1960 | return activeTarget != (i = targets[targets.length - 1]) && this.activate(i) 1961 | } 1962 | 1963 | if (activeTarget && scrollTop < offsets[0]) { 1964 | this.activeTarget = null 1965 | return this.clear() 1966 | } 1967 | 1968 | for (i = offsets.length; i--;) { 1969 | activeTarget != targets[i] 1970 | && scrollTop >= offsets[i] 1971 | && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1]) 1972 | && this.activate(targets[i]) 1973 | } 1974 | } 1975 | 1976 | ScrollSpy.prototype.activate = function (target) { 1977 | this.activeTarget = target 1978 | 1979 | this.clear() 1980 | 1981 | var selector = this.selector + 1982 | '[data-target="' + target + '"],' + 1983 | this.selector + '[href="' + target + '"]' 1984 | 1985 | var active = $(selector) 1986 | .parents('li') 1987 | .addClass('active') 1988 | 1989 | if (active.parent('.dropdown-menu').length) { 1990 | active = active 1991 | .closest('li.dropdown') 1992 | .addClass('active') 1993 | } 1994 | 1995 | active.trigger('activate.bs.scrollspy') 1996 | } 1997 | 1998 | ScrollSpy.prototype.clear = function () { 1999 | $(this.selector) 2000 | .parentsUntil(this.options.target, '.active') 2001 | .removeClass('active') 2002 | } 2003 | 2004 | 2005 | // SCROLLSPY PLUGIN DEFINITION 2006 | // =========================== 2007 | 2008 | function Plugin(option) { 2009 | return this.each(function () { 2010 | var $this = $(this) 2011 | var data = $this.data('bs.scrollspy') 2012 | var options = typeof option == 'object' && option 2013 | 2014 | if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options))) 2015 | if (typeof option == 'string') data[option]() 2016 | }) 2017 | } 2018 | 2019 | var old = $.fn.scrollspy 2020 | 2021 | $.fn.scrollspy = Plugin 2022 | $.fn.scrollspy.Constructor = ScrollSpy 2023 | 2024 | 2025 | // SCROLLSPY NO CONFLICT 2026 | // ===================== 2027 | 2028 | $.fn.scrollspy.noConflict = function () { 2029 | $.fn.scrollspy = old 2030 | return this 2031 | } 2032 | 2033 | 2034 | // SCROLLSPY DATA-API 2035 | // ================== 2036 | 2037 | $(window).on('load.bs.scrollspy.data-api', function () { 2038 | $('[data-spy="scroll"]').each(function () { 2039 | var $spy = $(this) 2040 | Plugin.call($spy, $spy.data()) 2041 | }) 2042 | }) 2043 | 2044 | }(jQuery); 2045 | 2046 | /* ======================================================================== 2047 | * Bootstrap: tab.js v3.3.6 2048 | * http://getbootstrap.com/javascript/#tabs 2049 | * ======================================================================== 2050 | * Copyright 2011-2015 Twitter, Inc. 2051 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 2052 | * ======================================================================== */ 2053 | 2054 | 2055 | +function ($) { 2056 | 'use strict'; 2057 | 2058 | // TAB CLASS DEFINITION 2059 | // ==================== 2060 | 2061 | var Tab = function (element) { 2062 | // jscs:disable requireDollarBeforejQueryAssignment 2063 | this.element = $(element) 2064 | // jscs:enable requireDollarBeforejQueryAssignment 2065 | } 2066 | 2067 | Tab.VERSION = '3.3.6' 2068 | 2069 | Tab.TRANSITION_DURATION = 150 2070 | 2071 | Tab.prototype.show = function () { 2072 | var $this = this.element 2073 | var $ul = $this.closest('ul:not(.dropdown-menu)') 2074 | var selector = $this.data('target') 2075 | 2076 | if (!selector) { 2077 | selector = $this.attr('href') 2078 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 2079 | } 2080 | 2081 | if ($this.parent('li').hasClass('active')) return 2082 | 2083 | var $previous = $ul.find('.active:last a') 2084 | var hideEvent = $.Event('hide.bs.tab', { 2085 | relatedTarget: $this[0] 2086 | }) 2087 | var showEvent = $.Event('show.bs.tab', { 2088 | relatedTarget: $previous[0] 2089 | }) 2090 | 2091 | $previous.trigger(hideEvent) 2092 | $this.trigger(showEvent) 2093 | 2094 | if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return 2095 | 2096 | var $target = $(selector) 2097 | 2098 | this.activate($this.closest('li'), $ul) 2099 | this.activate($target, $target.parent(), function () { 2100 | $previous.trigger({ 2101 | type: 'hidden.bs.tab', 2102 | relatedTarget: $this[0] 2103 | }) 2104 | $this.trigger({ 2105 | type: 'shown.bs.tab', 2106 | relatedTarget: $previous[0] 2107 | }) 2108 | }) 2109 | } 2110 | 2111 | Tab.prototype.activate = function (element, container, callback) { 2112 | var $active = container.find('> .active') 2113 | var transition = callback 2114 | && $.support.transition 2115 | && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length) 2116 | 2117 | function next() { 2118 | $active 2119 | .removeClass('active') 2120 | .find('> .dropdown-menu > .active') 2121 | .removeClass('active') 2122 | .end() 2123 | .find('[data-toggle="tab"]') 2124 | .attr('aria-expanded', false) 2125 | 2126 | element 2127 | .addClass('active') 2128 | .find('[data-toggle="tab"]') 2129 | .attr('aria-expanded', true) 2130 | 2131 | if (transition) { 2132 | element[0].offsetWidth // reflow for transition 2133 | element.addClass('in') 2134 | } else { 2135 | element.removeClass('fade') 2136 | } 2137 | 2138 | if (element.parent('.dropdown-menu').length) { 2139 | element 2140 | .closest('li.dropdown') 2141 | .addClass('active') 2142 | .end() 2143 | .find('[data-toggle="tab"]') 2144 | .attr('aria-expanded', true) 2145 | } 2146 | 2147 | callback && callback() 2148 | } 2149 | 2150 | $active.length && transition ? 2151 | $active 2152 | .one('bsTransitionEnd', next) 2153 | .emulateTransitionEnd(Tab.TRANSITION_DURATION) : 2154 | next() 2155 | 2156 | $active.removeClass('in') 2157 | } 2158 | 2159 | 2160 | // TAB PLUGIN DEFINITION 2161 | // ===================== 2162 | 2163 | function Plugin(option) { 2164 | return this.each(function () { 2165 | var $this = $(this) 2166 | var data = $this.data('bs.tab') 2167 | 2168 | if (!data) $this.data('bs.tab', (data = new Tab(this))) 2169 | if (typeof option == 'string') data[option]() 2170 | }) 2171 | } 2172 | 2173 | var old = $.fn.tab 2174 | 2175 | $.fn.tab = Plugin 2176 | $.fn.tab.Constructor = Tab 2177 | 2178 | 2179 | // TAB NO CONFLICT 2180 | // =============== 2181 | 2182 | $.fn.tab.noConflict = function () { 2183 | $.fn.tab = old 2184 | return this 2185 | } 2186 | 2187 | 2188 | // TAB DATA-API 2189 | // ============ 2190 | 2191 | var clickHandler = function (e) { 2192 | e.preventDefault() 2193 | Plugin.call($(this), 'show') 2194 | } 2195 | 2196 | $(document) 2197 | .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler) 2198 | .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler) 2199 | 2200 | }(jQuery); 2201 | 2202 | /* ======================================================================== 2203 | * Bootstrap: affix.js v3.3.6 2204 | * http://getbootstrap.com/javascript/#affix 2205 | * ======================================================================== 2206 | * Copyright 2011-2015 Twitter, Inc. 2207 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 2208 | * ======================================================================== */ 2209 | 2210 | 2211 | +function ($) { 2212 | 'use strict'; 2213 | 2214 | // AFFIX CLASS DEFINITION 2215 | // ====================== 2216 | 2217 | var Affix = function (element, options) { 2218 | this.options = $.extend({}, Affix.DEFAULTS, options) 2219 | 2220 | this.$target = $(this.options.target) 2221 | .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this)) 2222 | .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this)) 2223 | 2224 | this.$element = $(element) 2225 | this.affixed = null 2226 | this.unpin = null 2227 | this.pinnedOffset = null 2228 | 2229 | this.checkPosition() 2230 | } 2231 | 2232 | Affix.VERSION = '3.3.6' 2233 | 2234 | Affix.RESET = 'affix affix-top affix-bottom' 2235 | 2236 | Affix.DEFAULTS = { 2237 | offset: 0, 2238 | target: window 2239 | } 2240 | 2241 | Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) { 2242 | var scrollTop = this.$target.scrollTop() 2243 | var position = this.$element.offset() 2244 | var targetHeight = this.$target.height() 2245 | 2246 | if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false 2247 | 2248 | if (this.affixed == 'bottom') { 2249 | if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom' 2250 | return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom' 2251 | } 2252 | 2253 | var initializing = this.affixed == null 2254 | var colliderTop = initializing ? scrollTop : position.top 2255 | var colliderHeight = initializing ? targetHeight : height 2256 | 2257 | if (offsetTop != null && scrollTop <= offsetTop) return 'top' 2258 | if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom' 2259 | 2260 | return false 2261 | } 2262 | 2263 | Affix.prototype.getPinnedOffset = function () { 2264 | if (this.pinnedOffset) return this.pinnedOffset 2265 | this.$element.removeClass(Affix.RESET).addClass('affix') 2266 | var scrollTop = this.$target.scrollTop() 2267 | var position = this.$element.offset() 2268 | return (this.pinnedOffset = position.top - scrollTop) 2269 | } 2270 | 2271 | Affix.prototype.checkPositionWithEventLoop = function () { 2272 | setTimeout($.proxy(this.checkPosition, this), 1) 2273 | } 2274 | 2275 | Affix.prototype.checkPosition = function () { 2276 | if (!this.$element.is(':visible')) return 2277 | 2278 | var height = this.$element.height() 2279 | var offset = this.options.offset 2280 | var offsetTop = offset.top 2281 | var offsetBottom = offset.bottom 2282 | var scrollHeight = Math.max($(document).height(), $(document.body).height()) 2283 | 2284 | if (typeof offset != 'object') offsetBottom = offsetTop = offset 2285 | if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element) 2286 | if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element) 2287 | 2288 | var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom) 2289 | 2290 | if (this.affixed != affix) { 2291 | if (this.unpin != null) this.$element.css('top', '') 2292 | 2293 | var affixType = 'affix' + (affix ? '-' + affix : '') 2294 | var e = $.Event(affixType + '.bs.affix') 2295 | 2296 | this.$element.trigger(e) 2297 | 2298 | if (e.isDefaultPrevented()) return 2299 | 2300 | this.affixed = affix 2301 | this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null 2302 | 2303 | this.$element 2304 | .removeClass(Affix.RESET) 2305 | .addClass(affixType) 2306 | .trigger(affixType.replace('affix', 'affixed') + '.bs.affix') 2307 | } 2308 | 2309 | if (affix == 'bottom') { 2310 | this.$element.offset({ 2311 | top: scrollHeight - height - offsetBottom 2312 | }) 2313 | } 2314 | } 2315 | 2316 | 2317 | // AFFIX PLUGIN DEFINITION 2318 | // ======================= 2319 | 2320 | function Plugin(option) { 2321 | return this.each(function () { 2322 | var $this = $(this) 2323 | var data = $this.data('bs.affix') 2324 | var options = typeof option == 'object' && option 2325 | 2326 | if (!data) $this.data('bs.affix', (data = new Affix(this, options))) 2327 | if (typeof option == 'string') data[option]() 2328 | }) 2329 | } 2330 | 2331 | var old = $.fn.affix 2332 | 2333 | $.fn.affix = Plugin 2334 | $.fn.affix.Constructor = Affix 2335 | 2336 | 2337 | // AFFIX NO CONFLICT 2338 | // ================= 2339 | 2340 | $.fn.affix.noConflict = function () { 2341 | $.fn.affix = old 2342 | return this 2343 | } 2344 | 2345 | 2346 | // AFFIX DATA-API 2347 | // ============== 2348 | 2349 | $(window).on('load', function () { 2350 | $('[data-spy="affix"]').each(function () { 2351 | var $spy = $(this) 2352 | var data = $spy.data() 2353 | 2354 | data.offset = data.offset || {} 2355 | 2356 | if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom 2357 | if (data.offsetTop != null) data.offset.top = data.offsetTop 2358 | 2359 | Plugin.call($spy, data) 2360 | }) 2361 | }) 2362 | 2363 | }(jQuery); 2364 | --------------------------------------------------------------------------------