diff --git a/.gitignore b/.gitignore index d31b5aa9..041dcbd3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +tmp/ +db.sqlite3 + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -25,9 +28,6 @@ var/ # Continer extras .vagrant -_builds -_steps -_projects # PyInstaller # Usually these files are written by a python script from a template @@ -44,6 +44,7 @@ htmlcov/ .tox/ .coverage .cache +.pytest_cache nosetests.xml coverage.xml @@ -53,6 +54,7 @@ coverage.xml # Django stuff: *.log +db.sqlite3 # Sphinx documentation docs/_build/ @@ -106,3 +108,4 @@ atlassian-ide-plugin.xml com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties +.vscode/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 1efb7729..b2527c43 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,21 +1,32 @@ language: python python: - "2.7" + "3.6" +cache: pip -before_install: - - "export DISPLAY=:99.0" - - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16" +addons: + chrome: stable install: + - wget https://chromedriver.storage.googleapis.com/2.36/chromedriver_linux64.zip + - unzip chromedriver_linux64.zip + - export PATH=$PATH:$(pwd) + - chmod +x chromedriver - pip install -r requirements.txt - - pip install coveralls codeclimate-test-reporter + - pip install coveralls codeclimate-test-reporter pep8 before_script: + - export PATH=$PATH:/usr/lib/chromium-browser/ - python manage.py collectstatic --noinput script: - - coverage run manage.py test RIGS + - pep8 . --exclude=migrations,importer* + - python manage.py check + - python manage.py makemigrations --check --dry-run + - coverage run manage.py test --verbosity=2 after_success: - coveralls - codeclimate-test-reporter + +notifications: + webhooks: https://fathomless-fjord-24024.herokuapp.com/notify diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..7c01953b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.6 + +WORKDIR /app + +ADD . /app + +RUN pip install -r requirements.txt && \ + python manage.py collectstatic --noinput + +EXPOSE 8000 + +CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"] \ No newline at end of file diff --git a/Procfile b/Procfile index e6088524..91e43543 100644 --- a/Procfile +++ b/Procfile @@ -1 +1,2 @@ +release: python manage.py migrate web: gunicorn PyRIGS.wsgi --log-file - diff --git a/PyRIGS/decorators.py b/PyRIGS/decorators.py index 67d8964c..6d48e5e1 100644 --- a/PyRIGS/decorators.py +++ b/PyRIGS/decorators.py @@ -1,26 +1,63 @@ from django.contrib.auth import REDIRECT_FIELD_NAME -from django.shortcuts import render_to_response -from django.template import RequestContext +from django.shortcuts import render from django.http import HttpResponseRedirect +from django.urls import reverse -def user_passes_test_with_403(test_func, login_url=None): +from RIGS import models + + +def get_oembed(login_url, request, oembed_view, kwargs): + context = {} + context['oembed_url'] = "{0}://{1}{2}".format(request.scheme, request.META['HTTP_HOST'], reverse(oembed_view, kwargs=kwargs)) + context['login_url'] = "{0}?{1}={2}".format(login_url, REDIRECT_FIELD_NAME, request.get_full_path()) + resp = render(request, 'login_redirect.html', context=context) + return resp + + +def has_oembed(oembed_view, login_url=None): + if not login_url: + from django.conf import settings + login_url = settings.LOGIN_URL + + def _dec(view_func): + def _checklogin(request, *args, **kwargs): + if request.user.is_authenticated: + return view_func(request, *args, **kwargs) + else: + if oembed_view is not None: + return get_oembed(login_url, request, oembed_view, kwargs) + else: + return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME, request.get_full_path())) + _checklogin.__doc__ = view_func.__doc__ + _checklogin.__dict__ = view_func.__dict__ + return _checklogin + return _dec + + +def user_passes_test_with_403(test_func, login_url=None, oembed_view=None): """ Decorator for views that checks that the user passes the given test. - Anonymous users will be redirected to login_url, while users that fail the test will be given a 403 error. + If embed_view is set, then a JS redirect will be used, and a application/json+oembed + meta tag set with the url of oembed_view + (oembed_view will be passed the kwargs from the main function) """ if not login_url: from django.conf import settings login_url = settings.LOGIN_URL + def _dec(view_func): def _checklogin(request, *args, **kwargs): if test_func(request.user): return view_func(request, *args, **kwargs) - elif not request.user.is_authenticated(): - return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME, request.get_full_path())) + elif not request.user.is_authenticated: + if oembed_view is not None: + return get_oembed(login_url, request, oembed_view, kwargs) + else: + return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME, request.get_full_path())) else: - resp = render_to_response('403.html', context_instance=RequestContext(request)) + resp = render(request, '403.html') resp.status_code = 403 return resp _checklogin.__doc__ = view_func.__doc__ @@ -28,14 +65,14 @@ def user_passes_test_with_403(test_func, login_url=None): return _checklogin return _dec -def permission_required_with_403(perm, login_url=None): + +def permission_required_with_403(perm, login_url=None, oembed_view=None): """ Decorator for views that checks whether a user has a particular permission enabled, redirecting to the log-in page or rendering a 403 as necessary. """ - return user_passes_test_with_403(lambda u: u.has_perm(perm), login_url=login_url) + return user_passes_test_with_403(lambda u: u.has_perm(perm), login_url=login_url, oembed_view=oembed_view) -from RIGS import models def api_key_required(function): """ @@ -48,7 +85,7 @@ def api_key_required(function): userid = kwargs.get('api_pk') key = kwargs.get('api_key') - error_resp = render_to_response('403.html', context_instance=RequestContext(request)) + error_resp = render(request, '403.html') error_resp.status_code = 403 if key is None: @@ -64,4 +101,18 @@ def api_key_required(function): if user_object.api_key != key: return error_resp return function(request, *args, **kwargs) - return wrap \ No newline at end of file + return wrap + + +def nottinghamtec_address_required(function): + """ + Checks that the current user has an email address ending @nottinghamtec.co.uk + """ + def wrap(request, *args, **kwargs): + # Fail if current user's email address isn't @nottinghamtec.co.uk + if not request.user.email.endswith('@nottinghamtec.co.uk'): + error_resp = render(request, 'RIGS/eventauthorisation_request_error.html') + return error_resp + + return function(request, *args, **kwargs) + return wrap diff --git a/PyRIGS/formats/en/formats.py b/PyRIGS/formats/en/formats.py index f10454b6..201335b9 100644 --- a/PyRIGS/formats/en/formats.py +++ b/PyRIGS/formats/en/formats.py @@ -1,5 +1,5 @@ -from __future__ import unicode_literals + DATETIME_FORMAT = ('d/m/Y H:i') DATE_FORMAT = ('d/m/Y') -TIME_FORMAT = ('H:i') \ No newline at end of file +TIME_FORMAT = ('H:i') diff --git a/PyRIGS/settings.py b/PyRIGS/settings.py index 3098c3fb..5877ad74 100644 --- a/PyRIGS/settings.py +++ b/PyRIGS/settings.py @@ -10,24 +10,37 @@ https://docs.djangoproject.com/en/1.7/ref/settings/ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os +import raven +import secrets + BASE_DIR = os.path.dirname(os.path.dirname(__file__)) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = os.environ.get('SECRET_KEY') if os.environ.get('SECRET_KEY') else 'gxhy(a#5mhp289_=6xx$7jh=eh$ymxg^ymc+di*0c*geiu3p_e' +SECRET_KEY = os.environ.get('SECRET_KEY') if os.environ.get( + 'SECRET_KEY') else 'gxhy(a#5mhp289_=6xx$7jh=eh$ymxg^ymc+di*0c*geiu3p_e' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = bool(int(os.environ.get('DEBUG'))) if os.environ.get('DEBUG') else True -TEMPLATE_DEBUG = True + +STAGING = bool(int(os.environ.get('STAGING'))) if os.environ.get('STAGING') else False ALLOWED_HOSTS = ['pyrigs.nottinghamtec.co.uk', 'rigs.nottinghamtec.co.uk', 'pyrigs.herokuapp.com'] +if STAGING: + ALLOWED_HOSTS.append('.herokuapp.com') + +if DEBUG: + ALLOWED_HOSTS.append('localhost') + ALLOWED_HOSTS.append('example.com') + ALLOWED_HOSTS.append('127.0.0.1') + SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') if not DEBUG: - SECURE_SSL_REDIRECT = True # Redirect all http requests to https + SECURE_SSL_REDIRECT = True # Redirect all http requests to https INTERNAL_IPS = ['127.0.0.1'] @@ -35,7 +48,6 @@ ADMINS = ( ('Tom Price', 'tomtom5152@gmail.com') ) - # Application definition INSTALLED_APPS = ( @@ -46,6 +58,7 @@ INSTALLED_APPS = ( 'django.contrib.messages', 'django.contrib.staticfiles', 'RIGS', + 'assets', 'debug_toolbar', 'registration', @@ -55,15 +68,15 @@ INSTALLED_APPS = ( 'raven.contrib.django.raven_compat', ) -MIDDLEWARE_CLASSES = ( +MIDDLEWARE = ( 'raven.contrib.django.raven_compat.middleware.SentryResponseErrorIdMiddleware', 'django.middleware.security.SecurityMiddleware', + 'debug_toolbar.middleware.DebugToolbarMiddleware', 'reversion.middleware.RevisionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ) @@ -72,7 +85,6 @@ ROOT_URLCONF = 'PyRIGS.urls' WSGI_APPLICATION = 'PyRIGS.wsgi.application' - # Database # https://docs.djangoproject.com/en/1.7/ref/settings/#databases DATABASES = { @@ -84,9 +96,10 @@ DATABASES = { if not DEBUG: import dj_database_url + DATABASES['default'] = dj_database_url.config() -# Logging +# Logging LOGGING = { 'version': 1, 'disable_existing_loggers': False, @@ -114,12 +127,12 @@ LOGGING = { 'mail_admins': { 'class': 'django.utils.log.AdminEmailHandler', 'level': 'ERROR', - # But the emails are plain text by default - HTML is nicer + # But the emails are plain text by default - HTML is nicer 'include_html': True, }, }, 'loggers': { - # Again, default Django configuration to email unhandled exceptions + # Again, default Django configuration to email unhandled exceptions 'django.request': { 'handlers': ['mail_admins'], 'level': 'ERROR', @@ -134,8 +147,6 @@ LOGGING = { } } -import raven - RAVEN_CONFIG = { 'dsn': os.environ.get('RAVEN_DSN'), # If you are using git, you can also automatically configure the @@ -147,14 +158,14 @@ RAVEN_CONFIG = { AUTH_USER_MODEL = 'RIGS.Profile' LOGIN_REDIRECT_URL = '/' -LOGIN_URL = '/user/login' -LOGOUT_URL = '/user/logout' +LOGIN_URL = '/user/login/' +LOGOUT_URL = '/user/logout/' ACCOUNT_ACTIVATION_DAYS = 7 # reCAPTCHA settings -RECAPTCHA_PUBLIC_KEY = os.environ.get('RECAPTCHA_PUBLIC_KEY', None) -RECAPTCHA_PRIVATE_KEY = os.environ.get('RECAPTCHA_PRIVATE_KEY', None) +RECAPTCHA_PUBLIC_KEY = os.environ.get('RECAPTCHA_PUBLIC_KEY', "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI") # If not set, use development key +RECAPTCHA_PRIVATE_KEY = os.environ.get('RECAPTCHA_PRIVATE_KEY', "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe") # If not set, use development key NOCAPTCHA = True # Email @@ -162,7 +173,7 @@ EMAILER_TEST = False if not DEBUG or EMAILER_TEST: EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = os.environ.get('EMAIL_HOST') - EMAIL_PORT = int(os.environ.get('EMAIL_PORT')) + EMAIL_PORT = int(os.environ.get('EMAIL_PORT', 25)) EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER') EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD') EMAIL_USE_TLS = bool(int(os.environ.get('EMAIL_USE_TLS', 0))) @@ -186,19 +197,7 @@ USE_L10N = True USE_TZ = True -DATETIME_INPUT_FORMATS = ('%Y-%m-%dT%H:%M','%Y-%m-%dT%H:%M:%S') - -TEMPLATE_CONTEXT_PROCESSORS = ( - "django.contrib.auth.context_processors.auth", - "django.core.context_processors.debug", - "django.core.context_processors.i18n", - "django.core.context_processors.media", - "django.core.context_processors.static", - "django.core.context_processors.tz", - "django.core.context_processors.request", - "django.contrib.messages.context_processors.messages", -) - +DATETIME_INPUT_FORMATS = ('%Y-%m-%dT%H:%M', '%Y-%m-%dT%H:%M:%S') # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.7/howto/static-files/ @@ -209,10 +208,34 @@ STATIC_DIRS = ( os.path.join(BASE_DIR, 'static/') ) -TEMPLATE_DIRS = ( - os.path.join(BASE_DIR, 'templates'), -) +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [ + os.path.join(BASE_DIR, 'templates'), + ], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + "django.contrib.auth.context_processors.auth", + "django.template.context_processors.debug", + "django.template.context_processors.i18n", + "django.template.context_processors.media", + "django.template.context_processors.static", + "django.template.context_processors.tz", + "django.template.context_processors.request", + "django.contrib.messages.context_processors.messages", + ], + 'debug': DEBUG + }, + }, +] -USE_GRAVATAR=True +USE_GRAVATAR = True TERMS_OF_HIRE_URL = "http://www.nottinghamtec.co.uk/terms.pdf" +AUTHORISATION_NOTIFICATION_ADDRESS = 'productions@nottinghamtec.co.uk' +RISK_ASSESSMENT_URL = os.environ.get('RISK_ASSESSMENT_URL') if os.environ.get( + 'RISK_ASSESSMENT_URL') else "http://example.com" +RISK_ASSESSMENT_SECRET = os.environ.get('RISK_ASSESSMENT_SECRET') if os.environ.get( + 'RISK_ASSESSMENT_SECRET') else secrets.token_hex(15) diff --git a/PyRIGS/urls.py b/PyRIGS/urls.py index 9821ae20..cb78130c 100644 --- a/PyRIGS/urls.py +++ b/PyRIGS/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls import patterns, include, url +from django.conf.urls import include, url from django.contrib import admin from django.contrib.staticfiles.urls import staticfiles_urlpatterns from django.conf import settings @@ -6,19 +6,25 @@ from registration.backends.default.views import RegistrationView import RIGS from RIGS import regbackend -urlpatterns = patterns('', +urlpatterns = [ # Examples: # url(r'^$', 'PyRIGS.views.home', name='home'), # url(r'^blog/', include('blog.urls')), url(r'^', include('RIGS.urls')), - url('^user/register/$', RegistrationView.as_view(form_class=RIGS.forms.ProfileRegistrationFormUniqueEmail), + url('^assets/', include('assets.urls')), + url('^user/register/$', RegistrationView.as_view(form_class=RIGS.forms.ProfileRegistrationFormUniqueEmail), name="registration_register"), url('^user/', include('django.contrib.auth.urls')), url('^user/', include('registration.backends.default.urls')), - url(r'^admin/', include(admin.site.urls)), -) + url(r'^admin/', admin.site.urls), +] if settings.DEBUG: - urlpatterns += staticfiles_urlpatterns() \ No newline at end of file + urlpatterns += staticfiles_urlpatterns() + + import debug_toolbar + urlpatterns = [ + url(r'^__debug__/', include(debug_toolbar.urls)), + ] + urlpatterns diff --git a/PyRIGS/wsgi.py b/PyRIGS/wsgi.py index 07f922a1..aa4cc434 100644 --- a/PyRIGS/wsgi.py +++ b/PyRIGS/wsgi.py @@ -9,7 +9,7 @@ https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/ import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "PyRIGS.settings") -from django.core.wsgi import get_wsgi_application -from dj_static import Cling +from django.core.wsgi import get_wsgi_application # noqa +from dj_static import Cling # noqa application = Cling(get_wsgi_application()) diff --git a/README.md b/README.md index 809c037c..50bf51d4 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,13 @@ # TEC PA & Lighting - PyRIGS # [![Build Status](https://travis-ci.org/nottinghamtec/PyRIGS.svg?branch=develop)](https://travis-ci.org/nottinghamtec/PyRIGS) -[![Coverage Status](https://coveralls.io/repos/github/nottinghamtec/PyRIGS/badge.svg?branch=develop)](https://coveralls.io/github/nottinghamtec/PyRIGS?branch=develop) +[![Coverage Status](https://coveralls.io/repos/github/nottinghamtec/PyRIGS/badge.svg?branch=develop)](https://coveralls.io/github/nottinghamtec/PyRIGS) Welcome to TEC PA & Lightings PyRIGS program. This is a reimplementation of the existing Rig Information Gathering System (RIGS) that was developed using Ruby on Rails. The purpose of this project is to make the system more compatible and easier to understand such that should future changes be needed they can be made without having to understand the intricacies of Rails. -At this stage the project is very early on, and the main focus has been on getting a working system that can be tested and put into use ASAP due to the imminent failure of the existing system. Because of this, the documentation is still quite weak, but this should be fixed as time goes on. - -This document is intended to get you up and running, but if don't care about what I have to say, just clone the sodding repository and have a poke around with what's in it, but for GODS SAKE DO NOT PUSH WITHOUT TESTING. - ### What is this repository for? ### -For the rapid development of the application for medium term deployment, the main branch is being used. -Once the application is deployed in a production environment, other branches should be used to properly stage edits and pushes of new features. When a significant feature is developed on a branch, raise a pull request and it can be reviewed before being put into production. +When a significant feature is developed on a branch, raise a pull request and it can be reviewed before being put into production. Most of the documents here assume a basic knowledge of how Python and Django work (hint, if I don't say something, Google it, you will find 10000's of answers). The documentation is purely to be specific to TEC's application of the framework. @@ -24,7 +19,7 @@ For the more experienced developer/somebody who doesn't want a full IDE and want Please contact TJP for details on how to acquire these. ### Python Environment ### -Whilst the Python version used is not critical to the running of the application, using the same version usually helps avoid a lot of issues. Mainly the C implementation of Python 2 (CPython 2) has been used (specifically the Python 2.7 standard). Most of the application has been written with Python 3 in mind however, and should run without issue. Some level of testing on Python 3 has been done, but there is no guarantee it will work (for more information on this please see [[Python Version]] on the wiki) +Whilst the Python version used is not critical to the running of the application, using the same version usually helps avoid a lot of issues. Orginally written with the C implementation of Python 2 (CPython 2, specifically the Python 2.7 standard), the application now runs in Python 3. Once you have your Python distribution installed, go ahead an follow the steps to set up a virtualenv, which will isolate the project from the system environment. @@ -75,5 +70,42 @@ python manage.py runserver ``` Please refer to Django documentation for a full list of options available here. -### Committing, pushing and testing ### -Feel free to commit as you wish, on your own branch. On my branch (master for development) do not commit code that you either know doesn't work or don't know works. If you must commit this code, please make sure you say in the commit message that it isn't working, and if you can why it isn't working. If and only if you absolutely must push, then please don't leave it as the HEAD for too long, it's not much to ask but when you are done just make sure you haven't broken the HEAD for the next person. +### Development using docker + +``` +docker build . -t pyrigs +docker run -it --rm -p=8000:8000 -v $(pwd):/app pyrigs +``` + +### Sample Data ### +Sample data is available to aid local development and user acceptance testing. To load this data into your local database, first ensure the database is empty: +``` +python manage.py flush +``` +Then load the sample data using the command: +``` +python manage.py generateSampleData +``` +4 user accounts are created for convenience: + +|Username |Password | +|---------|---------| +|superuser|superuser| +|finance |finance | +|keyholder|keyholder| +|basic |basic | + +### Testing ### +Tests are contained in 3 files. `RIGS/test_models.py` contains tests for logic within the data models. `RIGS/test_unit.py` contains "Live server" tests, using raw web requests. `RIGS/test_integration.py` contains user interface tests which take control of a web browser. For automated Travis tests, we use [Sauce Labs](https://saucelabs.com). When debugging locally, ensure that you have the latest version of Google Chrome installed, then install [chromedriver](https://sites.google.com/a/chromium.org/chromedriver/) and ensure it is on the `PATH`. + +You can run the entire test suite, or you can run specific sections individually. For example, in order of specificity: + +``` +python manage.py test +python manage.py test RIGS.test_models +python manage.py test RIGS.test_models.EventTestCase +python manage.py test RIGS.test_models.EventTestCase.test_current_events + +``` + +[![forthebadge](https://forthebadge.com/images/badges/built-with-resentment.svg)](https://forthebadge.com) [![forthebadge](https://forthebadge.com/images/badges/contains-technical-debt.svg)](https://forthebadge.com) diff --git a/RIGS/__init__.py b/RIGS/__init__.py index e69de29b..f6f847cc 100644 --- a/RIGS/__init__.py +++ b/RIGS/__init__.py @@ -0,0 +1 @@ +default_app_config = 'RIGS.apps.RIGSAppConfig' diff --git a/RIGS/admin.py b/RIGS/admin.py index a351aed0..49b8aa1e 100644 --- a/RIGS/admin.py +++ b/RIGS/admin.py @@ -2,7 +2,7 @@ from django.contrib import admin from RIGS import models, forms from django.contrib.auth.admin import UserAdmin from django.utils.translation import ugettext_lazy as _ -import reversion +from reversion.admin import VersionAdmin from django.contrib.admin import helpers from django.template.response import TemplateResponse @@ -12,10 +12,12 @@ from django.core.exceptions import ObjectDoesNotExist from django.db.models import Count from django.forms import ModelForm +from reversion import revisions as reversion + # Register your models here. -admin.site.register(models.VatRate, reversion.VersionAdmin) -admin.site.register(models.Event, reversion.VersionAdmin) -admin.site.register(models.EventItem, reversion.VersionAdmin) +admin.site.register(models.VatRate, VersionAdmin) +admin.site.register(models.Event, VersionAdmin) +admin.site.register(models.EventItem, VersionAdmin) admin.site.register(models.Invoice) admin.site.register(models.Payment) @@ -41,7 +43,7 @@ class ProfileAdmin(UserAdmin): add_form = forms.ProfileCreationForm -class AssociateAdmin(reversion.VersionAdmin): +class AssociateAdmin(VersionAdmin): list_display = ('id', 'name', 'number_of_events') search_fields = ['id', 'name'] list_display_links = ['id', 'name'] @@ -93,8 +95,7 @@ class AssociateAdmin(reversion.VersionAdmin): 'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME, 'forms': forms } - return TemplateResponse(request, 'RIGS/admin_associate_merge.html', context, - current_app=self.admin_site.name) + return TemplateResponse(request, 'RIGS/admin_associate_merge.html', context) @admin.register(models.Person) diff --git a/RIGS/apps.py b/RIGS/apps.py new file mode 100644 index 00000000..a0dc3724 --- /dev/null +++ b/RIGS/apps.py @@ -0,0 +1,8 @@ +from django.apps import AppConfig + + +class RIGSAppConfig(AppConfig): + name = 'RIGS' + + def ready(self): + import RIGS.signals diff --git a/RIGS/finance.py b/RIGS/finance.py index c8e0a70b..f0a4ed43 100644 --- a/RIGS/finance.py +++ b/RIGS/finance.py @@ -1,9 +1,8 @@ -import cStringIO as StringIO import datetime import re from django.contrib import messages -from django.core.urlresolvers import reverse_lazy +from django.urls import reverse_lazy from django.http import Http404, HttpResponseRedirect from django.http import HttpResponse from django.shortcuts import get_object_or_404 @@ -16,6 +15,9 @@ from django.db.models import Q from RIGS import models +from django import forms +forms.DateField.widget = forms.DateInput(attrs={'type': 'date'}) + class InvoiceIndex(generic.ListView): model = models.Invoice @@ -56,8 +58,8 @@ class InvoicePrint(generic.View): invoice = get_object_or_404(models.Invoice, pk=pk) object = invoice.event template = get_template('RIGS/event_print.xml') - copies = ('TEC', 'Client') - context = RequestContext(request, { + + context = { 'object': object, 'fonts': { 'opensans': { @@ -67,10 +69,9 @@ class InvoicePrint(generic.View): }, 'invoice': invoice, 'current_user': request.user, - }) + } rml = template.render(context) - buffer = StringIO.StringIO() buffer = rml2pdf.parseString(rml) @@ -79,7 +80,7 @@ class InvoicePrint(generic.View): escapedEventName = re.sub('[^a-zA-Z0-9 \n\.]', '', object.name) response = HttpResponse(content_type='application/pdf') - response['Content-Disposition'] = "filename=Invoice %05d | %s.pdf" % (invoice.pk, escapedEventName) + response['Content-Disposition'] = "filename=Invoice %05d - N%05d | %s.pdf" % (invoice.pk, invoice.event.pk, escapedEventName) response.write(pdfData) return response @@ -96,6 +97,27 @@ class InvoiceVoid(generic.View): return HttpResponseRedirect(reverse_lazy('invoice_detail', kwargs={'pk': object.pk})) +class InvoiceDelete(generic.DeleteView): + model = models.Invoice + + def get(self, request, pk): + obj = self.get_object() + if obj.payment_set.all().count() > 0: + messages.info(self.request, 'To delete an invoice, delete the payments first.') + return HttpResponseRedirect(reverse_lazy('invoice_detail', kwargs={'pk': obj.pk})) + return super(InvoiceDelete, self).get(pk) + + def post(self, request, pk): + obj = self.get_object() + if obj.payment_set.all().count() > 0: + messages.info(self.request, 'To delete an invoice, delete the payments first.') + return HttpResponseRedirect(reverse_lazy('invoice_detail', kwargs={'pk': obj.pk})) + return super(InvoiceDelete, self).post(pk) + + def get_success_url(self): + return self.request.POST.get('next') + + class InvoiceArchive(generic.ListView): model = models.Invoice template_name = 'RIGS/invoice_list_archive.html' @@ -133,7 +155,7 @@ class InvoiceArchive(generic.ListView): class InvoiceWaiting(generic.ListView): model = models.Event - # paginate_by = 25 + paginate_by = 25 template_name = 'RIGS/event_invoice.html' def get_context_data(self, **kwargs): @@ -153,11 +175,11 @@ class InvoiceWaiting(generic.ListView): events = self.model.objects.filter( ( Q(start_date__lte=datetime.date.today(), end_date__isnull=True) | # Starts before with no end - Q(end_date__lte=datetime.date.today()) # Has end date, finishes before - ) & Q(invoice__isnull=True) # Has not already been invoiced - & Q(is_rig=True) # Is a rig (not non-rig) - - ).order_by('start_date') \ + Q(end_date__lte=datetime.date.today()) # Has end date, finishes before + ) & Q(invoice__isnull=True) & # Has not already been invoiced + Q(is_rig=True) # Is a rig (not non-rig) + + ).order_by('start_date') \ .select_related('person', 'organisation', 'venue', 'mic') \ @@ -186,7 +208,7 @@ class PaymentCreate(generic.CreateView): def get_initial(self): initial = super(generic.CreateView, self).get_initial() invoicepk = self.request.GET.get('invoice', self.request.POST.get('invoice', None)) - if invoicepk == None: + if invoicepk is None: raise Http404() invoice = get_object_or_404(models.Invoice, pk=invoicepk) initial.update({'invoice': invoice}) diff --git a/RIGS/forms.py b/RIGS/forms.py index e1e95012..47b0f062 100644 --- a/RIGS/forms.py +++ b/RIGS/forms.py @@ -1,4 +1,3 @@ -__author__ = 'Ghost' from django import forms from django.utils import formats from django.conf import settings @@ -10,14 +9,20 @@ import simplejson from RIGS import models +# Override the django form defaults to use the HTML date/time/datetime UI elements +forms.DateField.widget = forms.DateInput(attrs={'type': 'date'}) +forms.TimeField.widget = forms.TextInput(attrs={'type': 'time'}) +forms.DateTimeField.widget = forms.DateTimeInput(attrs={'type': 'datetime-local'}) # Registration + + class ProfileRegistrationFormUniqueEmail(RegistrationFormUniqueEmail): captcha = ReCaptchaField() class Meta: model = models.Profile - fields = ('username', 'email', 'first_name', 'last_name', 'initials', 'phone') + fields = ('username', 'email', 'first_name', 'last_name', 'initials') def clean_initials(self): """ @@ -28,7 +33,13 @@ class ProfileRegistrationFormUniqueEmail(RegistrationFormUniqueEmail): return self.cleaned_data['initials'] -# Login form +# Embedded Login form - remove the autofocus +class EmbeddedAuthenticationForm(AuthenticationForm): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['username'].widget.attrs.pop('autofocus', None) + + class PasswordReset(PasswordResetForm): captcha = ReCaptchaField(label='Captcha') @@ -45,7 +56,7 @@ class ProfileChangeForm(UserChangeForm): # Events Shit class EventForm(forms.ModelForm): - datetime_input_formats = formats.get_format_lazy("DATETIME_INPUT_FORMATS") + settings.DATETIME_INPUT_FORMATS + datetime_input_formats = formats.get_format_lazy("DATETIME_INPUT_FORMATS") + list(settings.DATETIME_INPUT_FORMATS) meet_at = forms.DateTimeField(input_formats=datetime_input_formats, required=False) access_at = forms.DateTimeField(input_formats=datetime_input_formats, required=False) @@ -118,6 +129,11 @@ class EventForm(forms.ModelForm): return item + def clean(self): + if self.cleaned_data.get("is_rig") and not (self.cleaned_data.get('person') or self.cleaned_data.get('organisation')): + raise forms.ValidationError('You haven\'t provided any client contact details. Please add a person or organisation.', code='contact') + return super(EventForm, self).clean() + def save(self, commit=True): m = super(EventForm, self).save(commit=False) @@ -140,4 +156,32 @@ class EventForm(forms.ModelForm): fields = ['is_rig', 'name', 'venue', 'start_time', 'end_date', 'start_date', 'end_time', 'meet_at', 'access_at', 'description', 'notes', 'mic', 'person', 'organisation', 'dry_hire', 'checked_in_by', 'status', - 'collector', 'purchase_order'] + 'purchase_order', 'collector'] + + +class BaseClientEventAuthorisationForm(forms.ModelForm): + tos = forms.BooleanField(required=True, label="Terms of hire") + name = forms.CharField(label="Your Name") + + def clean(self): + if self.cleaned_data.get('amount') != self.instance.event.total: + self.add_error('amount', 'The amount authorised must equal the total for the event (inc VAT).') + return super(BaseClientEventAuthorisationForm, self).clean() + + class Meta: + abstract = True + + +class InternalClientEventAuthorisationForm(BaseClientEventAuthorisationForm): + def __init__(self, **kwargs): + super(InternalClientEventAuthorisationForm, self).__init__(**kwargs) + self.fields['uni_id'].required = True + self.fields['account_code'].required = True + + class Meta: + model = models.EventAuthorisation + fields = ('tos', 'name', 'amount', 'uni_id', 'account_code') + + +class EventAuthorisationRequestForm(forms.Form): + email = forms.EmailField(required=True, label='Authoriser Email') diff --git a/RIGS/ical.py b/RIGS/ical.py index 47ee669e..6317f1fb 100644 --- a/RIGS/ical.py +++ b/RIGS/ical.py @@ -1,17 +1,19 @@ from RIGS import models, forms from django_ical.views import ICalFeed from django.db.models import Q -from django.core.urlresolvers import reverse_lazy, reverse, NoReverseMatch +from django.urls import reverse_lazy, reverse, NoReverseMatch from django.utils import timezone from django.conf import settings -import datetime, pytz +import datetime +import pytz + class CalendarICS(ICalFeed): """ A simple event calender """ - #Metadata which is passed on to clients + # Metadata which is passed on to clients product_id = 'RIGS' title = 'RIGS Calendar' timezone = settings.TIME_ZONE @@ -27,39 +29,39 @@ class CalendarICS(ICalFeed): def get_object(self, request, *args, **kwargs): params = {} - params['dry-hire'] = request.GET.get('dry-hire','true') == 'true' - params['non-rig'] = request.GET.get('non-rig','true') == 'true' - params['rig'] = request.GET.get('rig','true') == 'true' + params['dry-hire'] = request.GET.get('dry-hire', 'true') == 'true' + params['non-rig'] = request.GET.get('non-rig', 'true') == 'true' + params['rig'] = request.GET.get('rig', 'true') == 'true' - params['cancelled'] = request.GET.get('cancelled','false') == 'true' - params['provisional'] = request.GET.get('provisional','true') == 'true' - params['confirmed'] = request.GET.get('confirmed','true') == 'true' + params['cancelled'] = request.GET.get('cancelled', 'false') == 'true' + params['provisional'] = request.GET.get('provisional', 'true') == 'true' + params['confirmed'] = request.GET.get('confirmed', 'true') == 'true' return params - def description(self,params): + def description(self, params): desc = "Calendar generated by RIGS system. This includes event types: " + ('Rig, ' if params['rig'] else '') + ('Non-rig, ' if params['non-rig'] else '') + ('Dry Hire ' if params['dry-hire'] else '') + '\n' desc = desc + "Includes events with status: " + ('Cancelled, ' if params['cancelled'] else '') + ('Provisional, ' if params['provisional'] else '') + ('Confirmed/Booked, ' if params['confirmed'] else '') return desc def items(self, params): - #include events from up to 1 year ago + # include events from up to 1 year ago start = datetime.datetime.now() - datetime.timedelta(days=365) filter = Q(start_date__gte=start) - typeFilters = Q(pk=None) #Need something that is false for every entry + typeFilters = Q(pk=None) # Need something that is false for every entry if params['dry-hire']: typeFilters = typeFilters | Q(dry_hire=True, is_rig=True) if params['non-rig']: typeFilters = typeFilters | Q(is_rig=False) - + if params['rig']: typeFilters = typeFilters | Q(is_rig=True, dry_hire=False) - - statusFilters = Q(pk=None) #Need something that is false for every entry + + statusFilters = Q(pk=None) # Need something that is false for every entry if params['cancelled']: statusFilters = statusFilters | Q(status=models.Event.CANCELLED) @@ -87,9 +89,9 @@ class CalendarICS(ICalFeed): # Add the rig name title += item.name - + # Add the status - title += ' ('+str(item.get_status_display())+')' + title += ' (' + str(item.get_status_display()) + ')' return title @@ -97,12 +99,12 @@ class CalendarICS(ICalFeed): return item.earliest_time def item_end_datetime(self, item): - if type(item.latest_time) is datetime.date: # Ical end_datetime is non-inclusive, so add a day + if type(item.latest_time) == datetime.date: # Ical end_datetime is non-inclusive, so add a day return item.latest_time + datetime.timedelta(days=1) return item.latest_time - def item_location(self,item): + def item_location(self, item): return item.venue def item_description(self, item): @@ -111,34 +113,33 @@ class CalendarICS(ICalFeed): tz = pytz.timezone(self.timezone) - desc = 'Rig ID = '+str(item.pk)+'\n' + desc = 'Rig ID = ' + str(item.pk) + '\n' desc += 'Event = ' + item.name + '\n' desc += 'Venue = ' + (item.venue.name if item.venue else '---') + '\n' if item.is_rig and item.person: - desc += 'Client = ' + item.person.name + ( (' for '+item.organisation.name) if item.organisation else '') + '\n' + desc += 'Client = ' + item.person.name + ((' for ' + item.organisation.name) if item.organisation else '') + '\n' desc += 'Status = ' + str(item.get_status_display()) + '\n' desc += 'MIC = ' + (item.mic.name if item.mic else '---') + '\n' - - + desc += '\n' if item.meet_at: desc += 'Crew Meet = ' + (item.meet_at.astimezone(tz).strftime('%Y-%m-%d %H:%M') if item.meet_at else '---') + '\n' if item.access_at: desc += 'Access At = ' + (item.access_at.astimezone(tz).strftime('%Y-%m-%d %H:%M') if item.access_at else '---') + '\n' if item.start_date: - desc += 'Event Start = ' + item.start_date.strftime('%Y-%m-%d') + ((' '+item.start_time.strftime('%H:%M')) if item.has_start_time else '') + '\n' + desc += 'Event Start = ' + item.start_date.strftime('%Y-%m-%d') + ((' ' + item.start_time.strftime('%H:%M')) if item.has_start_time else '') + '\n' if item.end_date: - desc += 'Event End = ' + item.end_date.strftime('%Y-%m-%d') + ((' '+item.end_time.strftime('%H:%M')) if item.has_end_time else '') + '\n' + desc += 'Event End = ' + item.end_date.strftime('%Y-%m-%d') + ((' ' + item.end_time.strftime('%H:%M')) if item.has_end_time else '') + '\n' desc += '\n' if item.description: - desc += 'Event Description:\n'+item.description+'\n\n' + desc += 'Event Description:\n' + item.description + '\n\n' # if item.notes: // Need to add proper keyholder checks before this gets put back - # desc += 'Notes:\n'+item.notes+'\n\n' + # desc += 'Notes:\n'+item.notes+'\n\n' + + base_url = "https://rigs.nottinghamtec.co.uk" + desc += 'URL = ' + base_url + str(item.get_absolute_url()) - base_url = "http://rigs.nottinghamtec.co.uk" - desc += 'URL = '+base_url+str(item.get_absolute_url()) - return desc def item_link(self, item): @@ -149,8 +150,8 @@ class CalendarICS(ICalFeed): # def item_created(self, item): #TODO - Implement created date-time (using django-reversion?) - not really necessary though # return '' - def item_updated(self, item): # some ical clients will display this + def item_updated(self, item): # some ical clients will display this return item.last_edited_at - def item_guid(self, item): # use the rig-id as the ical unique event identifier - return item.pk \ No newline at end of file + def item_guid(self, item): # use the rig-id as the ical unique event identifier + return item.pk diff --git a/RIGS/management/__init__.py b/RIGS/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/RIGS/management/commands/__init__.py b/RIGS/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/RIGS/management/commands/generateSampleData.py b/RIGS/management/commands/generateSampleData.py new file mode 100644 index 00000000..e19c569d --- /dev/null +++ b/RIGS/management/commands/generateSampleData.py @@ -0,0 +1,11 @@ +from django.core.management.base import BaseCommand, CommandError +from django.core.management import call_command + + +class Command(BaseCommand): + help = 'Adds sample data to use for testing' + can_import_settings = True + + def handle(self, *args, **options): + call_command('generateSampleRIGSData') + call_command('generateSampleAssetsData') diff --git a/RIGS/management/commands/generateSampleRIGSData.py b/RIGS/management/commands/generateSampleRIGSData.py new file mode 100644 index 00000000..6b543971 --- /dev/null +++ b/RIGS/management/commands/generateSampleRIGSData.py @@ -0,0 +1,260 @@ +from django.core.management.base import BaseCommand, CommandError +from django.contrib.auth.models import Group, Permission +from django.db import transaction +from reversion import revisions as reversion + +import datetime +import random + +from RIGS import models + + +class Command(BaseCommand): + help = 'Adds sample data to use for testing' + can_import_settings = True + + people = [] + organisations = [] + venues = [] + profiles = [] + + keyholder_group = None + finance_group = None + + def handle(self, *args, **options): + from django.conf import settings + + if not (settings.DEBUG or settings.STAGING): + raise CommandError('You cannot run this command in production') + + random.seed('Some object to seed the random number generator') # otherwise it is done by time, which could lead to inconsistant tests + + with transaction.atomic(): + models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1') + + self.setupGenericProfiles() + + self.setupPeople() + self.setupOrganisations() + self.setupVenues() + + self.setupGroups() + + self.setupEvents() + + self.setupUsefulProfiles() + + def setupPeople(self): + names = ["Regulus Black", "Sirius Black", "Lavender Brown", "Cho Chang", "Vincent Crabbe", "Vincent Crabbe", "Bartemius Crouch", "Fleur Delacour", "Cedric Diggory", "Alberforth Dumbledore", "Albus Dumbledore", "Dudley Dursley", "Petunia Dursley", "Vernon Dursley", "Argus Filch", "Seamus Finnigan", "Nicolas Flamel", "Cornelius Fudge", "Goyle", "Gregory Goyle", "Hermione Granger", "Rubeus Hagrid", "Igor Karkaroff", "Viktor Krum", "Bellatrix Lestrange", "Alice Longbottom", "Frank Longbottom", "Neville Longbottom", "Luna Lovegood", "Xenophilius Lovegood", # noqa + "Remus Lupin", "Draco Malfoy", "Lucius Malfoy", "Narcissa Malfoy", "Olympe Maxime", "Minerva McGonagall", "Mad-Eye Moody", "Peter Pettigrew", "Harry Potter", "James Potter", "Lily Potter", "Quirinus Quirrell", "Tom Riddle", "Mary Riddle", "Lord Voldemort", "Rita Skeeter", "Severus Snape", "Nymphadora Tonks", "Dolores Janes Umbridge", "Arthur Weasley", "Bill Weasley", "Charlie Weasley", "Fred Weasley", "George Weasley", "Ginny Weasley", "Molly Weasley", "Percy Weasley", "Ron Weasley", "Dobby", "Fluffy", "Hedwig", "Moaning Myrtle", "Aragog", "Grawp"] # noqa + for i, name in enumerate(names): + with reversion.create_revision(): + reversion.set_user(random.choice(self.profiles)) + + newPerson = models.Person.objects.create(name=name) + if i % 3 == 0: + newPerson.email = "address@person.com" + + if i % 5 == 0: + newPerson.notes = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" + + if i % 7 == 0: + newPerson.address = "1 Person Test Street \n Demoton \n United States of TEC \n RMRF 567" + + if i % 9 == 0: + newPerson.phone = "01234 567894" + + newPerson.save() + self.people.append(newPerson) + + def setupOrganisations(self): + names = ["Acme, inc.", "Widget Corp", "123 Warehousing", "Demo Company", "Smith and Co.", "Foo Bars", "ABC Telecom", "Fake Brothers", "QWERTY Logistics", "Demo, inc.", "Sample Company", "Sample, inc", "Acme Corp", "Allied Biscuit", "Ankh-Sto Associates", "Extensive Enterprise", "Galaxy Corp", "Globo-Chem", "Mr. Sparkle", "Globex Corporation", "LexCorp", "LuthorCorp", "North Central Positronics", "Omni Consimer Products", "Praxis Corporation", "Sombra Corporation", "Sto Plains Holdings", "Tessier-Ashpool", "Wayne Enterprises", "Wentworth Industries", "ZiffCorp", "Bluth Company", "Strickland Propane", "Thatherton Fuels", "Three Waters", "Water and Power", "Western Gas & Electric", "Mammoth Pictures", "Mooby Corp", "Gringotts", "Thrift Bank", "Flowers By Irene", "The Legitimate Businessmens Club", "Osato Chemicals", "Transworld Consortium", "Universal Export", "United Fried Chicken", "Virtucon", "Kumatsu Motors", "Keedsler Motors", "Powell Motors", "Industrial Automation", "Sirius Cybernetics Corporation", "U.S. Robotics and Mechanical Men", "Colonial Movers", "Corellian Engineering Corporation", "Incom Corporation", "General Products", "Leeding Engines Ltd.", "Blammo", # noqa + "Input, Inc.", "Mainway Toys", "Videlectrix", "Zevo Toys", "Ajax", "Axis Chemical Co.", "Barrytron", "Carrys Candles", "Cogswell Cogs", "Spacely Sprockets", "General Forge and Foundry", "Duff Brewing Company", "Dunder Mifflin", "General Services Corporation", "Monarch Playing Card Co.", "Krustyco", "Initech", "Roboto Industries", "Primatech", "Sonky Rubber Goods", "St. Anky Beer", "Stay Puft Corporation", "Vandelay Industries", "Wernham Hogg", "Gadgetron", "Burleigh and Stronginthearm", "BLAND Corporation", "Nordyne Defense Dynamics", "Petrox Oil Company", "Roxxon", "McMahon and Tate", "Sixty Second Avenue", "Charles Townsend Agency", "Spade and Archer", "Megadodo Publications", "Rouster and Sideways", "C.H. Lavatory and Sons", "Globo Gym American Corp", "The New Firm", "SpringShield", "Compuglobalhypermeganet", "Data Systems", "Gizmonic Institute", "Initrode", "Taggart Transcontinental", "Atlantic Northern", "Niagular", "Plow King", "Big Kahuna Burger", "Big T Burgers and Fries", "Chez Quis", "Chotchkies", "The Frying Dutchman", "Klimpys", "The Krusty Krab", "Monks Diner", "Milliways", "Minuteman Cafe", "Taco Grande", "Tip Top Cafe", "Moes Tavern", "Central Perk", "Chasers"] # noqa + for i, name in enumerate(names): + with reversion.create_revision(): + reversion.set_user(random.choice(self.profiles)) + newOrganisation = models.Organisation.objects.create(name=name) + if i % 2 == 0: + newOrganisation.has_su_account = True + + if i % 3 == 0: + newOrganisation.email = "address@organisation.com" + + if i % 5 == 0: + newOrganisation.notes = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" + + if i % 7 == 0: + newOrganisation.address = "1 Organisation Test Street \n Demoton \n United States of TEC \n RMRF 567" + + if i % 9 == 0: + newOrganisation.phone = "01234 567894" + + newOrganisation.save() + self.organisations.append(newOrganisation) + + def setupVenues(self): + names = ["Bear Island", "Crossroads Inn", "Deepwood Motte", "The Dreadfort", "The Eyrie", "Greywater Watch", "The Iron Islands", "Karhold", "Moat Cailin", "Oldstones", "Raventree Hall", "Riverlands", "The Ruby Ford", "Saltpans", "Seagard", "Torrhen's Square", "The Trident", "The Twins", "The Vale of Arryn", "The Whispering Wood", "White Harbor", "Winterfell", "The Arbor", "Ashemark", "Brightwater Keep", "Casterly Rock", "Clegane's Keep", "Dragonstone", "Dorne", "God's Eye", "The Golden Tooth", # noqa + "Harrenhal", "Highgarden", "Horn Hill", "Fingers", "King's Landing", "Lannisport", "Oldtown", "Rainswood", "Storm's End", "Summerhall", "Sunspear", "Tarth", "Castle Black", "Craster's Keep", "Fist of the First Men", "The Frostfangs", "The Gift", "The Skirling Pass", "The Wall", "Asshai", "Astapor", "Braavos", "The Dothraki Sea", "Lys", "Meereen", "Myr", "Norvos", "Pentos", "Qarth", "Qohor", "The Red Waste", "Tyrosh", "Vaes Dothrak", "Valyria", "Village of the Lhazareen", "Volantis", "Yunkai"] # noqa + for i, name in enumerate(names): + with reversion.create_revision(): + reversion.set_user(random.choice(self.profiles)) + newVenue = models.Venue.objects.create(name=name) + if i % 2 == 0: + newVenue.three_phase_available = True + + if i % 3 == 0: + newVenue.email = "address@venue.com" + + if i % 5 == 0: + newVenue.notes = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" + + if i % 7 == 0: + newVenue.address = "1 Venue Test Street \n Demoton \n United States of TEC \n RMRF 567" + + if i % 9 == 0: + newVenue.phone = "01234 567894" + + newVenue.save() + self.venues.append(newVenue) + + def setupGroups(self): + self.keyholder_group = Group.objects.create(name='Keyholders') + self.finance_group = Group.objects.create(name='Finance') + + keyholderPerms = ["add_event", "change_event", "view_event", + "add_eventitem", "change_eventitem", "delete_eventitem", + "add_organisation", "change_organisation", "view_organisation", + "add_person", "change_person", "view_person", "view_profile", + "add_venue", "change_venue", "view_venue", + "add_asset", "change_asset", "delete_asset", + "asset_finance", "view_asset", "view_supplier", "asset_finance", + "add_supplier"] + financePerms = keyholderPerms + ["add_invoice", "change_invoice", "view_invoice", + "add_payment", "change_payment", "delete_payment"] + + for permId in keyholderPerms: + self.keyholder_group.permissions.add(Permission.objects.get(codename=permId)) + + for permId in financePerms: + self.finance_group.permissions.add(Permission.objects.get(codename=permId)) + + def setupGenericProfiles(self): + names = ["Clara Oswin Oswald", "Rory Williams", "Amy Pond", "River Song", "Martha Jones", "Donna Noble", "Jack Harkness", "Mickey Smith", "Rose Tyler"] + for i, name in enumerate(names): + newProfile = models.Profile.objects.create(username=name.replace(" ", ""), first_name=name.split(" ")[0], last_name=name.split(" ")[-1], + email=name.replace(" ", "") + "@example.com", + initials="".join([j[0].upper() for j in name.split()])) + if i % 2 == 0: + newProfile.phone = "01234 567894" + + newProfile.save() + self.profiles.append(newProfile) + + def setupUsefulProfiles(self): + superUser = models.Profile.objects.create(username="superuser", first_name="Super", last_name="User", initials="SU", + email="superuser@example.com", is_superuser=True, is_active=True, is_staff=True) + superUser.set_password('superuser') + superUser.save() + + financeUser = models.Profile.objects.create(username="finance", first_name="Finance", last_name="User", initials="FU", + email="financeuser@example.com", is_active=True) + financeUser.groups.add(self.finance_group) + financeUser.groups.add(self.keyholder_group) + financeUser.set_password('finance') + financeUser.save() + + keyholderUser = models.Profile.objects.create(username="keyholder", first_name="Keyholder", last_name="User", initials="KU", + email="keyholderuser@example.com", is_active=True) + keyholderUser.groups.add(self.keyholder_group) + keyholderUser.set_password('keyholder') + keyholderUser.save() + + basicUser = models.Profile.objects.create(username="basic", first_name="Basic", last_name="User", initials="BU", + email="basicuser@example.com", is_active=True) + basicUser.set_password('basic') + basicUser.save() + + def setupEvents(self): + names = ["Outdoor Concert", "Hall Open Mic Night", "Festival", "Weekend Event", "Magic Show", "Society Ball", "Evening Show", "Talent Show", "Acoustic Evening", "Hire of Things", "SU Event", + "End of Term Show", "Theatre Show", "Outdoor Fun Day", "Summer Carnival", "Open Days", "Magic Show", "Awards Ceremony", "Debating Event", "Club Night", "DJ Evening", "Building Projection", "Choir Concert"] + descriptions = ["A brief desciption of the event", "This event is boring", "Probably wont happen", "Warning: this has lots of kit"] + notes = ["The client came into the office at some point", "Who knows if this will happen", "Probably should check this event", "Maybe not happening", "Run away!"] + + itemOptions = [{'name': 'Speakers', 'description': 'Some really really big speakers \n these are very loud', 'quantity': 2, 'cost': 200.00}, + {'name': 'Projector', 'description': 'Some kind of video thinamejig, probably with unnecessary processing for free', 'quantity': 1, 'cost': 500.00}, + {'name': 'Lighting Desk', 'description': 'Cannot provide guarentee that it will work', 'quantity': 1, 'cost': 200.52}, + {'name': 'Moving lights', 'description': 'Flashy lights, with the copper', 'quantity': 8, 'cost': 50.00}, + {'name': 'Microphones', 'description': 'Make loud noise \n you will want speakers with this', 'quantity': 5, 'cost': 0.50}, + {'name': 'Sound Mixer Thing', 'description': 'Might be analogue, might be digital', 'quantity': 1, 'cost': 100.00}, + {'name': 'Electricity', 'description': 'You need this', 'quantity': 1, 'cost': 200.00}, + {'name': 'Crew', 'description': 'Costs nothing, because reasons', 'quantity': 1, 'cost': 0.00}, + {'name': 'Loyalty Discount', 'description': 'Have some negative moneys', 'quantity': 1, 'cost': -50.00}] + + dayDelta = -120 # start adding events from 4 months ago + + for i in range(150): # Let's add 100 events + with reversion.create_revision(): + reversion.set_user(random.choice(self.profiles)) + + name = names[i % len(names)] + + startDate = datetime.date.today() + datetime.timedelta(days=dayDelta) + dayDelta = dayDelta + random.randint(0, 3) + + newEvent = models.Event.objects.create(name=name, start_date=startDate) + + if random.randint(0, 2) > 1: # 1 in 3 have a start time + newEvent.start_time = datetime.time(random.randint(15, 20)) + if random.randint(0, 2) > 1: # of those, 1 in 3 have an end time on the same day + newEvent.end_time = datetime.time(random.randint(21, 23)) + elif random.randint(0, 1) > 0: # half of the others finish early the next day + newEvent.end_date = newEvent.start_date + datetime.timedelta(days=1) + newEvent.end_time = datetime.time(random.randint(0, 5)) + elif random.randint(0, 2) > 1: # 1 in 3 of the others finish a few days ahead + newEvent.end_date = newEvent.start_date + datetime.timedelta(days=random.randint(1, 4)) + + if random.randint(0, 6) > 0: # 5 in 6 have MIC + newEvent.mic = random.choice(self.profiles) + + if random.randint(0, 6) > 0: # 5 in 6 have organisation + newEvent.organisation = random.choice(self.organisations) + + if random.randint(0, 6) > 0: # 5 in 6 have person + newEvent.person = random.choice(self.people) + + if random.randint(0, 6) > 0: # 5 in 6 have venue + newEvent.venue = random.choice(self.venues) + + # Could have any status, equally weighted + newEvent.status = random.choice([models.Event.BOOKED, models.Event.CONFIRMED, models.Event.PROVISIONAL, models.Event.CANCELLED]) + + newEvent.dry_hire = (random.randint(0, 7) == 0) # 1 in 7 are dry hire + + if random.randint(0, 1) > 0: # 1 in 2 have description + newEvent.description = random.choice(descriptions) + + if random.randint(0, 1) > 0: # 1 in 2 have notes + newEvent.notes = random.choice(notes) + + newEvent.save() + + # Now add some items + for j in range(random.randint(1, 5)): + itemData = itemOptions[random.randint(0, len(itemOptions) - 1)] + newItem = models.EventItem.objects.create(event=newEvent, order=j, **itemData) + newItem.save() + + while newEvent.sum_total < 0: + itemData = itemOptions[random.randint(0, len(itemOptions) - 1)] + newItem = models.EventItem.objects.create(event=newEvent, order=j, **itemData) + newItem.save() + + with reversion.create_revision(): + reversion.set_user(random.choice(self.profiles)) + if newEvent.start_date < datetime.date.today(): # think about adding an invoice + if random.randint(0, 2) > 0: # 2 in 3 have had paperwork sent to treasury + newInvoice = models.Invoice.objects.create(event=newEvent) + if newEvent.status is models.Event.CANCELLED: # void cancelled events + newInvoice.void = True + elif random.randint(0, 2) > 1: # 1 in 3 have been paid + models.Payment.objects.create(invoice=newInvoice, amount=newInvoice.balance, date=datetime.date.today()) diff --git a/RIGS/migrations/0001_initial.py b/RIGS/migrations/0001_initial.py index 62687347..8440a997 100644 --- a/RIGS/migrations/0001_initial.py +++ b/RIGS/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations import django.core.validators diff --git a/RIGS/migrations/0002_modelcomment_person.py b/RIGS/migrations/0002_modelcomment_person.py index 346d46f0..28a37290 100644 --- a/RIGS/migrations/0002_modelcomment_person.py +++ b/RIGS/migrations/0002_modelcomment_person.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations from django.conf import settings @@ -18,7 +18,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('postedAt', models.DateTimeField(auto_now=True)), ('message', models.TextField()), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), ], options={ }, diff --git a/RIGS/migrations/0003_auto_20141031_0219.py b/RIGS/migrations/0003_auto_20141031_0219.py index 3bb2f635..ce949ac0 100644 --- a/RIGS/migrations/0003_auto_20141031_0219.py +++ b/RIGS/migrations/0003_auto_20141031_0219.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations diff --git a/RIGS/migrations/0004_organisation.py b/RIGS/migrations/0004_organisation.py index bf1bbbf4..06e29955 100644 --- a/RIGS/migrations/0004_organisation.py +++ b/RIGS/migrations/0004_organisation.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations import RIGS.models diff --git a/RIGS/migrations/0005_auto_20141104_1619.py b/RIGS/migrations/0005_auto_20141104_1619.py index 0e8b24d2..4bc9e97c 100644 --- a/RIGS/migrations/0005_auto_20141104_1619.py +++ b/RIGS/migrations/0005_auto_20141104_1619.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations diff --git a/RIGS/migrations/0006_auto_20141105_1553.py b/RIGS/migrations/0006_auto_20141105_1553.py index a125838e..67b53326 100644 --- a/RIGS/migrations/0006_auto_20141105_1553.py +++ b/RIGS/migrations/0006_auto_20141105_1553.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations diff --git a/RIGS/migrations/0007_vatrate.py b/RIGS/migrations/0007_vatrate.py index 07d7e497..02aa1625 100644 --- a/RIGS/migrations/0007_vatrate.py +++ b/RIGS/migrations/0007_vatrate.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations import RIGS.models diff --git a/RIGS/migrations/0008_auto_20141105_1908.py b/RIGS/migrations/0008_auto_20141105_1908.py index 2bba681c..141e9570 100644 --- a/RIGS/migrations/0008_auto_20141105_1908.py +++ b/RIGS/migrations/0008_auto_20141105_1908.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations from django.conf import settings @@ -33,11 +33,11 @@ class Migration(migrations.Migration): ('payment_method', models.CharField(blank=True, null=True, max_length=255)), ('payment_received', models.CharField(blank=True, null=True, max_length=255)), ('purchase_order', models.CharField(blank=True, null=True, max_length=255)), - ('based_on', models.ForeignKey(to='RIGS.Event', related_name='future_events')), - ('checked_in_by', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='event_checked_in')), - ('mic', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='event_mic')), - ('organisation', models.ForeignKey(to='RIGS.Organisation')), - ('person', models.ForeignKey(to='RIGS.Person')), + ('based_on', models.ForeignKey(to='RIGS.Event', related_name='future_events', on_delete=models.CASCADE)), + ('checked_in_by', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='event_checked_in', on_delete=models.CASCADE)), + ('mic', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='event_mic', on_delete=models.CASCADE)), + ('organisation', models.ForeignKey(to='RIGS.Organisation', on_delete=models.CASCADE)), + ('person', models.ForeignKey(to='RIGS.Person', on_delete=models.CASCADE)), ], options={ }, @@ -52,7 +52,7 @@ class Migration(migrations.Migration): ('quantity', models.IntegerField()), ('cost', models.DecimalField(max_digits=10, decimal_places=2)), ('order', models.IntegerField()), - ('event', models.ForeignKey(to='RIGS.Event', related_name='item')), + ('event', models.ForeignKey(to='RIGS.Event', related_name='item', on_delete=models.CASCADE)), ], options={ }, @@ -75,7 +75,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='event', name='venue', - field=models.ForeignKey(to='RIGS.Venue'), + field=models.ForeignKey(to='RIGS.Venue', on_delete=models.CASCADE), preserve_default=True, ), ] diff --git a/RIGS/migrations/0009_auto_20141105_1916.py b/RIGS/migrations/0009_auto_20141105_1916.py index 77973814..7ce3a310 100644 --- a/RIGS/migrations/0009_auto_20141105_1916.py +++ b/RIGS/migrations/0009_auto_20141105_1916.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations from django.conf import settings @@ -14,26 +14,26 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='event', name='based_on', - field=models.ForeignKey(to='RIGS.Event', related_name='future_events', blank=True, null=True), + field=models.ForeignKey(to='RIGS.Event', related_name='future_events', blank=True, null=True, on_delete=models.CASCADE), preserve_default=True, ), migrations.AlterField( model_name='event', name='checked_in_by', field=models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='event_checked_in', blank=True, - null=True), + null=True, on_delete=models.CASCADE), preserve_default=True, ), migrations.AlterField( model_name='event', name='mic', - field=models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='event_mic', blank=True, null=True), + field=models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='event_mic', blank=True, null=True, on_delete=models.CASCADE), preserve_default=True, ), migrations.AlterField( model_name='event', name='organisation', - field=models.ForeignKey(to='RIGS.Organisation', blank=True, null=True), + field=models.ForeignKey(to='RIGS.Organisation', blank=True, null=True, on_delete=models.CASCADE), preserve_default=True, ), migrations.AlterField( diff --git a/RIGS/migrations/0010_auto_20141105_2219.py b/RIGS/migrations/0010_auto_20141105_2219.py index e6a9ef25..88d3db5d 100644 --- a/RIGS/migrations/0010_auto_20141105_2219.py +++ b/RIGS/migrations/0010_auto_20141105_2219.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations from django.conf import settings @@ -19,8 +19,8 @@ class Migration(migrations.Migration): ('run', models.BooleanField(default=False)), ('derig', models.BooleanField(default=False)), ('notes', models.TextField(blank=True, null=True)), - ('event', models.ForeignKey(related_name='crew', to='RIGS.Event')), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ('event', models.ForeignKey(related_name='crew', to='RIGS.Event', on_delete=models.CASCADE)), + ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), ], options={ }, @@ -35,7 +35,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='eventitem', name='event', - field=models.ForeignKey(related_name='items', to='RIGS.Event'), + field=models.ForeignKey(related_name='items', to='RIGS.Event', on_delete=models.CASCADE), preserve_default=True, ), ] diff --git a/RIGS/migrations/0011_venue_address.py b/RIGS/migrations/0011_venue_address.py index 5973866f..eda0e1ba 100644 --- a/RIGS/migrations/0011_venue_address.py +++ b/RIGS/migrations/0011_venue_address.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations diff --git a/RIGS/migrations/0012_auto_20141106_0253.py b/RIGS/migrations/0012_auto_20141106_0253.py index 10fa6429..2c5c7b3a 100644 --- a/RIGS/migrations/0012_auto_20141106_0253.py +++ b/RIGS/migrations/0012_auto_20141106_0253.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations diff --git a/RIGS/migrations/0013_auto_20141202_0041.py b/RIGS/migrations/0013_auto_20141202_0041.py index 9403ad12..66519d3d 100644 --- a/RIGS/migrations/0013_auto_20141202_0041.py +++ b/RIGS/migrations/0013_auto_20141202_0041.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations @@ -14,7 +14,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='event', name='person', - field=models.ForeignKey(blank=True, null=True, to='RIGS.Person'), + field=models.ForeignKey(blank=True, null=True, to='RIGS.Person', on_delete=models.CASCADE), preserve_default=True, ), ] diff --git a/RIGS/migrations/0014_auto_20141208_0220.py b/RIGS/migrations/0014_auto_20141208_0220.py index f8d97f8f..389a8e89 100644 --- a/RIGS/migrations/0014_auto_20141208_0220.py +++ b/RIGS/migrations/0014_auto_20141208_0220.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations @@ -14,13 +14,13 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='event', name='venue', - field=models.ForeignKey(blank=True, to='RIGS.Venue', null=True), + field=models.ForeignKey(blank=True, to='RIGS.Venue', null=True, on_delete=models.CASCADE), preserve_default=True, ), migrations.AlterField( model_name='eventitem', name='event', - field=models.ForeignKey(related_name='items', blank=True, to='RIGS.Event'), + field=models.ForeignKey(related_name='items', blank=True, to='RIGS.Event', on_delete=models.CASCADE), preserve_default=True, ), ] diff --git a/RIGS/migrations/0015_auto_20141208_0233.py b/RIGS/migrations/0015_auto_20141208_0233.py index df842ece..9562110d 100644 --- a/RIGS/migrations/0015_auto_20141208_0233.py +++ b/RIGS/migrations/0015_auto_20141208_0233.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations diff --git a/RIGS/migrations/0016_auto_20150127_1905.py b/RIGS/migrations/0016_auto_20150127_1905.py index 9056c4f8..7c75e473 100644 --- a/RIGS/migrations/0016_auto_20150127_1905.py +++ b/RIGS/migrations/0016_auto_20150127_1905.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations from django.conf import settings @@ -18,7 +18,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('invoice_date', models.DateField(auto_now_add=True)), ('void', models.BooleanField()), - ('event', models.OneToOneField(to='RIGS.Event')), + ('event', models.OneToOneField(to='RIGS.Event', on_delete=models.CASCADE)), ], options={ }, @@ -31,7 +31,7 @@ class Migration(migrations.Migration): ('date', models.DateField()), ('amount', models.DecimalField(help_text=b'Please use ex. VAT', max_digits=10, decimal_places=2)), ('method', models.CharField(max_length=2, choices=[(b'C', b'Cash'), (b'I', b'Internal'), (b'E', b'External'), (b'SU', b'SU Core'), (b'M', b'Members')])), - ('invoice', models.ForeignKey(to='RIGS.Invoice')), + ('invoice', models.ForeignKey(to='RIGS.Invoice', on_delete=models.CASCADE)), ], options={ }, @@ -40,7 +40,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='event', name='mic', - field=models.ForeignKey(related_name='event_mic', verbose_name=b'MIC', blank=True, to=settings.AUTH_USER_MODEL, null=True), + field=models.ForeignKey(related_name='event_mic', verbose_name=b'MIC', blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE), preserve_default=True, ), ] diff --git a/RIGS/migrations/0017_auto_20150129_2041.py b/RIGS/migrations/0017_auto_20150129_2041.py index e90491a6..d55c658b 100644 --- a/RIGS/migrations/0017_auto_20150129_2041.py +++ b/RIGS/migrations/0017_auto_20150129_2041.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations diff --git a/RIGS/migrations/0018_auto_20150130_0016.py b/RIGS/migrations/0018_auto_20150130_0016.py index 67c3eb97..c0ac2e55 100644 --- a/RIGS/migrations/0018_auto_20150130_0016.py +++ b/RIGS/migrations/0018_auto_20150130_0016.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations diff --git a/RIGS/migrations/0019_auto_20150131_1919.py b/RIGS/migrations/0019_auto_20150131_1919.py index c55541b6..b696f28d 100644 --- a/RIGS/migrations/0019_auto_20150131_1919.py +++ b/RIGS/migrations/0019_auto_20150131_1919.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations diff --git a/RIGS/migrations/0020_auto_20150303_0243.py b/RIGS/migrations/0020_auto_20150303_0243.py index 9d9ee33f..bde7dd49 100644 --- a/RIGS/migrations/0020_auto_20150303_0243.py +++ b/RIGS/migrations/0020_auto_20150303_0243.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations diff --git a/RIGS/migrations/0021_auto_20150420_1155.py b/RIGS/migrations/0021_auto_20150420_1155.py index 269f9bc1..e89d5651 100644 --- a/RIGS/migrations/0021_auto_20150420_1155.py +++ b/RIGS/migrations/0021_auto_20150420_1155.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations diff --git a/RIGS/migrations/0022_auto_20150424_2104.py b/RIGS/migrations/0022_auto_20150424_2104.py index cdc3abe0..d5f818fb 100644 --- a/RIGS/migrations/0022_auto_20150424_2104.py +++ b/RIGS/migrations/0022_auto_20150424_2104.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations diff --git a/RIGS/migrations/0023_auto_20150529_0048.py b/RIGS/migrations/0023_auto_20150529_0048.py index 27fbc42b..2e18701f 100644 --- a/RIGS/migrations/0023_auto_20150529_0048.py +++ b/RIGS/migrations/0023_auto_20150529_0048.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations import django.core.validators diff --git a/RIGS/migrations/0024_auto_20160229_2042.py b/RIGS/migrations/0024_auto_20160229_2042.py index 163ff8e4..318d9ff3 100644 --- a/RIGS/migrations/0024_auto_20160229_2042.py +++ b/RIGS/migrations/0024_auto_20160229_2042.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from django.db import models, migrations import django.db.models.deletion diff --git a/RIGS/migrations/0025_auto_20160331_1302.py b/RIGS/migrations/0025_auto_20160331_1302.py new file mode 100644 index 00000000..8f6311c4 --- /dev/null +++ b/RIGS/migrations/0025_auto_20160331_1302.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2016-03-31 12:02 + + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('RIGS', '0024_auto_20160229_2042'), + ] + + operations = [ + migrations.AlterField( + model_name='profile', + name='username', + field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=30, unique=True, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.')], verbose_name='username'), + ), + migrations.AlterField( + model_name='vatrate', + name='start_at', + field=models.DateField(), + ), + ] diff --git a/RIGS/migrations/0025_eventauthorisation.py b/RIGS/migrations/0025_eventauthorisation.py new file mode 100644 index 00000000..9d6be0e7 --- /dev/null +++ b/RIGS/migrations/0025_eventauthorisation.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- + + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('RIGS', '0024_auto_20160229_2042'), + ] + + operations = [ + migrations.CreateModel( + name='EventAuthorisation', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('email', models.EmailField(max_length=254)), + ('name', models.CharField(max_length=255)), + ('uni_id', models.CharField(max_length=10, null=True, verbose_name=b'University ID', blank=True)), + ('account_code', models.CharField(max_length=50, null=True, blank=True)), + ('amount', models.DecimalField(verbose_name=b'authorisation amount', max_digits=10, decimal_places=2)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('event', models.ForeignKey(related_name='authroisations', to='RIGS.Event', on_delete=models.CASCADE)), + ], + ), + ] diff --git a/RIGS/migrations/0026_auto_20170510_1846.py b/RIGS/migrations/0026_auto_20170510_1846.py new file mode 100644 index 00000000..8ede101b --- /dev/null +++ b/RIGS/migrations/0026_auto_20170510_1846.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.1 on 2017-05-10 17:46 + + +import django.contrib.auth.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('RIGS', '0025_auto_20160331_1302'), + ] + + operations = [ + migrations.AlterField( + model_name='profile', + name='username', + field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.ASCIIUsernameValidator()], verbose_name='username'), + ), + ] diff --git a/RIGS/migrations/0026_remove_eventauthorisation_created_at.py b/RIGS/migrations/0026_remove_eventauthorisation_created_at.py new file mode 100644 index 00000000..57d00265 --- /dev/null +++ b/RIGS/migrations/0026_remove_eventauthorisation_created_at.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('RIGS', '0025_eventauthorisation'), + ] + + operations = [ + migrations.RemoveField( + model_name='eventauthorisation', + name='created_at', + ), + ] diff --git a/RIGS/migrations/0027_eventauthorisation_event_singular.py b/RIGS/migrations/0027_eventauthorisation_event_singular.py new file mode 100644 index 00000000..d9259914 --- /dev/null +++ b/RIGS/migrations/0027_eventauthorisation_event_singular.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('RIGS', '0026_remove_eventauthorisation_created_at'), + ] + + operations = [ + migrations.AlterField( + model_name='eventauthorisation', + name='event', + field=models.OneToOneField(related_name='authorisation', to='RIGS.Event', on_delete=models.CASCADE), + ), + ] diff --git a/RIGS/migrations/0029_eventauthorisation_sent_by.py b/RIGS/migrations/0029_eventauthorisation_sent_by.py new file mode 100644 index 00000000..58ac9ab7 --- /dev/null +++ b/RIGS/migrations/0029_eventauthorisation_sent_by.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + + +from django.db import models, migrations +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + ('RIGS', '0027_eventauthorisation_event_singular'), + ] + + operations = [ + migrations.AddField( + model_name='eventauthorisation', + name='sent_by', + field=models.ForeignKey(default=1, to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE), + preserve_default=False, + ), + ] diff --git a/RIGS/migrations/0030_auth_request_sending.py b/RIGS/migrations/0030_auth_request_sending.py new file mode 100644 index 00000000..db439e3a --- /dev/null +++ b/RIGS/migrations/0030_auth_request_sending.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- + + +from django.db import models, migrations +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + ('RIGS', '0029_eventauthorisation_sent_by'), + ] + + operations = [ + migrations.AddField( + model_name='event', + name='auth_request_at', + field=models.DateTimeField(null=True, blank=True), + ), + migrations.AddField( + model_name='event', + name='auth_request_by', + field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE), + ), + migrations.AddField( + model_name='event', + name='auth_request_to', + field=models.EmailField(max_length=254, null=True, blank=True), + ), + ] diff --git a/RIGS/migrations/0031_merge_20170512_2102.py b/RIGS/migrations/0031_merge_20170512_2102.py new file mode 100644 index 00000000..b8e04bfc --- /dev/null +++ b/RIGS/migrations/0031_merge_20170512_2102.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.1 on 2017-05-12 20:02 + + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('RIGS', '0030_auth_request_sending'), + ('RIGS', '0026_auto_20170510_1846'), + ] + + operations = [ + ] diff --git a/RIGS/migrations/0032_auto_20170904_2355.py b/RIGS/migrations/0032_auto_20170904_2355.py new file mode 100644 index 00000000..5dcec2c4 --- /dev/null +++ b/RIGS/migrations/0032_auto_20170904_2355.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2017-09-04 22:55 +from __future__ import unicode_literals + +from django.conf import settings +import django.contrib.auth.validators +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('reversion', '0001_squashed_0004_auto_20160611_1202'), + ('RIGS', '0031_merge_20170512_2102'), + ] + + operations = [ + migrations.CreateModel( + name='RIGSVersion', + fields=[ + ], + options={ + 'indexes': [], + 'proxy': True, + }, + bases=('reversion.version',), + ), + migrations.AlterField( + model_name='event', + name='collector', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='collected by'), + ), + migrations.AlterField( + model_name='event', + name='mic', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='event_mic', to=settings.AUTH_USER_MODEL, verbose_name='MIC'), + ), + migrations.AlterField( + model_name='event', + name='purchase_order', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='PO'), + ), + migrations.AlterField( + model_name='eventauthorisation', + name='amount', + field=models.DecimalField(decimal_places=2, max_digits=10, verbose_name='authorisation amount'), + ), + migrations.AlterField( + model_name='eventauthorisation', + name='uni_id', + field=models.CharField(blank=True, max_length=10, null=True, verbose_name='University ID'), + ), + migrations.AlterField( + model_name='payment', + name='amount', + field=models.DecimalField(decimal_places=2, help_text='Please use ex. VAT', max_digits=10), + ), + migrations.AlterField( + model_name='payment', + name='method', + field=models.CharField(blank=True, choices=[('C', 'Cash'), ('I', 'Internal'), ('E', 'External'), ('SU', 'SU Core'), ('T', 'TEC Adjustment')], max_length=2, null=True), + ), + migrations.AlterField( + model_name='profile', + name='username', + field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username'), + ), + ] diff --git a/RIGS/migrations/0033_auto_20180325_0016.py b/RIGS/migrations/0033_auto_20180325_0016.py new file mode 100644 index 00000000..d88bd79f --- /dev/null +++ b/RIGS/migrations/0033_auto_20180325_0016.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.3 on 2018-03-25 00:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('RIGS', '0032_auto_20170904_2355'), + ] + + operations = [ + migrations.AlterField( + model_name='profile', + name='last_name', + field=models.CharField(blank=True, max_length=150, verbose_name='last name'), + ), + ] diff --git a/RIGS/migrations/0034_event_risk_assessment_edit_url.py b/RIGS/migrations/0034_event_risk_assessment_edit_url.py new file mode 100644 index 00000000..d60cd4bd --- /dev/null +++ b/RIGS/migrations/0034_event_risk_assessment_edit_url.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.5 on 2019-07-28 21:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('RIGS', '0033_auto_20180325_0016'), + ] + + operations = [ + migrations.AddField( + model_name='event', + name='risk_assessment_edit_url', + field=models.CharField(blank=True, max_length=255, null=True), + ), + ] diff --git a/RIGS/migrations/0035_auto_20191124_1319.py b/RIGS/migrations/0035_auto_20191124_1319.py new file mode 100644 index 00000000..dd00494f --- /dev/null +++ b/RIGS/migrations/0035_auto_20191124_1319.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.13 on 2019-11-24 13:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('RIGS', '0034_event_risk_assessment_edit_url'), + ] + + operations = [ + migrations.AlterField( + model_name='event', + name='risk_assessment_edit_url', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='risk assessment'), + ), + ] diff --git a/RIGS/models.py b/RIGS/models.py index 0650d81c..937d5354 100644 --- a/RIGS/models.py +++ b/RIGS/models.py @@ -1,19 +1,24 @@ import datetime import hashlib +import datetime import pytz -import random + +from django.db import models +from django.contrib.auth.models import AbstractUser +from django.conf import settings +from django.utils import timezone +from django.utils.functional import cached_property +from django.utils.encoding import python_2_unicode_compatible +from reversion import revisions as reversion +from reversion.models import Version import string + +import random from collections import Counter from decimal import Decimal -import reversion -from django.conf import settings -from django.contrib.auth.models import AbstractUser from django.core.exceptions import ValidationError -from django.core.urlresolvers import reverse_lazy -from django.db import models -from django.utils.encoding import python_2_unicode_compatible -from django.utils.functional import cached_property +from django.urls import reverse_lazy # Create your models here. @@ -28,13 +33,13 @@ class Profile(AbstractUser): size = 20 chars = string.ascii_letters + string.digits new_api_key = ''.join(random.choice(chars) for x in range(size)) - return new_api_key; + return new_api_key @property def profile_picture(self): url = "" if settings.USE_GRAVATAR or settings.USE_GRAVATAR is None: - url = "https://www.gravatar.com/avatar/" + hashlib.md5(self.email).hexdigest() + "?d=wavatar&s=500" + url = "https://www.gravatar.com/avatar/" + hashlib.md5(self.email.encode('utf-8')).hexdigest() + "?d=wavatar&s=500" return url @property @@ -58,32 +63,31 @@ class Profile(AbstractUser): class RevisionMixin(object): + @property + def current_version(self): + version = Version.objects.get_for_object(self).select_related('revision').first() + return version + @property def last_edited_at(self): - versions = reversion.get_for_object(self) - if versions: - version = reversion.get_for_object(self)[0] - return version.revision.date_created - else: + version = self.current_version + if version is None: return None + return version.revision.date_created @property def last_edited_by(self): - versions = reversion.get_for_object(self) - if versions: - version = reversion.get_for_object(self)[0] - return version.revision.user - else: + version = self.current_version + if version is None: return None + return version.revision.user @property def current_version_id(self): - versions = reversion.get_for_object(self) - if versions: - version = reversion.get_for_object(self)[0] - return "V{0} | R{1}".format(version.pk, version.revision.pk) - else: + version = self.current_version + if version is None: return None + return "V{0} | R{1}".format(version.pk, version.revision.pk) @reversion.register @@ -175,7 +179,7 @@ class Organisation(models.Model, RevisionMixin): class VatManager(models.Manager): def current_rate(self): - return self.find_rate(datetime.datetime.now()) + return self.find_rate(timezone.now()) def find_rate(self, date): # return self.filter(startAt__lte=date).latest() @@ -190,7 +194,7 @@ class VatManager(models.Manager): @reversion.register @python_2_unicode_compatible class VatRate(models.Model, RevisionMixin): - start_at = models.DateTimeField() + start_at = models.DateField() rate = models.DecimalField(max_digits=6, decimal_places=6) comment = models.CharField(max_length=255) @@ -241,18 +245,12 @@ class Venue(models.Model, RevisionMixin): class EventManager(models.Manager): def current_events(self): events = self.filter( - (models.Q(start_date__gte=datetime.date.today(), end_date__isnull=True, dry_hire=False) & ~models.Q( - status=Event.CANCELLED)) | # Starts after with no end - (models.Q(end_date__gte=datetime.date.today(), dry_hire=False) & ~models.Q( - status=Event.CANCELLED)) | # Ends after - (models.Q(dry_hire=True, start_date__gte=datetime.date.today()) & ~models.Q( - status=Event.CANCELLED)) | # Active dry hire - (models.Q(dry_hire=True, checked_in_by__isnull=True) & ( - models.Q(status=Event.BOOKED) | models.Q(status=Event.CONFIRMED))) | # Active dry hire GT - models.Q(status=Event.CANCELLED, start_date__gte=datetime.date.today()) # Canceled but not started - ).order_by('start_date', 'end_date', 'start_time', 'end_time', 'meet_at').select_related('person', - 'organisation', - 'venue', 'mic') + (models.Q(start_date__gte=timezone.now().date(), end_date__isnull=True, dry_hire=False) & ~models.Q(status=Event.CANCELLED)) | # Starts after with no end + (models.Q(end_date__gte=timezone.now().date(), dry_hire=False) & ~models.Q(status=Event.CANCELLED)) | # Ends after + (models.Q(dry_hire=True, start_date__gte=timezone.now().date()) & ~models.Q(status=Event.CANCELLED)) | # Active dry hire + (models.Q(dry_hire=True, checked_in_by__isnull=True) & (models.Q(status=Event.BOOKED) | models.Q(status=Event.CONFIRMED))) | # Active dry hire GT + models.Q(status=Event.CANCELLED, start_date__gte=timezone.now().date()) # Canceled but not started + ).order_by('start_date', 'end_date', 'start_time', 'end_time', 'meet_at').select_related('person', 'organisation', 'venue', 'mic') return events def events_in_bounds(self, start, end): @@ -275,12 +273,12 @@ class EventManager(models.Manager): def rig_count(self): event_count = self.filter( - (models.Q(start_date__gte=datetime.date.today(), end_date__isnull=True, dry_hire=False, + (models.Q(start_date__gte=timezone.now().date(), end_date__isnull=True, dry_hire=False, is_rig=True) & ~models.Q( status=Event.CANCELLED)) | # Starts after with no end - (models.Q(end_date__gte=datetime.date.today(), dry_hire=False, is_rig=True) & ~models.Q( + (models.Q(end_date__gte=timezone.now().date(), dry_hire=False, is_rig=True) & ~models.Q( status=Event.CANCELLED)) | # Ends after - (models.Q(dry_hire=True, start_date__gte=datetime.date.today(), is_rig=True) & ~models.Q( + (models.Q(dry_hire=True, start_date__gte=timezone.now().date(), is_rig=True) & ~models.Q( status=Event.CANCELLED)) | # Active dry hire (models.Q(dry_hire=True, checked_in_by__isnull=True, is_rig=True) & ( models.Q(status=Event.BOOKED) | models.Q(status=Event.CONFIRMED))) # Active dry hire GT @@ -304,9 +302,9 @@ class Event(models.Model, RevisionMixin): ) name = models.CharField(max_length=255) - person = models.ForeignKey('Person', null=True, blank=True) - organisation = models.ForeignKey('Organisation', blank=True, null=True) - venue = models.ForeignKey('Venue', blank=True, null=True) + person = models.ForeignKey('Person', null=True, blank=True, on_delete=models.CASCADE) + organisation = models.ForeignKey('Organisation', blank=True, null=True, on_delete=models.CASCADE) + venue = models.ForeignKey('Venue', blank=True, null=True, on_delete=models.CASCADE) description = models.TextField(blank=True, null=True) notes = models.TextField(blank=True, null=True) status = models.IntegerField(choices=EVENT_STATUS_CHOICES, default=PROVISIONAL) @@ -325,9 +323,9 @@ class Event(models.Model, RevisionMixin): meet_info = models.CharField(max_length=255, blank=True, null=True) # Crew management - checked_in_by = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='event_checked_in', blank=True, null=True) + checked_in_by = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='event_checked_in', blank=True, null=True, on_delete=models.CASCADE) mic = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='event_mic', blank=True, null=True, - verbose_name="MIC") + verbose_name="MIC", on_delete=models.CASCADE) # Monies payment_method = models.CharField(max_length=255, blank=True, null=True) @@ -335,6 +333,14 @@ class Event(models.Model, RevisionMixin): purchase_order = models.CharField(max_length=255, blank=True, null=True, verbose_name='PO') collector = models.CharField(max_length=255, blank=True, null=True, verbose_name='collected by') + # Authorisation request details + auth_request_by = models.ForeignKey('Profile', null=True, blank=True, on_delete=models.CASCADE) + auth_request_at = models.DateTimeField(null=True, blank=True) + auth_request_to = models.EmailField(null=True, blank=True) + + # Risk assessment info + risk_assessment_edit_url = models.CharField(verbose_name="risk assessment", max_length=255, blank=True, null=True) + # Calculated values """ EX Vat @@ -367,7 +373,7 @@ class Event(models.Model, RevisionMixin): @property def vat(self): - return self.sum_total * self.vat_rate.rate + return Decimal(self.sum_total * self.vat_rate.rate).quantize(Decimal('.01')) """ Inc VAT @@ -375,7 +381,7 @@ class Event(models.Model, RevisionMixin): @property def total(self): - return self.sum_total + self.vat + return Decimal(self.sum_total + self.vat).quantize(Decimal('.01')) @property def cancelled(self): @@ -385,6 +391,10 @@ class Event(models.Model, RevisionMixin): def confirmed(self): return (self.status == self.BOOKED or self.status == self.CONFIRMED) + @property + def authorised(self): + return not self.internal and self.purchase_order or self.authorisation.amount == self.total + @property def has_start_time(self): return self.start_time is not None @@ -445,13 +455,17 @@ class Event(models.Model, RevisionMixin): else: return endDate + @property + def internal(self): + return self.organisation and self.organisation.union_account + objects = EventManager() def get_absolute_url(self): return reverse_lazy('event_detail', kwargs={'pk': self.pk}) def __str__(self): - return unicode(self.pk) + ": " + self.name + return str(self.pk) + ": " + self.name def clean(self): if self.end_date and self.start_date > self.end_date: @@ -474,7 +488,7 @@ class Event(models.Model, RevisionMixin): class EventItem(models.Model): - event = models.ForeignKey('Event', related_name='items', blank=True) + event = models.ForeignKey('Event', related_name='items', blank=True, on_delete=models.CASCADE) name = models.CharField(max_length=255) description = models.TextField(blank=True, null=True) quantity = models.IntegerField() @@ -493,17 +507,35 @@ class EventItem(models.Model): class EventCrew(models.Model): - event = models.ForeignKey('Event', related_name='crew') - user = models.ForeignKey(settings.AUTH_USER_MODEL) + event = models.ForeignKey('Event', related_name='crew', on_delete=models.CASCADE) + user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) rig = models.BooleanField(default=False) run = models.BooleanField(default=False) derig = models.BooleanField(default=False) notes = models.TextField(blank=True, null=True) +@reversion.register +class EventAuthorisation(models.Model, RevisionMixin): + event = models.OneToOneField('Event', related_name='authorisation', on_delete=models.CASCADE) + email = models.EmailField() + name = models.CharField(max_length=255) + uni_id = models.CharField(max_length=10, blank=True, null=True, verbose_name="University ID") + account_code = models.CharField(max_length=50, blank=True, null=True) + amount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="authorisation amount") + sent_by = models.ForeignKey('RIGS.Profile', on_delete=models.CASCADE) + + def get_absolute_url(self): + return reverse_lazy('event_detail', kwargs={'pk': self.event.pk}) + + @property + def activity_feed_string(self): + return str("N%05d" % self.event.pk + ' (requested by ' + self.sent_by.initials + ')') + + @python_2_unicode_compatible class Invoice(models.Model): - event = models.OneToOneField('Event') + event = models.OneToOneField('Event', on_delete=models.CASCADE) invoice_date = models.DateField(auto_now_add=True) void = models.BooleanField(default=False) @@ -555,7 +587,7 @@ class Payment(models.Model): (ADJUSTMENT, 'TEC Adjustment'), ) - invoice = models.ForeignKey('Invoice') + invoice = models.ForeignKey('Invoice', on_delete=models.CASCADE) date = models.DateField() amount = models.DecimalField(max_digits=10, decimal_places=2, help_text='Please use ex. VAT') method = models.CharField(max_length=2, choices=METHODS, null=True, blank=True) diff --git a/RIGS/regbackend.py b/RIGS/regbackend.py index 90828a39..c8007993 100644 --- a/RIGS/regbackend.py +++ b/RIGS/regbackend.py @@ -1,13 +1,15 @@ from RIGS.models import Profile from RIGS.forms import ProfileRegistrationFormUniqueEmail +from registration.signals import user_registered + def user_created(sender, user, request, **kwargs): - form = ProfileRegistrationFormUniqueEmail(request.POST) - user.first_name = form.data['first_name'] - user.last_name = form.data['last_name'] - user.initials = form.data['initials'] - user.phone = form.data['phone'] - user.save() + form = ProfileRegistrationFormUniqueEmail(request.POST) + user.first_name = form.data['first_name'] + user.last_name = form.data['last_name'] + user.initials = form.data['initials'] + # user.phone = form.data['phone'] + user.save() -from registration.signals import user_registered -user_registered.connect(user_created) \ No newline at end of file + +user_registered.connect(user_created) diff --git a/RIGS/rigboard.py b/RIGS/rigboard.py index 2ee50e3b..a13b861e 100644 --- a/RIGS/rigboard.py +++ b/RIGS/rigboard.py @@ -1,23 +1,34 @@ -import cStringIO as StringIO -import copy -import datetime -import re -import urllib2 from io import BytesIO +import urllib.request +import urllib.error +import urllib.parse -from PyPDF2 import PdfFileMerger, PdfFileReader -from django.conf import settings -from django.contrib import messages -from django.core.urlresolvers import reverse_lazy -from django.db.models import Q -from django.http import HttpResponse +from django.contrib.staticfiles.storage import staticfiles_storage +from django.core.mail import EmailMessage, EmailMultiAlternatives +from django.views import generic +from django.urls import reverse_lazy from django.shortcuts import get_object_or_404 from django.template import RequestContext from django.template.loader import get_template -from django.views import generic +from django.conf import settings +from django.urls import reverse +from django.core import signing +from django.http import HttpResponse +from django.core.exceptions import SuspiciousOperation +from django.db.models import Q +from django.contrib import messages +from django.utils.decorators import method_decorator +from django.views.decorators.csrf import csrf_exempt from z3c.rml import rml2pdf +from PyPDF2 import PdfFileMerger, PdfFileReader +import simplejson +import premailer from RIGS import models, forms +from PyRIGS import decorators +import datetime +import re +import copy __author__ = 'ghost' @@ -33,19 +44,62 @@ class RigboardIndex(generic.TemplateView): context['events'] = models.Event.objects.current_events() return context + class WebCalendar(generic.TemplateView): template_name = 'RIGS/calendar.html' def get_context_data(self, **kwargs): context = super(WebCalendar, self).get_context_data(**kwargs) - context['view'] = kwargs.get('view','') - context['date'] = kwargs.get('date','') + context['view'] = kwargs.get('view', '') + context['date'] = kwargs.get('date', '') return context + class EventDetail(generic.DetailView): model = models.Event +class EventOembed(generic.View): + model = models.Event + + def get(self, request, pk=None): + embed_url = reverse('event_embed', args=[pk]) + full_url = "{0}://{1}{2}".format(request.scheme, request.META['HTTP_HOST'], embed_url) + + data = { + 'html': ''.format(full_url), + 'version': '1.0', + 'type': 'rich', + 'height': '250' + } + + json = simplejson.JSONEncoderForHTML().encode(data) + return HttpResponse(json, content_type="application/json") + + +class EventEmbed(EventDetail): + template_name = 'RIGS/event_embed.html' + + +class EventRA(generic.base.RedirectView): + permanent = False + + def get_redirect_url(self, *args, **kwargs): + event = get_object_or_404(models.Event, pk=kwargs['pk']) + + if event.risk_assessment_edit_url: + return event.risk_assessment_edit_url + + params = { + 'entry.708610078': f'N{event.pk:05}', + 'entry.905899507': event.name, + 'entry.139491562': event.venue.name if event.venue else '', + 'entry.1689826056': event.start_date.strftime('%Y-%m-%d') + ((' - ' + event.end_date.strftime('%Y-%m-%d')) if event.end_date else ''), + 'entry.902421165': event.mic.name if event.mic else '' + } + return settings.RISK_ASSESSMENT_URL + "?" + urllib.parse.urlencode(params) + + class EventCreate(generic.CreateView): model = models.Event form_class = forms.EventForm @@ -58,10 +112,9 @@ class EventCreate(generic.CreateView): form = context['form'] if re.search('"-\d+"', form['items_json'].value()): messages.info(self.request, "Your item changes have been saved. Please fix the errors and save the event.") - # Get some other objects to include in the form. Used when there are errors but also nice and quick. - for field, model in form.related_models.iteritems(): + for field, model in form.related_models.items(): value = form[field].value() if value is not None and value != '': context[field] = model.objects.get(pk=value) @@ -82,25 +135,48 @@ class EventUpdate(generic.UpdateView): form = context['form'] # Get some other objects to include in the form. Used when there are errors but also nice and quick. - for field, model in form.related_models.iteritems(): + for field, model in form.related_models.items(): value = form[field].value() if value is not None and value != '': context[field] = model.objects.get(pk=value) + return context + def render_to_response(self, context, **response_kwargs): + if not hasattr(context, 'duplicate'): + # If this event has already been emailed to a client, show a warning + if self.object.auth_request_at is not None: + messages.info(self.request, 'This event has already been sent to the client for authorisation, any changes you make will be visible to them immediately.') + + if hasattr(self.object, 'authorised'): + messages.warning(self.request, 'This event has already been authorised by client, any changes to price will require reauthorisation.') + return super(EventUpdate, self).render_to_response(context, **response_kwargs) + def get_success_url(self): return reverse_lazy('event_detail', kwargs={'pk': self.object.pk}) + class EventDuplicate(EventUpdate): def get_object(self, queryset=None): - old = super(EventDuplicate, self).get_object(queryset) # Get the object (the event you're duplicating) - new = copy.copy(old) # Make a copy of the object in memory - new.based_on = old # Make the new event based on the old event + old = super(EventDuplicate, self).get_object(queryset) # Get the object (the event you're duplicating) + new = copy.copy(old) # Make a copy of the object in memory + new.based_on = old # Make the new event based on the old event + new.purchase_order = None # Remove old PO - if self.request.method in ('POST', 'PUT'): # This only happens on save (otherwise items won't display in editor) - new.pk = None # This means a new event will be created on save, and all items will be re-created + # Clear checked in by if it's a dry hire + if new.dry_hire is True: + new.checked_in_by = None - messages.info(self.request, 'Event data duplicated but not yet saved. Click save to complete operation.') + # Remove all the authorisation information from the new event + new.auth_request_to = None + new.auth_request_by = None + new.auth_request_at = None + + if self.request.method in ( + 'POST', 'PUT'): # This only happens on save (otherwise items won't display in editor) + new.pk = None # This means a new event will be created on save, and all items will be re-created + else: + messages.info(self.request, 'Event data duplicated but not yet saved. Click save to complete operation.') return new @@ -109,41 +185,34 @@ class EventDuplicate(EventUpdate): context["duplicate"] = True return context + class EventPrint(generic.View): def get(self, request, pk): object = get_object_or_404(models.Event, pk=pk) template = get_template('RIGS/event_print.xml') - copies = ('TEC', 'Client') merger = PdfFileMerger() - for copy in copies: + context = { + 'object': object, + 'fonts': { + 'opensans': { + 'regular': 'RIGS/static/fonts/OPENSANS-REGULAR.TTF', + 'bold': 'RIGS/static/fonts/OPENSANS-BOLD.TTF', + } + }, + 'quote': True, + 'current_user': request.user, + } - context = RequestContext(request, { # this should be outside the loop, but bug in 1.8.2 prevents this - 'object': object, - 'fonts': { - 'opensans': { - 'regular': 'RIGS/static/fonts/OPENSANS-REGULAR.TTF', - 'bold': 'RIGS/static/fonts/OPENSANS-BOLD.TTF', - } - }, - 'copy':copy, - 'current_user':request.user, - }) + rml = template.render(context) - # context['copy'] = copy # this is the way to do it once we upgrade to Django 1.8.3 + buffer = rml2pdf.parseString(rml) + merger.append(PdfFileReader(buffer)) + buffer.close() - rml = template.render(context) - buffer = StringIO.StringIO() - - buffer = rml2pdf.parseString(rml) - - merger.append(PdfFileReader(buffer)) - - buffer.close() - - terms = urllib2.urlopen(settings.TERMS_OF_HIRE_URL) - merger.append(StringIO.StringIO(terms.read())) + terms = urllib.request.urlopen(settings.TERMS_OF_HIRE_URL) + merger.append(BytesIO(terms.read())) merged = BytesIO() merger.write(merged) @@ -156,7 +225,8 @@ class EventPrint(generic.View): response.write(merged.getvalue()) return response -class EventArchive(generic.ListView): + +class EventArchive(generic.ArchiveIndexView): model = models.Event paginate_by = 25 template_name = "RIGS/event_archive.html" @@ -216,3 +286,174 @@ class EventArchive(generic.ListView): messages.add_message(self.request, messages.WARNING, "No events have been found matching those criteria.") return qs + + +class EventAuthorise(generic.UpdateView): + template_name = 'RIGS/eventauthorisation_form.html' + success_template = 'RIGS/eventauthorisation_success.html' + + def form_valid(self, form): + self.object = form.save() + + self.template_name = self.success_template + messages.add_message(self.request, messages.SUCCESS, + 'Success! Your event has been authorised. ' + + 'You will also receive email confirmation to %s.' % (self.object.email)) + return self.render_to_response(self.get_context_data()) + + @property + def event(self): + return models.Event.objects.select_related('organisation', 'person', 'venue').get(pk=self.kwargs['pk']) + + def get_object(self, queryset=None): + return getattr(self.event, 'authorisation', None) + + def get_form_class(self): + return forms.InternalClientEventAuthorisationForm + + def get_context_data(self, **kwargs): + context = super(EventAuthorise, self).get_context_data(**kwargs) + context['event'] = self.event + + context['tos_url'] = settings.TERMS_OF_HIRE_URL + return context + + def get(self, request, *args, **kwargs): + if self.get_object() is not None and self.get_object().pk is not None: + if self.event.authorised: + messages.add_message(self.request, messages.WARNING, + "This event has already been authorised. " + "Reauthorising is not necessary at this time.") + else: + messages.add_message(self.request, messages.WARNING, + "This event has already been authorised, but the amount has changed. " + + "Please check the amount and reauthorise.") + return super(EventAuthorise, self).get(request, *args, **kwargs) + + def get_form(self, **kwargs): + form = super(EventAuthorise, self).get_form(**kwargs) + form.instance.event = self.event + form.instance.email = self.request.email + form.instance.sent_by = self.request.sent_by + return form + + def dispatch(self, request, *args, **kwargs): + # Verify our signature matches up and all is well with the integrity of the URL + try: + data = signing.loads(kwargs.get('hmac')) + assert int(kwargs.get('pk')) == int(data.get('pk')) + request.email = data['email'] + request.sent_by = models.Profile.objects.get(pk=data['sent_by']) + except (signing.BadSignature, AssertionError, KeyError, models.Profile.DoesNotExist): + raise SuspiciousOperation( + "This URL is invalid. Please ask your TEC contact for a new URL") + return super(EventAuthorise, self).dispatch(request, *args, **kwargs) + + +class EventAuthorisationRequest(generic.FormView, generic.detail.SingleObjectMixin): + model = models.Event + form_class = forms.EventAuthorisationRequestForm + template_name = 'RIGS/eventauthorisation_request.html' + + @method_decorator(decorators.nottinghamtec_address_required) + def dispatch(self, *args, **kwargs): + return super(EventAuthorisationRequest, self).dispatch(*args, **kwargs) + + @property + def object(self): + return self.get_object() + + def get_success_url(self): + if self.request.is_ajax(): + url = reverse_lazy('closemodal') + messages.info(self.request, "location.reload()") + else: + url = reverse_lazy('event_detail', kwargs={ + 'pk': self.object.pk, + }) + messages.add_message(self.request, messages.SUCCESS, "Authorisation request successfully sent.") + return url + + def form_valid(self, form): + email = form.cleaned_data['email'] + event = self.object + event.auth_request_by = self.request.user + event.auth_request_at = datetime.datetime.now() + event.auth_request_to = email + event.save() + + context = { + 'object': self.object, + 'request': self.request, + 'hmac': signing.dumps({ + 'pk': self.object.pk, + 'email': email, + 'sent_by': self.request.user.pk, + }), + } + if event.person is not None and email == event.person.email: + context['to_name'] = event.person.name + elif event.organisation is not None and email == event.organisation.email: + context['to_name'] = event.organisation.name + + msg = EmailMultiAlternatives( + "N%05d | %s - Event Authorisation Request" % (self.object.pk, self.object.name), + get_template("RIGS/eventauthorisation_client_request.txt").render(context), + to=[email], + reply_to=[self.request.user.email], + ) + css = staticfiles_storage.path('css/email.css') + html = premailer.Premailer(get_template("RIGS/eventauthorisation_client_request.html").render(context), + external_styles=css).transform() + msg.attach_alternative(html, 'text/html') + + msg.send() + + return super(EventAuthorisationRequest, self).form_valid(form) + + +class EventAuthoriseRequestEmailPreview(generic.DetailView): + template_name = "RIGS/eventauthorisation_client_request.html" + model = models.Event + + def render_to_response(self, context, **response_kwargs): + from django.contrib.staticfiles.storage import staticfiles_storage + css = staticfiles_storage.path('css/email.css') + response = super(EventAuthoriseRequestEmailPreview, self).render_to_response(context, **response_kwargs) + assert isinstance(response, HttpResponse) + response.content = premailer.Premailer(response.rendered_content, external_styles=css).transform() + return response + + def get_context_data(self, **kwargs): + context = super(EventAuthoriseRequestEmailPreview, self).get_context_data(**kwargs) + context['hmac'] = signing.dumps({ + 'pk': self.object.pk, + 'email': self.request.GET.get('email', 'hello@world.test'), + 'sent_by': self.request.user.pk, + }) + context['to_name'] = self.request.GET.get('to_name', None) + return context + + +@method_decorator(csrf_exempt, name='dispatch') +class LogRiskAssessment(generic.View): + http_method_names = ["post"] + + def post(self, request, **kwargs): + data = request.POST + shared_secret = data.get("secret") + edit_url = data.get("editUrl") + rig_number = data.get("rigNum") + if shared_secret is None or edit_url is None or rig_number is None: + return HttpResponse(status=422) + + if shared_secret != settings.RISK_ASSESSMENT_SECRET: + return HttpResponse(status=403) + + rig_number = int(re.sub("[^0-9]", "", rig_number)) + + event = get_object_or_404(models.Event, pk=rig_number) + event.risk_assessment_edit_url = edit_url + event.save() + + return HttpResponse(status=200) diff --git a/RIGS/signals.py b/RIGS/signals.py new file mode 100644 index 00000000..ea0395d7 --- /dev/null +++ b/RIGS/signals.py @@ -0,0 +1,104 @@ +import re +import urllib.request +import urllib.error +import urllib.parse +from io import BytesIO + +from django.db.models.signals import post_save +from PyPDF2 import PdfFileReader, PdfFileMerger +from django.conf import settings +from django.contrib.staticfiles.storage import staticfiles_storage +from django.core.mail import EmailMessage, EmailMultiAlternatives +from django.template.loader import get_template +from premailer import Premailer +from z3c.rml import rml2pdf + +from RIGS import models + + +def send_eventauthorisation_success_email(instance): + # Generate PDF first to prevent context conflicts + context = { + 'object': instance.event, + 'fonts': { + 'opensans': { + 'regular': 'RIGS/static/fonts/OPENSANS-REGULAR.TTF', + 'bold': 'RIGS/static/fonts/OPENSANS-BOLD.TTF', + } + }, + 'receipt': True, + 'current_user': False, + } + + template = get_template('RIGS/event_print.xml') + merger = PdfFileMerger() + + rml = template.render(context) + + buffer = rml2pdf.parseString(rml) + merger.append(PdfFileReader(buffer)) + buffer.close() + + terms = urllib.request.urlopen(settings.TERMS_OF_HIRE_URL) + merger.append(BytesIO(terms.read())) + + merged = BytesIO() + merger.write(merged) + + # Produce email content + context = { + 'object': instance, + } + + if instance.event.person is not None and instance.email == instance.event.person.email: + context['to_name'] = instance.event.person.name + elif instance.event.organisation is not None and instance.email == instance.event.organisation.email: + context['to_name'] = instance.event.organisation.name + + subject = "N%05d | %s - Event Authorised" % (instance.event.pk, instance.event.name) + + client_email = EmailMultiAlternatives( + subject, + get_template("RIGS/eventauthorisation_client_success.txt").render(context), + to=[instance.email], + reply_to=[settings.AUTHORISATION_NOTIFICATION_ADDRESS], + ) + + css = staticfiles_storage.path('css/email.css') + html = Premailer(get_template("RIGS/eventauthorisation_client_success.html").render(context), + external_styles=css).transform() + client_email.attach_alternative(html, 'text/html') + + escapedEventName = re.sub('[^a-zA-Z0-9 \n\.]', '', instance.event.name) + + client_email.attach('N%05d - %s - CONFIRMATION.pdf' % (instance.event.pk, escapedEventName), + merged.getvalue(), + 'application/pdf' + ) + + if instance.event.mic: + mic_email_address = instance.event.mic.email + else: + mic_email_address = settings.AUTHORISATION_NOTIFICATION_ADDRESS + + mic_email = EmailMessage( + subject, + get_template("RIGS/eventauthorisation_mic_success.txt").render(context), + to=[mic_email_address] + ) + + # Now we have both emails successfully generated, send them out + client_email.send(fail_silently=True) + mic_email.send(fail_silently=True) + + # Set event to booked now that it's authorised + instance.event.status = models.Event.BOOKED + instance.event.save() + + +def on_revision_commit(sender, instance, created, **kwargs): + if created: + send_eventauthorisation_success_email(instance) + + +post_save.connect(on_revision_commit, sender=models.EventAuthorisation) diff --git a/RIGS/static/css/bootstrap-select.min.css b/RIGS/static/css/bootstrap-select.min.css old mode 100644 new mode 100755 index 4e9d1344..9d96ebb3 --- a/RIGS/static/css/bootstrap-select.min.css +++ b/RIGS/static/css/bootstrap-select.min.css @@ -1,6 +1,6 @@ -/*! - * Bootstrap-select v1.6.2 (http://silviomoreto.github.io/bootstrap-select/) - * - * Copyright 2013-2014 bootstrap-select - * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) - */.bootstrap-select{width:220px \0}.bootstrap-select>.btn{width:100%;padding-right:25px}.error .bootstrap-select .btn{border:1px solid #b94a48}.control-group.error .bootstrap-select .dropdown-toggle{border-color:#b94a48}.bootstrap-select.fit-width{width:auto!important}.bootstrap-select:not([class*=col-]):not([class*=form-control]):not(.input-group-btn){width:220px}.bootstrap-select .btn:focus{outline:thin dotted #333!important;outline:5px auto -webkit-focus-ring-color!important;outline-offset:-2px}.bootstrap-select.form-control{margin-bottom:0;padding:0;border:none}.bootstrap-select.form-control:not([class*=col-]){width:100%}.bootstrap-select.btn-group:not(.input-group-btn),.bootstrap-select.btn-group[class*=col-]{float:none;display:inline-block;margin-left:0}.bootstrap-select.btn-group.dropdown-menu-right,.bootstrap-select.btn-group[class*=col-].dropdown-menu-right,.row-fluid .bootstrap-select.btn-group[class*=col-].dropdown-menu-right{float:right}.form-search .bootstrap-select.btn-group,.form-inline .bootstrap-select.btn-group,.form-horizontal .bootstrap-select.btn-group,.form-group .bootstrap-select.btn-group{margin-bottom:0}.form-group-lg .bootstrap-select.btn-group.form-control,.form-group-sm .bootstrap-select.btn-group.form-control{padding:0}.form-inline .bootstrap-select.btn-group .form-control{width:100%}.input-append .bootstrap-select.btn-group{margin-left:-1px}.input-prepend .bootstrap-select.btn-group{margin-right:-1px}.bootstrap-select.btn-group>.disabled{cursor:not-allowed}.bootstrap-select.btn-group>.disabled:focus{outline:0!important}.bootstrap-select.btn-group .btn .filter-option{display:inline-block;overflow:hidden;width:100%;text-align:left}.bootstrap-select.btn-group .btn .caret{position:absolute;top:50%;right:12px;margin-top:-2px;vertical-align:middle}.bootstrap-select.btn-group[class*=col-] .btn{width:100%}.bootstrap-select.btn-group .dropdown-menu{min-width:100%;z-index:1035;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select.btn-group .dropdown-menu.inner{position:static;border:0;padding:0;margin:0;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.bootstrap-select.btn-group .dropdown-menu li{position:relative}.bootstrap-select.btn-group .dropdown-menu li:not(.disabled) a:hover small,.bootstrap-select.btn-group .dropdown-menu li:not(.disabled) a:focus small,.bootstrap-select.btn-group .dropdown-menu li.active:not(.disabled) a small{color:#64b1d8;color:rgba(100,177,216,.4)}.bootstrap-select.btn-group .dropdown-menu li.disabled a{cursor:not-allowed}.bootstrap-select.btn-group .dropdown-menu li a{cursor:pointer}.bootstrap-select.btn-group .dropdown-menu li a.opt{position:relative;padding-left:2.25em}.bootstrap-select.btn-group .dropdown-menu li a span.check-mark{display:none}.bootstrap-select.btn-group .dropdown-menu li a span.text{display:inline-block}.bootstrap-select.btn-group .dropdown-menu li small{padding-left:.5em}.bootstrap-select.btn-group .dropdown-menu .notify{position:absolute;bottom:5px;width:96%;margin:0 2%;min-height:26px;padding:3px 5px;background:#f5f5f5;border:1px solid #e3e3e3;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05);pointer-events:none;opacity:.9;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select.btn-group .no-results{padding:3px;background:#f5f5f5;margin:0 5px}.bootstrap-select.btn-group.fit-width .btn .filter-option{position:static}.bootstrap-select.btn-group.fit-width .btn .caret{position:static;top:auto;margin-top:-1px}.bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a span.check-mark{position:absolute;display:inline-block;right:15px;margin-top:5px}.bootstrap-select.btn-group.show-tick .dropdown-menu li a span.text{margin-right:34px}.bootstrap-select.show-menu-arrow.open>.btn{z-index:1035+1}.bootstrap-select.show-menu-arrow .dropdown-toggle:before{content:'';border-left:7px solid transparent;border-right:7px solid transparent;border-bottom-width:7px;border-bottom-style:solid;border-bottom-color:#ccc;border-bottom-color:rgba(204,204,204,.2);position:absolute;bottom:-4px;left:9px;display:none}.bootstrap-select.show-menu-arrow .dropdown-toggle:after{content:'';border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;bottom:-4px;left:10px;display:none}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:before{bottom:auto;top:-3px;border-bottom:0;border-top-width:7px;border-top-style:solid;border-top-color:#ccc;border-top-color:rgba(204,204,204,.2)}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:after{bottom:auto;top:-3px;border-top:6px solid #fff;border-bottom:0}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:before{right:12px;left:auto}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:after{right:13px;left:auto}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle:before,.bootstrap-select.show-menu-arrow.open>.dropdown-toggle:after{display:block}.bs-searchbox,.bs-actionsbox{padding:4px 8px}.bs-actionsbox{float:left;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-actionsbox .btn-group button{width:50%}.bs-searchbox+.bs-actionsbox{padding:0 8px 4px}.bs-searchbox input.form-control{margin-bottom:0;width:100%}.mobile-device{position:absolute;top:0;left:0;display:block!important;width:100%;height:100%!important;opacity:0} \ No newline at end of file +/*! + * Bootstrap-select v1.12.4 (http://silviomoreto.github.io/bootstrap-select) + * + * Copyright 2013-2017 bootstrap-select + * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) + */select.bs-select-hidden,select.selectpicker{display:none!important}.bootstrap-select{width:220px\9}.bootstrap-select>.dropdown-toggle{width:100%;padding-right:25px;z-index:1}.bootstrap-select>.dropdown-toggle.bs-placeholder,.bootstrap-select>.dropdown-toggle.bs-placeholder:active,.bootstrap-select>.dropdown-toggle.bs-placeholder:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder:hover{color:#999}.bootstrap-select>select{position:absolute!important;bottom:0;left:50%;display:block!important;width:.5px!important;height:100%!important;padding:0!important;opacity:0!important;border:none}.bootstrap-select>select.mobile-device{top:0;left:0;display:block!important;width:100%!important;z-index:2}.error .bootstrap-select .dropdown-toggle,.has-error .bootstrap-select .dropdown-toggle{border-color:#b94a48}.bootstrap-select.fit-width{width:auto!important}.bootstrap-select:not([class*=col-]):not([class*=form-control]):not(.input-group-btn){width:220px}.bootstrap-select .dropdown-toggle:focus{outline:thin dotted #333!important;outline:5px auto -webkit-focus-ring-color!important;outline-offset:-2px}.bootstrap-select.form-control{margin-bottom:0;padding:0;border:none}.bootstrap-select.form-control:not([class*=col-]){width:100%}.bootstrap-select.form-control.input-group-btn{z-index:auto}.bootstrap-select.form-control.input-group-btn:not(:first-child):not(:last-child)>.btn{border-radius:0}.bootstrap-select.btn-group:not(.input-group-btn),.bootstrap-select.btn-group[class*=col-]{float:none;display:inline-block;margin-left:0}.bootstrap-select.btn-group.dropdown-menu-right,.bootstrap-select.btn-group[class*=col-].dropdown-menu-right,.row .bootstrap-select.btn-group[class*=col-].dropdown-menu-right{float:right}.form-group .bootstrap-select.btn-group,.form-horizontal .bootstrap-select.btn-group,.form-inline .bootstrap-select.btn-group{margin-bottom:0}.form-group-lg .bootstrap-select.btn-group.form-control,.form-group-sm .bootstrap-select.btn-group.form-control{padding:0}.form-group-lg .bootstrap-select.btn-group.form-control .dropdown-toggle,.form-group-sm .bootstrap-select.btn-group.form-control .dropdown-toggle{height:100%;font-size:inherit;line-height:inherit;border-radius:inherit}.form-inline .bootstrap-select.btn-group .form-control{width:100%}.bootstrap-select.btn-group.disabled,.bootstrap-select.btn-group>.disabled{cursor:not-allowed}.bootstrap-select.btn-group.disabled:focus,.bootstrap-select.btn-group>.disabled:focus{outline:0!important}.bootstrap-select.btn-group.bs-container{position:absolute;height:0!important;padding:0!important}.bootstrap-select.btn-group.bs-container .dropdown-menu{z-index:1060}.bootstrap-select.btn-group .dropdown-toggle .filter-option{display:inline-block;overflow:hidden;width:100%;text-align:left}.bootstrap-select.btn-group .dropdown-toggle .caret{position:absolute;top:50%;right:12px;margin-top:-2px;vertical-align:middle}.bootstrap-select.btn-group[class*=col-] .dropdown-toggle{width:100%}.bootstrap-select.btn-group .dropdown-menu{min-width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select.btn-group .dropdown-menu.inner{position:static;float:none;border:0;padding:0;margin:0;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.bootstrap-select.btn-group .dropdown-menu li{position:relative}.bootstrap-select.btn-group .dropdown-menu li.active small{color:#fff}.bootstrap-select.btn-group .dropdown-menu li.disabled a{cursor:not-allowed}.bootstrap-select.btn-group .dropdown-menu li a{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.bootstrap-select.btn-group .dropdown-menu li a.opt{position:relative;padding-left:2.25em}.bootstrap-select.btn-group .dropdown-menu li a span.check-mark{display:none}.bootstrap-select.btn-group .dropdown-menu li a span.text{display:inline-block}.bootstrap-select.btn-group .dropdown-menu li small{padding-left:.5em}.bootstrap-select.btn-group .dropdown-menu .notify{position:absolute;bottom:5px;width:96%;margin:0 2%;min-height:26px;padding:3px 5px;background:#f5f5f5;border:1px solid #e3e3e3;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05);pointer-events:none;opacity:.9;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select.btn-group .no-results{padding:3px;background:#f5f5f5;margin:0 5px;white-space:nowrap}.bootstrap-select.btn-group.fit-width .dropdown-toggle .filter-option{position:static}.bootstrap-select.btn-group.fit-width .dropdown-toggle .caret{position:static;top:auto;margin-top:-1px}.bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a span.check-mark{position:absolute;display:inline-block;right:15px;margin-top:5px}.bootstrap-select.btn-group.show-tick .dropdown-menu li a span.text{margin-right:34px}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle{z-index:1061}.bootstrap-select.show-menu-arrow .dropdown-toggle:before{content:'';border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid rgba(204,204,204,.2);position:absolute;bottom:-4px;left:9px;display:none}.bootstrap-select.show-menu-arrow .dropdown-toggle:after{content:'';border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;bottom:-4px;left:10px;display:none}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:before{bottom:auto;top:-3px;border-top:7px solid rgba(204,204,204,.2);border-bottom:0}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:after{bottom:auto;top:-3px;border-top:6px solid #fff;border-bottom:0}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:before{right:12px;left:auto}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:after{right:13px;left:auto}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle:after,.bootstrap-select.show-menu-arrow.open>.dropdown-toggle:before{display:block}.bs-actionsbox,.bs-donebutton,.bs-searchbox{padding:4px 8px}.bs-actionsbox{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-actionsbox .btn-group button{width:50%}.bs-donebutton{float:left;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-donebutton .btn-group button{width:100%}.bs-searchbox+.bs-actionsbox{padding:0 8px 4px}.bs-searchbox .form-control{margin-bottom:0;width:100%;float:none} \ No newline at end of file diff --git a/RIGS/static/css/email.css b/RIGS/static/css/email.css new file mode 100644 index 00000000..915b52f3 --- /dev/null +++ b/RIGS/static/css/email.css @@ -0,0 +1 @@ +body{margin:0px}.main-table{width:100%;border-collapse:collapse}.client-header{background-image:url("https://www.nottinghamtec.co.uk/imgs/wof2014-1.jpg");background-color:#222;background-repeat:no-repeat;background-position:center;width:100%;margin-bottom:28px}.client-header .logos{width:100%;max-width:640px}.client-header img{height:110px}.content-container{width:100%}.content-container .content{font-family:"Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;width:100%;max-width:600px;padding:10px;text-align:left}.content-container .content .button-container{width:100%}.content-container .content .button-container .button{padding:6px 12px;background-color:#357ebf;border-radius:4px}.content-container .content .button-container .button a{color:#fff;text-decoration:none} diff --git a/RIGS/static/css/print.css b/RIGS/static/css/print.css index b89b3fe5..2befadb3 100644 --- a/RIGS/static/css/print.css +++ b/RIGS/static/css/print.css @@ -1 +1,5 @@ -/*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff !important}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:'Glyphicons Halflings';src:url(/static/fonts/glyphicons-halflings-regular.eot?1455294101);src:url(/static/fonts/glyphicons-halflings-regular.eot?&1455294101#iefix) format("embedded-opentype"),url(/static/fonts/glyphicons-halflings-regular.woff2?1455294101) format("woff2"),url(/static/fonts/glyphicons-halflings-regular.woff?1455294101) format("woff"),url(/static/fonts/glyphicons-halflings-regular.ttf?1455294101) format("truetype"),url(/static/fonts/glyphicons-halflings-regular.svg?1455294101#glyphicons_halflingsregular) format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:transparent}body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all 0.2s ease-in-out;-o-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h1 .small,h2 small,h2 .small,h3 small,h3 .small,h4 small,h4 .small,h5 small,h5 .small,h6 small,h6 .small,.h1 small,.h1 .small,.h2 small,.h2 .small,.h3 small,.h3 .small,.h4 small,.h4 .small,.h5 small,.h5 .small,.h6 small,.h6 .small{font-weight:normal;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,h1 .small,.h1 small,.h1 .small,h2 small,h2 .small,.h2 small,.h2 .small,h3 small,h3 .small,.h3 small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,h4 .small,.h4 small,.h4 .small,h5 small,h5 .small,.h5 small,.h5 .small,h6 small,h6 .small,.h6 small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width: 768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{background-color:#fcf8e3;padding:.2em}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase,.initialism{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#428bca}a.text-primary:hover{color:#3071a9}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff}.bg-primary{background-color:#428bca}a.bg-primary:hover{background-color:#3071a9}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ul ol,ol ul,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:bold}dd{margin-left:0}.dl-horizontal dd:before,.dl-horizontal dd:after{content:" ";display:table}.dl-horizontal dd:after{clear:both}@media (min-width: 768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}.blockquote-reverse footer:before,.blockquote-reverse small:before,.blockquote-reverse .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,.blockquote-reverse small:after,.blockquote-reverse .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:bold;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;word-break:break-all;word-wrap:break-word;color:#333;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.container:before,.container:after{content:" ";display:table}.container:after{clear:both}@media (min-width: 768px){.container{width:750px}}@media (min-width: 992px){.container{width:970px}}@media (min-width: 1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.container-fluid:before,.container-fluid:after{content:" ";display:table}.container-fluid:after{clear:both}.row{margin-left:-15px;margin-right:-15px}.row:before,.row:after{content:" ";display:table}.row:after{clear:both}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-1{width:8.33333333%}.col-xs-2{width:16.66666667%}.col-xs-3{width:25%}.col-xs-4{width:33.33333333%}.col-xs-5{width:41.66666667%}.col-xs-6{width:50%}.col-xs-7{width:58.33333333%}.col-xs-8{width:66.66666667%}.col-xs-9{width:75%}.col-xs-10{width:83.33333333%}.col-xs-11{width:91.66666667%}.col-xs-12{width:100%}.col-xs-pull-0{right:auto}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-3{right:25%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-6{right:50%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-9{right:75%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-12{right:100%}.col-xs-push-0{left:auto}.col-xs-push-1{left:8.33333333%}.col-xs-push-2{left:16.66666667%}.col-xs-push-3{left:25%}.col-xs-push-4{left:33.33333333%}.col-xs-push-5{left:41.66666667%}.col-xs-push-6{left:50%}.col-xs-push-7{left:58.33333333%}.col-xs-push-8{left:66.66666667%}.col-xs-push-9{left:75%}.col-xs-push-10{left:83.33333333%}.col-xs-push-11{left:91.66666667%}.col-xs-push-12{left:100%}.col-xs-offset-0{margin-left:0%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-12{margin-left:100%}@media (min-width: 768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-1{width:8.33333333%}.col-sm-2{width:16.66666667%}.col-sm-3{width:25%}.col-sm-4{width:33.33333333%}.col-sm-5{width:41.66666667%}.col-sm-6{width:50%}.col-sm-7{width:58.33333333%}.col-sm-8{width:66.66666667%}.col-sm-9{width:75%}.col-sm-10{width:83.33333333%}.col-sm-11{width:91.66666667%}.col-sm-12{width:100%}.col-sm-pull-0{right:auto}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-3{right:25%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-6{right:50%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-9{right:75%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-12{right:100%}.col-sm-push-0{left:auto}.col-sm-push-1{left:8.33333333%}.col-sm-push-2{left:16.66666667%}.col-sm-push-3{left:25%}.col-sm-push-4{left:33.33333333%}.col-sm-push-5{left:41.66666667%}.col-sm-push-6{left:50%}.col-sm-push-7{left:58.33333333%}.col-sm-push-8{left:66.66666667%}.col-sm-push-9{left:75%}.col-sm-push-10{left:83.33333333%}.col-sm-push-11{left:91.66666667%}.col-sm-push-12{left:100%}.col-sm-offset-0{margin-left:0%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-12{margin-left:100%}}@media (min-width: 992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-1{width:8.33333333%}.col-md-2{width:16.66666667%}.col-md-3{width:25%}.col-md-4{width:33.33333333%}.col-md-5{width:41.66666667%}.col-md-6{width:50%}.col-md-7{width:58.33333333%}.col-md-8{width:66.66666667%}.col-md-9{width:75%}.col-md-10{width:83.33333333%}.col-md-11{width:91.66666667%}.col-md-12{width:100%}.col-md-pull-0{right:auto}.col-md-pull-1{right:8.33333333%}.col-md-pull-2{right:16.66666667%}.col-md-pull-3{right:25%}.col-md-pull-4{right:33.33333333%}.col-md-pull-5{right:41.66666667%}.col-md-pull-6{right:50%}.col-md-pull-7{right:58.33333333%}.col-md-pull-8{right:66.66666667%}.col-md-pull-9{right:75%}.col-md-pull-10{right:83.33333333%}.col-md-pull-11{right:91.66666667%}.col-md-pull-12{right:100%}.col-md-push-0{left:auto}.col-md-push-1{left:8.33333333%}.col-md-push-2{left:16.66666667%}.col-md-push-3{left:25%}.col-md-push-4{left:33.33333333%}.col-md-push-5{left:41.66666667%}.col-md-push-6{left:50%}.col-md-push-7{left:58.33333333%}.col-md-push-8{left:66.66666667%}.col-md-push-9{left:75%}.col-md-push-10{left:83.33333333%}.col-md-push-11{left:91.66666667%}.col-md-push-12{left:100%}.col-md-offset-0{margin-left:0%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-3{margin-left:25%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-6{margin-left:50%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-9{margin-left:75%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-12{margin-left:100%}}@media (min-width: 1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-1{width:8.33333333%}.col-lg-2{width:16.66666667%}.col-lg-3{width:25%}.col-lg-4{width:33.33333333%}.col-lg-5{width:41.66666667%}.col-lg-6{width:50%}.col-lg-7{width:58.33333333%}.col-lg-8{width:66.66666667%}.col-lg-9{width:75%}.col-lg-10{width:83.33333333%}.col-lg-11{width:91.66666667%}.col-lg-12{width:100%}.col-lg-pull-0{right:auto}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-3{right:25%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-6{right:50%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-9{right:75%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-12{right:100%}.col-lg-push-0{left:auto}.col-lg-push-1{left:8.33333333%}.col-lg-push-2{left:16.66666667%}.col-lg-push-3{left:25%}.col-lg-push-4{left:33.33333333%}.col-lg-push-5{left:41.66666667%}.col-lg-push-6{left:50%}.col-lg-push-7{left:58.33333333%}.col-lg-push-8{left:66.66666667%}.col-lg-push-9{left:75%}.col-lg-push-10{left:83.33333333%}.col-lg-push-11{left:91.66666667%}.col-lg-push-12{left:100%}.col-lg-offset-0{margin-left:0%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-12{margin-left:100%}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>thead>tr>td,.table>tbody>tr>th,.table>tbody>tr>td,.table>tfoot>tr>th,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>th,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*="col-"]{position:static;float:none;display:table-column}table td[class*="col-"],table th[class*="col-"]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>thead>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>thead>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>thead>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>thead>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>thead>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{overflow-x:auto;min-height:0.01%}@media screen and (max-width: 767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;-o-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control::-moz-placeholder{color:#777;opacity:1}.form-control:-ms-input-placeholder{color:#777}.form-control::-webkit-input-placeholder{color:#777}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type="search"]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio: 0){input[type="date"],input[type="time"],input[type="datetime-local"],input[type="month"]{line-height:34px}input[type="date"].input-sm,.input-group-sm>input[type="date"].form-control,.input-group-sm>input[type="date"].input-group-addon,.input-group-sm>.input-group-btn>input[type="date"].btn,.input-group-sm input[type="date"],input[type="time"].input-sm,.input-group-sm>input[type="time"].form-control,.input-group-sm>input[type="time"].input-group-addon,.input-group-sm>.input-group-btn>input[type="time"].btn,.input-group-sm input[type="time"],input[type="datetime-local"].input-sm,.input-group-sm>input[type="datetime-local"].form-control,.input-group-sm>input[type="datetime-local"].input-group-addon,.input-group-sm>.input-group-btn>input[type="datetime-local"].btn,.input-group-sm input[type="datetime-local"],input[type="month"].input-sm,.input-group-sm>input[type="month"].form-control,.input-group-sm>input[type="month"].input-group-addon,.input-group-sm>.input-group-btn>input[type="month"].btn,.input-group-sm input[type="month"]{line-height:30px}input[type="date"].input-lg,.input-group-lg>input[type="date"].form-control,.input-group-lg>input[type="date"].input-group-addon,.input-group-lg>.input-group-btn>input[type="date"].btn,.input-group-lg input[type="date"],input[type="time"].input-lg,.input-group-lg>input[type="time"].form-control,.input-group-lg>input[type="time"].input-group-addon,.input-group-lg>.input-group-btn>input[type="time"].btn,.input-group-lg input[type="time"],input[type="datetime-local"].input-lg,.input-group-lg>input[type="datetime-local"].form-control,.input-group-lg>input[type="datetime-local"].input-group-addon,.input-group-lg>.input-group-btn>input[type="datetime-local"].btn,.input-group-lg input[type="datetime-local"],input[type="month"].input-lg,.input-group-lg>input[type="month"].form-control,.input-group-lg>input[type="month"].input-group-addon,.input-group-lg>.input-group-btn>input[type="month"].btn,.input-group-lg input[type="month"]{line-height:46px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-left:-20px;margin-top:4px \9}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:normal;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="radio"].disabled,fieldset[disabled] input[type="radio"],input[type="checkbox"][disabled],input[type="checkbox"].disabled,fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}.radio-inline.disabled,fieldset[disabled] .radio-inline,.checkbox-inline.disabled,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio.disabled label,fieldset[disabled] .radio label,.checkbox.disabled label,fieldset[disabled] .checkbox label{cursor:not-allowed}.form-control-static{padding-top:7px;padding-bottom:7px;margin-bottom:0;min-height:34px}.form-control-static.input-lg,.input-group-lg>.form-control-static.form-control,.input-group-lg>.form-control-static.input-group-addon,.input-group-lg>.input-group-btn>.form-control-static.btn,.form-control-static.input-sm,.input-group-sm>.form-control-static.form-control,.input-group-sm>.form-control-static.input-group-addon,.input-group-sm>.input-group-btn>.form-control-static.btn{padding-left:0;padding-right:0}.input-sm,.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm,.input-group-sm>select.form-control,.input-group-sm>select.input-group-addon,.input-group-sm>.input-group-btn>select.btn{height:30px;line-height:30px}textarea.input-sm,.input-group-sm>textarea.form-control,.input-group-sm>textarea.input-group-addon,.input-group-sm>.input-group-btn>textarea.btn,select[multiple].input-sm,.input-group-sm>select[multiple].form-control,.input-group-sm>select[multiple].input-group-addon,.input-group-sm>.input-group-btn>select[multiple].btn{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;min-height:32px}.input-lg,.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg,.input-group-lg>select.form-control,.input-group-lg>select.input-group-addon,.input-group-lg>.input-group-btn>select.btn{height:46px;line-height:46px}textarea.input-lg,.input-group-lg>textarea.form-control,.input-group-lg>textarea.input-group-addon,.input-group-lg>.input-group-btn>textarea.btn,select[multiple].input-lg,.input-group-lg>select[multiple].form-control,.input-group-lg>select[multiple].input-group-addon,.input-group-lg>.input-group-btn>select[multiple].btn{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;min-height:38px}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg>.form-control+.form-control-feedback,.input-group-lg>.input-group-addon+.form-control-feedback,.input-group-lg>.input-group-btn>.btn+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback,.input-group-sm>.form-control+.form-control-feedback,.input-group-sm>.input-group-addon+.form-control-feedback,.input-group-sm>.input-group-btn>.btn+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.has-error .form-control-feedback{color:#a94442}.has-feedback label ~ .form-control-feedback{top:25px}.has-feedback label.sr-only ~ .form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width: 768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}.form-horizontal .form-group:before,.form-horizontal .form-group:after{content:" ";display:table}.form-horizontal .form-group:after{clear:both}@media (min-width: 768px){.form-horizontal .control-label{text-align:right;margin-bottom:0;padding-top:7px}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width: 768px){.form-horizontal .form-group-lg .control-label{padding-top:14.3px}}@media (min-width: 768px){.form-horizontal .form-group-sm .control-label{padding-top:6px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn.focus,.btn:active:focus,.btn:active.focus,.btn.active:focus,.btn.active.focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;pointer-events:none;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default.focus,.btn-default:active,.btn-default.active,.open>.btn-default.dropdown-toggle{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.btn-default.dropdown-toggle{background-image:none}.btn-default.disabled,.btn-default.disabled:hover,.btn-default.disabled:focus,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled.active,.btn-default[disabled],.btn-default[disabled]:hover,.btn-default[disabled]:focus,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled].active,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default:hover,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:hover,.btn-primary:focus,.btn-primary.focus,.btn-primary:active,.btn-primary.active,.open>.btn-primary.dropdown-toggle{color:#fff;background-color:#3071a9;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open>.btn-primary.dropdown-toggle{background-image:none}.btn-primary.disabled,.btn-primary.disabled:hover,.btn-primary.disabled:focus,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled.active,.btn-primary[disabled],.btn-primary[disabled]:hover,.btn-primary[disabled]:focus,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary:hover,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success.focus,.btn-success:active,.btn-success.active,.open>.btn-success.dropdown-toggle{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.btn-success.dropdown-toggle{background-image:none}.btn-success.disabled,.btn-success.disabled:hover,.btn-success.disabled:focus,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled.active,.btn-success[disabled],.btn-success[disabled]:hover,.btn-success[disabled]:focus,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled].active,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success:hover,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info.focus,.btn-info:active,.btn-info.active,.open>.btn-info.dropdown-toggle{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.btn-info.dropdown-toggle{background-image:none}.btn-info.disabled,.btn-info.disabled:hover,.btn-info.disabled:focus,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled.active,.btn-info[disabled],.btn-info[disabled]:hover,.btn-info[disabled]:focus,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled].active,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info:hover,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning.focus,.btn-warning:active,.btn-warning.active,.open>.btn-warning.dropdown-toggle{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open>.btn-warning.dropdown-toggle{background-image:none}.btn-warning.disabled,.btn-warning.disabled:hover,.btn-warning.disabled:focus,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled.active,.btn-warning[disabled],.btn-warning[disabled]:hover,.btn-warning[disabled]:focus,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning:hover,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger.focus,.btn-danger:active,.btn-danger.active,.open>.btn-danger.dropdown-toggle{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.btn-danger.dropdown-toggle{background-image:none}.btn-danger.disabled,.btn-danger.disabled:hover,.btn-danger.disabled:focus,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled.active,.btn-danger[disabled],.btn-danger[disabled]:hover,.btn-danger[disabled]:focus,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger:hover,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{color:#428bca;font-weight:normal;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:hover,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-property:height,visibility;transition-property:height,visibility;-webkit-transition-duration:0.35s;transition-duration:0.35s;-webkit-transition-timing-function:ease;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:14px;text-align:left;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#262626;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;outline:0;background-color:#428bca}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#777}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width: 768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn:hover,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar:before,.btn-toolbar:after{content:" ";display:table}.btn-toolbar:after{clear:both}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle,.btn-group-lg.btn-group>.btn+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret,.btn-group-lg>.btn .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret,.dropup .btn-group-lg>.btn .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after{content:" ";display:table}.btn-group-vertical>.btn-group:after{clear:both}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-bottom-left-radius:4px;border-top-right-radius:0;border-top-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:normal;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.input-group-addon.btn{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.input-group-addon.btn{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav:before,.nav:after{content:" ";display:table}.nav:after{clear:both}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#777;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified,.nav-tabs.nav-justified{width:100%}.nav-justified>li,.nav-tabs.nav-justified>li{float:none}.nav-justified>li>a,.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width: 768px){.nav-justified>li,.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a,.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified,.nav-tabs.nav-justified{border-bottom:0}.nav-tabs-justified>li>a,.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs.nav-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width: 768px){.nav-tabs-justified>li>a,.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs.nav-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}.navbar:before,.navbar:after{content:" ";display:table}.navbar:after{clear:both}@media (min-width: 768px){.navbar{border-radius:4px}}.navbar-header:before,.navbar-header:after{content:" ";display:table}.navbar-header:after{clear:both}@media (min-width: 768px){.navbar-header{float:left}}.navbar-collapse{overflow-x:visible;padding-right:15px;padding-left:15px;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse:before,.navbar-collapse:after{content:" ";display:table}.navbar-collapse:after{clear:both}.navbar-collapse.in{overflow-y:auto}@media (min-width: 768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-left:0;padding-right:0}}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width: 480px) and (orientation: landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}.container>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-header,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width: 768px){.container>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-header,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width: 768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media (min-width: 768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:15px 15px;font-size:18px;line-height:20px;height:50px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width: 768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;margin-right:15px;padding:9px 10px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width: 768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width: 767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width: 768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{margin-left:-15px;margin-right:-15px;padding:10px 15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:8px;margin-bottom:8px}@media (min-width: 768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width: 767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width: 768px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-right-radius:4px;border-top-left-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm,.btn-group-sm>.navbar-btn.btn{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs,.btn-group-xs>.navbar-btn.btn{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width: 768px){.navbar-text{float:left;margin-left:15px;margin-right:15px}}@media (min-width: 768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right ~ .navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#e7e7e7;color:#555}@media (max-width: 767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#333}.navbar-default .btn-link[disabled]:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:hover,fieldset[disabled] .navbar-default .btn-link:focus{color:#ccc}.navbar-inverse{background-color:#222;border-color:#090909}.navbar-inverse .navbar-brand{color:#aaa}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#777}.navbar-inverse .navbar-nav>li>a{color:#aaa}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#090909}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#090909;color:#fff}@media (max-width: 767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#090909}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#090909}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#aaa}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#090909}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#aaa}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#aaa}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#fff}.navbar-inverse .btn-link[disabled]:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:hover,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{content:"/ ";padding:0 5px;color:#ccc}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.42857143;text-decoration:none;color:#428bca;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}.pagination>li>a:hover,.pagination>li>a:focus,.pagination>li>span:hover,.pagination>li>span:focus{color:#2a6496;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:hover,.pagination>.active>a:focus,.pagination>.active>span,.pagination>.active>span:hover,.pagination>.active>span:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#777;background-color:#fff;border-color:#ddd;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:20px 0;list-style:none;text-align:center}.pager:before,.pager:after{content:" ";display:table}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#777;background-color:#fff;cursor:not-allowed}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.label:empty{display:none}.btn .label{position:relative;top:-1px}a.label:hover,a.label:focus{color:#fff;text-decoration:none;cursor:pointer}.label-default{background-color:#777}.label-default[href]:hover,.label-default[href]:focus{background-color:#5e5e5e}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;color:#fff;line-height:1;vertical-align:baseline;white-space:nowrap;text-align:center;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.jumbotron{padding:30px 15px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width: 768px){.jumbotron{padding:48px 0}.container .jumbotron,.container-fluid .jumbotron{padding-left:60px;padding-right:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border 0.2s ease-in-out;-o-transition:border 0.2s ease-in-out;transition:border 0.2s ease-in-out}.thumbnail>img,.thumbnail a>img{display:block;max-width:100%;height:auto;margin-left:auto;margin-right:auto}.thumbnail .caption{padding:9px;color:#333}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#428bca}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{overflow:hidden;height:20px;margin-bottom:20px;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{zoom:1;overflow:hidden}.media-body{width:10000px}.media-object{display:block}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{margin-bottom:20px;padding-left:0}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{text-decoration:none;color:#555;background-color:#f5f5f5}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{background-color:#eee;color:#777;cursor:not-allowed}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#e1edf7}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-body:before,.panel-body:after{content:" ";display:table}.panel-body:after{clear:both}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:3px;border-top-left-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-left:15px;padding-right:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:3px;border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-left-radius:3px;border-bottom-right-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#428bca}.panel-primary>.panel-heading .badge{color:#428bca;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;left:0;bottom:0;height:100%;width:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:0.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:0.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:hidden;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);-ms-transform:translate(0, -25%);-o-transform:translate(0, -25%);transform:translate(0, -25%);-webkit-transition:-webkit-transform 0.3s ease-out;-moz-transition:-moz-transform 0.3s ease-out;-o-transition:-o-transform 0.3s ease-out;transition:transform 0.3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);background-clip:padding-box;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:0.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5;min-height:16.42857143px}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer:before,.modal-footer:after{content:" ";display:table}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width: 768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width: 992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-weight:normal;line-height:1.4;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:0.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{bottom:0;right:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-weight:normal;line-height:1.42857143;text-align:left;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);white-space:normal}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{margin:0;padding:8px 14px;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{border-width:10px;content:""}.popover.top>.arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);bottom:-11px}.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#fff}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0,0,0,0.25)}.popover.right>.arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#fff}.popover.bottom>.arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#fff;bottom:-10px}.carousel{position:relative}.carousel-inner{position:relative;overflow:hidden;width:100%}.carousel-inner>.item{display:none;position:relative;-webkit-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto;line-height:1}@media all and (transform-3d), (-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform 0.6s ease-in-out;-moz-transition:-moz-transform 0.6s ease-in-out;-o-transition:-o-transform 0.6s ease-in-out;transition:transform 0.6s ease-in-out;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000;-moz-perspective:1000;perspective:1000}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;left:0;bottom:0;width:15%;opacity:0.5;filter:alpha(opacity=50);font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-control.left{background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.0001) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.0001) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}.carousel-control.right{left:auto;right:0;background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.0001) 0%, rgba(0,0,0,0.5) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.0001) 0%, rgba(0,0,0,0.5) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0%, rgba(0,0,0,0.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}.carousel-control:hover,.carousel-control:focus{outline:0;color:#fff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;line-height:1;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;margin-left:-30%;padding-left:0;list-style:none;text-align:center}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;border:1px solid #fff;border-radius:10px;cursor:pointer;background-color:#000 \9;background-color:transparent}.carousel-indicators .active{margin:0;width:12px;height:12px;background-color:#fff}.carousel-caption{position:absolute;left:15%;right:15%;bottom:20px;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width: 768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{left:20%;right:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after{content:" ";display:table}.clearfix:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs{display:none !important}.visible-sm{display:none !important}.visible-md{display:none !important}.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width: 767px){.visible-xs{display:block !important}table.visible-xs{display:table}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width: 767px){.visible-xs-block{display:block !important}}@media (max-width: 767px){.visible-xs-inline{display:inline !important}}@media (max-width: 767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width: 768px) and (max-width: 991px){.visible-sm{display:block !important}table.visible-sm{display:table}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width: 768px) and (max-width: 991px){.visible-sm-block{display:block !important}}@media (min-width: 768px) and (max-width: 991px){.visible-sm-inline{display:inline !important}}@media (min-width: 768px) and (max-width: 991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width: 992px) and (max-width: 1199px){.visible-md{display:block !important}table.visible-md{display:table}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width: 992px) and (max-width: 1199px){.visible-md-block{display:block !important}}@media (min-width: 992px) and (max-width: 1199px){.visible-md-inline{display:inline !important}}@media (min-width: 992px) and (max-width: 1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width: 1200px){.visible-lg{display:block !important}table.visible-lg{display:table}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width: 1200px){.visible-lg-block{display:block !important}}@media (min-width: 1200px){.visible-lg-inline{display:inline !important}}@media (min-width: 1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width: 767px){.hidden-xs{display:none !important}}@media (min-width: 768px) and (max-width: 991px){.hidden-sm{display:none !important}}@media (min-width: 992px) and (max-width: 1199px){.hidden-md{display:none !important}}@media (min-width: 1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}} +/*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:"Glyphicons Halflings";src:url(/static/fonts/glyphicons-halflings-regular.eot?1543879568);src:url(/static/fonts/glyphicons-halflings-regular.eot?&1543879568#iefix) format("embedded-opentype"),url(/static/fonts/glyphicons-halflings-regular.woff2?1543879568) format("woff2"),url(/static/fonts/glyphicons-halflings-regular.woff?1543879568) format("woff"),url(/static/fonts/glyphicons-halflings-regular.ttf?1543879568) format("truetype"),url(/static/fonts/glyphicons-halflings-regular.svg?1543879568#glyphicons_halflingsregular) format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:transparent}body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all 0.2s ease-in-out;-o-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h1 .small,h2 small,h2 .small,h3 small,h3 .small,h4 small,h4 .small,h5 small,h5 .small,h6 small,h6 .small,.h1 small,.h1 .small,.h2 small,.h2 .small,.h3 small,.h3 .small,.h4 small,.h4 .small,.h5 small,.h5 .small,.h6 small,.h6 .small{font-weight:400;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,h1 .small,.h1 small,.h1 .small,h2 small,h2 .small,.h2 small,.h2 .small,h3 small,h3 .small,.h3 small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,h4 .small,.h4 small,.h4 .small,h5 small,h5 .small,.h5 small,.h5 .small,h6 small,h6 .small,.h6 small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width: 768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase,.initialism{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#428bca}a.text-primary:hover,a.text-primary:focus{color:#3071a9}.text-success{color:#3c763d}a.text-success:hover,a.text-success:focus{color:#2b542c}.text-info{color:#31708f}a.text-info:hover,a.text-info:focus{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover,a.text-warning:focus{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover,a.text-danger:focus{color:#843534}.bg-primary{color:#fff}.bg-primary{background-color:#428bca}a.bg-primary:hover,a.bg-primary:focus{background-color:#3071a9}.bg-success{background-color:#dff0d8}a.bg-success:hover,a.bg-success:focus{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover,a.bg-info:focus{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover,a.bg-warning:focus{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover,a.bg-danger:focus{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ul ol,ol ul,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}.dl-horizontal dd:before,.dl-horizontal dd:after{display:table;content:" "}.dl-horizontal dd:after{clear:both}@media (min-width: 768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse footer:before,.blockquote-reverse small:before,.blockquote-reverse .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,.blockquote-reverse small:after,.blockquote-reverse .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.container:before,.container:after{display:table;content:" "}.container:after{clear:both}@media (min-width: 768px){.container{width:750px}}@media (min-width: 992px){.container{width:970px}}@media (min-width: 1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.container-fluid:before,.container-fluid:after{display:table;content:" "}.container-fluid:after{clear:both}.row{margin-right:-15px;margin-left:-15px}.row:before,.row:after{display:table;content:" "}.row:after{clear:both}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-1{width:8.33333333%}.col-xs-2{width:16.66666667%}.col-xs-3{width:25%}.col-xs-4{width:33.33333333%}.col-xs-5{width:41.66666667%}.col-xs-6{width:50%}.col-xs-7{width:58.33333333%}.col-xs-8{width:66.66666667%}.col-xs-9{width:75%}.col-xs-10{width:83.33333333%}.col-xs-11{width:91.66666667%}.col-xs-12{width:100%}.col-xs-pull-0{right:auto}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-3{right:25%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-6{right:50%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-9{right:75%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-12{right:100%}.col-xs-push-0{left:auto}.col-xs-push-1{left:8.33333333%}.col-xs-push-2{left:16.66666667%}.col-xs-push-3{left:25%}.col-xs-push-4{left:33.33333333%}.col-xs-push-5{left:41.66666667%}.col-xs-push-6{left:50%}.col-xs-push-7{left:58.33333333%}.col-xs-push-8{left:66.66666667%}.col-xs-push-9{left:75%}.col-xs-push-10{left:83.33333333%}.col-xs-push-11{left:91.66666667%}.col-xs-push-12{left:100%}.col-xs-offset-0{margin-left:0%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-12{margin-left:100%}@media (min-width: 768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-1{width:8.33333333%}.col-sm-2{width:16.66666667%}.col-sm-3{width:25%}.col-sm-4{width:33.33333333%}.col-sm-5{width:41.66666667%}.col-sm-6{width:50%}.col-sm-7{width:58.33333333%}.col-sm-8{width:66.66666667%}.col-sm-9{width:75%}.col-sm-10{width:83.33333333%}.col-sm-11{width:91.66666667%}.col-sm-12{width:100%}.col-sm-pull-0{right:auto}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-3{right:25%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-6{right:50%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-9{right:75%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-12{right:100%}.col-sm-push-0{left:auto}.col-sm-push-1{left:8.33333333%}.col-sm-push-2{left:16.66666667%}.col-sm-push-3{left:25%}.col-sm-push-4{left:33.33333333%}.col-sm-push-5{left:41.66666667%}.col-sm-push-6{left:50%}.col-sm-push-7{left:58.33333333%}.col-sm-push-8{left:66.66666667%}.col-sm-push-9{left:75%}.col-sm-push-10{left:83.33333333%}.col-sm-push-11{left:91.66666667%}.col-sm-push-12{left:100%}.col-sm-offset-0{margin-left:0%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-12{margin-left:100%}}@media (min-width: 992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-1{width:8.33333333%}.col-md-2{width:16.66666667%}.col-md-3{width:25%}.col-md-4{width:33.33333333%}.col-md-5{width:41.66666667%}.col-md-6{width:50%}.col-md-7{width:58.33333333%}.col-md-8{width:66.66666667%}.col-md-9{width:75%}.col-md-10{width:83.33333333%}.col-md-11{width:91.66666667%}.col-md-12{width:100%}.col-md-pull-0{right:auto}.col-md-pull-1{right:8.33333333%}.col-md-pull-2{right:16.66666667%}.col-md-pull-3{right:25%}.col-md-pull-4{right:33.33333333%}.col-md-pull-5{right:41.66666667%}.col-md-pull-6{right:50%}.col-md-pull-7{right:58.33333333%}.col-md-pull-8{right:66.66666667%}.col-md-pull-9{right:75%}.col-md-pull-10{right:83.33333333%}.col-md-pull-11{right:91.66666667%}.col-md-pull-12{right:100%}.col-md-push-0{left:auto}.col-md-push-1{left:8.33333333%}.col-md-push-2{left:16.66666667%}.col-md-push-3{left:25%}.col-md-push-4{left:33.33333333%}.col-md-push-5{left:41.66666667%}.col-md-push-6{left:50%}.col-md-push-7{left:58.33333333%}.col-md-push-8{left:66.66666667%}.col-md-push-9{left:75%}.col-md-push-10{left:83.33333333%}.col-md-push-11{left:91.66666667%}.col-md-push-12{left:100%}.col-md-offset-0{margin-left:0%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-3{margin-left:25%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-6{margin-left:50%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-9{margin-left:75%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-12{margin-left:100%}}@media (min-width: 1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-1{width:8.33333333%}.col-lg-2{width:16.66666667%}.col-lg-3{width:25%}.col-lg-4{width:33.33333333%}.col-lg-5{width:41.66666667%}.col-lg-6{width:50%}.col-lg-7{width:58.33333333%}.col-lg-8{width:66.66666667%}.col-lg-9{width:75%}.col-lg-10{width:83.33333333%}.col-lg-11{width:91.66666667%}.col-lg-12{width:100%}.col-lg-pull-0{right:auto}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-3{right:25%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-6{right:50%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-9{right:75%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-12{right:100%}.col-lg-push-0{left:auto}.col-lg-push-1{left:8.33333333%}.col-lg-push-2{left:16.66666667%}.col-lg-push-3{left:25%}.col-lg-push-4{left:33.33333333%}.col-lg-push-5{left:41.66666667%}.col-lg-push-6{left:50%}.col-lg-push-7{left:58.33333333%}.col-lg-push-8{left:66.66666667%}.col-lg-push-9{left:75%}.col-lg-push-10{left:83.33333333%}.col-lg-push-11{left:91.66666667%}.col-lg-push-12{left:100%}.col-lg-offset-0{margin-left:0%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-12{margin-left:100%}}table{background-color:transparent}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>thead>tr>td,.table>tbody>tr>th,.table>tbody>tr>td,.table>tfoot>tr>th,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>th,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table>thead>tr>td.active,.table>thead>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>thead>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>thead>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>thead>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>thead>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width: 767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="radio"].disabled,fieldset[disabled] input[type="radio"],input[type="checkbox"][disabled],input[type="checkbox"].disabled,fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;-o-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control::-moz-placeholder{color:#777;opacity:1}.form-control:-ms-input-placeholder{color:#777}.form-control::-webkit-input-placeholder{color:#777}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio: 0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:34px}input[type="date"].input-sm,.input-group-sm>input[type="date"].form-control,.input-group-sm>input[type="date"].input-group-addon,.input-group-sm>.input-group-btn>input[type="date"].btn,.input-group-sm input[type="date"],input[type="time"].input-sm,.input-group-sm>input[type="time"].form-control,.input-group-sm>input[type="time"].input-group-addon,.input-group-sm>.input-group-btn>input[type="time"].btn,.input-group-sm input[type="time"],input[type="datetime-local"].input-sm,.input-group-sm>input[type="datetime-local"].form-control,.input-group-sm>input[type="datetime-local"].input-group-addon,.input-group-sm>.input-group-btn>input[type="datetime-local"].btn,.input-group-sm input[type="datetime-local"],input[type="month"].input-sm,.input-group-sm>input[type="month"].form-control,.input-group-sm>input[type="month"].input-group-addon,.input-group-sm>.input-group-btn>input[type="month"].btn,.input-group-sm input[type="month"]{line-height:30px}input[type="date"].input-lg,.input-group-lg>input[type="date"].form-control,.input-group-lg>input[type="date"].input-group-addon,.input-group-lg>.input-group-btn>input[type="date"].btn,.input-group-lg input[type="date"],input[type="time"].input-lg,.input-group-lg>input[type="time"].form-control,.input-group-lg>input[type="time"].input-group-addon,.input-group-lg>.input-group-btn>input[type="time"].btn,.input-group-lg input[type="time"],input[type="datetime-local"].input-lg,.input-group-lg>input[type="datetime-local"].form-control,.input-group-lg>input[type="datetime-local"].input-group-addon,.input-group-lg>.input-group-btn>input[type="datetime-local"].btn,.input-group-lg input[type="datetime-local"],input[type="month"].input-lg,.input-group-lg>input[type="month"].form-control,.input-group-lg>input[type="month"].input-group-addon,.input-group-lg>.input-group-btn>input[type="month"].btn,.input-group-lg input[type="month"]{line-height:46px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,fieldset[disabled] .radio label,.checkbox.disabled label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,fieldset[disabled] .radio-inline,.checkbox-inline.disabled,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.input-group-lg>.form-control-static.form-control,.input-group-lg>.form-control-static.input-group-addon,.input-group-lg>.input-group-btn>.form-control-static.btn,.form-control-static.input-sm,.input-group-sm>.form-control-static.form-control,.input-group-sm>.form-control-static.input-group-addon,.input-group-sm>.input-group-btn>.form-control-static.btn{padding-right:0;padding-left:0}.input-sm,.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm,.input-group-sm>select.form-control,.input-group-sm>select.input-group-addon,.input-group-sm>.input-group-btn>select.btn{height:30px;line-height:30px}textarea.input-sm,.input-group-sm>textarea.form-control,.input-group-sm>textarea.input-group-addon,.input-group-sm>.input-group-btn>textarea.btn,select[multiple].input-sm,.input-group-sm>select[multiple].form-control,.input-group-sm>select[multiple].input-group-addon,.input-group-sm>.input-group-btn>select[multiple].btn{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg,.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg,.input-group-lg>select.form-control,.input-group-lg>select.input-group-addon,.input-group-lg>.input-group-btn>select.btn{height:46px;line-height:46px}textarea.input-lg,.input-group-lg>textarea.form-control,.input-group-lg>textarea.input-group-addon,.input-group-lg>.input-group-btn>textarea.btn,select[multiple].input-lg,.input-group-lg>select[multiple].form-control,.input-group-lg>select[multiple].input-group-addon,.input-group-lg>.input-group-btn>select[multiple].btn{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.33}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg>.form-control+.form-control-feedback,.input-group-lg>.input-group-addon+.form-control-feedback,.input-group-lg>.input-group-btn>.btn+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback,.input-group-sm>.form-control+.form-control-feedback,.input-group-sm>.input-group-addon+.form-control-feedback,.input-group-sm>.input-group-btn>.btn+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label ~ .form-control-feedback{top:25px}.has-feedback label.sr-only ~ .form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width: 768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}.form-horizontal .form-group:before,.form-horizontal .form-group:after{display:table;content:" "}.form-horizontal .form-group:after{clear:both}@media (min-width: 768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width: 768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width: 768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn.focus,.btn:active:focus,.btn:active.focus,.btn.active:focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#333;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:.65;-webkit-box-shadow:none;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:focus,.btn-default.focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.btn-default.dropdown-toggle{color:#333;background-color:#e6e6e6;background-image:none;border-color:#adadad}.btn-default:active:hover,.btn-default:active:focus,.btn-default:active.focus,.btn-default.active:hover,.btn-default.active:focus,.btn-default.active.focus,.open>.btn-default.dropdown-toggle:hover,.open>.btn-default.dropdown-toggle:focus,.open>.btn-default.dropdown-toggle.focus{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.disabled:hover,.btn-default.disabled:focus,.btn-default.disabled.focus,.btn-default[disabled]:hover,.btn-default[disabled]:focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default:hover,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default.focus{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:focus,.btn-primary.focus{color:#fff;background-color:#3071a9;border-color:#193c5a}.btn-primary:hover{color:#fff;background-color:#3071a9;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open>.btn-primary.dropdown-toggle{color:#fff;background-color:#3071a9;background-image:none;border-color:#285e8e}.btn-primary:active:hover,.btn-primary:active:focus,.btn-primary:active.focus,.btn-primary.active:hover,.btn-primary.active:focus,.btn-primary.active.focus,.open>.btn-primary.dropdown-toggle:hover,.open>.btn-primary.dropdown-toggle:focus,.open>.btn-primary.dropdown-toggle.focus{color:#fff;background-color:#285e8e;border-color:#193c5a}.btn-primary.disabled:hover,.btn-primary.disabled:focus,.btn-primary.disabled.focus,.btn-primary[disabled]:hover,.btn-primary[disabled]:focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary:hover,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary.focus{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:focus,.btn-success.focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.btn-success.dropdown-toggle{color:#fff;background-color:#449d44;background-image:none;border-color:#398439}.btn-success:active:hover,.btn-success:active:focus,.btn-success:active.focus,.btn-success.active:hover,.btn-success.active:focus,.btn-success.active.focus,.open>.btn-success.dropdown-toggle:hover,.open>.btn-success.dropdown-toggle:focus,.open>.btn-success.dropdown-toggle.focus{color:#fff;background-color:#398439;border-color:#255625}.btn-success.disabled:hover,.btn-success.disabled:focus,.btn-success.disabled.focus,.btn-success[disabled]:hover,.btn-success[disabled]:focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success:hover,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success.focus{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:focus,.btn-info.focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.btn-info.dropdown-toggle{color:#fff;background-color:#31b0d5;background-image:none;border-color:#269abc}.btn-info:active:hover,.btn-info:active:focus,.btn-info:active.focus,.btn-info.active:hover,.btn-info.active:focus,.btn-info.active.focus,.open>.btn-info.dropdown-toggle:hover,.open>.btn-info.dropdown-toggle:focus,.open>.btn-info.dropdown-toggle.focus{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.disabled:hover,.btn-info.disabled:focus,.btn-info.disabled.focus,.btn-info[disabled]:hover,.btn-info[disabled]:focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info:hover,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info.focus{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:focus,.btn-warning.focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open>.btn-warning.dropdown-toggle{color:#fff;background-color:#ec971f;background-image:none;border-color:#d58512}.btn-warning:active:hover,.btn-warning:active:focus,.btn-warning:active.focus,.btn-warning.active:hover,.btn-warning.active:focus,.btn-warning.active.focus,.open>.btn-warning.dropdown-toggle:hover,.open>.btn-warning.dropdown-toggle:focus,.open>.btn-warning.dropdown-toggle.focus{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.disabled:hover,.btn-warning.disabled:focus,.btn-warning.disabled.focus,.btn-warning[disabled]:hover,.btn-warning[disabled]:focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning:hover,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning.focus{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:focus,.btn-danger.focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.btn-danger.dropdown-toggle{color:#fff;background-color:#c9302c;background-image:none;border-color:#ac2925}.btn-danger:active:hover,.btn-danger:active:focus,.btn-danger:active.focus,.btn-danger.active:hover,.btn-danger.active:focus,.btn-danger.active.focus,.open>.btn-danger.dropdown-toggle:hover,.open>.btn-danger.dropdown-toggle:focus,.open>.btn-danger.dropdown-toggle.focus{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.disabled:hover,.btn-danger.disabled:focus,.btn-danger.disabled.focus,.btn-danger[disabled]:hover,.btn-danger[disabled]:focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger:hover,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger.focus{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#428bca;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:hover,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-property:height,visibility;transition-property:height,visibility;-webkit-transition-duration:0.35s;transition-duration:0.35s;-webkit-transition-timing-function:ease;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#428bca;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#777}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width: 768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn:hover,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar:before,.btn-toolbar:after{display:table;content:" "}.btn-toolbar:after{clear:both}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle,.btn-group-lg.btn-group>.btn+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret,.btn-group-lg>.btn .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret,.dropup .btn-group-lg>.btn .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after{display:table;content:" "}.btn-group-vertical>.btn-group:after{clear:both}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.input-group-addon.btn{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.input-group-addon.btn{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav:before,.nav:after{display:table;content:" "}.nav:after{clear:both}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified,.nav-tabs.nav-justified{width:100%}.nav-justified>li,.nav-tabs.nav-justified>li{float:none}.nav-justified>li>a,.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width: 768px){.nav-justified>li,.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a,.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified,.nav-tabs.nav-justified{border-bottom:0}.nav-tabs-justified>li>a,.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs.nav-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width: 768px){.nav-tabs-justified>li>a,.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs.nav-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}.navbar:before,.navbar:after{display:table;content:" "}.navbar:after{clear:both}@media (min-width: 768px){.navbar{border-radius:4px}}.navbar-header:before,.navbar-header:after{display:table;content:" "}.navbar-header:after{clear:both}@media (min-width: 768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse:before,.navbar-collapse:after{display:table;content:" "}.navbar-collapse:after{clear:both}.navbar-collapse.in{overflow-y:auto}@media (min-width: 768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width: 480px) and (orientation: landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width: 768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-header,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width: 768px){.container>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-header,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width: 768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width: 768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width: 768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width: 767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width: 768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:8px;margin-bottom:8px}@media (min-width: 768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width: 767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width: 768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm,.btn-group-sm>.navbar-btn.btn{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs,.btn-group-xs>.navbar-btn.btn{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width: 768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width: 768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right ~ .navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#555;background-color:#e7e7e7}@media (max-width: 767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#333}.navbar-default .btn-link[disabled]:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:hover,fieldset[disabled] .navbar-default .btn-link:focus{color:#ccc}.navbar-inverse{background-color:#222;border-color:#090909}.navbar-inverse .navbar-brand{color:#aaa}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#777}.navbar-inverse .navbar-nav>li>a{color:#aaa}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#090909}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#fff;background-color:#090909}@media (max-width: 767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#090909}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#090909}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#aaa}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#090909}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-link{color:#aaa}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#aaa}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#fff}.navbar-inverse .btn-link[disabled]:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:hover,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/ "}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#428bca;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li>a:hover,.pagination>li>a:focus,.pagination>li>span:hover,.pagination>li>span:focus{z-index:2;color:#2a6496;background-color:#eee;border-color:#ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>a:hover,.pagination>.active>a:focus,.pagination>.active>span,.pagination>.active>span:hover,.pagination>.active>span:focus{z-index:3;color:#fff;cursor:default;background-color:#428bca;border-color:#428bca}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.33}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager:before,.pager:after{display:table;content:" "}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.label:empty{display:none}.btn .label{position:relative;top:-1px}a.label:hover,a.label:focus{color:#fff;text-decoration:none;cursor:pointer}.label-default{background-color:#777}.label-default[href]:hover,.label-default[href]:focus{background-color:#5e5e5e}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width: 768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border 0.2s ease-in-out;-o-transition:border 0.2s ease-in-out;transition:border 0.2s ease-in-out}.thumbnail>img,.thumbnail a>img{display:block;max-width:100%;height:auto;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#333}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#428bca}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#e1edf7}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus,button.list-group-item:hover,button.list-group-item:focus{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:hover,button.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active,button.list-group-item-success.active:hover,button.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:hover,button.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active,button.list-group-item-info.active:hover,button.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:hover,button.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active,button.list-group-item-warning.active:hover,button.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:hover,button.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active,button.list-group-item-danger.active:hover,button.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-body:before,.panel-body:after{display:table;content:" "}.panel-body:after{clear:both}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#428bca}.panel-primary>.panel-heading .badge{color:#428bca;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);-ms-transform:translate(0, -25%);-o-transform:translate(0, -25%);transform:translate(0, -25%);-webkit-transition:-webkit-transform 0.3s ease-out;-moz-transition:-moz-transform 0.3s ease-out;-o-transition:-o-transform 0.3s ease-out;transition:transform 0.3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header:before,.modal-header:after{display:table;content:" "}.modal-header:after{clear:both}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer:before,.modal-footer:after{display:table;content:" "}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width: 768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width: 992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:14px;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto;line-height:1}@media all and (transform-3d), (-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform 0.6s ease-in-out;-moz-transition:-moz-transform 0.6s ease-in-out;-o-transition:-o-transform 0.6s ease-in-out;transition:transform 0.6s ease-in-out;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;-moz-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:transparent;filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.0001) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.0001) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.0001) 0%, rgba(0,0,0,0.5) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.0001) 0%, rgba(0,0,0,0.5) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0%, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:transparent;border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width: 768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after{display:table;content:" "}.clearfix:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs{display:none !important}.visible-sm{display:none !important}.visible-md{display:none !important}.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width: 767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width: 767px){.visible-xs-block{display:block !important}}@media (max-width: 767px){.visible-xs-inline{display:inline !important}}@media (max-width: 767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width: 768px) and (max-width: 991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width: 768px) and (max-width: 991px){.visible-sm-block{display:block !important}}@media (min-width: 768px) and (max-width: 991px){.visible-sm-inline{display:inline !important}}@media (min-width: 768px) and (max-width: 991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width: 992px) and (max-width: 1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width: 992px) and (max-width: 1199px){.visible-md-block{display:block !important}}@media (min-width: 992px) and (max-width: 1199px){.visible-md-inline{display:inline !important}}@media (min-width: 992px) and (max-width: 1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width: 1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width: 1200px){.visible-lg-block{display:block !important}}@media (min-width: 1200px){.visible-lg-inline{display:inline !important}}@media (min-width: 1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width: 767px){.hidden-xs{display:none !important}}@media (min-width: 768px) and (max-width: 991px){.hidden-sm{display:none !important}}@media (min-width: 992px) and (max-width: 1199px){.hidden-md{display:none !important}}@media (min-width: 1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}} diff --git a/RIGS/static/css/screen.css b/RIGS/static/css/screen.css index d7ed2f52..3b4eac9c 100644 --- a/RIGS/static/css/screen.css +++ b/RIGS/static/css/screen.css @@ -1,4 +1,8 @@ -/*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff !important}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:'Glyphicons Halflings';src:url(/static/fonts/glyphicons-halflings-regular.eot?1455294101);src:url(/static/fonts/glyphicons-halflings-regular.eot?&1455294101#iefix) format("embedded-opentype"),url(/static/fonts/glyphicons-halflings-regular.woff2?1455294101) format("woff2"),url(/static/fonts/glyphicons-halflings-regular.woff?1455294101) format("woff"),url(/static/fonts/glyphicons-halflings-regular.ttf?1455294101) format("truetype"),url(/static/fonts/glyphicons-halflings-regular.svg?1455294101#glyphicons_halflingsregular) format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:transparent}body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all 0.2s ease-in-out;-o-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h1 .small,h2 small,h2 .small,h3 small,h3 .small,h4 small,h4 .small,h5 small,h5 .small,h6 small,h6 .small,.h1 small,.h1 .small,.h2 small,.h2 .small,.h3 small,.h3 .small,.h4 small,.h4 .small,.h5 small,.h5 .small,.h6 small,.h6 .small{font-weight:normal;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,h1 .small,.h1 small,.h1 .small,h2 small,h2 .small,.h2 small,.h2 .small,h3 small,h3 .small,.h3 small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,h4 .small,.h4 small,.h4 .small,h5 small,h5 .small,.h5 small,.h5 .small,h6 small,h6 .small,.h6 small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width: 768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{background-color:#fcf8e3;padding:.2em}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase,.initialism{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#428bca}a.text-primary:hover{color:#3071a9}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff}.bg-primary{background-color:#428bca}a.bg-primary:hover{background-color:#3071a9}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ul ol,ol ul,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:bold}dd{margin-left:0}.dl-horizontal dd:before,.dl-horizontal dd:after{content:" ";display:table}.dl-horizontal dd:after{clear:both}@media (min-width: 768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}.blockquote-reverse footer:before,.blockquote-reverse small:before,.blockquote-reverse .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,.blockquote-reverse small:after,.blockquote-reverse .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:bold;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;word-break:break-all;word-wrap:break-word;color:#333;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.container:before,.container:after{content:" ";display:table}.container:after{clear:both}@media (min-width: 768px){.container{width:750px}}@media (min-width: 992px){.container{width:970px}}@media (min-width: 1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.container-fluid:before,.container-fluid:after{content:" ";display:table}.container-fluid:after{clear:both}.row{margin-left:-15px;margin-right:-15px}.row:before,.row:after{content:" ";display:table}.row:after{clear:both}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-1{width:8.33333333%}.col-xs-2{width:16.66666667%}.col-xs-3{width:25%}.col-xs-4{width:33.33333333%}.col-xs-5{width:41.66666667%}.col-xs-6{width:50%}.col-xs-7{width:58.33333333%}.col-xs-8{width:66.66666667%}.col-xs-9{width:75%}.col-xs-10{width:83.33333333%}.col-xs-11{width:91.66666667%}.col-xs-12{width:100%}.col-xs-pull-0{right:auto}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-3{right:25%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-6{right:50%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-9{right:75%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-12{right:100%}.col-xs-push-0{left:auto}.col-xs-push-1{left:8.33333333%}.col-xs-push-2{left:16.66666667%}.col-xs-push-3{left:25%}.col-xs-push-4{left:33.33333333%}.col-xs-push-5{left:41.66666667%}.col-xs-push-6{left:50%}.col-xs-push-7{left:58.33333333%}.col-xs-push-8{left:66.66666667%}.col-xs-push-9{left:75%}.col-xs-push-10{left:83.33333333%}.col-xs-push-11{left:91.66666667%}.col-xs-push-12{left:100%}.col-xs-offset-0{margin-left:0%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-12{margin-left:100%}@media (min-width: 768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-1{width:8.33333333%}.col-sm-2{width:16.66666667%}.col-sm-3{width:25%}.col-sm-4{width:33.33333333%}.col-sm-5{width:41.66666667%}.col-sm-6{width:50%}.col-sm-7{width:58.33333333%}.col-sm-8{width:66.66666667%}.col-sm-9{width:75%}.col-sm-10{width:83.33333333%}.col-sm-11{width:91.66666667%}.col-sm-12{width:100%}.col-sm-pull-0{right:auto}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-3{right:25%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-6{right:50%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-9{right:75%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-12{right:100%}.col-sm-push-0{left:auto}.col-sm-push-1{left:8.33333333%}.col-sm-push-2{left:16.66666667%}.col-sm-push-3{left:25%}.col-sm-push-4{left:33.33333333%}.col-sm-push-5{left:41.66666667%}.col-sm-push-6{left:50%}.col-sm-push-7{left:58.33333333%}.col-sm-push-8{left:66.66666667%}.col-sm-push-9{left:75%}.col-sm-push-10{left:83.33333333%}.col-sm-push-11{left:91.66666667%}.col-sm-push-12{left:100%}.col-sm-offset-0{margin-left:0%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-12{margin-left:100%}}@media (min-width: 992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-1{width:8.33333333%}.col-md-2{width:16.66666667%}.col-md-3{width:25%}.col-md-4{width:33.33333333%}.col-md-5{width:41.66666667%}.col-md-6{width:50%}.col-md-7{width:58.33333333%}.col-md-8{width:66.66666667%}.col-md-9{width:75%}.col-md-10{width:83.33333333%}.col-md-11{width:91.66666667%}.col-md-12{width:100%}.col-md-pull-0{right:auto}.col-md-pull-1{right:8.33333333%}.col-md-pull-2{right:16.66666667%}.col-md-pull-3{right:25%}.col-md-pull-4{right:33.33333333%}.col-md-pull-5{right:41.66666667%}.col-md-pull-6{right:50%}.col-md-pull-7{right:58.33333333%}.col-md-pull-8{right:66.66666667%}.col-md-pull-9{right:75%}.col-md-pull-10{right:83.33333333%}.col-md-pull-11{right:91.66666667%}.col-md-pull-12{right:100%}.col-md-push-0{left:auto}.col-md-push-1{left:8.33333333%}.col-md-push-2{left:16.66666667%}.col-md-push-3{left:25%}.col-md-push-4{left:33.33333333%}.col-md-push-5{left:41.66666667%}.col-md-push-6{left:50%}.col-md-push-7{left:58.33333333%}.col-md-push-8{left:66.66666667%}.col-md-push-9{left:75%}.col-md-push-10{left:83.33333333%}.col-md-push-11{left:91.66666667%}.col-md-push-12{left:100%}.col-md-offset-0{margin-left:0%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-3{margin-left:25%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-6{margin-left:50%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-9{margin-left:75%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-12{margin-left:100%}}@media (min-width: 1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-1{width:8.33333333%}.col-lg-2{width:16.66666667%}.col-lg-3{width:25%}.col-lg-4{width:33.33333333%}.col-lg-5{width:41.66666667%}.col-lg-6{width:50%}.col-lg-7{width:58.33333333%}.col-lg-8{width:66.66666667%}.col-lg-9{width:75%}.col-lg-10{width:83.33333333%}.col-lg-11{width:91.66666667%}.col-lg-12{width:100%}.col-lg-pull-0{right:auto}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-3{right:25%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-6{right:50%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-9{right:75%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-12{right:100%}.col-lg-push-0{left:auto}.col-lg-push-1{left:8.33333333%}.col-lg-push-2{left:16.66666667%}.col-lg-push-3{left:25%}.col-lg-push-4{left:33.33333333%}.col-lg-push-5{left:41.66666667%}.col-lg-push-6{left:50%}.col-lg-push-7{left:58.33333333%}.col-lg-push-8{left:66.66666667%}.col-lg-push-9{left:75%}.col-lg-push-10{left:83.33333333%}.col-lg-push-11{left:91.66666667%}.col-lg-push-12{left:100%}.col-lg-offset-0{margin-left:0%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-12{margin-left:100%}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>thead>tr>td,.table>tbody>tr>th,.table>tbody>tr>td,.table>tfoot>tr>th,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>th,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*="col-"]{position:static;float:none;display:table-column}table td[class*="col-"],table th[class*="col-"]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>thead>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>thead>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>thead>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>thead>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>thead>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{overflow-x:auto;min-height:0.01%}@media screen and (max-width: 767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;-o-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control::-moz-placeholder{color:#777;opacity:1}.form-control:-ms-input-placeholder{color:#777}.form-control::-webkit-input-placeholder{color:#777}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type="search"]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio: 0){input[type="date"],input[type="time"],input[type="datetime-local"],input[type="month"]{line-height:34px}input[type="date"].input-sm,.input-group-sm>input[type="date"].form-control,.input-group-sm>input[type="date"].input-group-addon,.input-group-sm>.input-group-btn>input[type="date"].btn,.input-group-sm input[type="date"],input[type="time"].input-sm,.input-group-sm>input[type="time"].form-control,.input-group-sm>input[type="time"].input-group-addon,.input-group-sm>.input-group-btn>input[type="time"].btn,.input-group-sm input[type="time"],input[type="datetime-local"].input-sm,.input-group-sm>input[type="datetime-local"].form-control,.input-group-sm>input[type="datetime-local"].input-group-addon,.input-group-sm>.input-group-btn>input[type="datetime-local"].btn,.input-group-sm input[type="datetime-local"],input[type="month"].input-sm,.input-group-sm>input[type="month"].form-control,.input-group-sm>input[type="month"].input-group-addon,.input-group-sm>.input-group-btn>input[type="month"].btn,.input-group-sm input[type="month"]{line-height:30px}input[type="date"].input-lg,.input-group-lg>input[type="date"].form-control,.input-group-lg>input[type="date"].input-group-addon,.input-group-lg>.input-group-btn>input[type="date"].btn,.input-group-lg input[type="date"],input[type="time"].input-lg,.input-group-lg>input[type="time"].form-control,.input-group-lg>input[type="time"].input-group-addon,.input-group-lg>.input-group-btn>input[type="time"].btn,.input-group-lg input[type="time"],input[type="datetime-local"].input-lg,.input-group-lg>input[type="datetime-local"].form-control,.input-group-lg>input[type="datetime-local"].input-group-addon,.input-group-lg>.input-group-btn>input[type="datetime-local"].btn,.input-group-lg input[type="datetime-local"],input[type="month"].input-lg,.input-group-lg>input[type="month"].form-control,.input-group-lg>input[type="month"].input-group-addon,.input-group-lg>.input-group-btn>input[type="month"].btn,.input-group-lg input[type="month"]{line-height:46px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-left:-20px;margin-top:4px \9}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:normal;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="radio"].disabled,fieldset[disabled] input[type="radio"],input[type="checkbox"][disabled],input[type="checkbox"].disabled,fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}.radio-inline.disabled,fieldset[disabled] .radio-inline,.checkbox-inline.disabled,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio.disabled label,fieldset[disabled] .radio label,.checkbox.disabled label,fieldset[disabled] .checkbox label{cursor:not-allowed}.form-control-static{padding-top:7px;padding-bottom:7px;margin-bottom:0;min-height:34px}.form-control-static.input-lg,.input-group-lg>.form-control-static.form-control,.input-group-lg>.form-control-static.input-group-addon,.input-group-lg>.input-group-btn>.form-control-static.btn,.form-control-static.input-sm,.input-group-sm>.form-control-static.form-control,.input-group-sm>.form-control-static.input-group-addon,.input-group-sm>.input-group-btn>.form-control-static.btn{padding-left:0;padding-right:0}.input-sm,.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm,.input-group-sm>select.form-control,.input-group-sm>select.input-group-addon,.input-group-sm>.input-group-btn>select.btn{height:30px;line-height:30px}textarea.input-sm,.input-group-sm>textarea.form-control,.input-group-sm>textarea.input-group-addon,.input-group-sm>.input-group-btn>textarea.btn,select[multiple].input-sm,.input-group-sm>select[multiple].form-control,.input-group-sm>select[multiple].input-group-addon,.input-group-sm>.input-group-btn>select[multiple].btn{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;min-height:32px}.input-lg,.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg,.input-group-lg>select.form-control,.input-group-lg>select.input-group-addon,.input-group-lg>.input-group-btn>select.btn{height:46px;line-height:46px}textarea.input-lg,.input-group-lg>textarea.form-control,.input-group-lg>textarea.input-group-addon,.input-group-lg>.input-group-btn>textarea.btn,select[multiple].input-lg,.input-group-lg>select[multiple].form-control,.input-group-lg>select[multiple].input-group-addon,.input-group-lg>.input-group-btn>select[multiple].btn{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;min-height:38px}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg>.form-control+.form-control-feedback,.input-group-lg>.input-group-addon+.form-control-feedback,.input-group-lg>.input-group-btn>.btn+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback,.input-group-sm>.form-control+.form-control-feedback,.input-group-sm>.input-group-addon+.form-control-feedback,.input-group-sm>.input-group-btn>.btn+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.has-error .form-control-feedback{color:#a94442}.has-feedback label ~ .form-control-feedback{top:25px}.has-feedback label.sr-only ~ .form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width: 768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}.form-horizontal .form-group:before,.form-horizontal .form-group:after{content:" ";display:table}.form-horizontal .form-group:after{clear:both}@media (min-width: 768px){.form-horizontal .control-label{text-align:right;margin-bottom:0;padding-top:7px}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width: 768px){.form-horizontal .form-group-lg .control-label{padding-top:14.3px}}@media (min-width: 768px){.form-horizontal .form-group-sm .control-label{padding-top:6px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn.focus,.btn:active:focus,.btn:active.focus,.btn.active:focus,.btn.active.focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;pointer-events:none;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default.focus,.btn-default:active,.btn-default.active,.open>.btn-default.dropdown-toggle{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.btn-default.dropdown-toggle{background-image:none}.btn-default.disabled,.btn-default.disabled:hover,.btn-default.disabled:focus,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled.active,.btn-default[disabled],.btn-default[disabled]:hover,.btn-default[disabled]:focus,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled].active,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default:hover,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:hover,.btn-primary:focus,.btn-primary.focus,.btn-primary:active,.btn-primary.active,.open>.btn-primary.dropdown-toggle{color:#fff;background-color:#3071a9;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open>.btn-primary.dropdown-toggle{background-image:none}.btn-primary.disabled,.btn-primary.disabled:hover,.btn-primary.disabled:focus,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled.active,.btn-primary[disabled],.btn-primary[disabled]:hover,.btn-primary[disabled]:focus,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary:hover,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success.focus,.btn-success:active,.btn-success.active,.open>.btn-success.dropdown-toggle{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.btn-success.dropdown-toggle{background-image:none}.btn-success.disabled,.btn-success.disabled:hover,.btn-success.disabled:focus,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled.active,.btn-success[disabled],.btn-success[disabled]:hover,.btn-success[disabled]:focus,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled].active,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success:hover,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info.focus,.btn-info:active,.btn-info.active,.open>.btn-info.dropdown-toggle{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.btn-info.dropdown-toggle{background-image:none}.btn-info.disabled,.btn-info.disabled:hover,.btn-info.disabled:focus,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled.active,.btn-info[disabled],.btn-info[disabled]:hover,.btn-info[disabled]:focus,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled].active,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info:hover,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning.focus,.btn-warning:active,.btn-warning.active,.open>.btn-warning.dropdown-toggle{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open>.btn-warning.dropdown-toggle{background-image:none}.btn-warning.disabled,.btn-warning.disabled:hover,.btn-warning.disabled:focus,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled.active,.btn-warning[disabled],.btn-warning[disabled]:hover,.btn-warning[disabled]:focus,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning:hover,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger.focus,.btn-danger:active,.btn-danger.active,.open>.btn-danger.dropdown-toggle{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.btn-danger.dropdown-toggle{background-image:none}.btn-danger.disabled,.btn-danger.disabled:hover,.btn-danger.disabled:focus,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled.active,.btn-danger[disabled],.btn-danger[disabled]:hover,.btn-danger[disabled]:focus,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger:hover,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{color:#428bca;font-weight:normal;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:hover,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-property:height,visibility;transition-property:height,visibility;-webkit-transition-duration:0.35s;transition-duration:0.35s;-webkit-transition-timing-function:ease;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:14px;text-align:left;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#262626;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;outline:0;background-color:#428bca}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#777}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width: 768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn:hover,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar:before,.btn-toolbar:after{content:" ";display:table}.btn-toolbar:after{clear:both}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle,.btn-group-lg.btn-group>.btn+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret,.btn-group-lg>.btn .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret,.dropup .btn-group-lg>.btn .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after{content:" ";display:table}.btn-group-vertical>.btn-group:after{clear:both}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-bottom-left-radius:4px;border-top-right-radius:0;border-top-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:normal;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.input-group-addon.btn{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.input-group-addon.btn{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav:before,.nav:after{content:" ";display:table}.nav:after{clear:both}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#777;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified,.nav-tabs.nav-justified{width:100%}.nav-justified>li,.nav-tabs.nav-justified>li{float:none}.nav-justified>li>a,.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width: 768px){.nav-justified>li,.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a,.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified,.nav-tabs.nav-justified{border-bottom:0}.nav-tabs-justified>li>a,.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs.nav-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width: 768px){.nav-tabs-justified>li>a,.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs.nav-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}.navbar:before,.navbar:after{content:" ";display:table}.navbar:after{clear:both}@media (min-width: 768px){.navbar{border-radius:4px}}.navbar-header:before,.navbar-header:after{content:" ";display:table}.navbar-header:after{clear:both}@media (min-width: 768px){.navbar-header{float:left}}.navbar-collapse{overflow-x:visible;padding-right:15px;padding-left:15px;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse:before,.navbar-collapse:after{content:" ";display:table}.navbar-collapse:after{clear:both}.navbar-collapse.in{overflow-y:auto}@media (min-width: 768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-left:0;padding-right:0}}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width: 480px) and (orientation: landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}.container>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-header,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width: 768px){.container>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-header,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width: 768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media (min-width: 768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:15px 15px;font-size:18px;line-height:20px;height:50px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width: 768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;margin-right:15px;padding:9px 10px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width: 768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width: 767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width: 768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{margin-left:-15px;margin-right:-15px;padding:10px 15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:8px;margin-bottom:8px}@media (min-width: 768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width: 767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width: 768px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-right-radius:4px;border-top-left-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm,.btn-group-sm>.navbar-btn.btn{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs,.btn-group-xs>.navbar-btn.btn{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width: 768px){.navbar-text{float:left;margin-left:15px;margin-right:15px}}@media (min-width: 768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right ~ .navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#e7e7e7;color:#555}@media (max-width: 767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#333}.navbar-default .btn-link[disabled]:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:hover,fieldset[disabled] .navbar-default .btn-link:focus{color:#ccc}.navbar-inverse{background-color:#222;border-color:#090909}.navbar-inverse .navbar-brand{color:#aaa}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#777}.navbar-inverse .navbar-nav>li>a{color:#aaa}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#090909}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#090909;color:#fff}@media (max-width: 767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#090909}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#090909}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#aaa}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#090909}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#aaa}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#aaa}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#fff}.navbar-inverse .btn-link[disabled]:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:hover,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{content:"/ ";padding:0 5px;color:#ccc}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.42857143;text-decoration:none;color:#428bca;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}.pagination>li>a:hover,.pagination>li>a:focus,.pagination>li>span:hover,.pagination>li>span:focus{color:#2a6496;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:hover,.pagination>.active>a:focus,.pagination>.active>span,.pagination>.active>span:hover,.pagination>.active>span:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#777;background-color:#fff;border-color:#ddd;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:20px 0;list-style:none;text-align:center}.pager:before,.pager:after{content:" ";display:table}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#777;background-color:#fff;cursor:not-allowed}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.label:empty{display:none}.btn .label{position:relative;top:-1px}a.label:hover,a.label:focus{color:#fff;text-decoration:none;cursor:pointer}.label-default{background-color:#777}.label-default[href]:hover,.label-default[href]:focus{background-color:#5e5e5e}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;color:#fff;line-height:1;vertical-align:baseline;white-space:nowrap;text-align:center;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.jumbotron{padding:30px 15px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width: 768px){.jumbotron{padding:48px 0}.container .jumbotron,.container-fluid .jumbotron{padding-left:60px;padding-right:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border 0.2s ease-in-out;-o-transition:border 0.2s ease-in-out;transition:border 0.2s ease-in-out}.thumbnail>img,.thumbnail a>img{display:block;max-width:100%;height:auto;margin-left:auto;margin-right:auto}.thumbnail .caption{padding:9px;color:#333}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#428bca}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{overflow:hidden;height:20px;margin-bottom:20px;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{zoom:1;overflow:hidden}.media-body{width:10000px}.media-object{display:block}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{margin-bottom:20px;padding-left:0}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{text-decoration:none;color:#555;background-color:#f5f5f5}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{background-color:#eee;color:#777;cursor:not-allowed}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#e1edf7}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-body:before,.panel-body:after{content:" ";display:table}.panel-body:after{clear:both}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:3px;border-top-left-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-left:15px;padding-right:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:3px;border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-left-radius:3px;border-bottom-right-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#428bca}.panel-primary>.panel-heading .badge{color:#428bca;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;left:0;bottom:0;height:100%;width:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:0.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:0.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:hidden;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);-ms-transform:translate(0, -25%);-o-transform:translate(0, -25%);transform:translate(0, -25%);-webkit-transition:-webkit-transform 0.3s ease-out;-moz-transition:-moz-transform 0.3s ease-out;-o-transition:-o-transform 0.3s ease-out;transition:transform 0.3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);background-clip:padding-box;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:0.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5;min-height:16.42857143px}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer:before,.modal-footer:after{content:" ";display:table}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width: 768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width: 992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-weight:normal;line-height:1.4;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:0.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{bottom:0;right:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-weight:normal;line-height:1.42857143;text-align:left;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);white-space:normal}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{margin:0;padding:8px 14px;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{border-width:10px;content:""}.popover.top>.arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);bottom:-11px}.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#fff}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0,0,0,0.25)}.popover.right>.arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#fff}.popover.bottom>.arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#fff;bottom:-10px}.carousel{position:relative}.carousel-inner{position:relative;overflow:hidden;width:100%}.carousel-inner>.item{display:none;position:relative;-webkit-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto;line-height:1}@media all and (transform-3d), (-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform 0.6s ease-in-out;-moz-transition:-moz-transform 0.6s ease-in-out;-o-transition:-o-transform 0.6s ease-in-out;transition:transform 0.6s ease-in-out;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000;-moz-perspective:1000;perspective:1000}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;left:0;bottom:0;width:15%;opacity:0.5;filter:alpha(opacity=50);font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-control.left{background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.0001) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.0001) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}.carousel-control.right{left:auto;right:0;background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.0001) 0%, rgba(0,0,0,0.5) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.0001) 0%, rgba(0,0,0,0.5) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0%, rgba(0,0,0,0.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}.carousel-control:hover,.carousel-control:focus{outline:0;color:#fff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;line-height:1;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;margin-left:-30%;padding-left:0;list-style:none;text-align:center}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;border:1px solid #fff;border-radius:10px;cursor:pointer;background-color:#000 \9;background-color:transparent}.carousel-indicators .active{margin:0;width:12px;height:12px;background-color:#fff}.carousel-caption{position:absolute;left:15%;right:15%;bottom:20px;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width: 768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{left:20%;right:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after{content:" ";display:table}.clearfix:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs{display:none !important}.visible-sm{display:none !important}.visible-md{display:none !important}.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width: 767px){.visible-xs{display:block !important}table.visible-xs{display:table}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width: 767px){.visible-xs-block{display:block !important}}@media (max-width: 767px){.visible-xs-inline{display:inline !important}}@media (max-width: 767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width: 768px) and (max-width: 991px){.visible-sm{display:block !important}table.visible-sm{display:table}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width: 768px) and (max-width: 991px){.visible-sm-block{display:block !important}}@media (min-width: 768px) and (max-width: 991px){.visible-sm-inline{display:inline !important}}@media (min-width: 768px) and (max-width: 991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width: 992px) and (max-width: 1199px){.visible-md{display:block !important}table.visible-md{display:table}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width: 992px) and (max-width: 1199px){.visible-md-block{display:block !important}}@media (min-width: 992px) and (max-width: 1199px){.visible-md-inline{display:inline !important}}@media (min-width: 992px) and (max-width: 1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width: 1200px){.visible-lg{display:block !important}table.visible-lg{display:table}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width: 1200px){.visible-lg-block{display:block !important}}@media (min-width: 1200px){.visible-lg-inline{display:inline !important}}@media (min-width: 1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width: 767px){.hidden-xs{display:none !important}}@media (min-width: 768px) and (max-width: 991px){.hidden-sm{display:none !important}}@media (min-width: 992px) and (max-width: 1199px){.hidden-md{display:none !important}}@media (min-width: 1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{content:".";display:block;height:0;clear:both;visibility:hidden}.ui-helper-clearfix{display:block;min-height:0}* html .ui-helper-clearfix{height:1%}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0}.ui-front{z-index:100}.ui-state-disabled{cursor:default !important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:absolute;top:0;left:0;width:100%;height:100%}.ui-accordion{width:100%}.ui-accordion .ui-accordion-li-fix{display:inline}.ui-accordion .ui-accordion-header-active{border-bottom:0 !important}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin-top:2px;padding:.5em .5em .5em .7em;min-height:0}.ui-accordion .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-noicons{padding-left:.7em}.ui-accordion .ui-accordion-icons .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-accordion-header-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;margin-top:-2px;position:relative;top:1px;margin-bottom:2px;overflow:auto;display:none}.ui-accordion .ui-accordion-content-active{display:block}/*! +/*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:"Glyphicons Halflings";src:url(/static/fonts/glyphicons-halflings-regular.eot?1577029955);src:url(/static/fonts/glyphicons-halflings-regular.eot?&1577029955#iefix) format("embedded-opentype"),url(/static/fonts/glyphicons-halflings-regular.woff2?1577029955) format("woff2"),url(/static/fonts/glyphicons-halflings-regular.woff?1577029955) format("woff"),url(/static/fonts/glyphicons-halflings-regular.ttf?1577029955) format("truetype"),url(/static/fonts/glyphicons-halflings-regular.svg?1577029955#glyphicons_halflingsregular) format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:transparent}body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all 0.2s ease-in-out;-o-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h1 .small,h2 small,h2 .small,h3 small,h3 .small,h4 small,h4 .small,h5 small,h5 .small,h6 small,h6 .small,.h1 small,.h1 .small,.h2 small,.h2 .small,.h3 small,.h3 .small,.h4 small,.h4 .small,.h5 small,.h5 .small,.h6 small,.h6 .small{font-weight:400;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,h1 .small,.h1 small,.h1 .small,h2 small,h2 .small,.h2 small,.h2 .small,h3 small,h3 .small,.h3 small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,h4 .small,.h4 small,.h4 .small,h5 small,h5 .small,.h5 small,.h5 .small,h6 small,h6 .small,.h6 small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width: 768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase,.initialism{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#428bca}a.text-primary:hover,a.text-primary:focus{color:#3071a9}.text-success{color:#3c763d}a.text-success:hover,a.text-success:focus{color:#2b542c}.text-info{color:#31708f}a.text-info:hover,a.text-info:focus{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover,a.text-warning:focus{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover,a.text-danger:focus{color:#843534}.bg-primary{color:#fff}.bg-primary{background-color:#428bca}a.bg-primary:hover,a.bg-primary:focus{background-color:#3071a9}.bg-success{background-color:#dff0d8}a.bg-success:hover,a.bg-success:focus{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover,a.bg-info:focus{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover,a.bg-warning:focus{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover,a.bg-danger:focus{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ul ol,ol ul,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}.dl-horizontal dd:before,.dl-horizontal dd:after{display:table;content:" "}.dl-horizontal dd:after{clear:both}@media (min-width: 768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse footer:before,.blockquote-reverse small:before,.blockquote-reverse .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,.blockquote-reverse small:after,.blockquote-reverse .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.container:before,.container:after{display:table;content:" "}.container:after{clear:both}@media (min-width: 768px){.container{width:750px}}@media (min-width: 992px){.container{width:970px}}@media (min-width: 1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.container-fluid:before,.container-fluid:after{display:table;content:" "}.container-fluid:after{clear:both}.row{margin-right:-15px;margin-left:-15px}.row:before,.row:after{display:table;content:" "}.row:after{clear:both}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-1{width:8.33333333%}.col-xs-2{width:16.66666667%}.col-xs-3{width:25%}.col-xs-4{width:33.33333333%}.col-xs-5{width:41.66666667%}.col-xs-6{width:50%}.col-xs-7{width:58.33333333%}.col-xs-8{width:66.66666667%}.col-xs-9{width:75%}.col-xs-10{width:83.33333333%}.col-xs-11{width:91.66666667%}.col-xs-12{width:100%}.col-xs-pull-0{right:auto}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-3{right:25%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-6{right:50%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-9{right:75%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-12{right:100%}.col-xs-push-0{left:auto}.col-xs-push-1{left:8.33333333%}.col-xs-push-2{left:16.66666667%}.col-xs-push-3{left:25%}.col-xs-push-4{left:33.33333333%}.col-xs-push-5{left:41.66666667%}.col-xs-push-6{left:50%}.col-xs-push-7{left:58.33333333%}.col-xs-push-8{left:66.66666667%}.col-xs-push-9{left:75%}.col-xs-push-10{left:83.33333333%}.col-xs-push-11{left:91.66666667%}.col-xs-push-12{left:100%}.col-xs-offset-0{margin-left:0%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-12{margin-left:100%}@media (min-width: 768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-1{width:8.33333333%}.col-sm-2{width:16.66666667%}.col-sm-3{width:25%}.col-sm-4{width:33.33333333%}.col-sm-5{width:41.66666667%}.col-sm-6{width:50%}.col-sm-7{width:58.33333333%}.col-sm-8{width:66.66666667%}.col-sm-9{width:75%}.col-sm-10{width:83.33333333%}.col-sm-11{width:91.66666667%}.col-sm-12{width:100%}.col-sm-pull-0{right:auto}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-3{right:25%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-6{right:50%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-9{right:75%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-12{right:100%}.col-sm-push-0{left:auto}.col-sm-push-1{left:8.33333333%}.col-sm-push-2{left:16.66666667%}.col-sm-push-3{left:25%}.col-sm-push-4{left:33.33333333%}.col-sm-push-5{left:41.66666667%}.col-sm-push-6{left:50%}.col-sm-push-7{left:58.33333333%}.col-sm-push-8{left:66.66666667%}.col-sm-push-9{left:75%}.col-sm-push-10{left:83.33333333%}.col-sm-push-11{left:91.66666667%}.col-sm-push-12{left:100%}.col-sm-offset-0{margin-left:0%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-12{margin-left:100%}}@media (min-width: 992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-1{width:8.33333333%}.col-md-2{width:16.66666667%}.col-md-3{width:25%}.col-md-4{width:33.33333333%}.col-md-5{width:41.66666667%}.col-md-6{width:50%}.col-md-7{width:58.33333333%}.col-md-8{width:66.66666667%}.col-md-9{width:75%}.col-md-10{width:83.33333333%}.col-md-11{width:91.66666667%}.col-md-12{width:100%}.col-md-pull-0{right:auto}.col-md-pull-1{right:8.33333333%}.col-md-pull-2{right:16.66666667%}.col-md-pull-3{right:25%}.col-md-pull-4{right:33.33333333%}.col-md-pull-5{right:41.66666667%}.col-md-pull-6{right:50%}.col-md-pull-7{right:58.33333333%}.col-md-pull-8{right:66.66666667%}.col-md-pull-9{right:75%}.col-md-pull-10{right:83.33333333%}.col-md-pull-11{right:91.66666667%}.col-md-pull-12{right:100%}.col-md-push-0{left:auto}.col-md-push-1{left:8.33333333%}.col-md-push-2{left:16.66666667%}.col-md-push-3{left:25%}.col-md-push-4{left:33.33333333%}.col-md-push-5{left:41.66666667%}.col-md-push-6{left:50%}.col-md-push-7{left:58.33333333%}.col-md-push-8{left:66.66666667%}.col-md-push-9{left:75%}.col-md-push-10{left:83.33333333%}.col-md-push-11{left:91.66666667%}.col-md-push-12{left:100%}.col-md-offset-0{margin-left:0%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-3{margin-left:25%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-6{margin-left:50%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-9{margin-left:75%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-12{margin-left:100%}}@media (min-width: 1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-1{width:8.33333333%}.col-lg-2{width:16.66666667%}.col-lg-3{width:25%}.col-lg-4{width:33.33333333%}.col-lg-5{width:41.66666667%}.col-lg-6{width:50%}.col-lg-7{width:58.33333333%}.col-lg-8{width:66.66666667%}.col-lg-9{width:75%}.col-lg-10{width:83.33333333%}.col-lg-11{width:91.66666667%}.col-lg-12{width:100%}.col-lg-pull-0{right:auto}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-3{right:25%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-6{right:50%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-9{right:75%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-12{right:100%}.col-lg-push-0{left:auto}.col-lg-push-1{left:8.33333333%}.col-lg-push-2{left:16.66666667%}.col-lg-push-3{left:25%}.col-lg-push-4{left:33.33333333%}.col-lg-push-5{left:41.66666667%}.col-lg-push-6{left:50%}.col-lg-push-7{left:58.33333333%}.col-lg-push-8{left:66.66666667%}.col-lg-push-9{left:75%}.col-lg-push-10{left:83.33333333%}.col-lg-push-11{left:91.66666667%}.col-lg-push-12{left:100%}.col-lg-offset-0{margin-left:0%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-12{margin-left:100%}}table{background-color:transparent}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>thead>tr>td,.table>tbody>tr>th,.table>tbody>tr>td,.table>tfoot>tr>th,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>th,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table>thead>tr>td.active,.table>thead>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>thead>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>thead>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>thead>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>thead>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width: 767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="radio"].disabled,fieldset[disabled] input[type="radio"],input[type="checkbox"][disabled],input[type="checkbox"].disabled,fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;-o-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control::-moz-placeholder{color:#777;opacity:1}.form-control:-ms-input-placeholder{color:#777}.form-control::-webkit-input-placeholder{color:#777}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio: 0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:34px}input[type="date"].input-sm,.input-group-sm>input[type="date"].form-control,.input-group-sm>input[type="date"].input-group-addon,.input-group-sm>.input-group-btn>input[type="date"].btn,.input-group-sm input[type="date"],input[type="time"].input-sm,.input-group-sm>input[type="time"].form-control,.input-group-sm>input[type="time"].input-group-addon,.input-group-sm>.input-group-btn>input[type="time"].btn,.input-group-sm input[type="time"],input[type="datetime-local"].input-sm,.input-group-sm>input[type="datetime-local"].form-control,.input-group-sm>input[type="datetime-local"].input-group-addon,.input-group-sm>.input-group-btn>input[type="datetime-local"].btn,.input-group-sm input[type="datetime-local"],input[type="month"].input-sm,.input-group-sm>input[type="month"].form-control,.input-group-sm>input[type="month"].input-group-addon,.input-group-sm>.input-group-btn>input[type="month"].btn,.input-group-sm input[type="month"]{line-height:30px}input[type="date"].input-lg,.input-group-lg>input[type="date"].form-control,.input-group-lg>input[type="date"].input-group-addon,.input-group-lg>.input-group-btn>input[type="date"].btn,.input-group-lg input[type="date"],input[type="time"].input-lg,.input-group-lg>input[type="time"].form-control,.input-group-lg>input[type="time"].input-group-addon,.input-group-lg>.input-group-btn>input[type="time"].btn,.input-group-lg input[type="time"],input[type="datetime-local"].input-lg,.input-group-lg>input[type="datetime-local"].form-control,.input-group-lg>input[type="datetime-local"].input-group-addon,.input-group-lg>.input-group-btn>input[type="datetime-local"].btn,.input-group-lg input[type="datetime-local"],input[type="month"].input-lg,.input-group-lg>input[type="month"].form-control,.input-group-lg>input[type="month"].input-group-addon,.input-group-lg>.input-group-btn>input[type="month"].btn,.input-group-lg input[type="month"]{line-height:46px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,fieldset[disabled] .radio label,.checkbox.disabled label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,fieldset[disabled] .radio-inline,.checkbox-inline.disabled,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.input-group-lg>.form-control-static.form-control,.input-group-lg>.form-control-static.input-group-addon,.input-group-lg>.input-group-btn>.form-control-static.btn,.form-control-static.input-sm,.input-group-sm>.form-control-static.form-control,.input-group-sm>.form-control-static.input-group-addon,.input-group-sm>.input-group-btn>.form-control-static.btn{padding-right:0;padding-left:0}.input-sm,.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm,.input-group-sm>select.form-control,.input-group-sm>select.input-group-addon,.input-group-sm>.input-group-btn>select.btn{height:30px;line-height:30px}textarea.input-sm,.input-group-sm>textarea.form-control,.input-group-sm>textarea.input-group-addon,.input-group-sm>.input-group-btn>textarea.btn,select[multiple].input-sm,.input-group-sm>select[multiple].form-control,.input-group-sm>select[multiple].input-group-addon,.input-group-sm>.input-group-btn>select[multiple].btn{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg,.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg,.input-group-lg>select.form-control,.input-group-lg>select.input-group-addon,.input-group-lg>.input-group-btn>select.btn{height:46px;line-height:46px}textarea.input-lg,.input-group-lg>textarea.form-control,.input-group-lg>textarea.input-group-addon,.input-group-lg>.input-group-btn>textarea.btn,select[multiple].input-lg,.input-group-lg>select[multiple].form-control,.input-group-lg>select[multiple].input-group-addon,.input-group-lg>.input-group-btn>select[multiple].btn{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.33}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg>.form-control+.form-control-feedback,.input-group-lg>.input-group-addon+.form-control-feedback,.input-group-lg>.input-group-btn>.btn+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback,.input-group-sm>.form-control+.form-control-feedback,.input-group-sm>.input-group-addon+.form-control-feedback,.input-group-sm>.input-group-btn>.btn+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label ~ .form-control-feedback{top:25px}.has-feedback label.sr-only ~ .form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width: 768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}.form-horizontal .form-group:before,.form-horizontal .form-group:after{display:table;content:" "}.form-horizontal .form-group:after{clear:both}@media (min-width: 768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width: 768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width: 768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn.focus,.btn:active:focus,.btn:active.focus,.btn.active:focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#333;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:.65;-webkit-box-shadow:none;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:focus,.btn-default.focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.btn-default.dropdown-toggle{color:#333;background-color:#e6e6e6;background-image:none;border-color:#adadad}.btn-default:active:hover,.btn-default:active:focus,.btn-default:active.focus,.btn-default.active:hover,.btn-default.active:focus,.btn-default.active.focus,.open>.btn-default.dropdown-toggle:hover,.open>.btn-default.dropdown-toggle:focus,.open>.btn-default.dropdown-toggle.focus{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.disabled:hover,.btn-default.disabled:focus,.btn-default.disabled.focus,.btn-default[disabled]:hover,.btn-default[disabled]:focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default:hover,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default.focus{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:focus,.btn-primary.focus{color:#fff;background-color:#3071a9;border-color:#193c5a}.btn-primary:hover{color:#fff;background-color:#3071a9;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open>.btn-primary.dropdown-toggle{color:#fff;background-color:#3071a9;background-image:none;border-color:#285e8e}.btn-primary:active:hover,.btn-primary:active:focus,.btn-primary:active.focus,.btn-primary.active:hover,.btn-primary.active:focus,.btn-primary.active.focus,.open>.btn-primary.dropdown-toggle:hover,.open>.btn-primary.dropdown-toggle:focus,.open>.btn-primary.dropdown-toggle.focus{color:#fff;background-color:#285e8e;border-color:#193c5a}.btn-primary.disabled:hover,.btn-primary.disabled:focus,.btn-primary.disabled.focus,.btn-primary[disabled]:hover,.btn-primary[disabled]:focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary:hover,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary.focus{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:focus,.btn-success.focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.btn-success.dropdown-toggle{color:#fff;background-color:#449d44;background-image:none;border-color:#398439}.btn-success:active:hover,.btn-success:active:focus,.btn-success:active.focus,.btn-success.active:hover,.btn-success.active:focus,.btn-success.active.focus,.open>.btn-success.dropdown-toggle:hover,.open>.btn-success.dropdown-toggle:focus,.open>.btn-success.dropdown-toggle.focus{color:#fff;background-color:#398439;border-color:#255625}.btn-success.disabled:hover,.btn-success.disabled:focus,.btn-success.disabled.focus,.btn-success[disabled]:hover,.btn-success[disabled]:focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success:hover,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success.focus{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:focus,.btn-info.focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.btn-info.dropdown-toggle{color:#fff;background-color:#31b0d5;background-image:none;border-color:#269abc}.btn-info:active:hover,.btn-info:active:focus,.btn-info:active.focus,.btn-info.active:hover,.btn-info.active:focus,.btn-info.active.focus,.open>.btn-info.dropdown-toggle:hover,.open>.btn-info.dropdown-toggle:focus,.open>.btn-info.dropdown-toggle.focus{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.disabled:hover,.btn-info.disabled:focus,.btn-info.disabled.focus,.btn-info[disabled]:hover,.btn-info[disabled]:focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info:hover,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info.focus{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:focus,.btn-warning.focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open>.btn-warning.dropdown-toggle{color:#fff;background-color:#ec971f;background-image:none;border-color:#d58512}.btn-warning:active:hover,.btn-warning:active:focus,.btn-warning:active.focus,.btn-warning.active:hover,.btn-warning.active:focus,.btn-warning.active.focus,.open>.btn-warning.dropdown-toggle:hover,.open>.btn-warning.dropdown-toggle:focus,.open>.btn-warning.dropdown-toggle.focus{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.disabled:hover,.btn-warning.disabled:focus,.btn-warning.disabled.focus,.btn-warning[disabled]:hover,.btn-warning[disabled]:focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning:hover,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning.focus{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:focus,.btn-danger.focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.btn-danger.dropdown-toggle{color:#fff;background-color:#c9302c;background-image:none;border-color:#ac2925}.btn-danger:active:hover,.btn-danger:active:focus,.btn-danger:active.focus,.btn-danger.active:hover,.btn-danger.active:focus,.btn-danger.active.focus,.open>.btn-danger.dropdown-toggle:hover,.open>.btn-danger.dropdown-toggle:focus,.open>.btn-danger.dropdown-toggle.focus{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.disabled:hover,.btn-danger.disabled:focus,.btn-danger.disabled.focus,.btn-danger[disabled]:hover,.btn-danger[disabled]:focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger:hover,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger.focus{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#428bca;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:hover,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-property:height,visibility;transition-property:height,visibility;-webkit-transition-duration:0.35s;transition-duration:0.35s;-webkit-transition-timing-function:ease;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#428bca;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#777}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width: 768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn:hover,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar:before,.btn-toolbar:after{display:table;content:" "}.btn-toolbar:after{clear:both}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle,.btn-group-lg.btn-group>.btn+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret,.btn-group-lg>.btn .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret,.dropup .btn-group-lg>.btn .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after{display:table;content:" "}.btn-group-vertical>.btn-group:after{clear:both}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.input-group-addon.btn{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.input-group-addon.btn{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav:before,.nav:after{display:table;content:" "}.nav:after{clear:both}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified,.nav-tabs.nav-justified{width:100%}.nav-justified>li,.nav-tabs.nav-justified>li{float:none}.nav-justified>li>a,.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width: 768px){.nav-justified>li,.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a,.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified,.nav-tabs.nav-justified{border-bottom:0}.nav-tabs-justified>li>a,.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs.nav-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width: 768px){.nav-tabs-justified>li>a,.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs.nav-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}.navbar:before,.navbar:after{display:table;content:" "}.navbar:after{clear:both}@media (min-width: 768px){.navbar{border-radius:4px}}.navbar-header:before,.navbar-header:after{display:table;content:" "}.navbar-header:after{clear:both}@media (min-width: 768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse:before,.navbar-collapse:after{display:table;content:" "}.navbar-collapse:after{clear:both}.navbar-collapse.in{overflow-y:auto}@media (min-width: 768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width: 480px) and (orientation: landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width: 768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-header,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width: 768px){.container>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-header,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width: 768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width: 768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width: 768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width: 767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width: 768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:8px;margin-bottom:8px}@media (min-width: 768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width: 767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width: 768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm,.btn-group-sm>.navbar-btn.btn{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs,.btn-group-xs>.navbar-btn.btn{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width: 768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width: 768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right ~ .navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#555;background-color:#e7e7e7}@media (max-width: 767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#333}.navbar-default .btn-link[disabled]:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:hover,fieldset[disabled] .navbar-default .btn-link:focus{color:#ccc}.navbar-inverse{background-color:#222;border-color:#090909}.navbar-inverse .navbar-brand{color:#aaa}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#777}.navbar-inverse .navbar-nav>li>a{color:#aaa}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#090909}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#fff;background-color:#090909}@media (max-width: 767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#090909}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#090909}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#aaa}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#090909}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-link{color:#aaa}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#aaa}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#fff}.navbar-inverse .btn-link[disabled]:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:hover,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/ "}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#428bca;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li>a:hover,.pagination>li>a:focus,.pagination>li>span:hover,.pagination>li>span:focus{z-index:2;color:#2a6496;background-color:#eee;border-color:#ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>a:hover,.pagination>.active>a:focus,.pagination>.active>span,.pagination>.active>span:hover,.pagination>.active>span:focus{z-index:3;color:#fff;cursor:default;background-color:#428bca;border-color:#428bca}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.33}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager:before,.pager:after{display:table;content:" "}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.label:empty{display:none}.btn .label{position:relative;top:-1px}a.label:hover,a.label:focus{color:#fff;text-decoration:none;cursor:pointer}.label-default{background-color:#777}.label-default[href]:hover,.label-default[href]:focus{background-color:#5e5e5e}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width: 768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border 0.2s ease-in-out;-o-transition:border 0.2s ease-in-out;transition:border 0.2s ease-in-out}.thumbnail>img,.thumbnail a>img{display:block;max-width:100%;height:auto;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#333}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#428bca}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#e1edf7}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus,button.list-group-item:hover,button.list-group-item:focus{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:hover,button.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active,button.list-group-item-success.active:hover,button.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:hover,button.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active,button.list-group-item-info.active:hover,button.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:hover,button.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active,button.list-group-item-warning.active:hover,button.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:hover,button.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active,button.list-group-item-danger.active:hover,button.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-body:before,.panel-body:after{display:table;content:" "}.panel-body:after{clear:both}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#428bca}.panel-primary>.panel-heading .badge{color:#428bca;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);-ms-transform:translate(0, -25%);-o-transform:translate(0, -25%);transform:translate(0, -25%);-webkit-transition:-webkit-transform 0.3s ease-out;-moz-transition:-moz-transform 0.3s ease-out;-o-transition:-o-transform 0.3s ease-out;transition:transform 0.3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header:before,.modal-header:after{display:table;content:" "}.modal-header:after{clear:both}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer:before,.modal-footer:after{display:table;content:" "}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width: 768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width: 992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:14px;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto;line-height:1}@media all and (transform-3d), (-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform 0.6s ease-in-out;-moz-transition:-moz-transform 0.6s ease-in-out;-o-transition:-o-transform 0.6s ease-in-out;transition:transform 0.6s ease-in-out;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;-moz-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:transparent;filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.0001) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.0001) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.0001) 0%, rgba(0,0,0,0.5) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.0001) 0%, rgba(0,0,0,0.5) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0%, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:transparent;border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width: 768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after{display:table;content:" "}.clearfix:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs{display:none !important}.visible-sm{display:none !important}.visible-md{display:none !important}.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width: 767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width: 767px){.visible-xs-block{display:block !important}}@media (max-width: 767px){.visible-xs-inline{display:inline !important}}@media (max-width: 767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width: 768px) and (max-width: 991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width: 768px) and (max-width: 991px){.visible-sm-block{display:block !important}}@media (min-width: 768px) and (max-width: 991px){.visible-sm-inline{display:inline !important}}@media (min-width: 768px) and (max-width: 991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width: 992px) and (max-width: 1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width: 992px) and (max-width: 1199px){.visible-md-block{display:block !important}}@media (min-width: 992px) and (max-width: 1199px){.visible-md-inline{display:inline !important}}@media (min-width: 992px) and (max-width: 1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width: 1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width: 1200px){.visible-lg-block{display:block !important}}@media (min-width: 1200px){.visible-lg-inline{display:inline !important}}@media (min-width: 1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width: 767px){.hidden-xs{display:none !important}}@media (min-width: 768px) and (max-width: 991px){.hidden-sm{display:none !important}}@media (min-width: 992px) and (max-width: 1199px){.hidden-md{display:none !important}}@media (min-width: 1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{content:".";display:block;height:0;clear:both;visibility:hidden}.ui-helper-clearfix{display:block;min-height:0}* html .ui-helper-clearfix{height:1%}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0}.ui-front{z-index:100}.ui-state-disabled{cursor:default !important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:absolute;top:0;left:0;width:100%;height:100%}.ui-accordion{width:100%}.ui-accordion .ui-accordion-li-fix{display:inline}.ui-accordion .ui-accordion-header-active{border-bottom:0 !important}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin-top:2px;padding:.5em .5em .5em .7em;min-height:0}.ui-accordion .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-noicons{padding-left:.7em}.ui-accordion .ui-accordion-icons .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-accordion-header-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;margin-top:-2px;position:relative;top:1px;margin-bottom:2px;overflow:auto;display:none}.ui-accordion .ui-accordion-content-active{display:block}/*! * jQuery UI Bootstrap v1.0 Alpha * * jQuery UI Menu 1.10.3 @@ -16,4 +20,4 @@ * Portions copyright Addy Osmani, jQuery UI & Twitter Bootstrap * Created the LESS version by $dharapvj * Released under MIT - */.ui-tooltip{display:block;font-size:11px;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);opacity:0.8;position:absolute;visibility:visible;z-index:1070;max-width:200px;background:#000;border:1px solid #000;color:#fff;padding:3px 8px;text-align:center;text-decoration:none;-moz-box-shadow:inset 0 1px 0 #000;-webkit-box-shadow:inset 0 1px 0 #000;box-shadow:inset 0 1px 0 #000;-moz-border-radius:4px;-webkit-border-radius:4px;border-radius:4px;border-width:1px}.ui-tooltip .arrow{overflow:hidden;position:absolute;margin-left:0;height:20px;width:20px}.ui-tooltip .arrow.bottom{top:100%;left:38%}.ui-tooltip .arrow.bottom:after{border-top:8px solid #000;border-right:8px solid transparent;border-bottom:8px solid transparent;border-left:8px solid transparent}.ui-tooltip .arrow.top{top:-50%;bottom:22px;left:42%}.ui-tooltip .arrow.top:after{border-top:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #000;border-left:6px solid transparent}.ui-tooltip .arrow.left{top:25%;left:-15%;right:0;bottom:-16px}.ui-tooltip .arrow.left:after{width:0;border-top:6px solid transparent;border-right:6px solid #000;border-bottom:6px solid transparent;border-left:6px solid transparent}.ui-tooltip .arrow.right{top:26%;left:100%;right:0;bottom:-16px;margin-left:1px}.ui-tooltip .arrow.right:after{width:0;border-top:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid transparent;border-left:6px solid #000}.ui-tooltip .arrow:after{content:" ";position:absolute;height:0;left:0;top:0;width:0;margin-left:0;bottom:12px;box-shadow:6px 5px 9px -9px #000}body,.pad-top{padding-top:50px}#content{padding:40px 15px}#userdropdown>li{padding:0 0.3em}#userdropdown>li .media-object,#activity .media-object{max-width:3em}.table tbody>tr>td.vert-align{vertical-align:middle}.align-right{text-align:right}textarea{width:100%;resize:vertical}.btn-page,.btn-pad{margin:0 0 0.5em}.custom-combobox{display:block}.event-mic-photo{max-width:2em}.item-description{margin-left:1em}.overflow-ellipsis{text-overflow:ellipsis;display:inline-block;max-width:100%;overflow:hidden}.modal-dialog{z-index:inherit}.panel-default .default{background-color:#f5f5f5}del{background-color:#f2dede;border-radius:3px}ins{background-color:#dff0d8;border-radius:3px}.loading-animation{position:relative;margin:30px auto 0}.loading-animation .circle{background-color:transparent;border:5px solid rgba(0,183,229,0.9);opacity:.9;border-right:5px solid transparent;border-left:5px solid transparent;border-radius:50px;box-shadow:0 0 35px #2187e7;width:50px;height:50px;margin:0 auto;-moz-animation:spinPulse 1s infinite ease-in-out;-webkit-animation:spinPulse 1s infinite ease-in-out;animation:spinPulse 1s infinite ease-in-out}.loading-animation .circle1{background-color:transparent;border:5px solid rgba(0,183,229,0.9);opacity:.9;border-left:5px solid transparent;border-right:5px solid transparent;border-radius:50px;box-shadow:0 0 15px #2187e7;width:30px;height:30px;margin:0 auto;position:relative;top:-40px;-moz-animation:spinoffPulse 1s infinite linear;-webkit-animation:spinoffPulse 1s infinite linear;animation:spinoffPulse 1s infinite linear}@-moz-keyframes spinPulse{0%{-moz-transform:rotate(160deg);transform:rotate(160deg);opacity:0;box-shadow:0 0 1px #2187e7}50%{-moz-transform:rotate(145deg);transform:rotate(145deg);opacity:1}100%{-moz-transform:rotate(-320deg);transform:rotate(-320deg);opacity:0}}@-webkit-keyframes spinPulse{0%{-webkit-transform:rotate(160deg);transform:rotate(160deg);opacity:0;box-shadow:0 0 1px #2187e7}50%{-webkit-transform:rotate(145deg);transform:rotate(145deg);opacity:1}100%{-webkit-transform:rotate(-320deg);transform:rotate(-320deg);opacity:0}}@keyframes spinPulse{0%{-moz-transform:rotate(160deg);-ms-transform:rotate(160deg);-webkit-transform:rotate(160deg);transform:rotate(160deg);opacity:0;box-shadow:0 0 1px #2187e7}50%{-moz-transform:rotate(145deg);-ms-transform:rotate(145deg);-webkit-transform:rotate(145deg);transform:rotate(145deg);opacity:1}100%{-moz-transform:rotate(-320deg);-ms-transform:rotate(-320deg);-webkit-transform:rotate(-320deg);transform:rotate(-320deg);opacity:0}}@-moz-keyframes spinoffPulse{0%{-moz-transform:rotate(0deg);transform:rotate(0deg)}100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes spinoffPulse{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinoffPulse{0%{-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);-webkit-transform:rotate(360deg);transform:rotate(360deg)}} + */.ui-tooltip{display:block;font-size:11px;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);opacity:.8;position:absolute;visibility:visible;z-index:1070;max-width:200px;background:#000;border:1px solid #000;color:#fff;padding:3px 8px;text-align:center;text-decoration:none;-moz-box-shadow:inset 0 1px 0 #000;-webkit-box-shadow:inset 0 1px 0 #000;box-shadow:inset 0 1px 0 #000;-moz-border-radius:4px;-webkit-border-radius:4px;border-radius:4px;border-width:1px}.ui-tooltip .arrow{overflow:hidden;position:absolute;margin-left:0;height:20px;width:20px}.ui-tooltip .arrow.bottom{top:100%;left:38%}.ui-tooltip .arrow.bottom:after{border-top:8px solid #000;border-right:8px solid transparent;border-bottom:8px solid transparent;border-left:8px solid transparent}.ui-tooltip .arrow.top{top:-50%;bottom:22px;left:42%}.ui-tooltip .arrow.top:after{border-top:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #000;border-left:6px solid transparent}.ui-tooltip .arrow.left{top:25%;left:-15%;right:0;bottom:-16px}.ui-tooltip .arrow.left:after{width:0;border-top:6px solid transparent;border-right:6px solid #000;border-bottom:6px solid transparent;border-left:6px solid transparent}.ui-tooltip .arrow.right{top:26%;left:100%;right:0;bottom:-16px;margin-left:1px}.ui-tooltip .arrow.right:after{width:0;border-top:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid transparent;border-left:6px solid #000}.ui-tooltip .arrow:after{content:" ";position:absolute;height:0;left:0;top:0;width:0;margin-left:0;bottom:12px;box-shadow:6px 5px 9px -9px #000}body,.pad-top{padding-top:50px}#content{padding:40px 15px}#userdropdown>li{padding:0 0.3em}#userdropdown>li .media-object,#activity .media-object{max-width:3em}.table tbody>tr>td.vert-align{vertical-align:middle}.align-right{text-align:right}textarea{width:100%;resize:vertical}.btn-page,.btn-pad{margin:0 0 0.5em}.custom-combobox{display:block}.event-mic-photo{max-width:2em}.item-description{margin-left:1em}.overflow-ellipsis{text-overflow:ellipsis;display:inline-block;max-width:100%;overflow:hidden}.dont-break-out{overflow-wrap:break-word;word-wrap:break-word;-webkit-hyphens:auto;-ms-hyphens:auto;-moz-hyphens:auto;hyphens:auto}.modal-dialog{z-index:inherit}.panel-default .default{background-color:#f5f5f5}del{background-color:#f2dede;border-radius:3px}ins{background-color:#dff0d8;border-radius:3px}.loading-animation{position:relative;margin:30px auto 0}.loading-animation .circle{background-color:transparent;border:5px solid rgba(0,183,229,0.9);opacity:.9;border-right:5px solid transparent;border-left:5px solid transparent;border-radius:50px;box-shadow:0 0 35px #2187e7;width:50px;height:50px;margin:0 auto;-moz-animation:spinPulse 1s infinite ease-in-out;-webkit-animation:spinPulse 1s infinite ease-in-out;animation:spinPulse 1s infinite ease-in-out}.loading-animation .circle1{background-color:transparent;border:5px solid rgba(0,183,229,0.9);opacity:.9;border-left:5px solid transparent;border-right:5px solid transparent;border-radius:50px;box-shadow:0 0 15px #2187e7;width:30px;height:30px;margin:0 auto;position:relative;top:-40px;-moz-animation:spinoffPulse 1s infinite linear;-webkit-animation:spinoffPulse 1s infinite linear;animation:spinoffPulse 1s infinite linear}@-moz-keyframes spinPulse{0%{-moz-transform:rotate(160deg);transform:rotate(160deg);opacity:0;box-shadow:0 0 1px #2187e7}50%{-moz-transform:rotate(145deg);transform:rotate(145deg);opacity:1}100%{-moz-transform:rotate(-320deg);transform:rotate(-320deg);opacity:0}}@-webkit-keyframes spinPulse{0%{-webkit-transform:rotate(160deg);transform:rotate(160deg);opacity:0;box-shadow:0 0 1px #2187e7}50%{-webkit-transform:rotate(145deg);transform:rotate(145deg);opacity:1}100%{-webkit-transform:rotate(-320deg);transform:rotate(-320deg);opacity:0}}@keyframes spinPulse{0%{-moz-transform:rotate(160deg);-ms-transform:rotate(160deg);-webkit-transform:rotate(160deg);transform:rotate(160deg);opacity:0;box-shadow:0 0 1px #2187e7}50%{-moz-transform:rotate(145deg);-ms-transform:rotate(145deg);-webkit-transform:rotate(145deg);transform:rotate(145deg);opacity:1}100%{-moz-transform:rotate(-320deg);-ms-transform:rotate(-320deg);-webkit-transform:rotate(-320deg);transform:rotate(-320deg);opacity:0}}@-moz-keyframes spinoffPulse{0%{-moz-transform:rotate(0deg);transform:rotate(0deg)}100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes spinoffPulse{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinoffPulse{0%{-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);-webkit-transform:rotate(360deg);transform:rotate(360deg)}}html.embedded{display:flex;flex-direction:column;width:100%;height:100%;max-height:100%;overflow:hidden;justify-content:center}html.embedded body{padding:0;width:100%;background:none;overflow:auto}html.embedded .embed_container{border:5px solid #e9e9e9;padding:12px 0px;min-height:100%;width:100%}html.embedded .source{background:url("/static/imgs/pyrigs-avatar.png") no-repeat;background-size:16px 16px;padding-left:20px;color:#000}html.embedded h3{margin-top:10px;margin-bottom:5px}html.embedded p{margin-bottom:2px;font-size:11px}html.embedded .event-mic-photo{max-width:3em} diff --git a/RIGS/static/imgs/400.jpg b/RIGS/static/imgs/400.jpg index e4639f3e..bfcc2a24 100644 Binary files a/RIGS/static/imgs/400.jpg and b/RIGS/static/imgs/400.jpg differ diff --git a/RIGS/static/imgs/401.jpg b/RIGS/static/imgs/401.jpg index 6a947ff8..251f3e9b 100644 Binary files a/RIGS/static/imgs/401.jpg and b/RIGS/static/imgs/401.jpg differ diff --git a/RIGS/static/imgs/403.jpg b/RIGS/static/imgs/403.jpg index c349e33d..3d71d0b1 100644 Binary files a/RIGS/static/imgs/403.jpg and b/RIGS/static/imgs/403.jpg differ diff --git a/RIGS/static/imgs/404.jpg b/RIGS/static/imgs/404.jpg index bbbededa..9a3cbf9a 100644 Binary files a/RIGS/static/imgs/404.jpg and b/RIGS/static/imgs/404.jpg differ diff --git a/RIGS/static/imgs/500.jpg b/RIGS/static/imgs/500.jpg index 9e9f482b..13a2eef8 100644 Binary files a/RIGS/static/imgs/500.jpg and b/RIGS/static/imgs/500.jpg differ diff --git a/RIGS/static/imgs/paperwork/corner-tr-su.jpg b/RIGS/static/imgs/paperwork/corner-tr-su.jpg index 90aa1d5d..550ab40b 100644 Binary files a/RIGS/static/imgs/paperwork/corner-tr-su.jpg and b/RIGS/static/imgs/paperwork/corner-tr-su.jpg differ diff --git a/RIGS/static/imgs/paperwork/tec-logo.jpg b/RIGS/static/imgs/paperwork/tec-logo.jpg index c43ac20b..6ddf0539 100644 Binary files a/RIGS/static/imgs/paperwork/tec-logo.jpg and b/RIGS/static/imgs/paperwork/tec-logo.jpg differ diff --git a/RIGS/static/imgs/pyrigs-avatar.png b/RIGS/static/imgs/pyrigs-avatar.png index 38263bab..baeb6e1a 100644 Binary files a/RIGS/static/imgs/pyrigs-avatar.png and b/RIGS/static/imgs/pyrigs-avatar.png differ diff --git a/RIGS/static/js/affix.js b/RIGS/static/js/affix.js old mode 100644 new mode 100755 index aaebc8c0..7f651681 --- a/RIGS/static/js/affix.js +++ b/RIGS/static/js/affix.js @@ -1,8 +1,8 @@ /* ======================================================================== - * Bootstrap: affix.js v3.3.2 + * Bootstrap: affix.js v3.3.7 * http://getbootstrap.com/javascript/#affix * ======================================================================== - * Copyright 2011-2015 Twitter, Inc. + * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ @@ -21,14 +21,14 @@ .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this)) this.$element = $(element) - this.affixed = - this.unpin = + this.affixed = null + this.unpin = null this.pinnedOffset = null this.checkPosition() } - Affix.VERSION = '3.3.2' + Affix.VERSION = '3.3.7' Affix.RESET = 'affix affix-top affix-bottom' @@ -78,7 +78,7 @@ var offset = this.options.offset var offsetTop = offset.top var offsetBottom = offset.bottom - var scrollHeight = $('body').height() + var scrollHeight = Math.max($(document).height(), $(document.body).height()) if (typeof offset != 'object') offsetBottom = offsetTop = offset if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element) diff --git a/RIGS/static/js/ajax-bootstrap-select.js b/RIGS/static/js/ajax-bootstrap-select.js old mode 100755 new mode 100644 index da91c98b..e9bb833b --- a/RIGS/static/js/ajax-bootstrap-select.js +++ b/RIGS/static/js/ajax-bootstrap-select.js @@ -3,16 +3,16 @@ * * Extends existing [Bootstrap Select] implementations by adding the ability to search via AJAX requests as you type. Originally for CROSCON. * - * @version 1.3.1 + * @version 1.4.1 * @author Adam Heim - https://github.com/truckingsim * @link https://github.com/truckingsim/Ajax-Bootstrap-Select - * @copyright 2015 Adam Heim + * @copyright 2017 Adam Heim * @license Released under the MIT license. * * Contributors: * Mark Carver - https://github.com/markcarver * - * Last build: 2015-01-06 8:43:11 PM EST + * Last build: 2017-07-21 1:08:54 PM GMT-0400 */ !(function ($, window) { @@ -186,10 +186,25 @@ var AjaxBootstrapSelect = function (element, options) { if (dataKeys.length) { // Object containing the data attribute options. var dataOptions = {}; + var flattenedOptions = ['locale']; for (i = 0, l = dataKeys.length; i < l; i++) { var name = dataKeys[i].replace(/^abs([A-Z])/, matchToLowerCase).replace(/([A-Z])/g, '-$1').toLowerCase(); + var keys = name.split('-'); + + // Certain options should be flattened to a single object + // and not fully expanded (such as Locale). + if (keys[0] && keys.length > 1 && flattenedOptions.indexOf(keys[0]) !== -1) { + var newKeys = [keys.shift()]; + var property = ''; + // Combine the remaining keys as a single property. + for (var ii = 0; ii < keys.length; ii++) { + property += (ii === 0 ? keys[ii] : keys[ii].charAt(0).toUpperCase() + keys[ii].slice(1)); + } + newKeys.push(property); + keys = newKeys; + } this.log(this.LOG_DEBUG, 'Processing data attribute "data-abs-' + name + '":', data[dataKeys[i]]); - expandObject(name.split('-'), data[dataKeys[i]], dataOptions); + expandObject(keys, data[dataKeys[i]], dataOptions); } this.options = $.extend(true, {}, this.options, dataOptions); this.log(this.LOG_DEBUG, 'Merged in the data attribute options: ', dataOptions, this.options); @@ -315,6 +330,12 @@ AjaxBootstrapSelect.prototype.init = function () { plugin.log(plugin.LOG_DEBUG, 'Key ignored.'); return; } + + // Don't process if below minimum query length + if (query.length < plugin.options.minLength) { + plugin.list.setStatus(plugin.t('statusTooShort')); + return; + } // Clear out any existing timer. clearTimeout(requestDelayTimer); @@ -573,8 +594,25 @@ var AjaxBootstrapSelectList = function (plugin) { this.title = null; this.selectedTextFormat = plugin.selectpicker.options.selectedTextFormat; + // Save initial options + var initial_options = []; + plugin.$element.find('option').each(function() { + var $option = $(this); + var value = $option.attr('value'); + initial_options.push({ + value: value, + text: $option.text(), + 'class': $option.attr('class') || '', + data: $option.data() || {}, + preserved: plugin.options.preserveSelected, + selected: !!$option.attr('selected') + }); + }); + this.cacheSet(/*query=*/'', initial_options); + // Preserve selected options. if (plugin.options.preserveSelected) { + that.selected = initial_options; plugin.$element.on('change.abs.preserveSelected', function (e) { var $selected = plugin.$element.find(':selected'); that.selected = []; @@ -644,7 +682,7 @@ AjaxBootstrapSelectList.prototype.build = function (data) { } // Set various properties. - $option.val(item.value).text(item.text); + $option.val(item.value).text(item.text).attr('title', item.text); if (item['class'].length) { $option.attr('class', item['class']); } @@ -732,7 +770,13 @@ AjaxBootstrapSelectList.prototype.refresh = function (triggerChange) { if (!this.plugin.$element.find('option').length && emptyTitle && emptyTitle.length) { this.setTitle(emptyTitle); } - else if (this.title) { + else if ( + this.title || + ( + this.selectedTextFormat !== 'static' && + this.selectedTextFormat !== this.plugin.selectpicker.options.selectedTextFormat + ) + ) { this.restoreTitle(); } this.plugin.selectpicker.refresh(); @@ -1224,6 +1268,14 @@ $.fn.ajaxSelectPicker.defaults = { } }, + /** + * @member $.fn.ajaxSelectPicker.defaults + * @cfg {Number} minLength = 0 + * @markdown + * Invoke a request for empty search values. + */ + minLength: 0, + /** * @member $.fn.ajaxSelectPicker.defaults * @cfg {String} ajaxSearchUrl @@ -1296,8 +1348,7 @@ $.fn.ajaxSelectPicker.defaults = { * 39: "right", * 38: "up", * 40: "down", - * 91: "meta", - * 229: "unknown" + * 91: "meta" * } * ``` */ @@ -1311,8 +1362,7 @@ $.fn.ajaxSelectPicker.defaults = { 39: "right", 38: "up", 40: "down", - 91: "meta", - 229: "unknown" + 91: "meta" }, /** @@ -1406,7 +1456,7 @@ $.fn.ajaxSelectPicker.defaults = { * } * ``` */ - preprocessData: function(){}, + preprocessData: function () { }, /** * @member $.fn.ajaxSelectPicker.defaults @@ -1434,7 +1484,7 @@ $.fn.ajaxSelectPicker.defaults = { * @markdown * Process the data returned after this plugin, but before the list is built. */ - processData: function(){}, + processData: function () { }, /** * @member $.fn.ajaxSelectPicker.defaults @@ -1547,7 +1597,15 @@ $.fn.ajaxSelectPicker.locale['en-US'] = { * @markdown * The text to use in the status container when a request is being initiated. */ - statusSearching: 'Searching...' + statusSearching: 'Searching...', + + /** + * @member $.fn.ajaxSelectPicker.locale + * @cfg {String} statusToShort = 'Please enter more characters' + * @markdown + * The text used in the status container when the request returns no results. + */ + statusTooShort: 'Please enter more characters' }; $.fn.ajaxSelectPicker.locale.en = $.fn.ajaxSelectPicker.locale['en-US']; diff --git a/RIGS/static/js/alert.js b/RIGS/static/js/alert.js old mode 100644 new mode 100755 index e8212caf..db97f3b0 --- a/RIGS/static/js/alert.js +++ b/RIGS/static/js/alert.js @@ -1,8 +1,8 @@ /* ======================================================================== - * Bootstrap: alert.js v3.3.2 + * Bootstrap: alert.js v3.3.7 * http://getbootstrap.com/javascript/#alerts * ======================================================================== - * Copyright 2011-2015 Twitter, Inc. + * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ @@ -18,7 +18,7 @@ $(el).on('click', dismiss, this.close) } - Alert.VERSION = '3.3.2' + Alert.VERSION = '3.3.7' Alert.TRANSITION_DURATION = 150 @@ -31,7 +31,7 @@ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 } - var $parent = $(selector) + var $parent = $(selector === '#' ? [] : selector) if (e) e.preventDefault() diff --git a/RIGS/static/js/bootstrap-select.js b/RIGS/static/js/bootstrap-select.js index caa9615d..ee2f74a4 100755 --- a/RIGS/static/js/bootstrap-select.js +++ b/RIGS/static/js/bootstrap-select.js @@ -1,27 +1,212 @@ (function ($) { 'use strict'; - // Case insensitive search - $.expr[':'].icontains = function (obj, index, meta) { - return icontains($(obj).text(), meta[3]); - }; - - // Case and accent insensitive search - $.expr[':'].aicontains = function (obj, index, meta) { - return icontains($(obj).data('normalizedText') || $(obj).text(), meta[3]); - }; - - /** - * Actual implementation of the case insensitive search. - * @access private - * @param {String} haystack - * @param {String} needle - * @returns {boolean} - */ - function icontains(haystack, needle) { - return haystack.toUpperCase().indexOf(needle.toUpperCase()) > -1; + // + if (!String.prototype.includes) { + (function () { + 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` + var toString = {}.toString; + var defineProperty = (function () { + // IE 8 only supports `Object.defineProperty` on DOM elements + try { + var object = {}; + var $defineProperty = Object.defineProperty; + var result = $defineProperty(object, object, object) && $defineProperty; + } catch (error) { + } + return result; + }()); + var indexOf = ''.indexOf; + var includes = function (search) { + if (this == null) { + throw new TypeError(); + } + var string = String(this); + if (search && toString.call(search) == '[object RegExp]') { + throw new TypeError(); + } + var stringLength = string.length; + var searchString = String(search); + var searchLength = searchString.length; + var position = arguments.length > 1 ? arguments[1] : undefined; + // `ToInteger` + var pos = position ? Number(position) : 0; + if (pos != pos) { // better `isNaN` + pos = 0; + } + var start = Math.min(Math.max(pos, 0), stringLength); + // Avoid the `indexOf` call if no match is possible + if (searchLength + start > stringLength) { + return false; + } + return indexOf.call(string, searchString, pos) != -1; + }; + if (defineProperty) { + defineProperty(String.prototype, 'includes', { + 'value': includes, + 'configurable': true, + 'writable': true + }); + } else { + String.prototype.includes = includes; + } + }()); } + if (!String.prototype.startsWith) { + (function () { + 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` + var defineProperty = (function () { + // IE 8 only supports `Object.defineProperty` on DOM elements + try { + var object = {}; + var $defineProperty = Object.defineProperty; + var result = $defineProperty(object, object, object) && $defineProperty; + } catch (error) { + } + return result; + }()); + var toString = {}.toString; + var startsWith = function (search) { + if (this == null) { + throw new TypeError(); + } + var string = String(this); + if (search && toString.call(search) == '[object RegExp]') { + throw new TypeError(); + } + var stringLength = string.length; + var searchString = String(search); + var searchLength = searchString.length; + var position = arguments.length > 1 ? arguments[1] : undefined; + // `ToInteger` + var pos = position ? Number(position) : 0; + if (pos != pos) { // better `isNaN` + pos = 0; + } + var start = Math.min(Math.max(pos, 0), stringLength); + // Avoid the `indexOf` call if no match is possible + if (searchLength + start > stringLength) { + return false; + } + var index = -1; + while (++index < searchLength) { + if (string.charCodeAt(start + index) != searchString.charCodeAt(index)) { + return false; + } + } + return true; + }; + if (defineProperty) { + defineProperty(String.prototype, 'startsWith', { + 'value': startsWith, + 'configurable': true, + 'writable': true + }); + } else { + String.prototype.startsWith = startsWith; + } + }()); + } + + if (!Object.keys) { + Object.keys = function ( + o, // object + k, // key + r // result array + ){ + // initialize object and result + r=[]; + // iterate over object keys + for (k in o) + // fill result array with non-prototypical keys + r.hasOwnProperty.call(o, k) && r.push(k); + // return result + return r; + }; + } + + // set data-selected on select element if the value has been programmatically selected + // prior to initialization of bootstrap-select + // * consider removing or replacing an alternative method * + var valHooks = { + useDefault: false, + _set: $.valHooks.select.set + }; + + $.valHooks.select.set = function(elem, value) { + if (value && !valHooks.useDefault) $(elem).data('selected', true); + + return valHooks._set.apply(this, arguments); + }; + + var changed_arguments = null; + + var EventIsSupported = (function() { + try { + new Event('change'); + return true; + } catch (e) { + return false; + } + })(); + + $.fn.triggerNative = function (eventName) { + var el = this[0], + event; + + if (el.dispatchEvent) { // for modern browsers & IE9+ + if (EventIsSupported) { + // For modern browsers + event = new Event(eventName, { + bubbles: true + }); + } else { + // For IE since it doesn't support Event constructor + event = document.createEvent('Event'); + event.initEvent(eventName, true, false); + } + + el.dispatchEvent(event); + } else if (el.fireEvent) { // for IE8 + event = document.createEventObject(); + event.eventType = eventName; + el.fireEvent('on' + eventName, event); + } else { + // fall back to jQuery.trigger + this.trigger(eventName); + } + }; + // + + // Case insensitive contains search + $.expr.pseudos.icontains = function (obj, index, meta) { + var $obj = $(obj).find('a'); + var haystack = ($obj.data('tokens') || $obj.text()).toString().toUpperCase(); + return haystack.includes(meta[3].toUpperCase()); + }; + + // Case insensitive begins search + $.expr.pseudos.ibegins = function (obj, index, meta) { + var $obj = $(obj).find('a'); + var haystack = ($obj.data('tokens') || $obj.text()).toString().toUpperCase(); + return haystack.startsWith(meta[3].toUpperCase()); + }; + + // Case and accent insensitive contains search + $.expr.pseudos.aicontains = function (obj, index, meta) { + var $obj = $(obj).find('a'); + var haystack = ($obj.data('tokens') || $obj.data('normalizedText') || $obj.text()).toString().toUpperCase(); + return haystack.includes(meta[3].toUpperCase()); + }; + + // Case and accent insensitive begins search + $.expr.pseudos.aibegins = function (obj, index, meta) { + var $obj = $(obj).find('a'); + var haystack = ($obj.data('tokens') || $obj.data('normalizedText') || $obj.text()).toString().toUpperCase(); + return haystack.startsWith(meta[3].toUpperCase()); + }; + /** * Remove all diatrics from the given text. * @access private @@ -45,34 +230,54 @@ {re: /[\xF1]/g, ch: "n"} ]; $.each(rExps, function () { - text = text.replace(this.re, this.ch); + text = text ? text.replace(this.re, this.ch) : ''; }); return text; } - function htmlEscape(html) { - var escapeMap = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''', - '`': '`' - }; - var source = '(?:' + Object.keys(escapeMap).join('|') + ')', - testRegexp = new RegExp(source), - replaceRegexp = new RegExp(source, 'g'), - string = html == null ? '' : '' + html; - return testRegexp.test(string) ? string.replace(replaceRegexp, function (match) { - return escapeMap[match]; - }) : string; - } + // List of HTML entities for escaping. + var escapeMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '`': '`' + }; + + var unescapeMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + ''': "'", + '`': '`' + }; - var Selectpicker = function (element, options, e) { - if (e) { - e.stopPropagation(); - e.preventDefault(); + // Functions for escaping and unescaping strings to/from HTML interpolation. + var createEscaper = function(map) { + var escaper = function(match) { + return map[match]; + }; + // Regexes for identifying a key that needs to be escaped. + var source = '(?:' + Object.keys(map).join('|') + ')'; + var testRegexp = RegExp(source); + var replaceRegexp = RegExp(source, 'g'); + return function(string) { + string = string == null ? '' : '' + string; + return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string; + }; + }; + + var htmlEscape = createEscaper(escapeMap); + var htmlUnescape = createEscaper(unescapeMap); + + var Selectpicker = function (element, options) { + // bootstrap-select has been initialized - revert valHooks.select.set back to its original function + if (!valHooks.useDefault) { + $.valHooks.select.set = valHooks._set; + valHooks.useDefault = true; } this.$element = $(element); @@ -88,6 +293,12 @@ this.options.title = this.$element.attr('title'); } + // Format window padding + var winPad = this.options.windowPadding; + if (typeof winPad === 'number') { + this.options.windowPadding = [winPad, winPad, winPad, winPad]; + } + //Expose public methods this.val = Selectpicker.prototype.val; this.render = Selectpicker.prototype.render; @@ -95,7 +306,7 @@ this.setStyle = Selectpicker.prototype.setStyle; this.selectAll = Selectpicker.prototype.selectAll; this.deselectAll = Selectpicker.prototype.deselectAll; - this.destroy = Selectpicker.prototype.remove; + this.destroy = Selectpicker.prototype.destroy; this.remove = Selectpicker.prototype.remove; this.show = Selectpicker.prototype.show; this.hide = Selectpicker.prototype.hide; @@ -103,26 +314,27 @@ this.init(); }; - Selectpicker.VERSION = '1.6.3'; + Selectpicker.VERSION = '1.12.4'; // part of this is duplicated in i18n/defaults-en_US.js. Make sure to update both. Selectpicker.DEFAULTS = { noneSelectedText: 'Nothing selected', - noneResultsText: 'No results match', + noneResultsText: 'No results matched {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected == 1) ? "{0} item selected" : "{0} items selected"; }, maxOptionsText: function (numAll, numGroup) { - var arr = []; - - arr[0] = (numAll == 1) ? 'Limit reached ({n} item max)' : 'Limit reached ({n} items max)'; - arr[1] = (numGroup == 1) ? 'Group limit reached ({n} item max)' : 'Group limit reached ({n} items max)'; - - return arr; + return [ + (numAll == 1) ? 'Limit reached ({n} item max)' : 'Limit reached ({n} items max)', + (numGroup == 1) ? 'Group limit reached ({n} item max)' : 'Group limit reached ({n} items max)' + ]; }, selectAllText: 'Select All', deselectAllText: 'Deselect All', + doneButton: false, + doneButtonText: 'Close', multipleSeparator: ', ', + styleBase: 'btn', style: 'btn-default', size: 'auto', title: null, @@ -136,14 +348,21 @@ dropupAuto: true, header: false, liveSearch: false, + liveSearchPlaceholder: null, + liveSearchNormalize: false, + liveSearchStyle: 'contains', actionsBox: false, iconBase: 'glyphicon', tickIcon: 'glyphicon-ok', + showTick: false, + template: { + caret: '' + }, maxOptions: false, mobile: false, selectOnTab: false, dropdownAlignRight: false, - searchAccentInsensitive: false + windowPadding: 0 }; Selectpicker.prototype = { @@ -154,17 +373,25 @@ var that = this, id = this.$element.attr('id'); - this.$element.hide(); + this.$element.addClass('bs-select-hidden'); + + // store originalIndex (key) and newIndex (value) in this.liObj for fast accessibility + // allows us to do this.$lis.eq(that.liObj[index]) instead of this.$lis.filter('[data-original-index="' + index + '"]') + this.liObj = {}; this.multiple = this.$element.prop('multiple'); this.autofocus = this.$element.prop('autofocus'); this.$newElement = this.createView(); - this.$element.after(this.$newElement); - this.$menu = this.$newElement.find('> .dropdown-menu'); - this.$button = this.$newElement.find('> button'); - this.$searchbox = this.$newElement.find('input'); + this.$element + .after(this.$newElement) + .appendTo(this.$newElement); + this.$button = this.$newElement.children('button'); + this.$menu = this.$newElement.children('.dropdown-menu'); + this.$menuInner = this.$menu.children('.inner'); + this.$searchbox = this.$menu.find('input'); - if (this.options.dropdownAlignRight) - this.$menu.addClass('dropdown-menu-right'); + this.$element.removeClass('bs-select-hidden'); + + if (this.options.dropdownAlignRight === true) this.$menu.addClass('dropdown-menu-right'); if (typeof id !== 'undefined') { this.$button.attr('data-id', id); @@ -178,47 +405,113 @@ this.clickListener(); if (this.options.liveSearch) this.liveSearchListener(); this.render(); - this.liHeight(); this.setStyle(); this.setWidth(); if (this.options.container) this.selectPosition(); this.$menu.data('this', this); this.$newElement.data('this', this); if (this.options.mobile) this.mobile(); + + this.$newElement.on({ + 'hide.bs.dropdown': function (e) { + that.$menuInner.attr('aria-expanded', false); + that.$element.trigger('hide.bs.select', e); + }, + 'hidden.bs.dropdown': function (e) { + that.$element.trigger('hidden.bs.select', e); + }, + 'show.bs.dropdown': function (e) { + that.$menuInner.attr('aria-expanded', true); + that.$element.trigger('show.bs.select', e); + }, + 'shown.bs.dropdown': function (e) { + that.$element.trigger('shown.bs.select', e); + } + }); + + if (that.$element[0].hasAttribute('required')) { + this.$element.on('invalid', function () { + that.$button.addClass('bs-invalid'); + + that.$element.on({ + 'focus.bs.select': function () { + that.$button.focus(); + that.$element.off('focus.bs.select'); + }, + 'shown.bs.select': function () { + that.$element + .val(that.$element.val()) // set the value to hide the validation message in Chrome when menu is opened + .off('shown.bs.select'); + }, + 'rendered.bs.select': function () { + // if select is no longer invalid, remove the bs-invalid class + if (this.validity.valid) that.$button.removeClass('bs-invalid'); + that.$element.off('rendered.bs.select'); + } + }); + + that.$button.on('blur.bs.select', function() { + that.$element.focus().blur(); + that.$button.off('blur.bs.select'); + }); + }); + } + + setTimeout(function () { + that.$element.trigger('loaded.bs.select'); + }); }, createDropdown: function () { // Options - // If we are multiple, then add the show-tick class by default - var multiple = this.multiple ? ' show-tick' : '', + // If we are multiple or showTick option is set, then add the show-tick class + var showTick = (this.multiple || this.options.showTick) ? ' show-tick' : '', inputGroup = this.$element.parent().hasClass('input-group') ? ' input-group-btn' : '', - autofocus = this.autofocus ? ' autofocus' : '', - btnSize = this.$element.parents().hasClass('form-group-lg') ? ' btn-lg' : (this.$element.parents().hasClass('form-group-sm') ? ' btn-sm' : ''); + autofocus = this.autofocus ? ' autofocus' : ''; // Elements var header = this.options.header ? '
' + this.options.header + '
' : ''; - var searchbox = this.options.liveSearch ? '' : ''; - var actionsbox = this.options.actionsBox ? '
' + - '
' + - '' + - '' + '
' + - '
' : ''; + '' + : ''; + var donebutton = this.multiple && this.options.doneButton ? + '
' + + '
' + + '' + + '
' + + '
' + : ''; var drop = - '
' + - '' + - ''; @@ -226,111 +519,198 @@ }, createView: function () { - var $drop = this.createDropdown(); - var $li = this.createLi(); - $drop.find('ul').append($li); + var $drop = this.createDropdown(), + li = this.createLi(); + + $drop.find('ul')[0].innerHTML = li; return $drop; }, reloadLi: function () { - //Remove all children. - this.destroyLi(); - //Re build - var $li = this.createLi(); - this.$menu.find('ul').append($li); - }, - - destroyLi: function () { - this.$menu.find('li').remove(); + // rebuild + var li = this.createLi(); + this.$menuInner[0].innerHTML = li; }, createLi: function () { var that = this, _li = [], - optID = 0; + optID = 0, + titleOption = document.createElement('option'), + liIndex = -1; // increment liIndex whenever a new
  • element is created to ensure liObj is correct // Helper functions /** * @param content * @param [index] * @param [classes] + * @param [optgroup] * @returns {string} */ - var generateLI = function (content, index, classes) { + var generateLI = function (content, index, classes, optgroup) { return '' + content + '
  • '; + ((typeof classes !== 'undefined' && '' !== classes) ? ' class="' + classes + '"' : '') + + ((typeof index !== 'undefined' && null !== index) ? ' data-original-index="' + index + '"' : '') + + ((typeof optgroup !== 'undefined' && null !== optgroup) ? 'data-optgroup="' + optgroup + '"' : '') + + '>' + content + ''; }; /** * @param text * @param [classes] * @param [inline] - * @param [optgroup] + * @param [tokens] * @returns {string} */ - var generateA = function (text, classes, inline, optgroup) { - var normText = normalizeToBase(htmlEscape(text)); + var generateA = function (text, classes, inline, tokens) { return '' + text + - '' + - ''; + (typeof classes !== 'undefined' ? ' class="' + classes + '"' : '') + + (inline ? ' style="' + inline + '"' : '') + + (that.options.liveSearchNormalize ? ' data-normalized-text="' + normalizeToBase(htmlEscape($(text).html())) + '"' : '') + + (typeof tokens !== 'undefined' || tokens !== null ? ' data-tokens="' + tokens + '"' : '') + + ' role="option">' + text + + '' + + ''; }; - this.$element.find('option').each(function () { + if (this.options.title && !this.multiple) { + // this option doesn't create a new
  • element, but does add a new option, so liIndex is decreased + // since liObj is recalculated on every refresh, liIndex needs to be decreased even if the titleOption is already appended + liIndex--; + + if (!this.$element.find('.bs-title-option').length) { + // Use native JS to prepend option (faster) + var element = this.$element[0]; + titleOption.className = 'bs-title-option'; + titleOption.innerHTML = this.options.title; + titleOption.value = ''; + element.insertBefore(titleOption, element.firstChild); + // Check if selected or data-selected attribute is already set on an option. If not, select the titleOption option. + // the selected item may have been changed by user or programmatically before the bootstrap select plugin runs, + // if so, the select will have the data-selected attribute + var $opt = $(element.options[element.selectedIndex]); + if ($opt.attr('selected') === undefined && this.$element.data('selected') === undefined) { + titleOption.selected = true; + } + } + } + + var $selectOptions = this.$element.find('option'); + + $selectOptions.each(function (index) { var $this = $(this); + liIndex++; + + if ($this.hasClass('bs-title-option')) return; + // Get the class and text for the option - var optionClass = $this.attr('class') || '', - inline = $this.attr('style'), + var optionClass = this.className || '', + inline = htmlEscape(this.style.cssText), text = $this.data('content') ? $this.data('content') : $this.html(), - subtext = typeof $this.data('subtext') !== 'undefined' ? '' + $this.data('subtext') + '' : '', + tokens = $this.data('tokens') ? $this.data('tokens') : null, + subtext = typeof $this.data('subtext') !== 'undefined' ? '' + $this.data('subtext') + '' : '', icon = typeof $this.data('icon') !== 'undefined' ? ' ' : '', - isDisabled = $this.is(':disabled') || $this.parent().is(':disabled'), - index = $this[0].index; + $parent = $this.parent(), + isOptgroup = $parent[0].tagName === 'OPTGROUP', + isOptgroupDisabled = isOptgroup && $parent[0].disabled, + isDisabled = this.disabled || isOptgroupDisabled, + prevHiddenIndex; + if (icon !== '' && isDisabled) { icon = '' + icon + ''; } + if (that.options.hideDisabled && (isDisabled && !isOptgroup || isOptgroupDisabled)) { + // set prevHiddenIndex - the index of the first hidden option in a group of hidden options + // used to determine whether or not a divider should be placed after an optgroup if there are + // hidden options between the optgroup and the first visible option + prevHiddenIndex = $this.data('prevHiddenIndex'); + $this.next().data('prevHiddenIndex', (prevHiddenIndex !== undefined ? prevHiddenIndex : index)); + + liIndex--; + return; + } + if (!$this.data('content')) { // Prepend any icon and append any subtext to the main text. text = icon + '' + text + subtext + ''; } - if (that.options.hideDisabled && isDisabled) { - return; - } + if (isOptgroup && $this.data('divider') !== true) { + if (that.options.hideDisabled && isDisabled) { + if ($parent.data('allOptionsDisabled') === undefined) { + var $options = $parent.children(); + $parent.data('allOptionsDisabled', $options.filter(':disabled').length === $options.length); + } + + if ($parent.data('allOptionsDisabled')) { + liIndex--; + return; + } + } + + var optGroupClass = ' ' + $parent[0].className || ''; - if ($this.parent().is('optgroup') && $this.data('divider') !== true) { if ($this.index() === 0) { // Is it the first option of the optgroup? optID += 1; // Get the opt group label - var label = $this.parent().attr('label'); - var labelSubtext = typeof $this.parent().data('subtext') !== 'undefined' ? '' + $this.parent().data('subtext') + '' : ''; - var labelIcon = $this.parent().data('icon') ? ' ' : ''; - label = labelIcon + '' + label + labelSubtext + ''; + var label = $parent[0].label, + labelSubtext = typeof $parent.data('subtext') !== 'undefined' ? '' + $parent.data('subtext') + '' : '', + labelIcon = $parent.data('icon') ? ' ' : ''; + + label = labelIcon + '' + htmlEscape(label) + labelSubtext + ''; if (index !== 0 && _li.length > 0) { // Is it NOT the first option of the select && are there elements in the dropdown? - _li.push(generateLI('', null, 'divider')); + liIndex++; + _li.push(generateLI('', null, 'divider', optID + 'div')); } - - _li.push(generateLI(label, null, 'dropdown-header')); + liIndex++; + _li.push(generateLI(label, null, 'dropdown-header' + optGroupClass, optID)); } - _li.push(generateLI(generateA(text, 'opt ' + optionClass, inline, optID), index)); + if (that.options.hideDisabled && isDisabled) { + liIndex--; + return; + } + + _li.push(generateLI(generateA(text, 'opt ' + optionClass + optGroupClass, inline, tokens), index, '', optID)); } else if ($this.data('divider') === true) { _li.push(generateLI('', index, 'divider')); } else if ($this.data('hidden') === true) { - _li.push(generateLI(generateA(text, optionClass, inline), index, 'hide is-hidden')); + // set prevHiddenIndex - the index of the first hidden option in a group of hidden options + // used to determine whether or not a divider should be placed after an optgroup if there are + // hidden options between the optgroup and the first visible option + prevHiddenIndex = $this.data('prevHiddenIndex'); + $this.next().data('prevHiddenIndex', (prevHiddenIndex !== undefined ? prevHiddenIndex : index)); + + _li.push(generateLI(generateA(text, optionClass, inline, tokens), index, 'hidden is-hidden')); } else { - _li.push(generateLI(generateA(text, optionClass, inline), index)); + var showDivider = this.previousElementSibling && this.previousElementSibling.tagName === 'OPTGROUP'; + + // if previous element is not an optgroup and hideDisabled is true + if (!showDivider && that.options.hideDisabled) { + prevHiddenIndex = $this.data('prevHiddenIndex'); + + if (prevHiddenIndex !== undefined) { + // select the element **before** the first hidden element in the group + var prevHidden = $selectOptions.eq(prevHiddenIndex)[0].previousElementSibling; + + if (prevHidden && prevHidden.tagName === 'OPTGROUP' && !prevHidden.disabled) { + showDivider = true; + } + } + } + + if (showDivider) { + liIndex++; + _li.push(generateLI('', null, 'divider', optID + 'div')); + } + _li.push(generateLI(generateA(text, optionClass, inline, tokens), index)); } + + that.liObj[index] = liIndex; }); //If we are not multiple, we don't have a selected item, and we don't have a title, select the first element so something is set in the button @@ -338,7 +718,7 @@ this.$element.find('option').eq(0).prop('selected', true).attr('selected', 'selected'); } - return $(_li.join('')); + return _li.join(''); }, findLis: function () { @@ -350,33 +730,44 @@ * @param [updateLi] defaults to true */ render: function (updateLi) { - var that = this; + var that = this, + notDisabled, + $selectOptions = this.$element.find('option'); //Update the LI to match the SELECT if (updateLi !== false) { - this.$element.find('option').each(function (index) { - that.setDisabled(index, $(this).is(':disabled') || $(this).parent().is(':disabled')); - that.setSelected(index, $(this).is(':selected')); + $selectOptions.each(function (index) { + var $lis = that.findLis().eq(that.liObj[index]); + + that.setDisabled(index, this.disabled || this.parentNode.tagName === 'OPTGROUP' && this.parentNode.disabled, $lis); + that.setSelected(index, this.selected, $lis); }); } + this.togglePlaceholder(); + this.tabIndex(); - var notDisabled = this.options.hideDisabled ? ':not([disabled])' : ''; - var selectedItems = this.$element.find('option:selected' + notDisabled).map(function () { - var $this = $(this); - var icon = $this.data('icon') && that.options.showIcon ? ' ' : ''; - var subtext; - if (that.options.showSubtext && $this.attr('data-subtext') && !that.multiple) { - subtext = ' ' + $this.data('subtext') + ''; - } else { - subtext = ''; - } - if ($this.data('content') && that.options.showContent) { - return $this.data('content'); - } else if (typeof $this.attr('title') !== 'undefined') { - return $this.attr('title'); - } else { - return icon + $this.html() + subtext; + + var selectedItems = $selectOptions.map(function () { + if (this.selected) { + if (that.options.hideDisabled && (this.disabled || this.parentNode.tagName === 'OPTGROUP' && this.parentNode.disabled)) return; + + var $this = $(this), + icon = $this.data('icon') && that.options.showIcon ? ' ' : '', + subtext; + + if (that.options.showSubtext && $this.data('subtext') && !that.multiple) { + subtext = ' ' + $this.data('subtext') + ''; + } else { + subtext = ''; + } + if (typeof $this.attr('title') !== 'undefined') { + return $this.attr('title'); + } else if ($this.data('content') && that.options.showContent) { + return $this.data('content').toString(); + } else { + return icon + $this.html() + subtext; + } } }).toArray(); @@ -389,13 +780,15 @@ var max = this.options.selectedTextFormat.split('>'); if ((max.length > 1 && selectedItems.length > max[1]) || (max.length == 1 && selectedItems.length >= 2)) { notDisabled = this.options.hideDisabled ? ', [disabled]' : ''; - var totalCount = this.$element.find('option').not('[data-divider="true"], [data-hidden="true"]' + notDisabled).length, + var totalCount = $selectOptions.not('[data-divider="true"], [data-hidden="true"]' + notDisabled).length, tr8nText = (typeof this.options.countSelectedText === 'function') ? this.options.countSelectedText(selectedItems.length, totalCount) : this.options.countSelectedText; title = tr8nText.replace('{0}', selectedItems.length.toString()).replace('{1}', totalCount.toString()); } } - this.options.title = this.$element.attr('title'); + if (this.options.title == undefined) { + this.options.title = this.$element.attr('title'); + } if (this.options.selectedTextFormat == 'static') { title = this.options.title; @@ -406,8 +799,11 @@ title = typeof this.options.title !== 'undefined' ? this.options.title : this.options.noneSelectedText; } - this.$button.attr('title', htmlEscape(title)); - this.$newElement.find('.filter-option').html(title); + //strip all HTML tags and trim the result, then unescape any escaped tags + this.$button.attr('title', htmlUnescape($.trim(title.replace(/<[^>]*>?/g, '')))); + this.$button.children('.filter-option').html(title); + + this.$element.trigger('rendered.bs.select'); }, /** @@ -416,7 +812,7 @@ */ setStyle: function (style, status) { if (this.$element.attr('class')) { - this.$newElement.addClass(this.$element.attr('class').replace(/selectpicker|mobile-device|validate\[.*\]/gi, '')); + this.$newElement.addClass(this.$element.attr('class').replace(/selectpicker|mobile-device|bs-select-hidden|validate\[.*\]/gi, '')); } var buttonClass = style ? style : this.options.style; @@ -431,117 +827,251 @@ } }, - liHeight: function () { - if (this.options.size === false) return; + liHeight: function (refresh) { + if (!refresh && (this.options.size === false || this.sizeInfo)) return; - var $selectClone = this.$menu.parent().clone().find('> .dropdown-toggle').prop('autofocus', false).end().appendTo('body'), - $menuClone = $selectClone.addClass('open').find('> .dropdown-menu'), - liHeight = $menuClone.find('li').not('.divider').not('.dropdown-header').filter(':visible').children('a').outerHeight(), - headerHeight = this.options.header ? $menuClone.find('.popover-title').outerHeight() : 0, - searchHeight = this.options.liveSearch ? $menuClone.find('.bs-searchbox').outerHeight() : 0, - actionsHeight = this.options.actionsBox ? $menuClone.find('.bs-actionsbox').outerHeight() : 0; + var newElement = document.createElement('div'), + menu = document.createElement('div'), + menuInner = document.createElement('ul'), + divider = document.createElement('li'), + li = document.createElement('li'), + a = document.createElement('a'), + text = document.createElement('span'), + header = this.options.header && this.$menu.find('.popover-title').length > 0 ? this.$menu.find('.popover-title')[0].cloneNode(true) : null, + search = this.options.liveSearch ? document.createElement('div') : null, + actions = this.options.actionsBox && this.multiple && this.$menu.find('.bs-actionsbox').length > 0 ? this.$menu.find('.bs-actionsbox')[0].cloneNode(true) : null, + doneButton = this.options.doneButton && this.multiple && this.$menu.find('.bs-donebutton').length > 0 ? this.$menu.find('.bs-donebutton')[0].cloneNode(true) : null; - $selectClone.remove(); + text.className = 'text'; + newElement.className = this.$menu[0].parentNode.className + ' open'; + menu.className = 'dropdown-menu open'; + menuInner.className = 'dropdown-menu inner'; + divider.className = 'divider'; - this.$newElement - .data('liHeight', liHeight) - .data('headerHeight', headerHeight) - .data('searchHeight', searchHeight) - .data('actionsHeight', actionsHeight); + text.appendChild(document.createTextNode('Inner text')); + a.appendChild(text); + li.appendChild(a); + menuInner.appendChild(li); + menuInner.appendChild(divider); + if (header) menu.appendChild(header); + if (search) { + var input = document.createElement('input'); + search.className = 'bs-searchbox'; + input.className = 'form-control'; + search.appendChild(input); + menu.appendChild(search); + } + if (actions) menu.appendChild(actions); + menu.appendChild(menuInner); + if (doneButton) menu.appendChild(doneButton); + newElement.appendChild(menu); + + document.body.appendChild(newElement); + + var liHeight = a.offsetHeight, + headerHeight = header ? header.offsetHeight : 0, + searchHeight = search ? search.offsetHeight : 0, + actionsHeight = actions ? actions.offsetHeight : 0, + doneButtonHeight = doneButton ? doneButton.offsetHeight : 0, + dividerHeight = $(divider).outerHeight(true), + // fall back to jQuery if getComputedStyle is not supported + menuStyle = typeof getComputedStyle === 'function' ? getComputedStyle(menu) : false, + $menu = menuStyle ? null : $(menu), + menuPadding = { + vert: parseInt(menuStyle ? menuStyle.paddingTop : $menu.css('paddingTop')) + + parseInt(menuStyle ? menuStyle.paddingBottom : $menu.css('paddingBottom')) + + parseInt(menuStyle ? menuStyle.borderTopWidth : $menu.css('borderTopWidth')) + + parseInt(menuStyle ? menuStyle.borderBottomWidth : $menu.css('borderBottomWidth')), + horiz: parseInt(menuStyle ? menuStyle.paddingLeft : $menu.css('paddingLeft')) + + parseInt(menuStyle ? menuStyle.paddingRight : $menu.css('paddingRight')) + + parseInt(menuStyle ? menuStyle.borderLeftWidth : $menu.css('borderLeftWidth')) + + parseInt(menuStyle ? menuStyle.borderRightWidth : $menu.css('borderRightWidth')) + }, + menuExtras = { + vert: menuPadding.vert + + parseInt(menuStyle ? menuStyle.marginTop : $menu.css('marginTop')) + + parseInt(menuStyle ? menuStyle.marginBottom : $menu.css('marginBottom')) + 2, + horiz: menuPadding.horiz + + parseInt(menuStyle ? menuStyle.marginLeft : $menu.css('marginLeft')) + + parseInt(menuStyle ? menuStyle.marginRight : $menu.css('marginRight')) + 2 + } + + document.body.removeChild(newElement); + + this.sizeInfo = { + liHeight: liHeight, + headerHeight: headerHeight, + searchHeight: searchHeight, + actionsHeight: actionsHeight, + doneButtonHeight: doneButtonHeight, + dividerHeight: dividerHeight, + menuPadding: menuPadding, + menuExtras: menuExtras + }; }, setSize: function () { this.findLis(); + this.liHeight(); + + if (this.options.header) this.$menu.css('padding-top', 0); + if (this.options.size === false) return; + var that = this, - menu = this.$menu, - menuInner = menu.find('.inner'), - selectHeight = this.$newElement.outerHeight(), - liHeight = this.$newElement.data('liHeight'), - headerHeight = this.$newElement.data('headerHeight'), - searchHeight = this.$newElement.data('searchHeight'), - actionsHeight = this.$newElement.data('actionsHeight'), - divHeight = this.$lis.filter('.divider').outerHeight(true), - menuPadding = parseInt(menu.css('padding-top')) + - parseInt(menu.css('padding-bottom')) + - parseInt(menu.css('border-top-width')) + - parseInt(menu.css('border-bottom-width')), - notDisabled = this.options.hideDisabled ? ', .disabled' : '', + $menu = this.$menu, + $menuInner = this.$menuInner, $window = $(window), - menuExtras = menuPadding + parseInt(menu.css('margin-top')) + parseInt(menu.css('margin-bottom')) + 2, + selectHeight = this.$newElement[0].offsetHeight, + selectWidth = this.$newElement[0].offsetWidth, + liHeight = this.sizeInfo['liHeight'], + headerHeight = this.sizeInfo['headerHeight'], + searchHeight = this.sizeInfo['searchHeight'], + actionsHeight = this.sizeInfo['actionsHeight'], + doneButtonHeight = this.sizeInfo['doneButtonHeight'], + divHeight = this.sizeInfo['dividerHeight'], + menuPadding = this.sizeInfo['menuPadding'], + menuExtras = this.sizeInfo['menuExtras'], + notDisabled = this.options.hideDisabled ? '.disabled' : '', menuHeight, + menuWidth, + getHeight, + getWidth, selectOffsetTop, selectOffsetBot, - posVert = function () { - // JQuery defines a scrollTop function, but in pure JS it's a property - //noinspection JSValidateTypes - selectOffsetTop = that.$newElement.offset().top - $window.scrollTop(); - selectOffsetBot = $window.height() - selectOffsetTop - selectHeight; - }; - posVert(); - if (this.options.header) menu.css('padding-top', 0); + selectOffsetLeft, + selectOffsetRight, + getPos = function() { + var pos = that.$newElement.offset(), + $container = $(that.options.container), + containerPos; - if (this.options.size == 'auto') { + if (that.options.container && !$container.is('body')) { + containerPos = $container.offset(); + containerPos.top += parseInt($container.css('borderTopWidth')); + containerPos.left += parseInt($container.css('borderLeftWidth')); + } else { + containerPos = { top: 0, left: 0 }; + } + + var winPad = that.options.windowPadding; + selectOffsetTop = pos.top - containerPos.top - $window.scrollTop(); + selectOffsetBot = $window.height() - selectOffsetTop - selectHeight - containerPos.top - winPad[2]; + selectOffsetLeft = pos.left - containerPos.left - $window.scrollLeft(); + selectOffsetRight = $window.width() - selectOffsetLeft - selectWidth - containerPos.left - winPad[1]; + selectOffsetTop -= winPad[0]; + selectOffsetLeft -= winPad[3]; + }; + + getPos(); + + if (this.options.size === 'auto') { var getSize = function () { var minHeight, - lisVis = that.$lis.not('.hide'); + hasClass = function (className, include) { + return function (element) { + if (include) { + return (element.classList ? element.classList.contains(className) : $(element).hasClass(className)); + } else { + return !(element.classList ? element.classList.contains(className) : $(element).hasClass(className)); + } + }; + }, + lis = that.$menuInner[0].getElementsByTagName('li'), + lisVisible = Array.prototype.filter ? Array.prototype.filter.call(lis, hasClass('hidden', false)) : that.$lis.not('.hidden'), + optGroup = Array.prototype.filter ? Array.prototype.filter.call(lisVisible, hasClass('dropdown-header', true)) : lisVisible.filter('.dropdown-header'); - posVert(); - menuHeight = selectOffsetBot - menuExtras; + getPos(); + menuHeight = selectOffsetBot - menuExtras.vert; + menuWidth = selectOffsetRight - menuExtras.horiz; + + if (that.options.container) { + if (!$menu.data('height')) $menu.data('height', $menu.height()); + getHeight = $menu.data('height'); + + if (!$menu.data('width')) $menu.data('width', $menu.width()); + getWidth = $menu.data('width'); + } else { + getHeight = $menu.height(); + getWidth = $menu.width(); + } if (that.options.dropupAuto) { - that.$newElement.toggleClass('dropup', (selectOffsetTop > selectOffsetBot) && ((menuHeight - menuExtras) < menu.height())); - } - if (that.$newElement.hasClass('dropup')) { - menuHeight = selectOffsetTop - menuExtras; + that.$newElement.toggleClass('dropup', selectOffsetTop > selectOffsetBot && (menuHeight - menuExtras.vert) < getHeight); } - if ((lisVis.length + lisVis.filter('.dropdown-header').length) > 3) { - minHeight = liHeight * 3 + menuExtras - 2; + if (that.$newElement.hasClass('dropup')) { + menuHeight = selectOffsetTop - menuExtras.vert; + } + + if (that.options.dropdownAlignRight === 'auto') { + $menu.toggleClass('dropdown-menu-right', selectOffsetLeft > selectOffsetRight && (menuWidth - menuExtras.horiz) < (getWidth - selectWidth)); + } + + if ((lisVisible.length + optGroup.length) > 3) { + minHeight = liHeight * 3 + menuExtras.vert - 2; } else { minHeight = 0; } - menu.css({ + $menu.css({ 'max-height': menuHeight + 'px', 'overflow': 'hidden', - 'min-height': minHeight + headerHeight + searchHeight + actionsHeight + 'px' + 'min-height': minHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight + 'px' }); - menuInner.css({ - 'max-height': menuHeight - headerHeight - searchHeight - actionsHeight - menuPadding + 'px', + $menuInner.css({ + 'max-height': menuHeight - headerHeight - searchHeight - actionsHeight - doneButtonHeight - menuPadding.vert + 'px', 'overflow-y': 'auto', - 'min-height': Math.max(minHeight - menuPadding, 0) + 'px' + 'min-height': Math.max(minHeight - menuPadding.vert, 0) + 'px' }); }; getSize(); this.$searchbox.off('input.getSize propertychange.getSize').on('input.getSize propertychange.getSize', getSize); - $(window).off('resize.getSize').on('resize.getSize', getSize); - $(window).off('scroll.getSize').on('scroll.getSize', getSize); - } else if (this.options.size && this.options.size != 'auto' && menu.find('li' + notDisabled).length > this.options.size) { - var optIndex = this.$lis.not('.divider' + notDisabled).find(' > *').slice(0, this.options.size).last().parent().index(); - var divLength = this.$lis.slice(0, optIndex + 1).filter('.divider').length; - menuHeight = liHeight * this.options.size + divLength * divHeight + menuPadding; + $window.off('resize.getSize scroll.getSize').on('resize.getSize scroll.getSize', getSize); + } else if (this.options.size && this.options.size != 'auto' && this.$lis.not(notDisabled).length > this.options.size) { + var optIndex = this.$lis.not('.divider').not(notDisabled).children().slice(0, this.options.size).last().parent().index(), + divLength = this.$lis.slice(0, optIndex + 1).filter('.divider').length; + menuHeight = liHeight * this.options.size + divLength * divHeight + menuPadding.vert; + + if (that.options.container) { + if (!$menu.data('height')) $menu.data('height', $menu.height()); + getHeight = $menu.data('height'); + } else { + getHeight = $menu.height(); + } + if (that.options.dropupAuto) { //noinspection JSUnusedAssignment - this.$newElement.toggleClass('dropup', (selectOffsetTop > selectOffsetBot) && (menuHeight < menu.height())); + this.$newElement.toggleClass('dropup', selectOffsetTop > selectOffsetBot && (menuHeight - menuExtras.vert) < getHeight); } - menu.css({'max-height': menuHeight + headerHeight + searchHeight + actionsHeight + 'px', 'overflow': 'hidden'}); - menuInner.css({'max-height': menuHeight - menuPadding + 'px', 'overflow-y': 'auto'}); + $menu.css({ + 'max-height': menuHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight + 'px', + 'overflow': 'hidden', + 'min-height': '' + }); + $menuInner.css({ + 'max-height': menuHeight - menuPadding.vert + 'px', + 'overflow-y': 'auto', + 'min-height': '' + }); } }, setWidth: function () { - if (this.options.width == 'auto') { + if (this.options.width === 'auto') { this.$menu.css('min-width', '0'); - // Get correct width if element hidden - var selectClone = this.$newElement.clone().appendTo('body'); - var ulWidth = selectClone.find('> .dropdown-menu').css('width'); - var btnWidth = selectClone.css('width', 'auto').find('> button').css('width'); - selectClone.remove(); + // Get correct width if element is hidden + var $selectClone = this.$menu.parent().clone().appendTo('body'), + $selectClone2 = this.options.container ? this.$newElement.clone().appendTo('body') : $selectClone, + ulWidth = $selectClone.children('.dropdown-menu').outerWidth(), + btnWidth = $selectClone2.css('width', 'auto').children('button').outerWidth(); + + $selectClone.remove(); + $selectClone2.remove(); // Set width to whatever's larger, button title or longest option - this.$newElement.css('width', Math.max(parseInt(ulWidth), parseInt(btnWidth)) + 'px'); - } else if (this.options.width == 'fit') { + this.$newElement.css('width', Math.max(ulWidth, btnWidth) + 'px'); + } else if (this.options.width === 'fit') { // Remove inline min-width so width can be changed from 'auto' this.$menu.css('min-width', ''); this.$newElement.css('width', '').addClass('fit-width'); @@ -561,74 +1091,108 @@ }, selectPosition: function () { + this.$bsContainer = $('
    '); + var that = this, - drop = '
    ', - $drop = $(drop), + $container = $(this.options.container), pos, + containerPos, actualHeight, getPlacement = function ($element) { - $drop.addClass($element.attr('class').replace(/form-control/gi, '')).toggleClass('dropup', $element.hasClass('dropup')); + that.$bsContainer.addClass($element.attr('class').replace(/form-control|fit-width/gi, '')).toggleClass('dropup', $element.hasClass('dropup')); pos = $element.offset(); + + if (!$container.is('body')) { + containerPos = $container.offset(); + containerPos.top += parseInt($container.css('borderTopWidth')) - $container.scrollTop(); + containerPos.left += parseInt($container.css('borderLeftWidth')) - $container.scrollLeft(); + } else { + containerPos = { top: 0, left: 0 }; + } + actualHeight = $element.hasClass('dropup') ? 0 : $element[0].offsetHeight; - $drop.css({ - 'top': pos.top + actualHeight, - 'left': pos.left, - 'width': $element[0].offsetWidth, - 'position': 'absolute' + + that.$bsContainer.css({ + 'top': pos.top - containerPos.top + actualHeight, + 'left': pos.left - containerPos.left, + 'width': $element[0].offsetWidth }); }; - this.$newElement.on('click', function () { + + this.$button.on('click', function () { + var $this = $(this); + if (that.isDisabled()) { return; } - getPlacement($(this)); - $drop.appendTo(that.options.container); - $drop.toggleClass('open', !$(this).hasClass('open')); - $drop.append(that.$menu); + + getPlacement(that.$newElement); + + that.$bsContainer + .appendTo(that.options.container) + .toggleClass('open', !$this.hasClass('open')) + .append(that.$menu); }); - $(window).resize(function () { + + $(window).on('resize scroll', function () { getPlacement(that.$newElement); }); - $(window).on('scroll', function () { - getPlacement(that.$newElement); - }); - $('html').on('click', function (e) { - if ($(e.target).closest(that.$newElement).length < 1) { - $drop.removeClass('open'); - } + + this.$element.on('hide.bs.select', function () { + that.$menu.data('height', that.$menu.height()); + that.$bsContainer.detach(); }); }, - setSelected: function (index, selected) { - this.findLis(); - this.$lis.filter('[data-original-index="' + index + '"]').toggleClass('selected', selected); + /** + * @param {number} index - the index of the option that is being changed + * @param {boolean} selected - true if the option is being selected, false if being deselected + * @param {JQuery} $lis - the 'li' element that is being modified + */ + setSelected: function (index, selected, $lis) { + if (!$lis) { + this.togglePlaceholder(); // check if setSelected is being called by changing the value of the select + $lis = this.findLis().eq(this.liObj[index]); + } + + $lis.toggleClass('selected', selected).find('a').attr('aria-selected', selected); }, - setDisabled: function (index, disabled) { - this.findLis(); + /** + * @param {number} index - the index of the option that is being disabled + * @param {boolean} disabled - true if the option is being disabled, false if being enabled + * @param {JQuery} $lis - the 'li' element that is being modified + */ + setDisabled: function (index, disabled, $lis) { + if (!$lis) { + $lis = this.findLis().eq(this.liObj[index]); + } + if (disabled) { - this.$lis.filter('[data-original-index="' + index + '"]').addClass('disabled').find('a').attr('href', '#').attr('tabindex', -1); + $lis.addClass('disabled').children('a').attr('href', '#').attr('tabindex', -1).attr('aria-disabled', true); } else { - this.$lis.filter('[data-original-index="' + index + '"]').removeClass('disabled').find('a').removeAttr('href').attr('tabindex', 0); + $lis.removeClass('disabled').children('a').removeAttr('href').attr('tabindex', 0).attr('aria-disabled', false); } }, isDisabled: function () { - return this.$element.is(':disabled'); + return this.$element[0].disabled; }, checkDisabled: function () { var that = this; if (this.isDisabled()) { - this.$button.addClass('disabled').attr('tabindex', -1); + this.$newElement.addClass('disabled'); + this.$button.addClass('disabled').attr('tabindex', -1).attr('aria-disabled', true); } else { if (this.$button.hasClass('disabled')) { - this.$button.removeClass('disabled'); + this.$newElement.removeClass('disabled'); + this.$button.removeClass('disabled').attr('aria-disabled', false); } - if (this.$button.attr('tabindex') == -1) { - if (!this.$element.data('tabindex')) this.$button.removeAttr('tabindex'); + if (this.$button.attr('tabindex') == -1 && !this.$element.data('tabindex')) { + this.$button.removeAttr('tabindex'); } } @@ -637,37 +1201,62 @@ }); }, + togglePlaceholder: function () { + var value = this.$element.val(); + this.$button.toggleClass('bs-placeholder', value === null || value === '' || (value.constructor === Array && value.length === 0)); + }, + tabIndex: function () { - if (this.$element.is('[tabindex]')) { + if (this.$element.data('tabindex') !== this.$element.attr('tabindex') && + (this.$element.attr('tabindex') !== -98 && this.$element.attr('tabindex') !== '-98')) { this.$element.data('tabindex', this.$element.attr('tabindex')); this.$button.attr('tabindex', this.$element.data('tabindex')); } + + this.$element.attr('tabindex', -98); }, clickListener: function () { - var that = this; + var that = this, + $document = $(document); - this.$newElement.on('touchstart.dropdown', '.dropdown-menu', function (e) { - e.stopPropagation(); - }); + $document.data('spaceSelect', false); - this.$newElement.on('click', function () { - that.setSize(); - if (!that.options.liveSearch && !that.multiple) { - setTimeout(function () { - that.$menu.find('.selected a').focus(); - }, 10); + this.$button.on('keyup', function (e) { + if (/(32)/.test(e.keyCode.toString(10)) && $document.data('spaceSelect')) { + e.preventDefault(); + $document.data('spaceSelect', false); } }); - this.$menu.on('click', 'li a', function (e) { + this.$button.on('click', function () { + that.setSize(); + }); + + this.$element.on('shown.bs.select', function () { + if (!that.options.liveSearch && !that.multiple) { + that.$menuInner.find('.selected a').focus(); + } else if (!that.multiple) { + var selectedIndex = that.liObj[that.$element[0].selectedIndex]; + + if (typeof selectedIndex !== 'number' || that.options.size === false) return; + + // scroll to selected option + var offset = that.$lis.eq(selectedIndex)[0].offsetTop - that.$menuInner[0].offsetTop; + offset = offset - that.$menuInner[0].offsetHeight/2 + that.sizeInfo.liHeight/2; + that.$menuInner[0].scrollTop = offset; + } + }); + + this.$menuInner.on('click', 'li a', function (e) { var $this = $(this), clickedIndex = $this.parent().data('originalIndex'), prevValue = that.$element.val(), - prevIndex = that.$element.prop('selectedIndex'); + prevIndex = that.$element.prop('selectedIndex'), + triggerChange = true; // Don't close on multi choice menu - if (that.multiple) { + if (that.multiple && that.options.maxOptions !== 1) { e.stopPropagation(); } @@ -685,14 +1274,14 @@ if (!that.multiple) { // Deselect all others if not multi select box $options.prop('selected', false); $option.prop('selected', true); - that.$menu.find('.selected').removeClass('selected'); + that.$menuInner.find('.selected').removeClass('selected').find('a').attr('aria-selected', false); that.setSelected(clickedIndex, true); } else { // Toggle the one we have chosen if we are multi select. $option.prop('selected', !state); that.setSelected(clickedIndex, !state); $this.blur(); - if ((maxOptions !== false) || (maxOptionsGrp !== false)) { + if (maxOptions !== false || maxOptionsGrp !== false) { var maxReached = maxOptions < $options.filter(':selected').length, maxReachedGrp = maxOptionsGrp < $optgroup.find('option:selected').length; @@ -700,19 +1289,17 @@ if (maxOptions && maxOptions == 1) { $options.prop('selected', false); $option.prop('selected', true); - that.$menu.find('.selected').removeClass('selected'); + that.$menuInner.find('.selected').removeClass('selected'); that.setSelected(clickedIndex, true); } else if (maxOptionsGrp && maxOptionsGrp == 1) { $optgroup.find('option:selected').prop('selected', false); $option.prop('selected', true); - var optgroupID = $this.data('optgroup'); - - that.$menu.find('.selected').has('a[data-optgroup="' + optgroupID + '"]').removeClass('selected'); - + var optgroupID = $this.parent().data('optgroup'); + that.$menuInner.find('[data-optgroup="' + optgroupID + '"]').removeClass('selected'); that.setSelected(clickedIndex, true); } else { - var maxOptionsArr = (typeof that.options.maxOptionsText === 'function') ? - that.options.maxOptionsText(maxOptions, maxOptionsGrp) : that.options.maxOptionsText, + var maxOptionsText = typeof that.options.maxOptionsText === 'string' ? [that.options.maxOptionsText, that.options.maxOptionsText] : that.options.maxOptionsText, + maxOptionsArr = typeof maxOptionsText === 'function' ? maxOptionsText(maxOptions, maxOptionsGrp) : maxOptionsText, maxTxt = maxOptionsArr[0].replace('{n}', maxOptions), maxTxtGrp = maxOptionsArr[1].replace('{n}', maxOptionsGrp), $notify = $('
    '); @@ -729,11 +1316,13 @@ if (maxOptions && maxReached) { $notify.append($('
    ' + maxTxt + '
    ')); + triggerChange = false; that.$element.trigger('maxReached.bs.select'); } if (maxOptionsGrp && maxReachedGrp) { $notify.append($('
    ' + maxTxtGrp + '
    ')); + triggerChange = false; that.$element.trigger('maxReachedGrp.bs.select'); } @@ -749,50 +1338,54 @@ } } - if (!that.multiple) { + if (!that.multiple || (that.multiple && that.options.maxOptions === 1)) { that.$button.focus(); } else if (that.options.liveSearch) { that.$searchbox.focus(); } // Trigger select 'change' - if ((prevValue != that.$element.val() && that.multiple) || (prevIndex != that.$element.prop('selectedIndex') && !that.multiple)) { - that.$element.change(); + if (triggerChange) { + if ((prevValue != that.$element.val() && that.multiple) || (prevIndex != that.$element.prop('selectedIndex') && !that.multiple)) { + // $option.prop('selected') is current option state (selected/unselected). state is previous option state. + changed_arguments = [clickedIndex, $option.prop('selected'), state]; + that.$element + .triggerNative('change'); + } } } }); this.$menu.on('click', 'li.disabled a, .popover-title, .popover-title :not(.close)', function (e) { - if (e.target == this) { + if (e.currentTarget == this) { e.preventDefault(); e.stopPropagation(); - if (!that.options.liveSearch) { - that.$button.focus(); - } else { + if (that.options.liveSearch && !$(e.target).hasClass('close')) { that.$searchbox.focus(); + } else { + that.$button.focus(); } } }); - this.$menu.on('click', 'li.divider, li.dropdown-header', function (e) { + this.$menuInner.on('click', '.divider, .dropdown-header', function (e) { e.preventDefault(); e.stopPropagation(); - if (!that.options.liveSearch) { - that.$button.focus(); - } else { + if (that.options.liveSearch) { that.$searchbox.focus(); + } else { + that.$button.focus(); } }); this.$menu.on('click', '.popover-title .close', function () { - that.$button.focus(); + that.$button.click(); }); this.$searchbox.on('click', function (e) { e.stopPropagation(); }); - this.$menu.on('click', '.actions-btn', function (e) { if (that.options.liveSearch) { that.$searchbox.focus(); @@ -803,31 +1396,32 @@ e.preventDefault(); e.stopPropagation(); - if ($(this).is('.bs-select-all')) { + if ($(this).hasClass('bs-select-all')) { that.selectAll(); } else { that.deselectAll(); } - that.$element.change(); }); this.$element.change(function () { that.render(false); + that.$element.trigger('changed.bs.select', changed_arguments); + changed_arguments = null; }); }, liveSearchListener: function () { var that = this, - no_results = $('
  • '); + $no_results = $('
  • '); - this.$newElement.on('click.dropdown.data-api touchstart.dropdown.data-api', function () { - that.$menu.find('.active').removeClass('active'); + this.$button.on('click.dropdown.data-api', function () { + that.$menuInner.find('.active').removeClass('active'); if (!!that.$searchbox.val()) { that.$searchbox.val(''); - that.$lis.not('.is-hidden').removeClass('hide'); - if (!!no_results.parent().length) no_results.remove(); + that.$lis.not('.is-hidden').removeClass('hidden'); + if (!!$no_results.parent().length) $no_results.remove(); } - if (!that.multiple) that.$menu.find('.selected').addClass('active'); + if (!that.multiple) that.$menuInner.find('.selected').addClass('active'); setTimeout(function () { that.$searchbox.focus(); }, 10); @@ -838,33 +1432,64 @@ }); this.$searchbox.on('input propertychange', function () { + that.$lis.not('.is-hidden').removeClass('hidden'); + that.$lis.filter('.active').removeClass('active'); + $no_results.remove(); + if (that.$searchbox.val()) { - - if (that.options.searchAccentInsensitive) { - that.$lis.not('.is-hidden').removeClass('hide').find('a').not(':aicontains(' + normalizeToBase(that.$searchbox.val()) + ')').parent().addClass('hide'); + var $searchBase = that.$lis.not('.is-hidden, .divider, .dropdown-header'), + $hideItems; + if (that.options.liveSearchNormalize) { + $hideItems = $searchBase.not(':a' + that._searchStyle() + '("' + normalizeToBase(that.$searchbox.val()) + '")'); } else { - that.$lis.not('.is-hidden').removeClass('hide').find('a').not(':icontains(' + that.$searchbox.val() + ')').parent().addClass('hide'); + $hideItems = $searchBase.not(':' + that._searchStyle() + '("' + that.$searchbox.val() + '")'); } - if (!that.$menu.find('li').filter(':visible:not(.no-results)').length) { - if (!!no_results.parent().length) no_results.remove(); - no_results.html(that.options.noneResultsText + ' "' + htmlEscape(that.$searchbox.val()) + '"').show(); - that.$menu.find('li').last().after(no_results); - } else if (!!no_results.parent().length) { - no_results.remove(); - } + if ($hideItems.length === $searchBase.length) { + $no_results.html(that.options.noneResultsText.replace('{0}', '"' + htmlEscape(that.$searchbox.val()) + '"')); + that.$menuInner.append($no_results); + that.$lis.addClass('hidden'); + } else { + $hideItems.addClass('hidden'); - } else { - that.$lis.not('.is-hidden').removeClass('hide'); - if (!!no_results.parent().length) no_results.remove(); + var $lisVisible = that.$lis.not('.hidden'), + $foundDiv; + + // hide divider if first or last visible, or if followed by another divider + $lisVisible.each(function (index) { + var $this = $(this); + + if ($this.hasClass('divider')) { + if ($foundDiv === undefined) { + $this.addClass('hidden'); + } else { + if ($foundDiv) $foundDiv.addClass('hidden'); + $foundDiv = $this; + } + } else if ($this.hasClass('dropdown-header') && $lisVisible.eq(index + 1).data('optgroup') !== $this.data('optgroup')) { + $this.addClass('hidden'); + } else { + $foundDiv = null; + } + }); + if ($foundDiv) $foundDiv.addClass('hidden'); + + $searchBase.not('.hidden').first().addClass('active'); + that.$menuInner.scrollTop(0); + } } - - that.$menu.find('li.active').removeClass('active'); - that.$menu.find('li').filter(':visible:not(.divider)').eq(0).addClass('active').find('a').focus(); - $(this).focus(); }); }, + _searchStyle: function () { + var styles = { + begins: 'ibegins', + startsWith: 'ibegins' + }; + + return styles[this.options.liveSearchStyle] || 'icontains'; + }, + val: function (value) { if (typeof value !== 'undefined') { this.$element.val(value); @@ -876,29 +1501,65 @@ } }, - selectAll: function () { + changeAll: function (status) { + if (!this.multiple) return; + if (typeof status === 'undefined') status = true; + this.findLis(); - this.$lis.not('.divider').not('.disabled').not('.selected').filter(':visible').find('a').click(); + + var $options = this.$element.find('option'), + $lisVisible = this.$lis.not('.divider, .dropdown-header, .disabled, .hidden'), + lisVisLen = $lisVisible.length, + selectedOptions = []; + + if (status) { + if ($lisVisible.filter('.selected').length === $lisVisible.length) return; + } else { + if ($lisVisible.filter('.selected').length === 0) return; + } + + $lisVisible.toggleClass('selected', status); + + for (var i = 0; i < lisVisLen; i++) { + var origIndex = $lisVisible[i].getAttribute('data-original-index'); + selectedOptions[selectedOptions.length] = $options.eq(origIndex)[0]; + } + + $(selectedOptions).prop('selected', status); + + this.render(false); + + this.togglePlaceholder(); + + this.$element + .triggerNative('change'); + }, + + selectAll: function () { + return this.changeAll(true); }, deselectAll: function () { - this.findLis(); - this.$lis.not('.divider').not('.disabled').filter('.selected').filter(':visible').find('a').click(); + return this.changeAll(false); + }, + + toggle: function (e) { + e = e || window.event; + + if (e) e.stopPropagation(); + + this.$button.trigger('click'); }, keydown: function (e) { var $this = $(this), - $parent = ($this.is('input')) ? $this.parent().parent() : $this.parent(), + $parent = $this.is('input') ? $this.parent().parent() : $this.parent(), $items, that = $parent.data('this'), index, - next, - first, - last, - prev, - nextPrev, prevIndex, isActive, + selector = ':not(.disabled, .hidden, .dropdown-header, .divider)', keyCodeMap = { 32: ' ', 48: '0', @@ -950,93 +1611,58 @@ 105: '9' }; - if (that.options.liveSearch) $parent = $this.parent().parent(); - if (that.options.container) $parent = that.$menu; + isActive = that.$newElement.hasClass('open'); - $items = $('[role=menu] li a', $parent); - - isActive = that.$menu.parent().hasClass('open'); - - if (!isActive && /([0-9]|[A-z])/.test(String.fromCharCode(e.keyCode))) { + if (!isActive && (e.keyCode >= 48 && e.keyCode <= 57 || e.keyCode >= 96 && e.keyCode <= 105 || e.keyCode >= 65 && e.keyCode <= 90)) { if (!that.options.container) { that.setSize(); that.$menu.parent().addClass('open'); isActive = true; } else { - that.$newElement.trigger('click'); + that.$button.trigger('click'); } that.$searchbox.focus(); + return; } if (that.options.liveSearch) { - if (/(^9$|27)/.test(e.keyCode.toString(10)) && isActive && that.$menu.find('.active').length === 0) { + if (/(^9$|27)/.test(e.keyCode.toString(10)) && isActive) { e.preventDefault(); - that.$menu.parent().removeClass('open'); + e.stopPropagation(); + that.$menuInner.click(); that.$button.focus(); } - $items = $('[role=menu] li:not(.divider):not(.dropdown-header):visible', $parent); - if (!$this.val() && !/(38|40)/.test(e.keyCode.toString(10))) { - if ($items.filter('.active').length === 0) { - if (that.options.searchAccentInsensitive) { - $items = that.$newElement.find('li').filter(':aicontains(' + normalizeToBase(keyCodeMap[e.keyCode]) + ')'); - } else { - $items = that.$newElement.find('li').filter(':icontains(' + keyCodeMap[e.keyCode] + ')'); - } - } - } } - if (!$items.length) return; - if (/(38|40)/.test(e.keyCode.toString(10))) { - index = $items.index($items.filter(':focus')); - first = $items.parent(':not(.disabled):visible').first().index(); - last = $items.parent(':not(.disabled):visible').last().index(); - next = $items.eq(index).parent().nextAll(':not(.disabled):visible').eq(0).index(); - prev = $items.eq(index).parent().prevAll(':not(.disabled):visible').eq(0).index(); - nextPrev = $items.eq(next).parent().prevAll(':not(.disabled):visible').eq(0).index(); - - if (that.options.liveSearch) { - $items.each(function (i) { - if ($(this).is(':not(.disabled)')) { - $(this).data('index', i); - } - }); - index = $items.index($items.filter('.active')); - first = $items.filter(':not(.disabled):visible').first().data('index'); - last = $items.filter(':not(.disabled):visible').last().data('index'); - next = $items.eq(index).nextAll(':not(.disabled):visible').eq(0).data('index'); - prev = $items.eq(index).prevAll(':not(.disabled):visible').eq(0).data('index'); - nextPrev = $items.eq(next).prevAll(':not(.disabled):visible').eq(0).data('index'); - } - - prevIndex = $this.data('prevIndex'); - - if (e.keyCode == 38) { - if (that.options.liveSearch) index -= 1; - if (index != nextPrev && index > prev) index = prev; - if (index < first) index = first; - if (index == prevIndex) index = last; - } - - if (e.keyCode == 40) { - if (that.options.liveSearch) index += 1; - if (index == -1) index = 0; - if (index != nextPrev && index < next) index = next; - if (index > last) index = last; - if (index == prevIndex) index = first; - } - - $this.data('prevIndex', index); + $items = that.$lis.filter(selector); + if (!$items.length) return; if (!that.options.liveSearch) { - $items.eq(index).focus(); + index = $items.index($items.find('a').filter(':focus').parent()); + } else { + index = $items.index($items.filter('.active')); + } + + prevIndex = that.$menuInner.data('prevIndex'); + + if (e.keyCode == 38) { + if ((that.options.liveSearch || index == prevIndex) && index != -1) index--; + if (index < 0) index += $items.length; + } else if (e.keyCode == 40) { + if (that.options.liveSearch || index == prevIndex) index++; + index = index % $items.length; + } + + that.$menuInner.data('prevIndex', index); + + if (!that.options.liveSearch) { + $items.eq(index).children('a').focus(); } else { e.preventDefault(); - if (!$this.is('.dropdown-toggle')) { - $items.removeClass('active'); - $items.eq(index).addClass('active').find('a').focus(); + if (!$this.hasClass('dropdown-toggle')) { + $items.removeClass('active').eq(index).addClass('active').children('a').focus(); $this.focus(); } } @@ -1046,11 +1672,10 @@ count, prevKey; - $items.each(function () { - if ($(this).parent().is(':not(.disabled)')) { - if ($.trim($(this).text().toLowerCase()).substring(0, 1) == keyCodeMap[e.keyCode]) { - keyIndex.push($(this).parent().index()); - } + $items = that.$lis.filter(selector); + $items.each(function (i) { + if ($.trim($(this).children('a').text().toLowerCase()).substring(0, 1) == keyCodeMap[e.keyCode]) { + keyIndex.push(i); } }); @@ -1068,16 +1693,23 @@ if (count > keyIndex.length) count = 1; } - $items.eq(keyIndex[count - 1]).focus(); + $items.eq(keyIndex[count - 1]).children('a').focus(); } // Select focused option if "Enter", "Spacebar" or "Tab" (when selectOnTab is true) are pressed inside the menu. if ((/(13|32)/.test(e.keyCode.toString(10)) || (/(^9$)/.test(e.keyCode.toString(10)) && that.options.selectOnTab)) && isActive) { if (!/(32)/.test(e.keyCode.toString(10))) e.preventDefault(); if (!that.options.liveSearch) { - $(':focus').click(); + var elem = $(':focus'); + elem.click(); + // Bring back focus for multiselects + elem.focus(); + // Prevent screen from scrolling if the user hit the spacebar + e.preventDefault(); + // Fixes spacebar selection of dropdown items in FF & IE + $(document).data('spaceSelect', true); } else if (!/(32)/.test(e.keyCode.toString(10))) { - that.$menu.find('.active a').click(); + that.$menuInner.find('.active a').click(); $this.focus(); } $(document).data('keycount', 0); @@ -1085,31 +1717,27 @@ if ((/(^9$|27)/.test(e.keyCode.toString(10)) && isActive && (that.multiple || that.options.liveSearch)) || (/(27)/.test(e.keyCode.toString(10)) && !isActive)) { that.$menu.parent().removeClass('open'); + if (that.options.container) that.$newElement.removeClass('open'); that.$button.focus(); } }, mobile: function () { - this.$element.addClass('mobile-device').appendTo(this.$newElement); - if (this.options.container) this.$menu.hide(); + this.$element.addClass('mobile-device'); }, refresh: function () { this.$lis = null; + this.liObj = {}; this.reloadLi(); this.render(); - this.setWidth(); - this.setStyle(); this.checkDisabled(); - this.liHeight(); - }, + this.liHeight(true); + this.setStyle(); + this.setWidth(); + if (this.$lis) this.$searchbox.trigger('propertychange'); - update: function () { - this.reloadLi(); - this.setWidth(); - this.setStyle(); - this.checkDisabled(); - this.liHeight(); + this.$element.trigger('refreshed.bs.select'); }, hide: function () { @@ -1123,37 +1751,46 @@ remove: function () { this.$newElement.remove(); this.$element.remove(); + }, + + destroy: function () { + this.$newElement.before(this.$element).remove(); + + if (this.$bsContainer) { + this.$bsContainer.remove(); + } else { + this.$menu.remove(); + } + + this.$element + .off('.bs.select') + .removeData('selectpicker') + .removeClass('bs-select-hidden selectpicker'); } }; // SELECTPICKER PLUGIN DEFINITION // ============================== - function Plugin(option, event) { + function Plugin(option) { // get the args of the outer function.. var args = arguments; // The arguments of the function are explicitly re-defined from the argument list, because the shift causes them - // to get lost - //noinspection JSDuplicatedDeclaration - var _option = option, - option = args[0], - event = args[1]; - [].shift.apply(args); + // to get lost/corrupted in android 2.3 and IE9 #715 #775 + var _option = option; - // This fixes a bug in the js implementation on android 2.3 #715 - if (typeof option == 'undefined') { - option = _option; - } + [].shift.apply(args); var value; var chain = this.each(function () { var $this = $(this); if ($this.is('select')) { var data = $this.data('selectpicker'), - options = typeof option == 'object' && option; + options = typeof _option == 'object' && _option; if (!data) { var config = $.extend({}, Selectpicker.DEFAULTS, $.fn.selectpicker.defaults || {}, $this.data(), options); - $this.data('selectpicker', (data = new Selectpicker(this, config, event))); + config.template = $.extend({}, Selectpicker.DEFAULTS.template, ($.fn.selectpicker.defaults ? $.fn.selectpicker.defaults.template : {}), $this.data().template, options.template); + $this.data('selectpicker', (data = new Selectpicker(this, config))); } else if (options) { for (var i in options) { if (options.hasOwnProperty(i)) { @@ -1162,11 +1799,11 @@ } } - if (typeof option == 'string') { - if (data[option] instanceof Function) { - value = data[option].apply(data, args); + if (typeof _option == 'string') { + if (data[_option] instanceof Function) { + value = data[_option].apply(data, args); } else { - value = data.options[option]; + value = data.options[_option]; } } } @@ -1193,8 +1830,8 @@ $(document) .data('keycount', 0) - .on('keydown', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role=menu], .bs-searchbox input', Selectpicker.prototype.keydown) - .on('focusin.modal', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role=menu], .bs-searchbox input', function (e) { + .on('keydown.bs.select', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role="listbox"], .bs-searchbox input', Selectpicker.prototype.keydown) + .on('focusin.modal', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role="listbox"], .bs-searchbox input', function (e) { e.stopPropagation(); }); diff --git a/RIGS/static/js/button.js b/RIGS/static/js/button.js old mode 100644 new mode 100755 index 4d569017..843b39c9 --- a/RIGS/static/js/button.js +++ b/RIGS/static/js/button.js @@ -1,8 +1,8 @@ /* ======================================================================== - * Bootstrap: button.js v3.3.2 + * Bootstrap: button.js v3.3.7 * http://getbootstrap.com/javascript/#buttons * ======================================================================== - * Copyright 2011-2015 Twitter, Inc. + * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ @@ -19,7 +19,7 @@ this.isLoading = false } - Button.VERSION = '3.3.2' + Button.VERSION = '3.3.7' Button.DEFAULTS = { loadingText: 'loading...' @@ -31,7 +31,7 @@ var val = $el.is('input') ? 'val' : 'html' var data = $el.data() - state = state + 'Text' + state += 'Text' if (data.resetText == null) $el.data('resetText', $el[val]()) @@ -41,10 +41,10 @@ if (state == 'loadingText') { this.isLoading = true - $el.addClass(d).attr(d, d) + $el.addClass(d).attr(d, d).prop(d, true) } else if (this.isLoading) { this.isLoading = false - $el.removeClass(d).removeAttr(d) + $el.removeClass(d).removeAttr(d).prop(d, false) } }, this), 0) } @@ -56,15 +56,19 @@ if ($parent.length) { var $input = this.$element.find('input') if ($input.prop('type') == 'radio') { - if ($input.prop('checked') && this.$element.hasClass('active')) changed = false - else $parent.find('.active').removeClass('active') + if ($input.prop('checked')) changed = false + $parent.find('.active').removeClass('active') + this.$element.addClass('active') + } else if ($input.prop('type') == 'checkbox') { + if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false + this.$element.toggleClass('active') } - if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change') + $input.prop('checked', this.$element.hasClass('active')) + if (changed) $input.trigger('change') } else { this.$element.attr('aria-pressed', !this.$element.hasClass('active')) + this.$element.toggleClass('active') } - - if (changed) this.$element.toggleClass('active') } @@ -104,10 +108,15 @@ $(document) .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { - var $btn = $(e.target) - if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') + var $btn = $(e.target).closest('.btn') Plugin.call($btn, 'toggle') - e.preventDefault() + if (!($(e.target).is('input[type="radio"], input[type="checkbox"]'))) { + // Prevent double click on radios, and the double selections (so cancellation) on checkboxes + e.preventDefault() + // The target component still receive the focus + if ($btn.is('input,button')) $btn.trigger('focus') + else $btn.find('input:visible,button:visible').first().trigger('focus') + } }) .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type)) diff --git a/RIGS/static/js/carousel.js b/RIGS/static/js/carousel.js old mode 100644 new mode 100755 index 450e1812..6ff954c9 --- a/RIGS/static/js/carousel.js +++ b/RIGS/static/js/carousel.js @@ -1,8 +1,8 @@ /* ======================================================================== - * Bootstrap: carousel.js v3.3.2 + * Bootstrap: carousel.js v3.3.7 * http://getbootstrap.com/javascript/#carousel * ======================================================================== - * Copyright 2011-2015 Twitter, Inc. + * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ @@ -17,10 +17,10 @@ this.$element = $(element) this.$indicators = this.$element.find('.carousel-indicators') this.options = options - this.paused = - this.sliding = - this.interval = - this.$active = + this.paused = null + this.sliding = null + this.interval = null + this.$active = null this.$items = null this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) @@ -30,7 +30,7 @@ .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) } - Carousel.VERSION = '3.3.2' + Carousel.VERSION = '3.3.7' Carousel.TRANSITION_DURATION = 600 diff --git a/RIGS/static/js/collapse.js b/RIGS/static/js/collapse.js old mode 100644 new mode 100755 index 2bc30e7b..12038693 --- a/RIGS/static/js/collapse.js +++ b/RIGS/static/js/collapse.js @@ -1,11 +1,12 @@ /* ======================================================================== - * Bootstrap: collapse.js v3.3.2 + * Bootstrap: collapse.js v3.3.7 * http://getbootstrap.com/javascript/#collapse * ======================================================================== - * Copyright 2011-2015 Twitter, Inc. + * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +/* jshint latedef: false */ +function ($) { 'use strict'; @@ -16,7 +17,8 @@ var Collapse = function (element, options) { this.$element = $(element) this.options = $.extend({}, Collapse.DEFAULTS, options) - this.$trigger = $(this.options.trigger).filter('[href="#' + element.id + '"], [data-target="#' + element.id + '"]') + this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' + + '[data-toggle="collapse"][data-target="#' + element.id + '"]') this.transitioning = null if (this.options.parent) { @@ -28,13 +30,12 @@ if (this.options.toggle) this.toggle() } - Collapse.VERSION = '3.3.2' + Collapse.VERSION = '3.3.7' Collapse.TRANSITION_DURATION = 350 Collapse.DEFAULTS = { - toggle: true, - trigger: '[data-toggle="collapse"]' + toggle: true } Collapse.prototype.dimension = function () { @@ -172,7 +173,7 @@ var data = $this.data('bs.collapse') var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) - if (!data && options.toggle && option == 'show') options.toggle = false + if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) if (typeof option == 'string') data[option]() }) @@ -203,7 +204,7 @@ var $target = getTargetFromTrigger($this) var data = $target.data('bs.collapse') - var option = data ? 'toggle' : $.extend({}, $this.data(), { trigger: this }) + var option = data ? 'toggle' : $this.data() Plugin.call($target, option) }) diff --git a/RIGS/static/js/dropdown.js b/RIGS/static/js/dropdown.js old mode 100644 new mode 100755 index 200e1c67..04e9c2de --- a/RIGS/static/js/dropdown.js +++ b/RIGS/static/js/dropdown.js @@ -1,8 +1,8 @@ /* ======================================================================== - * Bootstrap: dropdown.js v3.3.2 + * Bootstrap: dropdown.js v3.3.7 * http://getbootstrap.com/javascript/#dropdowns * ======================================================================== - * Copyright 2011-2015 Twitter, Inc. + * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ @@ -19,7 +19,41 @@ $(element).on('click.bs.dropdown', this.toggle) } - Dropdown.VERSION = '3.3.2' + Dropdown.VERSION = '3.3.7' + + function getParent($this) { + var selector = $this.attr('data-target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + var $parent = selector && $(selector) + + return $parent && $parent.length ? $parent : $this.parent() + } + + function clearMenus(e) { + if (e && e.which === 3) return + $(backdrop).remove() + $(toggle).each(function () { + var $this = $(this) + var $parent = getParent($this) + var relatedTarget = { relatedTarget: this } + + if (!$parent.hasClass('open')) return + + if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return + + $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)) + + if (e.isDefaultPrevented()) return + + $this.attr('aria-expanded', 'false') + $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget)) + }) + } Dropdown.prototype.toggle = function (e) { var $this = $(this) @@ -34,7 +68,10 @@ if (!isActive) { if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { // if mobile we use a backdrop because click events don't delegate - $('
    -
    - - {% if event.is_rig %} - - {% endif %} - - {% if event.is_rig %} - {% if perms.RIGS.add_invoice %} - - - {% endif %} - {% endif %} -
    + {% include 'RIGS/event_detail_buttons.html' %}
    - + {% endif %} {% if object.is_rig %} {# only need contact details for a rig #} @@ -149,7 +122,7 @@
     
    Event Description
    -
    {{ event.description|linebreaksbr }}
    +
    {{ event.description|linebreaksbr }}
     
    @@ -174,7 +147,8 @@
    {{ object.collector }}
    {% endif %} - {% if event.is_rig %} + {% if event.is_rig and not event.internal %} +
     
    PO
    {{ object.purchase_order }}
    {% endif %} @@ -182,36 +156,65 @@
    + {% if event.is_rig and event.internal %} +
    +
    +
    Client Authorisation
    +
    +
    +
    Authorisation Request
    +
    {{ object.auth_request_to|yesno:"Yes,No" }}
    + +
    By
    +
    {{ object.auth_request_by }}
    + +
    At
    +
    {{ object.auth_request_at|date:"D d M Y H:i"|default:"" }}
    + +
    To
    +
    {{ object.auth_request_to }}
    +
    +
     
    +
    +
    Authorised
    +
    {{ object.authorised|yesno:"Yes,No" }}
    + +
    Authorised by
    +
    + {% if object.authorisation %} + {{ object.authorisation.name }} + ({{ object.authorisation.email }}) + {% endif %} +
    + +
    Authorised at
    +
    {{ object.authorisation.last_edited_at|date:"D d M Y H:i" }}
    + +
    Authorised amount
    +
    + {% if object.authorisation %} + £ {{ object.authorisation.amount|floatformat:"2" }} + {% endif %} +
    + +
    Requested by
    +
    {{ object.authorisation.sent_by }}
    +
    +
    +
    +
    + {% endif %} {% if not request.is_ajax %}
    -
    - - {% if event.is_rig %} - - {% endif %} - - {% if event.is_rig %} - {% if perms.RIGS.add_invoice %} - - - {% endif %} - {% endif %} -
    + {% include 'RIGS/event_detail_buttons.html' %}
    {% endif %} {% if event.is_rig %} @@ -221,7 +224,7 @@

    Notes

    - {{ event.notes|linebreaksbr }} +
    {{ event.notes|linebreaksbr }}
    {% include 'RIGS/item_table.html' %}
    @@ -229,34 +232,7 @@
    {% if not request.is_ajax %}
    -
    - - {% if event.is_rig %} - - {% endif %} - - {% if event.is_rig %} - {% if perms.RIGS.add_invoice %} - - - {% endif %} - {% endif %} -
    + {% include 'RIGS/event_detail_buttons.html' %}
    {% endif %} {% endif %} @@ -277,7 +253,7 @@
    diff --git a/RIGS/templates/RIGS/event_detail_buttons.html b/RIGS/templates/RIGS/event_detail_buttons.html new file mode 100644 index 00000000..2f05c7f1 --- /dev/null +++ b/RIGS/templates/RIGS/event_detail_buttons.html @@ -0,0 +1,65 @@ + diff --git a/RIGS/templates/RIGS/event_embed.html b/RIGS/templates/RIGS/event_embed.html new file mode 100644 index 00000000..a6e3e586 --- /dev/null +++ b/RIGS/templates/RIGS/event_embed.html @@ -0,0 +1,106 @@ +{% extends 'base_embed.html' %} +{% load static from staticfiles %} + +{% block content %} + +
    + + +
    + + {% if object.mic %} +
    + +
    + {% elif object.is_rig %} + + {% endif %} +
    + +

    + + {% if object.is_rig %}N{{ object.pk|stringformat:"05d" }}{% else %}{{ object.pk }}{% endif %} + | {{ object.name }} + {% if object.venue %} + at {{ object.venue }} + {% endif %} +
    + {{ object.start_date|date:"D d/m/Y" }} + {% if object.has_start_time %} + {{ object.start_time|date:"H:i" }} + {% endif %} + {% if object.end_date or object.has_end_time %} + – + {% endif %} + {% if object.end_date and object.end_date != object.start_date %} + {{ object.end_date|date:"D d/m/Y" }} + {% endif %} + {% if object.has_end_time %} + {{ object.end_time|date:"H:i" }} + {% endif %} + +

    + +
    +
    +

    + Status: + {{ object.get_status_display }} +

    +

    + {% if object.is_rig %} + Client: {{ object.person.name }} + {% if object.organisation %} + for {{ object.organisation.name }} + {% endif %} + {% if object.dry_hire %}(Dry Hire){% endif %} + {% else %} + Non-Rig + {% endif %} +

    +

    + MIC: + {% if object.mic %} + {{object.mic.name}} + {% else %} + None + {% endif %} +

    +
    +
    + + {% if object.meet_at %} +

    + Crew meet: + {{ object.meet_at|date:"H:i" }} {{ object.meet_at|date:"(Y-m-d)" }} +

    + {% endif %} + {% if object.access_at %} +

    + Access at: + {{ object.access_at|date:"H:i" }} {{ object.access_at|date:"(Y-m-d)" }} +

    + {% endif %} +

    + Last updated: + {{ object.last_edited_at }} by "{{ object.last_edited_by.initials }}" +

    +
    +
    + {% if object.description %} +

    + Description: + {{ object.description|linebreaksbr }} +

    + {% endif %} + + +
    +
    + + +{% endblock %} diff --git a/RIGS/templates/RIGS/event_form.html b/RIGS/templates/RIGS/event_form.html index 28e307ca..954ebdbd 100644 --- a/RIGS/templates/RIGS/event_form.html +++ b/RIGS/templates/RIGS/event_form.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'base_rigs.html' %} {% load widget_tweaks %} {% load static %} {% load multiply from filters %} @@ -63,7 +63,7 @@ } else { $('.form-is_rig').slideDown(); } - $('.form-hws').css('overflow', 'visible'); + $('.form-hws, .form-hws .form-is_rig').css('overflow', 'visible'); } else { $('#{{form.is_rig.auto_id}}').prop('checked', false); $('.form-is_rig').slideUp(); @@ -289,10 +289,10 @@
    - {% render_field form.start_date type="date" class+="form-control" %} + {% render_field form.start_date class+="form-control" %}
    - {% render_field form.start_time type="time" class+="form-control" %} + {% render_field form.start_time class+="form-control" %}
    @@ -304,10 +304,10 @@
    - {% render_field form.end_date type="date" class+="form-control" %} + {% render_field form.end_date class+="form-control" %}
    - {% render_field form.end_time type="time" class+="form-control" %} + {% render_field form.end_time class+="form-control" %}
    @@ -329,7 +329,7 @@ class="col-sm-4 control-label">{{ form.access_at.label }}
    - {% render_field form.access_at type="datetime-local" class+="form-control" %} + {% render_field form.access_at class+="form-control" %}
    @@ -337,7 +337,7 @@ class="col-sm-4 control-label">{{ form.meet_at.label }}
    - {% render_field form.meet_at type="datetime-local" class+="form-control" %} + {% render_field form.meet_at class+="form-control" %}
    @@ -398,6 +398,7 @@ {% render_field form.collector class+="form-control" %}
    +
    @@ -445,4 +446,4 @@ {% include 'RIGS/item_modal.html' %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/RIGS/templates/RIGS/event_invoice.html b/RIGS/templates/RIGS/event_invoice.html index fcbe5e87..415293e9 100644 --- a/RIGS/templates/RIGS/event_invoice.html +++ b/RIGS/templates/RIGS/event_invoice.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'base_rigs.html' %} {% load paginator from filters %} {% load static %} @@ -54,20 +54,22 @@ N{{ object.pk|stringformat:"05d" }}
    {{ object.get_status_display }} {{ object.start_date }} - {{ object.name }} - {% if object.organisation %} - {{ object.organisation.name }} -
    - {{ object.organisation.union_account|yesno:'Internal,External' }} - {% else %} - {{ object.person.name }} -
    - External + {{ object.name }} + {% if object.is_rig and perms.RIGS.view_event and object.authorised %} + {% endif %} - - {{ object.sum_total|floatformat:2 }} + + {{ object.organisation.name }} +
    + {{ object.internal|yesno:'Internal,External' }} + + + {{ object.sum_total|floatformat:2 }} +
    + {% if not object.internal %}{{ object.purchase_order }}{% endif %} + {% if object.mic %} {{ object.mic.initials }}
    @@ -77,7 +79,10 @@ {% endif %} - + @@ -92,4 +97,4 @@
    {% endif %}
    -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/RIGS/templates/RIGS/event_print.xml b/RIGS/templates/RIGS/event_print.xml index 6d693117..f91409ac 100644 --- a/RIGS/templates/RIGS/event_print.xml +++ b/RIGS/templates/RIGS/event_print.xml @@ -22,21 +22,21 @@ - + - - + + - + @@ -100,10 +100,11 @@ - {% if not invoice %}[{{ copy }} Copy]{% endif %} [Page of ] - [Paperwork generated by {{current_user.name}} | {% now "d/m/Y H:i" %} | {{object.current_version_id}}] + + [Paperwork generated{% if current_user %} by {{current_user.name}} |{% endif %} {% now "d/m/Y H:i" %} | {{object.current_version_id}}] + @@ -115,10 +116,11 @@ - {% if not invoice %}[{{ copy }} Copy]{% endif %} [Page of ] - [Paperwork generated by {{current_user.name}} | {% now "d/m/Y H:i" %} | {{object.current_version_id}}] + + [Paperwork generated{% if current_user %} by {{current_user.name}} |{% endif %} {% now "d/m/Y H:i" %} | {{object.current_version_id}}] + @@ -128,4 +130,4 @@ {% include "RIGS/event_print_page.xml" %} - \ No newline at end of file + diff --git a/RIGS/templates/RIGS/event_print_page.xml b/RIGS/templates/RIGS/event_print_page.xml index a7049459..4b1ddfdf 100644 --- a/RIGS/templates/RIGS/event_print_page.xml +++ b/RIGS/templates/RIGS/event_print_page.xml @@ -1,59 +1,70 @@ -{% if invoice %} - + - {% endif %} +

    N{{ object.pk|stringformat:"05d" }}: '{{ object.name }}'

    -

    N{{ object.pk|stringformat:"05d" }}: '{{ object.name }}'

    - - -{{object.start_date|date:"D jS N Y"}} - - - - - {{ object.description|default_if_none:""|linebreaksbr }} + + {{object.start_date|date:"D jS N Y"}} - - -{% if invoice %} + + + {{ object.description|default_if_none:""|linebreaksbr }} + + - INVOICE - - - - Invoice Number - - {{ invoice.pk|stringformat:"05d" }} - - - - Invoice Date - - {{ invoice.invoice_date|date:"d/m/Y" }} - - - - PO Number - - {{ object.purchase_order|default_if_none:"" }} - - + {% if invoice %} + INVOICE + + + + Invoice Number + + {{ invoice.pk|stringformat:"05d" }} + + + + Invoice Date + + {{ invoice.invoice_date|date:"d/m/Y" }} + + - + {% if not object.internal %} + + PO + {{ object.purchase_order }} + + {% endif %} + + + {% elif quote %} + + QUOTE + + + + Quote Date + + {% now "d/m/Y" %} + + + + + {% elif receipt %} + + CONFIRMATION + + {% endif %}
    - -{% endif %} - @@ -189,16 +200,16 @@ - {% if not invoice %}VAT Registration Number: 170734807{% endif %} + {% if quote %}VAT Registration Number: 170734807{% endif %} Total (ex. VAT) £ {{ object.sum_total|floatformat:2 }} - {% if not invoice %} - - The full hire fee is payable at least 10 days before the event. - + {% if quote %} + + This quote is valid for 30 days unless otherwise arranged. + {% endif %} VAT @ {{ object.vat_rate.as_percent|floatformat:2 }}% @@ -206,113 +217,140 @@ - - - {% if invoice %} - VAT Registration Number: 170734807 + {% if quote %} + + The full hire fee is payable at least 10 days before the event. + + {% endif %} + + {% if invoice %} + Total + £ {{ object.total|floatformat:2 }} + {% else %} + + + Total + + + + + £ {{ object.total|floatformat:2 }} + + + {% endif %} + + + + +{% if invoice %} + + +

    Payments

    + + + + + Method + + + + + Date + + + + + Amount + + + + {% for payment in object.invoice.payment_set.all %} + + {{ payment.get_method_display }} + {{ payment.date }} + £ {{ payment.amount|floatformat:2 }} + + {% endfor %} + + + + + Payment Total + £ {{ object.invoice.payment_total|floatformat:2 }} + + + + + + Balance (ex. VAT) + + + + + £ {{ object.invoice.balance|floatformat:2 }} + + + + +
    +{% endif %} + + + > + {% if quote %} + + + + {% if object.internal %} + Bookings will + not + be confirmed until the event is authorised online. + {% else %} - This contract is not an invoice. + Bookings will + not + be confirmed until we have received written confirmation and a Purchase Order. + {% endif %} - - - + + + + 24 Hour Emergency Contacts: 07825 065681 and 07825 065678 + + {% else %} + - - Total - + VAT Registration Number: 170734807 + + {% endif %} + + + + - - £ {{ object.total|floatformat:2 }} - + {% if object.internal and object.authorised %} + + Event authorised online by {{ object.authorisation.name }} ({{ object.authorisation.email }}) at + {{ object.authorisation.last_edited_at }}. + + + + + University ID + Account Code + Authorised Amount + + + {{ object.authorisation.uni_id }} + {{ object.authorisation.account_code }} + £ {{ object.authorisation.amount|floatformat:2 }} + + + {% endif %} -{% if not invoice %} - - - - - Bookings will - not - be confirmed until payment is received and the contract is signed. - - - - - 24 Hour Emergency Contacts: 07825 065681 and 07825 065678 - - - - - - - To be signed on booking: - - {% if object.organisation.union_account %} - - - I agree that am authorised to sign this invoice. I agree that I am the President/Treasurer of the hirer, or - that I have provided written permission from either the President or Treasurer of the hirer stating that I can - sign for this invoice. - - - - - I have read, understood and fully accepted the current conditions of hire. I agree to return any dry hire - items to TEC PA & Lighting in the same condition at the end of the hire period. - - - - - - Conditions of hire attached and available on the TEC PA & Lighting website. E&OE - - - - - Please return this form directly to TEC PA & Lighting and not the Students' Union Finance Department. - - - - - Account Code - - - - - - {% else %} - - - I, the hirer, have read, understand and fully accept the current conditions of hire. This document forms a - binding contract between TEC PA & Lighting and the hirer, the aforementioned conditions of hire forming - an integral part of it. - - - - - - Conditions of hire attached and available on the TEC PA & Lighting website. E&OE - - - - {% include "RIGS/event_print_signature.xml" %} - - - To be signed on the day of the event/hire: - - - - I, the hirer, have received the goods/services as requested and in good order. I agree to return any dry hire - items to TEC PA & Lighting in a similar condition at the end of the hire period. - - - {% endif %} - - {% include "RIGS/event_print_signature.xml" %} - - {% endif %} - \ No newline at end of file + diff --git a/RIGS/templates/RIGS/event_table.html b/RIGS/templates/RIGS/event_table.html index 949c710a..c84b6624 100644 --- a/RIGS/templates/RIGS/event_table.html +++ b/RIGS/templates/RIGS/event_table.html @@ -33,13 +33,18 @@

    - {{ event.name }} + + {{ event.name }} + {% if event.venue %} at {{ event.venue }} {% endif %} {% if event.dry_hire %} Dry Hire {% endif %} + {% if event.is_rig and perms.RIGS.view_event and event.authorised %} + + {% endif %}

    {% if event.is_rig and not event.cancelled %}
    @@ -99,4 +104,4 @@ {% endfor %} -
    \ No newline at end of file + diff --git a/RIGS/templates/RIGS/eventauthorisation_client_request.html b/RIGS/templates/RIGS/eventauthorisation_client_request.html new file mode 100644 index 00000000..a6bb7bb9 --- /dev/null +++ b/RIGS/templates/RIGS/eventauthorisation_client_request.html @@ -0,0 +1,41 @@ +{% extends 'base_client_email.html' %} + +{% block content %} + +

    Hi {{ to_name|default:"there" }},

    + +

    {{ request.user.get_full_name }} has requested that you authorise N{{ object.pk|stringformat:"05d" }} + | {{ object.name }}{% if not to_name %} on behalf of {{ object.person.name }}{% endif %}.

    + +

    + Please find the link below to complete the event booking process. + {% if object.event.organisation and object.event.organisation.union_account %}{# internal #} + Remember that only Presidents or Treasurers are allowed to sign off payments. You may need to forward + this + email on. + {% endif %} +

    + + + + + + +
    + + + + +
    + + Complete Authorisation Form + +
    +
    + + +

    Your event will not be booked until you complete this form.

    + +

    TEC PA & Lighting
    + +{% endblock %} diff --git a/RIGS/templates/RIGS/eventauthorisation_client_request.txt b/RIGS/templates/RIGS/eventauthorisation_client_request.txt new file mode 100644 index 00000000..7b1297b1 --- /dev/null +++ b/RIGS/templates/RIGS/eventauthorisation_client_request.txt @@ -0,0 +1,16 @@ +Hi {{ to_name|default:"there" }}, + +{{ request.user.get_full_name }} has requested that you authorise N{{ object.pk|stringformat:"05d" }}| {{ object.name }}{% if not to_name %} on behalf of {{ object.person.name }}{% endif %}. + + Please find the link below to complete the event booking process. + {% if object.event.organisation and object.event.organisation.union_account %}{# internal #} + Remember that only Presidents or Treasurers are allowed to sign off payments. You may need to forward + this + email on. + {% endif %} + +{{ request.scheme }}://{{ request.get_host }}{% url 'event_authorise' object.pk hmac %} + +Please note you event will not be booked until you complete this form. + +TEC PA & Lighting diff --git a/RIGS/templates/RIGS/eventauthorisation_client_success.html b/RIGS/templates/RIGS/eventauthorisation_client_success.html new file mode 100644 index 00000000..398dee80 --- /dev/null +++ b/RIGS/templates/RIGS/eventauthorisation_client_success.html @@ -0,0 +1,21 @@ +{% extends 'base_client_email.html' %} + +{% block content %} +

    Hi {{ to_name|default:"there" }},

    + +

    + Your event N{{ object.event.pk|stringformat:"05d" }} has been successfully authorised + for £{{ object.amount }} + by {{ object.name }} as of {{ object.event.last_edited_at }}. +

    + +

    + {% if object.event.organisation and object.event.organisation.union_account %}{# internal #} + Your event is now fully booked and payment will be processed by the finance department automatically. + {% else %}{# external #} + Your event is now fully booked and our finance department will be contact to arrange payment. + {% endif %} +

    + +

    TEC PA & Lighting

    +{% endblock %} diff --git a/RIGS/templates/RIGS/eventauthorisation_client_success.txt b/RIGS/templates/RIGS/eventauthorisation_client_success.txt new file mode 100644 index 00000000..c511fc72 --- /dev/null +++ b/RIGS/templates/RIGS/eventauthorisation_client_success.txt @@ -0,0 +1,11 @@ +Hi {{ to_name|default_if_none:"there" }}, + +Your event N{{object.event.pk|stringformat:"05d"}} has been successfully authorised for £{{object.amount}} by {{object.name}} as of {{object.event.last_edited_at}}. + +{% if object.event.organisation and object.event.organisation.union_account %}{# internal #} +Your event is now fully booked and payment will be processed by the finance department automatically. +{% else %}{# external #} +Your event is now fully booked and our finance department will be contact to arrange payment. +{% endif %} + +TEC PA & Lighting diff --git a/RIGS/templates/RIGS/eventauthorisation_form.html b/RIGS/templates/RIGS/eventauthorisation_form.html new file mode 100644 index 00000000..e2ae78ce --- /dev/null +++ b/RIGS/templates/RIGS/eventauthorisation_form.html @@ -0,0 +1,133 @@ +{% extends 'base_client.html' %} +{% load widget_tweaks %} +{% load static %} + +{% block js %} + + +{% endblock %} + +{% block title %} + {% if event.is_rig %}N{{ event.pk|stringformat:"05d" }}{% else %}{{ event.pk }}{% endif %} | {{ event.name }} +{% endblock %} + +{% block content %} +
    +
    +

    + {% if event.is_rig %}N{{ event.pk|stringformat:"05d" }}{% else %}{{ event.pk }}{% endif %} + | {{ event.name }} {% if event.dry_hire %}Dry Hire{% endif %} +

    +
    +
    + +
    + {% include 'RIGS/client_eventdetails.html' %} +
    + +
    +
    + {% with object=event %} + {% include 'RIGS/item_table.html' %} + {% endwith %} +
    +
    + +
    +
    +
    +
    Event Authorisation
    + +
    +
    + {% csrf_token %} + {% include 'form_errors.html' %} +
    +
    +

    + I agree that I am authorised to approve this event. I agree that I am the + President/Treasurer or account holder of the hirer, or that I + have the written permission of the + President/Treasurer or account holder of the hirer stating that + I can authorise this event. +

    +
    + +
    +
    + + +
    + {% render_field form.name class+="form-control" %} +
    +
    + +
    + +
    + {% render_field form.uni_id class+="form-control" %} +
    +
    +
    + +
    +
    + +
    + {% render_field form.account_code class+="form-control" %} +
    +
    + +
    + +
    + {% render_field form.amount class+="form-control" %} +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +{% endblock %} diff --git a/RIGS/templates/RIGS/eventauthorisation_mic_success.txt b/RIGS/templates/RIGS/eventauthorisation_mic_success.txt new file mode 100644 index 00000000..43ddd5e1 --- /dev/null +++ b/RIGS/templates/RIGS/eventauthorisation_mic_success.txt @@ -0,0 +1,5 @@ +Hi {{object.event.mic.get_full_name|default_if_none:"somebody"}}, + +Just to let you know your event N{{object.event.pk|stringformat:"05d"}} has been successfully authorised for £{{object.amount}} by {{object.name}} as of {{object.event.last_edited_at}}. + +The TEC Rig Information Gathering System diff --git a/RIGS/templates/RIGS/eventauthorisation_request.html b/RIGS/templates/RIGS/eventauthorisation_request.html new file mode 100644 index 00000000..dbb617c3 --- /dev/null +++ b/RIGS/templates/RIGS/eventauthorisation_request.html @@ -0,0 +1,59 @@ +{% extends request.is_ajax|yesno:'base_ajax.html,base_rigs.html' %} +{% load widget_tweaks %} + +{% block title %}Request Authorisation{% endblock %} + +{% block content %} +
    +
    +
    +

    Send authorisation request email.

    +

    Pressing send will email the address provided. Please triple check everything before continuing.

    +
    + +
    +
    +
    Person Email
    +
    {{ object.person.email }}
    + +
    Organisation Email
    +
    {{ object.organisation.email }}
    +
    +
    +
    +
    +
    + {% csrf_token %} + +
    + {% include 'form_errors.html' %} + +
    + + +
    + {% render_field form.email type="email" class+="form-control" %} +
    +
    + +
    +
    + +
    +
    +
    +
    +
    +
    + + +{% endblock %} diff --git a/RIGS/templates/RIGS/eventauthorisation_request_error.html b/RIGS/templates/RIGS/eventauthorisation_request_error.html new file mode 100644 index 00000000..aaebe4c0 --- /dev/null +++ b/RIGS/templates/RIGS/eventauthorisation_request_error.html @@ -0,0 +1,15 @@ +{% extends request.is_ajax|yesno:'base_ajax.html,base_rigs.html' %} +{% load widget_tweaks %} + +{% block title %}NottinghamTEC Email Address Required{% endblock %} + +{% block content %} +
    +
    +
    +

    An error occured.

    +

    Your RIGS account must have an @nottinghamtec.co.uk email address before you can send emails to clients.

    +
    +
    +
    +{% endblock %} diff --git a/RIGS/templates/RIGS/eventauthorisation_success.html b/RIGS/templates/RIGS/eventauthorisation_success.html new file mode 100644 index 00000000..cb24738a --- /dev/null +++ b/RIGS/templates/RIGS/eventauthorisation_success.html @@ -0,0 +1,64 @@ +{% extends 'base_client.html' %} +{% load widget_tweaks %} + +{% block title %} + {% if event.is_rig %}N{{ event.pk|stringformat:"05d" }}{% else %}{{ event.pk }}{% endif %} | {{ event.name }} +{% endblock %} + +{% block content %} +
    +
    +

    + {% if event.is_rig %}N{{ event.pk|stringformat:"05d" }}{% else %}{{ event.pk }}{% endif %} + | {{ event.name }} {% if event.dry_hire %}Dry Hire{% endif %} +

    +
    +
    + + {% include 'RIGS/client_eventdetails.html' %} + +
    +
    + {% with object=event %} + {% include 'RIGS/item_table.html' %} + {% endwith %} +
    +
    + +
    +
    +
    +
    Event Authorisation
    + +
    +
    +
    +
    +
    Name
    +
    {{ object.name }}
    + +
    Email
    +
    {{ object.email }}
    + + {% if internal %} +
    University ID
    +
    {{ object.uni_id }}
    + {% endif %} +
    +
    + +
    +
    +
    Account code
    +
    {{ object.account_code }}
    + +
    Authorised amount
    +
    £ {{ object.amount|floatformat:2 }}
    +
    +
    +
    +
    +
    +
    +
    +{% endblock %} diff --git a/RIGS/templates/RIGS/index.html b/RIGS/templates/RIGS/index.html index 6e46eb10..f3b4d1a7 100644 --- a/RIGS/templates/RIGS/index.html +++ b/RIGS/templates/RIGS/index.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'base_rigs.html' %} {% block title %}RIGS{% endblock %} {% block content %} @@ -20,12 +20,15 @@ Rigboard Calendar {% if perms.RIGS.add_event %} New Event{% endif %} + Asset Database
    - TEC Forum + TEC Forum TEC Wiki + Pre-Event Risk Assessment Price List + Subhire Insurance Form @@ -76,10 +79,9 @@ {% if perms.RIGS.view_event %} -
    +
    {% include 'RIGS/activity_feed.html' %} -
    {% endif %}
    -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/RIGS/templates/RIGS/invoice_confirm_delete.html b/RIGS/templates/RIGS/invoice_confirm_delete.html new file mode 100644 index 00000000..4b575cb5 --- /dev/null +++ b/RIGS/templates/RIGS/invoice_confirm_delete.html @@ -0,0 +1,35 @@ +{% extends 'base_rigs.html' %} + +{% block title %}Delete payment on invoice {{ object.invoice.pk }}{% endblock %} + +{% block content %} +
    + +
    +{% endblock %} \ No newline at end of file diff --git a/RIGS/templates/RIGS/invoice_detail.html b/RIGS/templates/RIGS/invoice_detail.html index af26ad21..2e0211da 100644 --- a/RIGS/templates/RIGS/invoice_detail.html +++ b/RIGS/templates/RIGS/invoice_detail.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'base_rigs.html' %} {% block title %}Invoice {{ object.pk }}{% endblock %} @@ -11,11 +11,15 @@
    @@ -72,8 +76,43 @@
    {{ object.checked_in_by.name }}
    {% endif %} -
    PO
    -
    {{ object.event.purchase_order }}
    +
     
    + +
    Authorised
    +
    {{ object.event.authorised|yesno:"Yes,No" }}
    + +
    Authorised by
    +
    + {% if object.event.authorised %} + {{ object.event.authorisation.name }} + ({{ object.event.authorisation.email }}) + {% endif %} +
    + + {% if object.event.internal %} + {# internal #} +
    Uni ID
    +
    {{ object.event.authorisation.uni_id }}
    + +
    Account code
    +
    {{ object.event.authorisation.account_code }}
    + {% else %} +
    PO
    +
    {{ object.event.purchase_order }}
    + {% endif %} + +
    Authorised at
    +
    {{ object.event.authorisation.last_edited_at }}
    + +
    Authorised amount
    +
    + {% if object.event.authorised %} + £ {{ object.event.authorisation.amount|floatformat:"2" }} + {% endif %} +
    + +
    Authorsation request sent by
    +
    {{ object.authorisation.sent_by }}
    @@ -135,4 +174,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/RIGS/templates/RIGS/invoice_list.html b/RIGS/templates/RIGS/invoice_list.html index c31023cd..c5d3dcaf 100644 --- a/RIGS/templates/RIGS/invoice_list.html +++ b/RIGS/templates/RIGS/invoice_list.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'base_rigs.html' %} {% load paginator from filters %} {% block title %}Invoices{% endblock %} @@ -51,7 +51,7 @@ {% if object.event.organisation %} {{ object.event.organisation.name }}
    - {{ object.event.organisation.union_account|yesno:'Internal,External' }} + {{ object.event.internal|yesno:'Internal,External' }} {% else %} {{ object.event.person.name }}
    @@ -60,7 +60,11 @@ {{ object.event.start_date }} {{ object.invoice_date }} - {{ object.balance|floatformat:2 }} + + {{ object.balance|floatformat:2 }} +
    + {{ object.event.purchase_order }} + @@ -77,4 +81,4 @@ {% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/RIGS/templates/RIGS/object_button.html b/RIGS/templates/RIGS/object_button.html index 92d4ed5a..be907795 100644 --- a/RIGS/templates/RIGS/object_button.html +++ b/RIGS/templates/RIGS/object_button.html @@ -1,4 +1,4 @@ {% load to_class_name from filters %} {# pass in variable "object" to this template #} -{% if object.is_rig == False %}Non-rig{% elif object.dry_hire %}Dry Hire{% elif object.is_rig %}Rig{%else%}{{object|to_class_name}}{% endif %} | '{{object.name}}' \ No newline at end of file +{% if object.is_rig == False %}Non-rig{% elif object.dry_hire %}Dry Hire{% elif object.is_rig %}Rig{%else%}{{object|to_class_name}}{% endif %} | '{{ object.activity_feed_string|default:object.name }}' \ No newline at end of file diff --git a/RIGS/templates/RIGS/organisation_detail.html b/RIGS/templates/RIGS/organisation_detail.html index 7743086c..b12b6391 100644 --- a/RIGS/templates/RIGS/organisation_detail.html +++ b/RIGS/templates/RIGS/organisation_detail.html @@ -1,4 +1,4 @@ -{% extends request.is_ajax|yesno:"base_ajax.html,base.html" %} +{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %} {% load widget_tweaks %} {% block title %}Organisation | {{ object.name }}{% endblock %} diff --git a/RIGS/templates/RIGS/organisation_form.html b/RIGS/templates/RIGS/organisation_form.html index 7ef77e0d..f95e31d7 100644 --- a/RIGS/templates/RIGS/organisation_form.html +++ b/RIGS/templates/RIGS/organisation_form.html @@ -1,4 +1,4 @@ -{% extends request.is_ajax|yesno:'base_ajax.html,base.html' %} +{% extends request.is_ajax|yesno:'base_ajax.html,base_rigs.html' %} {% load widget_tweaks %} {% block title %}{% if object.pk %}Edit {{ object.name }}{% else %}Add Organisation{% endif %}{% endblock %} diff --git a/RIGS/templates/RIGS/organisation_list.html b/RIGS/templates/RIGS/organisation_list.html index c8856886..080ca938 100644 --- a/RIGS/templates/RIGS/organisation_list.html +++ b/RIGS/templates/RIGS/organisation_list.html @@ -1,4 +1,4 @@ -{% extends request.is_ajax|yesno:"base_ajax.html,base.html" %} +{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %} {% load widget_tweaks %} {% load paginator from filters %} {% load url_replace from filters %} diff --git a/RIGS/templates/RIGS/password_reset_disable.html b/RIGS/templates/RIGS/password_reset_disable.html new file mode 100644 index 00000000..eec6e791 --- /dev/null +++ b/RIGS/templates/RIGS/password_reset_disable.html @@ -0,0 +1,9 @@ +{% extends 'base_rigs.html' %} + +{% block title %}Password Reset Disabled{% endblock %} + +{% block content %} +

    Password reset is disabled

    +

    We are very sorry for the inconvenience, but due to a security vulnerability, password reset is currently disabled until the vulnerability can be patched.

    +

    If you are locked out of your account, please contact an administrator and we can manually perform a reset

    +{% endblock %} \ No newline at end of file diff --git a/RIGS/templates/RIGS/payment_confirm_delete.html b/RIGS/templates/RIGS/payment_confirm_delete.html index daa5d004..ab58e243 100644 --- a/RIGS/templates/RIGS/payment_confirm_delete.html +++ b/RIGS/templates/RIGS/payment_confirm_delete.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'base_rigs.html' %} {% block title %}Delete payment on invoice {{ object.invoice.pk }}{% endblock %} diff --git a/RIGS/templates/RIGS/payment_form.html b/RIGS/templates/RIGS/payment_form.html index 41bbf15b..13f4a694 100644 --- a/RIGS/templates/RIGS/payment_form.html +++ b/RIGS/templates/RIGS/payment_form.html @@ -16,7 +16,7 @@ for="{{ form.date.id_for_label }}">{{ form.date.label }}
    - {% render_field form.date type="date" class+="form-control" %} + {% render_field form.date class+="form-control" %}
    diff --git a/RIGS/templates/RIGS/person_detail.html b/RIGS/templates/RIGS/person_detail.html index 10c995ae..e8a4f3ad 100644 --- a/RIGS/templates/RIGS/person_detail.html +++ b/RIGS/templates/RIGS/person_detail.html @@ -1,4 +1,4 @@ -{% extends request.is_ajax|yesno:"base_ajax.html,base.html" %} +{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %} {% load widget_tweaks %} {% block title %}Person | {{ object.name }}{% endblock %} diff --git a/RIGS/templates/RIGS/person_form.html b/RIGS/templates/RIGS/person_form.html index a5720b9f..3ebf6409 100644 --- a/RIGS/templates/RIGS/person_form.html +++ b/RIGS/templates/RIGS/person_form.html @@ -1,4 +1,4 @@ -{% extends request.is_ajax|yesno:'base_ajax.html,base.html' %} +{% extends request.is_ajax|yesno:'base_ajax.html,base_rigs.html' %} {% load widget_tweaks %} {% block title %}{% if object.pk %}Edit {{ object.name }}{% else %}Add Person{% endif %}{% endblock %} diff --git a/RIGS/templates/RIGS/person_list.html b/RIGS/templates/RIGS/person_list.html index 2cbdff8e..02b9eb2c 100644 --- a/RIGS/templates/RIGS/person_list.html +++ b/RIGS/templates/RIGS/person_list.html @@ -1,4 +1,4 @@ -{% extends request.is_ajax|yesno:"base_ajax.html,base.html" %} +{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %} {% load widget_tweaks %} {% load paginator from filters %} {% load url_replace from filters %} diff --git a/RIGS/templates/RIGS/profile_detail.html b/RIGS/templates/RIGS/profile_detail.html index 11904b18..d0a86444 100644 --- a/RIGS/templates/RIGS/profile_detail.html +++ b/RIGS/templates/RIGS/profile_detail.html @@ -1,4 +1,4 @@ -{% extends request.is_ajax|yesno:"base_ajax.html,base.html" %} +{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %} {% block title %}RIGS Profile {{object.pk}}{% endblock %} diff --git a/RIGS/templates/RIGS/profile_form.html b/RIGS/templates/RIGS/profile_form.html index baa33424..b8426875 100644 --- a/RIGS/templates/RIGS/profile_form.html +++ b/RIGS/templates/RIGS/profile_form.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'base_rigs.html' %} {% load widget_tweaks %} {% block title %}Update Profile {{object.name}}{% endblock %} diff --git a/RIGS/templates/RIGS/rigboard.html b/RIGS/templates/RIGS/rigboard.html index 9292e304..786f9385 100644 --- a/RIGS/templates/RIGS/rigboard.html +++ b/RIGS/templates/RIGS/rigboard.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'base_rigs.html' %} {% block title %}Rigboard{% endblock %} diff --git a/RIGS/templates/RIGS/venue_detail.html b/RIGS/templates/RIGS/venue_detail.html index ede9d4ec..25d3daef 100644 --- a/RIGS/templates/RIGS/venue_detail.html +++ b/RIGS/templates/RIGS/venue_detail.html @@ -1,4 +1,4 @@ -{% extends request.is_ajax|yesno:"base_ajax.html,base.html" %} +{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %} {% load widget_tweaks %} {% block title %}Venue | {{ object.name }}{% endblock %} diff --git a/RIGS/templates/RIGS/venue_form.html b/RIGS/templates/RIGS/venue_form.html index 4d99af7e..b410a062 100644 --- a/RIGS/templates/RIGS/venue_form.html +++ b/RIGS/templates/RIGS/venue_form.html @@ -1,4 +1,4 @@ -{% extends request.is_ajax|yesno:'base_ajax.html,base.html' %} +{% extends request.is_ajax|yesno:'base_ajax.html,base_rigs.html' %} {% load widget_tweaks %} {% block title %}{{ object.pk|yesno:"Edit,Add" }} Venue{% endblock %} diff --git a/RIGS/templates/RIGS/venue_list.html b/RIGS/templates/RIGS/venue_list.html index 88ae61ac..80c9d8ac 100644 --- a/RIGS/templates/RIGS/venue_list.html +++ b/RIGS/templates/RIGS/venue_list.html @@ -1,4 +1,4 @@ -{% extends request.is_ajax|yesno:"base_ajax.html,base.html" %} +{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %} {% load widget_tweaks %} {% load paginator from filters %} {% load url_replace from filters %} diff --git a/RIGS/templates/RIGS/version_changes.html b/RIGS/templates/RIGS/version_changes.html index ca4e1569..3c1647bf 100644 --- a/RIGS/templates/RIGS/version_changes.html +++ b/RIGS/templates/RIGS/version_changes.html @@ -1,21 +1,26 @@ -{% for change in version.field_changes %} +{% if version.changes.item_changes or version.changes.field_changes or version.changes.old == None %} - + {% for change in version.changes.field_changes %} -{% endfor %} + -{% for itemChange in version.item_changes %} - -{% endfor %} \ No newline at end of file + {% endfor %} + + {% for itemChange in version.changes.item_changes %} + + {% endfor %} +{% else %} + nothing useful +{% endif %} diff --git a/RIGS/templates/RIGS/version_changes_change.html b/RIGS/templates/RIGS/version_changes_change.html index fe634f16..08ec2c83 100644 --- a/RIGS/templates/RIGS/version_changes_change.html +++ b/RIGS/templates/RIGS/version_changes_change.html @@ -2,11 +2,11 @@ {% if change.linebreaks and change.new and change.old %} {% for diff in change.diff %} {% if diff.type == "insert" %} - {{ diff.text|linebreaksbr }} + {{ diff.text|linebreaksbr }} {% elif diff.type == "delete" %} - {{diff.text|linebreaksbr}} + {{diff.text|linebreaksbr}} {% else %} - {{diff.text|linebreaksbr}} + {{diff.text|linebreaksbr}} {% endif %} {% endfor %} {% else %} diff --git a/RIGS/templates/RIGS/version_history.html b/RIGS/templates/RIGS/version_history.html index 3261c5df..676d231e 100644 --- a/RIGS/templates/RIGS/version_history.html +++ b/RIGS/templates/RIGS/version_history.html @@ -1,4 +1,4 @@ -{% extends request.is_ajax|yesno:"base_ajax.html,base.html" %} +{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %} {% load to_class_name from filters %} {% load paginator from filters %} {% load static %} @@ -40,13 +40,13 @@ {% for version in object_list %} - {% if version.item_changes or version.field_changes or version.old == None %} + {{ version.revision.date_created }} - {{ version.version.pk }}|{{ version.revision.pk }} + {{ version.pk }}|{{ version.revision.pk }} {{ version.revision.user.name }} - {% if version.old == None %} + {% if version.changes.old is None %} {{object|to_class_name}} Created {% else %} {% include 'RIGS/version_changes.html' %} @@ -56,7 +56,8 @@ {{ version.revision.comment }} - {% endif %} + + {% endfor %} diff --git a/RIGS/templatetags/filters.py b/RIGS/templatetags/filters.py index b14f5798..a90ecd64 100644 --- a/RIGS/templatetags/filters.py +++ b/RIGS/templatetags/filters.py @@ -5,19 +5,22 @@ from django.forms.utils import ErrorDict register = template.Library() + @register.filter def multiply(value, arg): - return value*arg + return value * arg + @register.filter def to_class_name(value): return value.__class__.__name__ - + + @register.filter def nice_errors(form, non_field_msg='General form errors'): nice_errors = ErrorDict() if isinstance(form, forms.BaseForm): - for field, errors in form.errors.items(): + for field, errors in list(form.errors.items()): if field == NON_FIELD_ERRORS: key = non_field_msg else: @@ -25,6 +28,7 @@ def nice_errors(form, non_field_msg='General form errors'): nice_errors[key] = errors return nice_errors + def paginator(context, adjacent_pages=3): """ To be used in conjunction with the object_list generic view. @@ -37,11 +41,13 @@ def paginator(context, adjacent_pages=3): page = context['page_obj'] paginator = context['paginator'] startPage = max(page.number - adjacent_pages, 1) - if startPage <= 3: startPage = 1 + if startPage <= 3: + startPage = 1 endPage = page.number + adjacent_pages + 1 - if endPage >= paginator.num_pages - 1: endPage = paginator.num_pages + 1 - page_numbers = [n for n in range(startPage, endPage) \ - if n > 0 and n <= paginator.num_pages] + if endPage >= paginator.num_pages - 1: + endPage = paginator.num_pages + 1 + page_numbers = [n for n in range(startPage, endPage) + if n > 0 and n <= paginator.num_pages] dict = { 'request': context['request'], @@ -57,15 +63,18 @@ def paginator(context, adjacent_pages=3): 'has_next': page.has_next(), 'has_previous': page.has_previous(), } - + if page.has_next(): dict['next'] = page.next_page_number() if page.has_previous(): dict['previous'] = page.previous_page_number() return dict + + register.inclusion_tag('pagination.html', takes_context=True)(paginator) + @register.simple_tag def url_replace(request, field, value): @@ -75,6 +84,7 @@ def url_replace(request, field, value): return dict_.urlencode() + @register.simple_tag def orderby(request, field, attr): @@ -88,4 +98,4 @@ def orderby(request, field, attr): else: dict_[field] = attr - return dict_.urlencode() \ No newline at end of file + return dict_.urlencode() diff --git a/RIGS/test_functional.py b/RIGS/test_functional.py index 154fdfb2..8ea2481b 100644 --- a/RIGS/test_functional.py +++ b/RIGS/test_functional.py @@ -1,25 +1,41 @@ # -*- coding: utf-8 -*- -from django.test import LiveServerTestCase -from django.test.client import Client -from django.core import mail -from selenium import webdriver -from selenium.webdriver.common.keys import Keys -from selenium.common.exceptions import StaleElementReferenceException, WebDriverException -from selenium.webdriver.support.ui import WebDriverWait -from RIGS import models -import re import os -from datetime import date, timedelta +import re +from datetime import date, time, datetime, timedelta + +import pytz +from django.conf import settings +from django.core import mail, signing from django.db import transaction -import reversion -import json +from django.http import HttpResponseBadRequest +from django.test import LiveServerTestCase, TestCase +from django.test.client import Client +from django.urls import reverse +from reversion import revisions as reversion +from selenium import webdriver +from selenium.common.exceptions import StaleElementReferenceException +from selenium.webdriver.support import expected_conditions +from selenium.webdriver.common.keys import Keys +from selenium.webdriver.support.ui import WebDriverWait + +from RIGS import models + + +def create_browser(): + options = webdriver.ChromeOptions() + options.add_argument("--window-size=1920,1080") + if os.environ.get('CI', False): + options.add_argument("--headless") + options.add_argument("--no-sandbox") + driver = webdriver.Chrome(chrome_options=options) + return driver class UserRegistrationTest(LiveServerTestCase): - def setUp(self): - self.browser = webdriver.Firefox() - self.browser.implicitly_wait(3) # Set implicit wait session wide + self.browser = create_browser() + + self.browser.implicitly_wait(3) # Set implicit wait session wide os.environ['RECAPTCHA_TESTING'] = 'True' def tearDown(self): @@ -52,8 +68,9 @@ class UserRegistrationTest(LiveServerTestCase): self.assertEqual(last_name.get_attribute('placeholder'), 'Last name') initials = self.browser.find_element_by_id('id_initials') self.assertEqual(initials.get_attribute('placeholder'), 'Initials') - phone = self.browser.find_element_by_id('id_phone') - self.assertEqual(phone.get_attribute('placeholder'), 'Phone') + # No longer required for new users + # phone = self.browser.find_element_by_id('id_phone') + # self.assertEqual(phone.get_attribute('placeholder'), 'Phone') # Fill the form out incorrectly username.send_keys('TestUsername') @@ -64,9 +81,9 @@ class UserRegistrationTest(LiveServerTestCase): first_name.send_keys('John') last_name.send_keys('Smith') initials.send_keys('JS') - phone.send_keys('0123456789') + # phone.send_keys('0123456789') self.browser.execute_script( - "return jQuery('#g-recaptcha-response').val('PASSED')") + "return function() {jQuery('#g-recaptcha-response').val('PASSED'); return 0}()") # Submit incorrect form submit = self.browser.find_element_by_xpath("//input[@type='submit']") @@ -88,8 +105,9 @@ class UserRegistrationTest(LiveServerTestCase): # Correct error password1.send_keys('correcthorsebatterystaple') password2.send_keys('correcthorsebatterystaple') + self.browser.execute_script("console.log('Hello, world!')") self.browser.execute_script( - "return jQuery('#g-recaptcha-response').val('PASSED')") + "return function() {jQuery('#g-recaptcha-response').val('PASSED'); return 0}()") # Submit again password2.send_keys(Keys.ENTER) @@ -103,7 +121,7 @@ class UserRegistrationTest(LiveServerTestCase): # Check Email self.assertEqual(len(mail.outbox), 1) email = mail.outbox[0] - self.assertIn('activation required', email.subject) + self.assertIn('John Smith "JS" activation required', email.subject) urls = re.findall( 'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', email.body) self.assertEqual(len(urls), 1) @@ -128,7 +146,7 @@ class UserRegistrationTest(LiveServerTestCase): username.send_keys('TestUsername') password.send_keys('correcthorsebatterystaple') self.browser.execute_script( - "return jQuery('#g-recaptcha-response').val('PASSED')") + "return function() {jQuery('#g-recaptcha-response').val('PASSED'); return 0}()") password.send_keys(Keys.ENTER) # Check we are logged in @@ -141,25 +159,25 @@ class UserRegistrationTest(LiveServerTestCase): self.assertEqual(profileObject.first_name, 'John') self.assertEqual(profileObject.last_name, 'Smith') self.assertEqual(profileObject.initials, 'JS') - self.assertEqual(profileObject.phone, '0123456789') + # self.assertEqual(profileObject.phone, '0123456789') self.assertEqual(profileObject.email, 'test@example.com') # All is well class EventTest(LiveServerTestCase): - def setUp(self): self.profile = models.Profile( username="EventTest", first_name="Event", last_name="Test", initials="ETU", is_superuser=True) self.profile.set_password("EventTestPassword") self.profile.save() - self.vatrate = models.VatRate.objects.create(start_at='2014-03-05',rate=0.20,comment='test1') - - self.browser = webdriver.Firefox() - self.browser.implicitly_wait(3) # Set implicit wait session wide - self.browser.maximize_window() + self.vatrate = models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1') + + self.browser = create_browser() + self.browser.implicitly_wait(10) # Set implicit wait session wide + # self.browser.maximize_window() + os.environ['RECAPTCHA_TESTING'] = 'True' def tearDown(self): @@ -196,318 +214,329 @@ class EventTest(LiveServerTestCase): self.browser.get(self.live_server_url + '/rigboard/') def testRigCreate(self): - try: - # Requests address - self.browser.get(self.live_server_url + '/event/create/') - # Gets redirected to login and back - self.authenticate('/event/create/') + # Requests address + self.browser.get(self.live_server_url + '/event/create/') + # Gets redirected to login and back + self.authenticate('/event/create/') - wait = WebDriverWait(self.browser, 10) #setup WebDriverWait to use later (to wait for animations) + wait = WebDriverWait(self.browser, 3) # setup WebDriverWait to use later (to wait for animations) - wait.until(animation_is_finished()) - - # Check has slided up correctly - second save button hidden - save = self.browser.find_element_by_xpath( - '(//button[@type="submit"])[3]') - self.assertFalse(save.is_displayed()) - - # Click Rig button - self.browser.find_element_by_xpath('//button[.="Rig"]').click() - - # Slider expands and save button visible - self.assertTrue(save.is_displayed()) - form = self.browser.find_element_by_tag_name('form') - - # Create new person - wait.until(animation_is_finished()) - add_person_button = self.browser.find_element_by_xpath( - '//a[@data-target="#id_person" and contains(@href, "add")]') - add_person_button.click() - - # See modal has opened - modal = self.browser.find_element_by_id('modal') - wait.until(animation_is_finished()) - self.assertTrue(modal.is_displayed()) - self.assertIn("Add Person", modal.find_element_by_tag_name('h3').text) - - # Fill person form out and submit - modal.find_element_by_xpath( - '//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Person 1") - modal.find_element_by_xpath( - '//div[@id="modal"]//input[@type="submit"]').click() - wait.until(animation_is_finished()) - self.assertFalse(modal.is_displayed()) - - # See new person selected - person1 = models.Person.objects.get(name="Test Person 1") - self.assertEqual(person1.name, form.find_element_by_xpath( - '//button[@data-id="id_person"]/span').text) - # and backend - option = form.find_element_by_xpath( - '//select[@id="id_person"]//option[@selected="selected"]') - self.assertEqual(person1.pk, int(option.get_attribute("value"))) - - # Change mind and add another - wait.until(animation_is_finished()) - add_person_button.click() - - wait.until(animation_is_finished()) - self.assertTrue(modal.is_displayed()) - self.assertIn("Add Person", modal.find_element_by_tag_name('h3').text) - - modal.find_element_by_xpath( - '//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Person 2") - modal.find_element_by_xpath( - '//div[@id="modal"]//input[@type="submit"]').click() - wait.until(animation_is_finished()) - self.assertFalse(modal.is_displayed()) - - person2 = models.Person.objects.get(name="Test Person 2") - self.assertEqual(person2.name, form.find_element_by_xpath( - '//button[@data-id="id_person"]/span').text) - # Have to do this explcitly to force the wait for it to update - option = form.find_element_by_xpath( - '//select[@id="id_person"]//option[@selected="selected"]') - self.assertEqual(person2.pk, int(option.get_attribute("value"))) - - # Was right the first time, change it back - person_select = form.find_element_by_xpath( - '//button[@data-id="id_person"]') - person_select.send_keys(person1.name) - person_dropped = form.find_element_by_xpath( - '//ul[contains(@class, "inner selectpicker")]//span[contains(text(), "%s")]' % person1.name) - person_dropped.click() - - self.assertEqual(person1.name, form.find_element_by_xpath( - '//button[@data-id="id_person"]/span').text) - option = form.find_element_by_xpath( - '//select[@id="id_person"]//option[@selected="selected"]') - self.assertEqual(person1.pk, int(option.get_attribute("value"))) - - # Edit Person 1 to have a better name - form.find_element_by_xpath( - '//a[@data-target="#id_person" and contains(@href, "%s/edit/")]' % person1.pk).click() - wait.until(animation_is_finished()) - self.assertTrue(modal.is_displayed()) - self.assertIn("Edit Person", modal.find_element_by_tag_name('h3').text) - name = modal.find_element_by_xpath( - '//div[@id="modal"]//input[@id="id_name"]') - self.assertEqual(person1.name, name.get_attribute('value')) - name.clear() - name.send_keys('Rig ' + person1.name) - name.send_keys(Keys.ENTER) - - wait.until(animation_is_finished()) - - self.assertFalse(modal.is_displayed()) - person1 = models.Person.objects.get(pk=person1.pk) - self.assertEqual(person1.name, form.find_element_by_xpath( - '//button[@data-id="id_person"]/span').text) - - # Create organisation - wait.until(animation_is_finished()) - add_button = self.browser.find_element_by_xpath( - '//a[@data-target="#id_organisation" and contains(@href, "add")]') - add_button.click() - modal = self.browser.find_element_by_id('modal') - wait.until(animation_is_finished()) - self.assertTrue(modal.is_displayed()) - self.assertIn("Add Organisation", modal.find_element_by_tag_name('h3').text) - modal.find_element_by_xpath( - '//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Organisation") - modal.find_element_by_xpath( - '//div[@id="modal"]//input[@type="submit"]').click() - - # See it is selected - wait.until(animation_is_finished()) - self.assertFalse(modal.is_displayed()) - obj = models.Organisation.objects.get(name="Test Organisation") - self.assertEqual(obj.name, form.find_element_by_xpath( - '//button[@data-id="id_organisation"]/span').text) - # and backend - option = form.find_element_by_xpath( - '//select[@id="id_organisation"]//option[@selected="selected"]') - self.assertEqual(obj.pk, int(option.get_attribute("value"))) - - # Create venue - wait.until(animation_is_finished()) - add_button = self.browser.find_element_by_xpath( - '//a[@data-target="#id_venue" and contains(@href, "add")]') - wait.until(animation_is_finished()) - add_button.click() - wait.until(animation_is_finished()) - modal = self.browser.find_element_by_id('modal') - wait.until(animation_is_finished()) - self.assertTrue(modal.is_displayed()) - self.assertIn("Add Venue", modal.find_element_by_tag_name('h3').text) - modal.find_element_by_xpath( - '//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Venue") - modal.find_element_by_xpath( - '//div[@id="modal"]//input[@type="submit"]').click() - - # See it is selected - wait.until(animation_is_finished()) - self.assertFalse(modal.is_displayed()) - obj = models.Venue.objects.get(name="Test Venue") - self.assertEqual(obj.name, form.find_element_by_xpath( - '//button[@data-id="id_venue"]/span').text) - # and backend - option = form.find_element_by_xpath( - '//select[@id="id_venue"]//option[@selected="selected"]') - self.assertEqual(obj.pk, int(option.get_attribute("value"))) - - # Set start date/time - form.find_element_by_id('id_start_date').send_keys('3015-05-25') - form.find_element_by_id('id_start_time').send_keys('06:59') - - # Set end date/time - form.find_element_by_id('id_end_date').send_keys('4000-06-27') - form.find_element_by_id('id_end_time').send_keys('07:00') - - # Add item - form.find_element_by_xpath('//button[contains(@class, "item-add")]').click() - wait.until(animation_is_finished()) - modal = self.browser.find_element_by_id("itemModal") - modal.find_element_by_id("item_name").send_keys("Test Item 1") - modal.find_element_by_id("item_description").send_keys("This is an item description\nthat for reasons unkown spans two lines") - e = modal.find_element_by_id("item_quantity") - e.click() - e.send_keys(Keys.UP) - e.send_keys(Keys.UP) - e = modal.find_element_by_id("item_cost") - e.send_keys("23.95") - e.send_keys(Keys.ENTER) # enter submit - - # Confirm item has been saved to json field - objectitems = self.browser.execute_script("return objectitems;") - self.assertEqual(1, len(objectitems)) - testitem = objectitems["-1"]['fields'] # as we are deliberately creating this we know the ID - self.assertEqual("Test Item 1", testitem['name']) - self.assertEqual("2", testitem['quantity']) # test a couple of "worse case" fields - - # See new item appear in table - row = self.browser.find_element_by_id('item--1') # ID number is known, see above - self.assertIn("Test Item 1", row.find_element_by_xpath('//span[@class="name"]').text) - self.assertIn("This is an item description", row.find_element_by_xpath('//div[@class="item-description"]').text) - self.assertEqual(u'£ 23.95', row.find_element_by_xpath('//tr[@id="item--1"]/td[2]').text) - self.assertEqual("2", row.find_element_by_xpath('//td[@class="quantity"]').text) - self.assertEqual(u'£ 47.90', row.find_element_by_xpath('//tr[@id="item--1"]/td[4]').text) - - # Check totals - self.assertEqual("47.90", self.browser.find_element_by_id('sumtotal').text) - self.assertIn("(TBC)", self.browser.find_element_by_id('vat-rate').text) - self.assertEqual("9.58", self.browser.find_element_by_id('vat').text) - self.assertEqual("57.48", self.browser.find_element_by_id('total').text) - - # Attempt to save - missing title - save.click() - - # See error - error = self.browser.find_element_by_xpath('//div[contains(@class, "alert-danger")]') - self.assertTrue(error.is_displayed()) - # Should only have one error message - self.assertEqual("Name", error.find_element_by_xpath('//dt[1]').text) - self.assertEqual("This field is required.", error.find_element_by_xpath('//dd[1]/ul/li').text) - # don't need error so close it - error.find_element_by_xpath('//div[contains(@class, "alert-danger")]//button[@class="close"]').click() - try: - self.assertFalse(error.is_displayed()) - except StaleElementReferenceException: - pass - except: - self.assertFail("Element does not appear to have been deleted") - - # Check at least some data is preserved. Some = all will be there - option = self.browser.find_element_by_xpath( - '//select[@id="id_person"]//option[@selected="selected"]') - self.assertEqual(person1.pk, int(option.get_attribute("value"))) - - # Set title - e = self.browser.find_element_by_id('id_name') - e.send_keys('Test Event Name') - e.send_keys(Keys.ENTER) - - # See redirected to success page - successTitle = self.browser.find_element_by_xpath('//h1').text - event = models.Event.objects.get(name='Test Event Name') - self.assertIn("N0000%d | Test Event Name"%event.pk, successTitle) - except WebDriverException: - # This is a dirty workaround for wercker being a bit funny and not running it correctly. - # Waiting for wercker to get back to me about this - pass - - def testEventDuplicate(self): - testEvent = models.Event.objects.create(name="TE E1", status=models.Event.PROVISIONAL, start_date=date.today() + timedelta(days=6), description="start future no end") - - item1 = models.EventItem( - event=testEvent, - name="Test Item 1", - cost="10.00", - quantity="1", - order=1 - ).save() - item2 = models.EventItem( - event=testEvent, - name="Test Item 2", - description="Foo", - cost="9.72", - quantity="3", - order=2, - ).save() - - self.browser.get(self.live_server_url + '/event/' + str(testEvent.pk) + '/duplicate/') - self.authenticate('/event/' + str(testEvent.pk) + '/duplicate/') - - wait = WebDriverWait(self.browser, 10) #setup WebDriverWait to use later (to wait for animations) + wait.until(animation_is_finished()) + # Check has slided up correctly - second save button hidden save = self.browser.find_element_by_xpath( '(//button[@type="submit"])[3]') + self.assertFalse(save.is_displayed()) + + # Click Rig button + self.browser.find_element_by_xpath('//button[.="Rig"]').click() + + # Slider expands and save button visible + self.assertTrue(save.is_displayed()) form = self.browser.find_element_by_tag_name('form') + # For now, just check that HTML5 Client validation is in place TODO Test needs rewriting to properly test all levels of validation. + self.assertTrue(self.browser.find_element_by_id('id_name').get_attribute('required') is not None) - # Check the items are visible - table = self.browser.find_element_by_id('item-table') # ID number is known, see above - self.assertIn("Test Item 1", table.text) - self.assertIn("Test Item 2", table.text) + # Set title + e = self.browser.find_element_by_id('id_name') + e.send_keys('Test Event Name') + + # Create new person + wait.until(animation_is_finished()) + add_person_button = self.browser.find_element_by_xpath( + '//a[@data-target="#id_person" and contains(@href, "add")]') + add_person_button.click() + + # See modal has opened + modal = self.browser.find_element_by_id('modal') + wait.until(animation_is_finished()) + self.assertTrue(modal.is_displayed()) + self.assertIn("Add Person", modal.find_element_by_tag_name('h3').text) + + # Fill person form out and submit + modal.find_element_by_xpath( + '//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Person 1") + modal.find_element_by_xpath( + '//div[@id="modal"]//input[@type="submit"]').click() + wait.until(animation_is_finished()) + self.assertFalse(modal.is_displayed()) + + # See new person selected + person1 = models.Person.objects.get(name="Test Person 1") + self.assertEqual(person1.name, form.find_element_by_xpath( + '//button[@data-id="id_person"]/span').text) + # and backend + option = form.find_element_by_xpath( + '//select[@id="id_person"]//option[@selected="selected"]') + self.assertEqual(person1.pk, int(option.get_attribute("value"))) + + # Change mind and add another + wait.until(animation_is_finished()) + add_person_button.click() + + wait.until(animation_is_finished()) + self.assertTrue(modal.is_displayed()) + self.assertIn("Add Person", modal.find_element_by_tag_name('h3').text) + + modal.find_element_by_xpath( + '//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Person 2") + modal.find_element_by_xpath( + '//div[@id="modal"]//input[@type="submit"]').click() + wait.until(animation_is_finished()) + self.assertFalse(modal.is_displayed()) + + person2 = models.Person.objects.get(name="Test Person 2") + self.assertEqual(person2.name, form.find_element_by_xpath( + '//button[@data-id="id_person"]/span').text) + # Have to do this explcitly to force the wait for it to update + option = form.find_element_by_xpath( + '//select[@id="id_person"]//option[@selected="selected"]') + self.assertEqual(person2.pk, int(option.get_attribute("value"))) + + # Was right the first time, change it back + person_select = form.find_element_by_xpath( + '//button[@data-id="id_person"]') + person_select.send_keys(person1.name) + person_dropped = form.find_element_by_xpath( + '//ul[contains(@class, "dropdown-menu")]//span[contains(text(), "%s")]' % person1.name) + person_dropped.click() + + self.assertEqual(person1.name, form.find_element_by_xpath( + '//button[@data-id="id_person"]/span').text) + option = form.find_element_by_xpath( + '//select[@id="id_person"]//option[@selected="selected"]') + self.assertEqual(person1.pk, int(option.get_attribute("value"))) + + # Edit Person 1 to have a better name + form.find_element_by_xpath( + '//a[@data-target="#id_person" and contains(@href, "%s/edit/")]' % person1.pk).click() + wait.until(animation_is_finished()) + self.assertTrue(modal.is_displayed()) + self.assertIn("Edit Person", modal.find_element_by_tag_name('h3').text) + name = modal.find_element_by_xpath( + '//div[@id="modal"]//input[@id="id_name"]') + self.assertEqual(person1.name, name.get_attribute('value')) + name.clear() + name.send_keys('Rig ' + person1.name) + name.send_keys(Keys.ENTER) + + wait.until(animation_is_finished()) + + self.assertFalse(modal.is_displayed()) + person1 = models.Person.objects.get(pk=person1.pk) + self.assertEqual(person1.name, form.find_element_by_xpath( + '//button[@data-id="id_person"]/span').text) + + # Create organisation + wait.until(animation_is_finished()) + add_button = self.browser.find_element_by_xpath( + '//a[@data-target="#id_organisation" and contains(@href, "add")]') + add_button.click() + modal = self.browser.find_element_by_id('modal') + wait.until(animation_is_finished()) + self.assertTrue(modal.is_displayed()) + self.assertIn("Add Organisation", modal.find_element_by_tag_name('h3').text) + modal.find_element_by_xpath( + '//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Organisation") + modal.find_element_by_xpath( + '//div[@id="modal"]//input[@type="submit"]').click() + + # See it is selected + wait.until(animation_is_finished()) + self.assertFalse(modal.is_displayed()) + obj = models.Organisation.objects.get(name="Test Organisation") + self.assertEqual(obj.name, form.find_element_by_xpath( + '//button[@data-id="id_organisation"]/span').text) + # and backend + option = form.find_element_by_xpath( + '//select[@id="id_organisation"]//option[@selected="selected"]') + self.assertEqual(obj.pk, int(option.get_attribute("value"))) + + # Create venue + wait.until(animation_is_finished()) + add_button = self.browser.find_element_by_xpath( + '//a[@data-target="#id_venue" and contains(@href, "add")]') + wait.until(animation_is_finished()) + add_button.click() + wait.until(animation_is_finished()) + modal = self.browser.find_element_by_id('modal') + wait.until(animation_is_finished()) + self.assertTrue(modal.is_displayed()) + self.assertIn("Add Venue", modal.find_element_by_tag_name('h3').text) + modal.find_element_by_xpath( + '//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Venue") + modal.find_element_by_xpath( + '//div[@id="modal"]//input[@type="submit"]').click() + + # See it is selected + wait.until(animation_is_finished()) + self.assertFalse(modal.is_displayed()) + obj = models.Venue.objects.get(name="Test Venue") + self.assertEqual(obj.name, form.find_element_by_xpath( + '//button[@data-id="id_venue"]/span').text) + # and backend + option = form.find_element_by_xpath( + '//select[@id="id_venue"]//option[@selected="selected"]') + self.assertEqual(obj.pk, int(option.get_attribute("value"))) + + # Set start date/time + form.find_element_by_id('id_start_date').send_keys('25/05/3015') + form.find_element_by_id('id_start_time').send_keys('06:59') + + # Set end date/time + form.find_element_by_id('id_end_date').send_keys('27/06/4000') + form.find_element_by_id('id_end_time').send_keys('07:00') # Add item form.find_element_by_xpath('//button[contains(@class, "item-add")]').click() wait.until(animation_is_finished()) modal = self.browser.find_element_by_id("itemModal") - modal.find_element_by_id("item_name").send_keys("Test Item 3") - modal.find_element_by_id("item_description").send_keys("This is an item description\nthat for reasons unkown spans two lines") + modal.find_element_by_id("item_name").send_keys("Test Item 1") + modal.find_element_by_id("item_description").send_keys( + "This is an item description\nthat for reasons unknown spans two lines") e = modal.find_element_by_id("item_quantity") e.click() e.send_keys(Keys.UP) e.send_keys(Keys.UP) e = modal.find_element_by_id("item_cost") e.send_keys("23.95") - e.send_keys(Keys.ENTER) # enter submit + e.send_keys(Keys.ENTER) # enter submit + + # Confirm item has been saved to json field + objectitems = self.browser.execute_script("return objectitems;") + self.assertEqual(1, len(objectitems)) + testitem = objectitems["-1"]['fields'] # as we are deliberately creating this we know the ID + self.assertEqual("Test Item 1", testitem['name']) + self.assertEqual("2", testitem['quantity']) # test a couple of "worse case" fields + + # See new item appear in table + row = self.browser.find_element_by_id('item--1') # ID number is known, see above + self.assertIn("Test Item 1", row.find_element_by_xpath('//span[@class="name"]').text) + self.assertIn("This is an item description", + row.find_element_by_xpath('//div[@class="item-description"]').text) + self.assertEqual('£ 23.95', row.find_element_by_xpath('//tr[@id="item--1"]/td[2]').text) + self.assertEqual("2", row.find_element_by_xpath('//td[@class="quantity"]').text) + self.assertEqual('£ 47.90', row.find_element_by_xpath('//tr[@id="item--1"]/td[4]').text) + + # Check totals + self.assertEqual("47.90", self.browser.find_element_by_id('sumtotal').text) + self.assertIn("(TBC)", self.browser.find_element_by_id('vat-rate').text) + self.assertEqual("9.58", self.browser.find_element_by_id('vat').text) + self.assertEqual("57.48", self.browser.find_element_by_id('total').text) + + save = self.browser.find_element_by_xpath( + '(//button[@type="submit"])[3]') + save.click() + + # TODO Testing of requirement for contact details + + # TODO Something seems broken with the CI tests here. + # See redirected to success page + # successTitle = self.browser.find_element_by_xpath('//h1').text + # event = models.Event.objects.get(name='Test Event Name') + # self.assertIn("N%05d | Test Event Name" % event.pk, successTitle) + + def testEventDuplicate(self): + client = models.Person.objects.create(name='Duplicate Test Person', email='duplicate@functional.test') + testEvent = models.Event.objects.create(name="TE E1", status=models.Event.PROVISIONAL, + start_date=date.today() + timedelta(days=6), + description="start future no end", + purchase_order='TESTPO', + person=client, + auth_request_by=self.profile, + auth_request_at=self.create_datetime(2015, 0o6, 0o4, 10, 00), + auth_request_to="some@email.address") + + item1 = models.EventItem( + event=testEvent, + name="Test Item 1", + cost="10.00", + quantity="1", + order=1 + ).save() + item2 = models.EventItem( + event=testEvent, + name="Test Item 2", + description="Foo", + cost="9.72", + quantity="3", + order=2, + ).save() + + self.browser.get(self.live_server_url + '/event/' + str(testEvent.pk) + '/duplicate/') + self.authenticate('/event/' + str(testEvent.pk) + '/duplicate/') + + wait = WebDriverWait(self.browser, 3) # setup WebDriverWait to use later (to wait for animations) + + save = self.browser.find_element_by_xpath( + '(//button[@type="submit"])[3]') + form = self.browser.find_element_by_tag_name('form') + + # Check the items are visible + table = self.browser.find_element_by_id('item-table') # ID number is known, see above + self.assertIn("Test Item 1", table.text) + self.assertIn("Test Item 2", table.text) + + # Check the info message is visible + self.assertIn("Event data duplicated but not yet saved", self.browser.find_element_by_id('content').text) + + # Add item + form.find_element_by_xpath('//button[contains(@class, "item-add")]').click() + wait.until(animation_is_finished()) + modal = self.browser.find_element_by_id("itemModal") + modal.find_element_by_id("item_name").send_keys("Test Item 3") + modal.find_element_by_id("item_description").send_keys( + "This is an item description\nthat for reasons unknown spans two lines") + e = modal.find_element_by_id("item_quantity") + e.click() + e.send_keys(Keys.UP) + e.send_keys(Keys.UP) + e = modal.find_element_by_id("item_cost") + e.send_keys("23.95") + e.send_keys(Keys.ENTER) # enter submit # Attempt to save save.click() - self.assertNotIn("N0000%d"%testEvent.pk, self.browser.find_element_by_xpath('//h1').text) + newEvent = models.Event.objects.latest('pk') + + self.assertEqual(newEvent.auth_request_to, None) + self.assertEqual(newEvent.auth_request_by, None) + self.assertEqual(newEvent.auth_request_at, None) + + self.assertFalse(hasattr(newEvent, 'authorised')) + + self.assertNotIn("N%05d" % testEvent.pk, self.browser.find_element_by_xpath('//h1').text) + self.assertNotIn("Event data duplicated but not yet saved", self.browser.find_element_by_id('content').text) # Check info message not visible # Check the new items are visible - table = self.browser.find_element_by_id('item-table') # ID number is known, see above + table = self.browser.find_element_by_id('item-table') # ID number is known, see above self.assertIn("Test Item 1", table.text) self.assertIn("Test Item 2", table.text) self.assertIn("Test Item 3", table.text) infoPanel = self.browser.find_element_by_xpath('//div[contains(text(), "Event Info")]/..') - self.assertIn("N0000%d"%testEvent.pk, infoPanel.find_element_by_xpath('//dt[text()="Based On"]/following-sibling::dd[1]').text) + self.assertIn("N0000%d" % testEvent.pk, + infoPanel.find_element_by_xpath('//dt[text()="Based On"]/following-sibling::dd[1]').text) + # Check the PO hasn't carried through + self.assertNotIn("TESTPO", infoPanel.find_element_by_xpath('//dt[text()="PO"]/following-sibling::dd[1]').text) + self.assertIn("N%05d" % testEvent.pk, infoPanel.find_element_by_xpath('//dt[text()="Based On"]/following-sibling::dd[1]').text) + self.browser.get(self.live_server_url + '/event/' + str(testEvent.pk)) # Go back to the old event - self.browser.get(self.live_server_url + '/event/' + str(testEvent.pk)) #Go back to the old event - - #Check that based-on hasn't crept into the old event + # Check that based-on hasn't crept into the old event infoPanel = self.browser.find_element_by_xpath('//div[contains(text(), "Event Info")]/..') - self.assertNotIn("N0000%d"%testEvent.pk, infoPanel.find_element_by_xpath('//dt[text()="Based On"]/following-sibling::dd[1]').text) + self.assertNotIn("N0000%d" % testEvent.pk, + infoPanel.find_element_by_xpath('//dt[text()="Based On"]/following-sibling::dd[1]').text) + # Check the PO remains on the old event + self.assertIn("TESTPO", infoPanel.find_element_by_xpath('//dt[text()="PO"]/following-sibling::dd[1]').text) + + self.assertNotIn("N%05d" % testEvent.pk, infoPanel.find_element_by_xpath('//dt[text()="Based On"]/following-sibling::dd[1]').text) # Check the items are as they were - table = self.browser.find_element_by_id('item-table') # ID number is known, see above + table = self.browser.find_element_by_id('item-table') # ID number is known, see above self.assertIn("Test Item 1", table.text) self.assertIn("Test Item 2", table.text) self.assertNotIn("Test Item 3", table.text) @@ -517,7 +546,7 @@ class EventTest(LiveServerTestCase): # Gets redirected to login and back self.authenticate('/event/create/') - wait = WebDriverWait(self.browser, 10) #setup WebDriverWait to use later (to wait for animations) + wait = WebDriverWait(self.browser, 3) # setup WebDriverWait to use later (to wait for animations) wait.until(animation_is_finished()) @@ -531,33 +560,39 @@ class EventTest(LiveServerTestCase): e = self.browser.find_element_by_id('id_name') e.send_keys('Test Event Name') + # Set person + person = models.Person.objects.create(name='Date Validation Person', email='datevalidation@functional.test') + person_select = form.find_element_by_xpath( + '//button[@data-id="id_person"]') + person_select.send_keys(person.name) + person_dropped = form.find_element_by_xpath( + '//ul[contains(@class, "dropdown-menu")]//span[contains(text(), "%s")]' % person.name) + person_dropped.click() + # Both dates, no times, end before start - form.find_element_by_id('id_start_date').clear() - form.find_element_by_id('id_start_date').send_keys('3015-04-24') + self.browser.execute_script("document.getElementById('id_start_date').value='3015-04-24'") - form.find_element_by_id('id_end_date').clear() - form.find_element_by_id('id_end_date').send_keys('3015-04-23') + self.browser.execute_script("document.getElementById('id_end_date').value='3015-04-23'") # Attempt to save - should fail + wait.until(animation_is_finished()) save.click() + error = self.browser.find_element_by_xpath('//div[contains(@class, "alert-danger")]') self.assertTrue(error.is_displayed()) self.assertIn("can't finish before it has started", error.find_element_by_xpath('//dd[1]/ul/li').text) - # Same date, end time before start time form = self.browser.find_element_by_tag_name('form') save = self.browser.find_element_by_xpath('(//button[@type="submit"])[3]') - form.find_element_by_id('id_start_date').clear() - form.find_element_by_id('id_start_date').send_keys('3015-04-24') - form.find_element_by_id('id_end_date').clear() - form.find_element_by_id('id_end_date').send_keys('3015-04-23') + self.browser.execute_script("document.getElementById('id_start_date').value='3015-04-24'") + self.browser.execute_script("document.getElementById('id_end_date').value='3015-04-23'") - form.find_element_by_id('id_start_time').clear() + form.find_element_by_id('id_start_time').send_keys(Keys.DELETE) form.find_element_by_id('id_start_time').send_keys('06:59') - form.find_element_by_id('id_end_time').clear() + form.find_element_by_id('id_end_time').send_keys(Keys.DELETE) form.find_element_by_id('id_end_time').send_keys('06:00') # Attempt to save - should fail @@ -566,35 +601,30 @@ class EventTest(LiveServerTestCase): self.assertTrue(error.is_displayed()) self.assertIn("can't finish before it has started", error.find_element_by_xpath('//dd[1]/ul/li').text) - # Same date, end time before start time form = self.browser.find_element_by_tag_name('form') save = self.browser.find_element_by_xpath('(//button[@type="submit"])[3]') - form.find_element_by_id('id_start_date').clear() - form.find_element_by_id('id_start_date').send_keys('3015-04-24') - form.find_element_by_id('id_end_date').clear() - form.find_element_by_id('id_end_date').send_keys('3015-04-23') + self.browser.execute_script("document.getElementById('id_start_date').value='3015-04-24'") + self.browser.execute_script("document.getElementById('id_end_date').value='3015-04-24'") - form.find_element_by_id('id_start_time').clear() + form.find_element_by_id('id_start_time').send_keys(Keys.DELETE) form.find_element_by_id('id_start_time').send_keys('06:59') - form.find_element_by_id('id_end_time').clear() + form.find_element_by_id('id_end_time').send_keys(Keys.DELETE) form.find_element_by_id('id_end_time').send_keys('06:00') - # No end date, end time before start time form = self.browser.find_element_by_tag_name('form') save = self.browser.find_element_by_xpath('(//button[@type="submit"])[3]') - form.find_element_by_id('id_start_date').clear() - form.find_element_by_id('id_start_date').send_keys('3015-04-24') - form.find_element_by_id('id_end_date').clear() + self.browser.execute_script("document.getElementById('id_start_date').value='3015-04-24'") + self.browser.execute_script("document.getElementById('id_end_date').value=''") - form.find_element_by_id('id_start_time').clear() + form.find_element_by_id('id_start_time').send_keys(Keys.DELETE) form.find_element_by_id('id_start_time').send_keys('06:59') - form.find_element_by_id('id_end_time').clear() + form.find_element_by_id('id_end_time').send_keys(Keys.DELETE) form.find_element_by_id('id_end_time').send_keys('06:00') # Attempt to save - should fail @@ -603,35 +633,31 @@ class EventTest(LiveServerTestCase): self.assertTrue(error.is_displayed()) self.assertIn("can't finish before it has started", error.find_element_by_xpath('//dd[1]/ul/li').text) - # 2 dates, end after start form = self.browser.find_element_by_tag_name('form') save = self.browser.find_element_by_xpath('(//button[@type="submit"])[3]') - form.find_element_by_id('id_start_date').clear() - form.find_element_by_id('id_start_date').send_keys('3015-04-24') + self.browser.execute_script("document.getElementById('id_start_date').value='3015-04-24'") + self.browser.execute_script("document.getElementById('id_end_date').value='3015-04-26'") - form.find_element_by_id('id_end_date').clear() - form.find_element_by_id('id_end_date').send_keys('3015-04-26') + self.browser.execute_script("document.getElementById('id_start_time').value=''") + self.browser.execute_script("document.getElementById('id_end_time').value=''") - form.find_element_by_id('id_start_time').clear() - - form.find_element_by_id('id_end_time').clear() - # Attempt to save - should succeed save.click() - + # See redirected to success page successTitle = self.browser.find_element_by_xpath('//h1').text event = models.Event.objects.get(name='Test Event Name') - self.assertIn("N0000%d | Test Event Name"%event.pk, successTitle) - + + self.assertIn("N%05d | Test Event Name" % event.pk, successTitle) + def testRigNonRig(self): self.browser.get(self.live_server_url + '/event/create/') # Gets redirected to login and back self.authenticate('/event/create/') - wait = WebDriverWait(self.browser, 10) #setup WebDriverWait to use later (to wait for animations) - self.browser.implicitly_wait(3) #Set session-long wait (only works for non-existant DOM objects) + wait = WebDriverWait(self.browser, 3) # setup WebDriverWait to use later (to wait for animations) + self.browser.implicitly_wait(3) # Set session-long wait (only works for non-existant DOM objects) wait.until(animation_is_finished()) @@ -648,11 +674,20 @@ class EventTest(LiveServerTestCase): e = self.browser.find_element_by_id('id_name') e.send_keys('Test Event Name') + # Set person + person = models.Person.objects.create(name='Rig Non-Rig Person', email='rignonrig@functional.test') + person_select = form.find_element_by_xpath( + '//button[@data-id="id_person"]') + person_select.send_keys(person.name) + person_dropped = form.find_element_by_xpath( + '//ul[contains(@class, "dropdown-menu")]//span[contains(text(), "%s")]' % person.name) + person_dropped.click() + # Set an arbitrary date - form.find_element_by_id('id_start_date').clear() - form.find_element_by_id('id_start_date').send_keys('3015-04-24') + self.browser.execute_script("document.getElementById('id_start_date').value='3015-04-24'") # Save the rig + wait.until(animation_is_finished()) save.click() detail_panel = self.browser.find_element_by_xpath("//div[@id='content']/div/div[6]/div/div") self.assertTrue(detail_panel.is_displayed()) @@ -663,7 +698,8 @@ class EventTest(LiveServerTestCase): person = models.Person(name="Event Detail Person", email="eventdetail@person.tests.rigs", phone="123 123") person.save() with transaction.atomic(), reversion.create_revision(): - organisation = models.Organisation(name="Event Detail Organisation", email="eventdetail@organisation.tests.rigs", phone="123 456").save() + organisation = models.Organisation(name="Event Detail Organisation", + email="eventdetail@organisation.tests.rigs", phone="123 456").save() with transaction.atomic(), reversion.create_revision(): venue = models.Venue(name="Event Detail Venue").save() with transaction.atomic(), reversion.create_revision(): @@ -693,59 +729,145 @@ class EventTest(LiveServerTestCase): order=2, ).save() - - self.browser.get(self.live_server_url + '/event/%d'%event.pk) - self.authenticate('/event/%d/'%event.pk) - self.assertIn("N%05d | %s"%(event.pk, event.name), self.browser.find_element_by_xpath('//h1').text) + self.browser.get(self.live_server_url + '/event/%d' % event.pk) + self.authenticate('/event/%d/' % event.pk) + self.assertIn("N%05d | %s" % (event.pk, event.name), self.browser.find_element_by_xpath('//h1').text) personPanel = self.browser.find_element_by_xpath('//div[contains(text(), "Contact Details")]/..') - self.assertEqual(person.name, personPanel.find_element_by_xpath('//dt[text()="Person"]/following-sibling::dd[1]').text) - self.assertEqual(person.email, personPanel.find_element_by_xpath('//dt[text()="Email"]/following-sibling::dd[1]').text) - self.assertEqual(person.phone, personPanel.find_element_by_xpath('//dt[text()="Phone Number"]/following-sibling::dd[1]').text) + self.assertEqual(person.name, + personPanel.find_element_by_xpath('//dt[text()="Person"]/following-sibling::dd[1]').text) + self.assertEqual(person.email, + personPanel.find_element_by_xpath('//dt[text()="Email"]/following-sibling::dd[1]').text) + self.assertEqual(person.phone, + personPanel.find_element_by_xpath('//dt[text()="Phone Number"]/following-sibling::dd[1]').text) organisationPanel = self.browser.find_element_by_xpath('//div[contains(text(), "Contact Details")]/..') -class IcalTest(LiveServerTestCase): + def testEventEdit(self): + person = models.Person.objects.create(name="Event Edit Person", email="eventdetail@person.tests.rigs", phone="123 123") + organisation = models.Organisation.objects.create(name="Event Edit Organisation", email="eventdetail@organisation.tests.rigs", phone="123 456") + venue = models.Venue.objects.create(name="Event Detail Venue") + eventData = { + 'name': "Detail Test", + 'description': "This is an event to test the detail view", + 'notes': "It is going to be awful", + 'person': person, + 'organisation': organisation, + 'venue': venue, + 'mic': self.profile, + 'start_date': date(2015, 0o6, 0o4), + 'end_date': date(2015, 0o6, 0o5), + 'start_time': time(10, 00), + 'end_time': time(15, 00), + 'meet_at': self.create_datetime(2015, 0o6, 0o4, 10, 00), + 'access_at': self.create_datetime(2015, 0o6, 0o4, 10, 00), + 'collector': 'A Person' + } + + event = models.Event(**eventData) + event.save() + + item1Data = { + 'event': event, + 'name': "Detail Item 1", + 'cost': "10.00", + 'quantity': "1", + 'order': 1 + } + + models.EventItem(**item1Data).save() + + self.browser.get(self.live_server_url + '/event/%d/edit/' % event.pk) + self.authenticate('/event/%d/edit/' % event.pk) + + save = self.browser.find_element_by_xpath('(//button[@type="submit"])[1]') + save.click() + + successTitle = self.browser.find_element_by_xpath('//h1').text + self.assertIn("N%05d | Detail Test" % event.pk, successTitle) + + reloadedEvent = models.Event.objects.get(name='Detail Test') + reloadedItem = models.EventItem.objects.get(name='Detail Item 1') + + # Check the event + for key, value in eventData.items(): + self.assertEqual(str(getattr(reloadedEvent, key)), str(value)) + + # Check the item + for key, value in item1Data.items(): + self.assertEqual(str(getattr(reloadedItem, key)), str(value)) + + def create_datetime(self, year, month, day, hour, min): + tz = pytz.timezone(settings.TIME_ZONE) + return tz.localize(datetime(year, month, day, hour, min)).astimezone(pytz.utc) + + +class IcalTest(LiveServerTestCase): def setUp(self): self.all_events = set(range(1, 18)) self.current_events = (1, 2, 3, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18) self.not_current_events = set(self.all_events) - set(self.current_events) - self.vatrate = models.VatRate.objects.create(start_at='2014-03-05',rate=0.20,comment='test1') + self.vatrate = models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1') self.profile = models.Profile( username="EventTest", first_name="Event", last_name="Test", initials="ETU", is_superuser=True) self.profile.set_password("EventTestPassword") self.profile.save() # produce 7 normal events - 5 current - 1 last week - 1 two years ago - 2 provisional - 2 confirmed - 3 booked - models.Event.objects.create(name="TE E1", status=models.Event.PROVISIONAL, start_date=date.today() + timedelta(days=6), description="start future no end") - models.Event.objects.create(name="TE E2", status=models.Event.PROVISIONAL, start_date=date.today(), description="start today no end") - models.Event.objects.create(name="TE E3", status=models.Event.CONFIRMED, start_date=date.today(), end_date=date.today(), description="start today with end today") - models.Event.objects.create(name="TE E4", status=models.Event.CONFIRMED, start_date=date.today()-timedelta(weeks=104), description="start past 2 years no end") - models.Event.objects.create(name="TE E5", status=models.Event.BOOKED, start_date=date.today()-timedelta(days=7), end_date=date.today()-timedelta(days=1), description="start past 1 week with end past") - models.Event.objects.create(name="TE E6", status=models.Event.BOOKED, start_date=date.today()-timedelta(days=2), end_date=date.today()+timedelta(days=2), description="start past, end future") - models.Event.objects.create(name="TE E7", status=models.Event.BOOKED, start_date=date.today()+timedelta(days=2), end_date=date.today()+timedelta(days=2), description="start + end in future") + models.Event.objects.create(name="TE E1", status=models.Event.PROVISIONAL, + start_date=date.today() + timedelta(days=6), description="start future no end") + models.Event.objects.create(name="TE E2", status=models.Event.PROVISIONAL, start_date=date.today(), + description="start today no end") + models.Event.objects.create(name="TE E3", status=models.Event.CONFIRMED, start_date=date.today(), + end_date=date.today(), description="start today with end today") + models.Event.objects.create(name="TE E4", status=models.Event.CONFIRMED, + start_date=date.today() - timedelta(weeks=104), + description="start past 2 years no end") + models.Event.objects.create(name="TE E5", status=models.Event.BOOKED, + start_date=date.today() - timedelta(days=7), + end_date=date.today() - timedelta(days=1), + description="start past 1 week with end past") + models.Event.objects.create(name="TE E6", status=models.Event.BOOKED, + start_date=date.today() - timedelta(days=2), + start_time=time(8, 00), + end_date=date.today() + timedelta(days=2), + end_time=time(23, 00), description="start past, end future") + models.Event.objects.create(name="TE E7", status=models.Event.BOOKED, + start_date=date.today() + timedelta(days=2), + end_date=date.today() + timedelta(days=2), description="start + end in future") # 2 cancelled - 1 current - models.Event.objects.create(name="TE E8", start_date=date.today()+timedelta(days=2), end_date=date.today()+timedelta(days=2), status=models.Event.CANCELLED, description="cancelled in future") - models.Event.objects.create(name="TE E9", start_date=date.today()-timedelta(days=1), end_date=date.today()+timedelta(days=2), status=models.Event.CANCELLED, description="cancelled and started") + models.Event.objects.create(name="TE E8", start_date=date.today() + timedelta(days=2), + end_date=date.today() + timedelta(days=2), status=models.Event.CANCELLED, + description="cancelled in future") + models.Event.objects.create(name="TE E9", start_date=date.today() - timedelta(days=1), + end_date=date.today() + timedelta(days=2), status=models.Event.CANCELLED, + description="cancelled and started") # 5 dry hire - 3 current - 1 cancelled models.Event.objects.create(name="TE E10", start_date=date.today(), dry_hire=True, description="dryhire today") - models.Event.objects.create(name="TE E11", start_date=date.today(), dry_hire=True, checked_in_by=self.profile, description="dryhire today, checked in") - models.Event.objects.create(name="TE E12", start_date=date.today()-timedelta(days=1), dry_hire=True, status=models.Event.BOOKED, description="dryhire past") - models.Event.objects.create(name="TE E13", start_date=date.today()-timedelta(days=2), dry_hire=True, checked_in_by=self.profile, description="dryhire past checked in") - models.Event.objects.create(name="TE E14", start_date=date.today(), dry_hire=True, status=models.Event.CANCELLED, description="dryhire today cancelled") + models.Event.objects.create(name="TE E11", start_date=date.today(), dry_hire=True, checked_in_by=self.profile, + description="dryhire today, checked in") + models.Event.objects.create(name="TE E12", start_date=date.today() - timedelta(days=1), dry_hire=True, + status=models.Event.BOOKED, description="dryhire past") + models.Event.objects.create(name="TE E13", start_date=date.today() - timedelta(days=2), dry_hire=True, + checked_in_by=self.profile, description="dryhire past checked in") + models.Event.objects.create(name="TE E14", start_date=date.today(), dry_hire=True, + status=models.Event.CANCELLED, description="dryhire today cancelled") # 4 non rig - 3 current models.Event.objects.create(name="TE E15", start_date=date.today(), is_rig=False, description="non rig today") - models.Event.objects.create(name="TE E16", start_date=date.today()+timedelta(days=1), is_rig=False, description="non rig tomorrow") - models.Event.objects.create(name="TE E17", start_date=date.today()-timedelta(days=1), is_rig=False, description="non rig yesterday") - models.Event.objects.create(name="TE E18", start_date=date.today(), is_rig=False, status=models.Event.CANCELLED, description="non rig today cancelled") + models.Event.objects.create(name="TE E16", start_date=date.today() + timedelta(days=1), is_rig=False, + description="non rig tomorrow") + models.Event.objects.create(name="TE E17", start_date=date.today() - timedelta(days=1), is_rig=False, + description="non rig yesterday") + models.Event.objects.create(name="TE E18", start_date=date.today(), is_rig=False, status=models.Event.CANCELLED, + description="non rig today cancelled") - self.browser = webdriver.Firefox() - self.browser.implicitly_wait(3) # Set implicit wait session wide + self.browser = create_browser() + self.browser.implicitly_wait(3) # Set implicit wait session wide os.environ['RECAPTCHA_TESTING'] = 'True' def tearDown(self): @@ -776,14 +898,15 @@ class IcalTest(LiveServerTestCase): # Completes and comes back to /user/ # Checks that no api key is displayed - self.assertEqual("No API Key Generated", self.browser.find_element_by_xpath("//div[@id='content']/div/div/div[3]/dl[2]/dd").text) + self.assertEqual("No API Key Generated", + self.browser.find_element_by_xpath("//div[@id='content']/div/div/div[3]/dl[2]/dd").text) self.assertEqual("No API Key Generated", self.browser.find_element_by_css_selector("pre").text) - + # Now creates an API key, and check a URL is displayed one self.browser.find_element_by_link_text("Generate API Key").click() self.assertIn("rigs.ics", self.browser.find_element_by_id("cal-url").text) self.assertNotIn("?", self.browser.find_element_by_id("cal-url").text) - + # Lets change everything so it's not the default value self.browser.find_element_by_xpath("//input[@value='rig']").click() self.browser.find_element_by_xpath("//input[@value='non-rig']").click() @@ -793,11 +916,15 @@ class IcalTest(LiveServerTestCase): self.browser.find_element_by_xpath("//input[@value='confirmed']").click() # and then check the url is correct - self.assertIn("rigs.ics?rig=false&non-rig=false&dry-hire=false&cancelled=true&provisional=false&confirmed=false", self.browser.find_element_by_id("cal-url").text) + self.assertIn( + "rigs.ics?rig=false&non-rig=false&dry-hire=false&cancelled=true&provisional=false&confirmed=false", + self.browser.find_element_by_id("cal-url").text) # Awesome - all seems to work def testICSFiles(self): + specialEvent = models.Event.objects.get(name="TE E6") + # Requests address self.browser.get(self.live_server_url + '/user/') # Gets redirected to login @@ -806,27 +933,30 @@ class IcalTest(LiveServerTestCase): # Now creates an API key, and check a URL is displayed one self.browser.find_element_by_link_text("Generate API Key").click() - - c = Client() - + # Default settings - should have all non-cancelled events # Get the ical file (can't do this in selanium because reasons) icalUrl = self.browser.find_element_by_id("cal-url").text response = c.get(icalUrl) self.assertEqual(200, response.status_code) - #Check has entire file - self.assertIn("BEGIN:VCALENDAR", response.content) - self.assertIn("END:VCALENDAR", response.content) + # content = response.content.decode('utf-8') - expectedIn= [1,2,3,5,6,7,10,11,12,13,15,16,17] - for test in range(1,18): + # Check has entire file + self.assertContains(response, "BEGIN:VCALENDAR") + self.assertContains(response, "END:VCALENDAR") + + expectedIn = [1, 2, 3, 5, 6, 7, 10, 11, 12, 13, 15, 16, 17] + for test in range(1, 18): if test in expectedIn: - self.assertIn("TE E"+str(test)+" ", response.content) + self.assertContains(response, "TE E" + str(test) + " ") else: - self.assertNotIn("TE E"+str(test)+" ", response.content) + self.assertNotContains(response, "TE E" + str(test) + " ") + # Check that times have been included correctly + self.assertContains(response, specialEvent.start_date.strftime('%Y%m%d') + 'T' + specialEvent.start_time.strftime('%H%M%S')) + self.assertContains(response, specialEvent.end_date.strftime('%Y%m%d') + 'T' + specialEvent.end_time.strftime('%H%M%S')) # Only dry hires self.browser.find_element_by_xpath("//input[@value='rig']").click() @@ -836,13 +966,12 @@ class IcalTest(LiveServerTestCase): response = c.get(icalUrl) self.assertEqual(200, response.status_code) - expectedIn= [10,11,12,13] - for test in range(1,18): + expectedIn = [10, 11, 12, 13] + for test in range(1, 18): if test in expectedIn: - self.assertIn("TE E"+str(test)+" ", response.content) + self.assertContains(response, "TE E" + str(test) + " ") else: - self.assertNotIn("TE E"+str(test)+" ", response.content) - + self.assertNotContains(response, "TE E" + str(test) + " ") # Only provisional rigs self.browser.find_element_by_xpath("//input[@value='rig']").click() @@ -853,12 +982,12 @@ class IcalTest(LiveServerTestCase): response = c.get(icalUrl) self.assertEqual(200, response.status_code) - expectedIn= [1,2] - for test in range(1,18): + expectedIn = [1, 2] + for test in range(1, 18): if test in expectedIn: - self.assertIn("TE E"+str(test)+" ", response.content) + self.assertContains(response, "TE E" + str(test) + " ") else: - self.assertNotIn("TE E"+str(test)+" ", response.content) + self.assertNotContains(response, "TE E" + str(test) + " ") # Only cancelled non-rigs self.browser.find_element_by_xpath("//input[@value='rig']").click() @@ -870,12 +999,12 @@ class IcalTest(LiveServerTestCase): response = c.get(icalUrl) self.assertEqual(200, response.status_code) - expectedIn= [18] - for test in range(1,18): + expectedIn = [18] + for test in range(1, 18): if test in expectedIn: - self.assertIn("TE E"+str(test)+" ", response.content) + self.assertContains(response, "TE E" + str(test) + " ") else: - self.assertNotIn("TE E"+str(test)+" ", response.content) + self.assertNotContains(response, "TE E" + str(test) + " ") # Nothing selected self.browser.find_element_by_xpath("//input[@value='non-rig']").click() @@ -885,17 +1014,19 @@ class IcalTest(LiveServerTestCase): response = c.get(icalUrl) self.assertEqual(200, response.status_code) - expectedIn= [] - for test in range(1,18): + expectedIn = [] + for test in range(1, 18): if test in expectedIn: - self.assertIn("TE E"+str(test)+" ", response.content) + self.assertContains(response, "TE E" + str(test) + " ") else: - self.assertNotIn("TE E"+str(test)+" ", response.content) - - # Wow - that was a lot of tests + self.assertNotContains(response, "TE E" + str(test) + " ") + + # Wow - that was a lot of tests + class animation_is_finished(object): """ Checks if animation is done """ + def __init__(self): pass @@ -906,3 +1037,167 @@ class animation_is_finished(object): import time time.sleep(0.1) return finished + + +class ClientEventAuthorisationTest(TestCase): + auth_data = { + 'name': 'Test ABC', + 'po': '1234ABCZXY', + 'account_code': 'ABC TEST 12345', + 'uni_id': 1234567890, + 'tos': True + } + + def setUp(self): + self.profile = models.Profile.objects.get_or_create( + first_name='Test', + last_name='TEC User', + username='eventauthtest', + email='teccie@functional.test', + is_superuser=True # lazily grant all permissions + )[0] + self.vatrate = models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1') + venue = models.Venue.objects.create(name='Authorisation Test Venue') + client = models.Person.objects.create(name='Authorisation Test Person', email='authorisation@functional.test') + organisation = models.Organisation.objects.create(name='Authorisation Test Organisation', union_account=False) + self.event = models.Event.objects.create( + name='Authorisation Test', + start_date=date.today(), + venue=venue, + person=client, + organisation=organisation, + ) + self.hmac = signing.dumps({'pk': self.event.pk, 'email': 'authemail@function.test', + 'sent_by': self.profile.pk}) + self.url = reverse('event_authorise', kwargs={'pk': self.event.pk, 'hmac': self.hmac}) + + def test_requires_valid_hmac(self): + bad_hmac = self.hmac[:-1] + url = reverse('event_authorise', kwargs={'pk': self.event.pk, 'hmac': bad_hmac}) + response = self.client.get(url) + self.assertIsInstance(response, HttpResponseBadRequest) + # TODO: Add some form of sensbile user facing error + # self.assertIn(response.content, "new URL") # check there is some level of sane instruction + + response = self.client.get(self.url) + self.assertContains(response, self.event.organisation.name) + + def test_generic_validation(self): + response = self.client.get(self.url) + self.assertContains(response, "Terms of Hire") + + response = self.client.post(self.url) + self.assertContains(response, "This field is required.", 5) + + data = self.auth_data + data['amount'] = self.event.total + 1 + + response = self.client.post(self.url, data) + self.assertContains(response, "The amount authorised must equal the total for the event") + self.assertNotContains(response, "This field is required.") + + data['amount'] = self.event.total + response = self.client.post(self.url, data) + self.assertContains(response, "Your event has been authorised") + + self.event.refresh_from_db() + self.assertTrue(self.event.authorised) + self.assertEqual(self.event.authorisation.email, "authemail@function.test") + + def test_internal_validation(self): + self.event.organisation.union_account = True + self.event.organisation.save() + + response = self.client.get(self.url) + self.assertContains(response, "Account code") + self.assertContains(response, "University ID") + + response = self.client.post(self.url) + self.assertContains(response, "This field is required.", 5) + + data = self.auth_data + response = self.client.post(self.url, data) + self.assertContains(response, "Your event has been authorised.") + + def test_duplicate_warning(self): + auth = models.EventAuthorisation.objects.create(event=self.event, name='Test ABC', email='dupe@functional.test', + amount=self.event.total, sent_by=self.profile) + response = self.client.get(self.url) + self.assertContains(response, 'This event has already been authorised.') + + auth.amount += 1 + auth.save() + + response = self.client.get(self.url) + self.assertContains(response, 'amount has changed') + + def test_email_sent(self): + mail.outbox = [] + + data = self.auth_data + data['amount'] = self.event.total + + response = self.client.post(self.url, data) + self.assertContains(response, "Your event has been authorised.") + self.assertEqual(len(mail.outbox), 2) + + self.assertEqual(mail.outbox[0].to, ['authemail@function.test']) + self.assertEqual(mail.outbox[1].to, [settings.AUTHORISATION_NOTIFICATION_ADDRESS]) + + +class TECEventAuthorisationTest(TestCase): + @classmethod + def setUpTestData(cls): + cls.profile = models.Profile.objects.get_or_create( + first_name='Test', + last_name='TEC User', + username='eventauthtest', + email='teccie@nottinghamtec.co.uk', + is_superuser=True # lazily grant all permissions + )[0] + cls.profile.set_password('eventauthtest123') + cls.profile.save() + + def setUp(self): + self.vatrate = models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1') + venue = models.Venue.objects.create(name='Authorisation Test Venue') + client = models.Person.objects.create(name='Authorisation Test Person', email='authorisation@functional.test') + organisation = models.Organisation.objects.create(name='Authorisation Test Organisation', union_account=False) + self.event = models.Event.objects.create( + name='Authorisation Test', + start_date=date.today(), + venue=venue, + person=client, + organisation=organisation, + ) + self.url = reverse('event_authorise_request', kwargs={'pk': self.event.pk}) + + def test_email_check(self): + self.profile.email = 'teccie@someotherdomain.com' + self.profile.save() + + self.assertTrue(self.client.login(username=self.profile.username, password='eventauthtest123')) + + response = self.client.post(self.url) + + self.assertContains(response, 'must have an @nottinghamtec.co.uk email address') + + def test_request_send(self): + self.assertTrue(self.client.login(username=self.profile.username, password='eventauthtest123')) + response = self.client.post(self.url) + self.assertContains(response, 'This field is required.') + + mail.outbox = [] + + response = self.client.post(self.url, {'email': 'client@functional.test'}) + self.assertEqual(response.status_code, 302) + self.assertEqual(len(mail.outbox), 1) + email = mail.outbox[0] + self.assertIn('client@functional.test', email.to) + self.assertIn('/event/%d/' % (self.event.pk), email.body) + + # Check sent by details are populated + self.event.refresh_from_db() + self.assertEqual(self.event.auth_request_by, self.profile) + self.assertEqual(self.event.auth_request_to, 'client@functional.test') + self.assertIsNotNone(self.event.auth_request_at) diff --git a/RIGS/test_models.py b/RIGS/test_models.py index a7896cbe..424e2f06 100644 --- a/RIGS/test_models.py +++ b/RIGS/test_models.py @@ -1,7 +1,11 @@ + + import pytz +from reversion import revisions as reversion from django.conf import settings +from django.core.exceptions import ValidationError from django.test import TestCase -from RIGS import models +from RIGS import models, versioning from datetime import date, timedelta, datetime, time from decimal import * @@ -15,71 +19,82 @@ class ProfileTestCase(TestCase): class VatRateTestCase(TestCase): - def setUp(self): - models.VatRate.objects.create(start_at='2014-03-01', rate=0.20, comment='test1') - models.VatRate.objects.create(start_at='2016-03-01', rate=0.15, comment='test2') + @classmethod + def setUpTestData(cls): + cls.rates = { + 0: models.VatRate.objects.create(start_at='2014-03-01', rate=0.20, comment='test1'), + 1: models.VatRate.objects.create(start_at='2016-03-01', rate=0.15, comment='test2'), + } def test_find_correct(self): r = models.VatRate.objects.find_rate('2015-03-01') - self.assertEqual(r.comment, 'test1') + self.assertEqual(r, self.rates[0]) r = models.VatRate.objects.find_rate('2016-03-01') - self.assertEqual(r.comment, 'test2') + self.assertEqual(r, self.rates[1]) def test_percent_correct(self): - r = models.VatRate.objects.get(rate=0.20) - self.assertEqual(r.as_percent, 20) + self.assertEqual(self.rates[0].as_percent, 20) class EventTestCase(TestCase): - def setUp(self): - self.all_events = set(range(1, 18)) - self.current_events = (1, 2, 3, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18) - self.not_current_events = set(self.all_events) - set(self.current_events) + @classmethod + def setUpTestData(cls): + cls.all_events = set(range(1, 18)) + cls.current_events = (1, 2, 3, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18) + cls.not_current_events = set(cls.all_events) - set(cls.current_events) - self.vatrate = models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1') - self.profile = models.Profile.objects.create(username="testuser1", email="1@test.com") + cls.vatrate = models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1') + cls.profile = models.Profile.objects.create(username="testuser1", email="1@test.com") - # produce 7 normal events - 5 current - models.Event.objects.create(name="TE E1", start_date=date.today() + timedelta(days=6), - description="start future no end") - models.Event.objects.create(name="TE E2", start_date=date.today(), description="start today no end") - models.Event.objects.create(name="TE E3", start_date=date.today(), end_date=date.today(), - description="start today with end today") - models.Event.objects.create(name="TE E4", start_date='2014-03-20', description="start past no end") - models.Event.objects.create(name="TE E5", start_date='2014-03-20', end_date='2014-03-21', - description="start past with end past") - models.Event.objects.create(name="TE E6", start_date=date.today() - timedelta(days=2), - end_date=date.today() + timedelta(days=2), description="start past, end future") - models.Event.objects.create(name="TE E7", start_date=date.today() + timedelta(days=2), - end_date=date.today() + timedelta(days=2), description="start + end in future") + cls.events = { + # produce 7 normal events - 5 current + 1: models.Event.objects.create(name="TE E1", start_date=date.today() + timedelta(days=6), + description="start future no end"), + 2: models.Event.objects.create(name="TE E2", start_date=date.today(), description="start today no end"), + 3: models.Event.objects.create(name="TE E3", start_date=date.today(), end_date=date.today(), + description="start today with end today"), + 4: models.Event.objects.create(name="TE E4", start_date='2014-03-20', description="start past no end"), + 5: models.Event.objects.create(name="TE E5", start_date='2014-03-20', end_date='2014-03-21', + description="start past with end past"), + 6: models.Event.objects.create(name="TE E6", start_date=date.today() - timedelta(days=2), + end_date=date.today() + timedelta(days=2), + description="start past, end future"), + 7: models.Event.objects.create(name="TE E7", start_date=date.today() + timedelta(days=2), + end_date=date.today() + timedelta(days=2), + description="start + end in future"), - # 2 cancelled - 1 current - models.Event.objects.create(name="TE E8", start_date=date.today() + timedelta(days=2), - end_date=date.today() + timedelta(days=2), status=models.Event.CANCELLED, - description="cancelled in future") - models.Event.objects.create(name="TE E9", start_date=date.today() - timedelta(days=1), - end_date=date.today() + timedelta(days=2), status=models.Event.CANCELLED, - description="cancelled and started") + # 2 cancelled - 1 current + 8: models.Event.objects.create(name="TE E8", start_date=date.today() + timedelta(days=2), + end_date=date.today() + timedelta(days=2), status=models.Event.CANCELLED, + description="cancelled in future"), + 9: models.Event.objects.create(name="TE E9", start_date=date.today() - timedelta(days=1), + end_date=date.today() + timedelta(days=2), status=models.Event.CANCELLED, + description="cancelled and started"), - # 5 dry hire - 3 current - models.Event.objects.create(name="TE E10", start_date=date.today(), dry_hire=True, description="dryhire today") - models.Event.objects.create(name="TE E11", start_date=date.today(), dry_hire=True, checked_in_by=self.profile, - description="dryhire today, checked in") - models.Event.objects.create(name="TE E12", start_date=date.today() - timedelta(days=1), dry_hire=True, - status=models.Event.BOOKED, description="dryhire past") - models.Event.objects.create(name="TE E13", start_date=date.today() - timedelta(days=2), dry_hire=True, - checked_in_by=self.profile, description="dryhire past checked in") - models.Event.objects.create(name="TE E14", start_date=date.today(), dry_hire=True, - status=models.Event.CANCELLED, description="dryhire today cancelled") + # 5 dry hire - 3 current + 10: models.Event.objects.create(name="TE E10", start_date=date.today(), dry_hire=True, + description="dryhire today"), + 11: models.Event.objects.create(name="TE E11", start_date=date.today(), dry_hire=True, + checked_in_by=cls.profile, + description="dryhire today, checked in"), + 12: models.Event.objects.create(name="TE E12", start_date=date.today() - timedelta(days=1), dry_hire=True, + status=models.Event.BOOKED, description="dryhire past"), + 13: models.Event.objects.create(name="TE E13", start_date=date.today() - timedelta(days=2), dry_hire=True, + checked_in_by=cls.profile, description="dryhire past checked in"), + 14: models.Event.objects.create(name="TE E14", start_date=date.today(), dry_hire=True, + status=models.Event.CANCELLED, description="dryhire today cancelled"), - # 4 non rig - 3 current - models.Event.objects.create(name="TE E15", start_date=date.today(), is_rig=False, description="non rig today") - models.Event.objects.create(name="TE E16", start_date=date.today() + timedelta(days=1), is_rig=False, - description="non rig tomorrow") - models.Event.objects.create(name="TE E17", start_date=date.today() - timedelta(days=1), is_rig=False, - description="non rig yesterday") - models.Event.objects.create(name="TE E18", start_date=date.today(), is_rig=False, status=models.Event.CANCELLED, - description="non rig today cancelled") + # 4 non rig - 3 current + 15: models.Event.objects.create(name="TE E15", start_date=date.today(), is_rig=False, + description="non rig today"), + 16: models.Event.objects.create(name="TE E16", start_date=date.today() + timedelta(days=1), is_rig=False, + description="non rig tomorrow"), + 17: models.Event.objects.create(name="TE E17", start_date=date.today() - timedelta(days=1), is_rig=False, + description="non rig yesterday"), + 18: models.Event.objects.create(name="TE E18", start_date=date.today(), is_rig=False, + status=models.Event.CANCELLED, + description="non rig today cancelled"), + } def test_count(self): # Santiy check we have the expected events created @@ -101,17 +116,23 @@ class EventTestCase(TestCase): def test_related_venue(self): v1 = models.Venue.objects.create(name="TE V1") v2 = models.Venue.objects.create(name="TE V2") - events = models.Event.objects.all() - for event in events[:2]: - event.venue = v1 - event.save() - for event in events[3:4]: - event.venue = v2 + + e1 = [] + e2 = [] + for (key, event) in self.events.items(): + if event.pk % 2: + event.venue = v1 + e1.append(event) + else: + event.venue = v2 + e2.append(event) event.save() - events = models.Event.objects.all() - self.assertItemsEqual(events[:2], v1.latest_events) - self.assertItemsEqual(events[3:4], v2.latest_events) + self.assertCountEqual(e1, v1.latest_events) + self.assertCountEqual(e2, v2.latest_events) + + for (key, event) in self.events.items(): + event.venue = None def test_related_vatrate(self): self.assertEqual(self.vatrate, models.Event.objects.all()[0].vat_rate) @@ -120,33 +141,43 @@ class EventTestCase(TestCase): p1 = models.Person.objects.create(name="TE P1") p2 = models.Person.objects.create(name="TE P2") - events = models.Event.objects.all() - for event in events[:2]: - event.person = p1 - event.save() - for event in events[3:4]: - event.person = p2 + e1 = [] + e2 = [] + for (key, event) in self.events.items(): + if event.pk % 2: + event.person = p1 + e1.append(event) + else: + event.person = p2 + e2.append(event) event.save() - events = models.Event.objects.all() - self.assertItemsEqual(events[:2], p1.latest_events) - self.assertItemsEqual(events[3:4], p2.latest_events) + self.assertCountEqual(e1, p1.latest_events) + self.assertCountEqual(e2, p2.latest_events) + + for (key, event) in self.events.items(): + event.person = None def test_related_organisation(self): o1 = models.Organisation.objects.create(name="TE O1") o2 = models.Organisation.objects.create(name="TE O2") - events = models.Event.objects.all() - for event in events[:2]: - event.organisation = o1 - event.save() - for event in events[3:4]: - event.organisation = o2 + e1 = [] + e2 = [] + for (key, event) in self.events.items(): + if event.pk % 2: + event.organisation = o1 + e1.append(event) + else: + event.organisation = o2 + e2.append(event) event.save() - events = models.Event.objects.all() - self.assertItemsEqual(events[:2], o1.latest_events) - self.assertItemsEqual(events[3:4], o2.latest_events) + self.assertCountEqual(e1, o1.latest_events) + self.assertCountEqual(e2, o2.latest_events) + + for (key, event) in self.events.items(): + event.organisation = None def test_organisation_person_join(self): p1 = models.Person.objects.create(name="TE P1") @@ -184,49 +215,49 @@ class EventTestCase(TestCase): self.assertEqual(len(o2.persons), 1) def test_cancelled_property(self): - event = models.Event.objects.all()[0] - event.status = models.Event.CANCELLED - event.save() - event = models.Event.objects.all()[0] + edit = self.events[1] + edit.status = models.Event.CANCELLED + edit.save() + event = models.Event.objects.get(pk=edit.pk) self.assertEqual(event.status, models.Event.CANCELLED) self.assertTrue(event.cancelled) event.status = models.Event.PROVISIONAL event.save() def test_confirmed_property(self): - event = models.Event.objects.all()[0] - event.status = models.Event.CONFIRMED - event.save() - event = models.Event.objects.all()[0] + edit = self.events[1] + edit.status = models.Event.CONFIRMED + edit.save() + event = models.Event.objects.get(pk=edit.pk) self.assertEqual(event.status, models.Event.CONFIRMED) self.assertTrue(event.confirmed) event.status = models.Event.PROVISIONAL event.save() def test_earliest_time(self): - event = models.Event(name="TE ET", start_date=date(2016, 01, 01)) + event = models.Event(name="TE ET", start_date=date(2016, 0o1, 0o1)) # Just a start date - self.assertEqual(event.earliest_time, date(2016, 01, 01)) + self.assertEqual(event.earliest_time, date(2016, 0o1, 0o1)) # With start time event.start_time = time(9, 00) self.assertEqual(event.earliest_time, self.create_datetime(2016, 1, 1, 9, 00)) # With access time - event.access_at = self.create_datetime(2015, 12, 03, 9, 57) + event.access_at = self.create_datetime(2015, 12, 0o3, 9, 57) self.assertEqual(event.earliest_time, event.access_at) # With meet time - event.meet_at = self.create_datetime(2015, 12, 03, 9, 55) + event.meet_at = self.create_datetime(2015, 12, 0o3, 9, 55) self.assertEqual(event.earliest_time, event.meet_at) # Check order isn't important - event.start_date = date(2015, 12, 03) - self.assertEqual(event.earliest_time, self.create_datetime(2015, 12, 03, 9, 00)) + event.start_date = date(2015, 12, 0o3) + self.assertEqual(event.earliest_time, self.create_datetime(2015, 12, 0o3, 9, 00)) def test_latest_time(self): - event = models.Event(name="TE LT", start_date=date(2016, 01, 01)) + event = models.Event(name="TE LT", start_date=date(2016, 0o1, 0o1)) # Just start date self.assertEqual(event.latest_time, event.start_date) @@ -248,14 +279,14 @@ class EventTestCase(TestCase): # basic checks manager.create(name='TE IB2', start_date='2016-01-02', end_date='2016-01-04'), manager.create(name='TE IB3', start_date='2015-12-31', end_date='2016-01-03'), - manager.create(name='TE IB4', start_date='2016-01-04', access_at='2016-01-03'), - manager.create(name='TE IB5', start_date='2016-01-04', meet_at='2016-01-02'), + manager.create(name='TE IB4', start_date='2016-01-04', access_at=self.create_datetime(2016, 0o1, 0o3, 00, 00)), + manager.create(name='TE IB5', start_date='2016-01-04', meet_at=self.create_datetime(2016, 0o1, 0o2, 00, 00)), # negative check manager.create(name='TE IB6', start_date='2015-12-31', end_date='2016-01-01'), ] - in_bounds = manager.events_in_bounds(datetime(2016, 1, 2), datetime(2016, 1, 3)) + in_bounds = manager.events_in_bounds(self.create_datetime(2016, 1, 2, 0, 0), self.create_datetime(2016, 1, 3, 0, 0)) self.assertIn(events[0], in_bounds) self.assertIn(events[1], in_bounds) self.assertIn(events[2], in_bounds) @@ -327,3 +358,220 @@ class EventPricingTestCase(TestCase): def test_grand_total(self): self.assertEqual(self.e1.total, Decimal('84.48')) self.assertEqual(self.e2.total, Decimal('419.32')) + + +class EventAuthorisationTestCase(TestCase): + def setUp(self): + models.VatRate.objects.create(rate=0.20, comment="TP V1", start_at='2013-01-01') + self.profile = models.Profile.objects.get_or_create( + first_name='Test', + last_name='TEC User', + username='eventauthtest', + email='teccie@functional.test', + is_superuser=True # lazily grant all permissions + )[0] + self.person = models.Person.objects.create(name='Authorisation Test Person') + self.organisation = models.Organisation.objects.create(name='Authorisation Test Organisation') + self.event = models.Event.objects.create(name="AuthorisationTestCase", person=self.person, + start_date=date.today()) + # Add some items + models.EventItem.objects.create(event=self.event, name="Authorisation test item", quantity=2, cost=123.45, + order=1) + + def test_event_property(self): + auth1 = models.EventAuthorisation.objects.create(event=self.event, email="authroisation@model.test.case", + name="Test Auth 1", amount=self.event.total - 1, + sent_by=self.profile) + self.assertFalse(self.event.authorised) + auth1.amount = self.event.total + auth1.save() + self.assertTrue(self.event.authorised) + + def test_last_edited(self): + with reversion.create_revision(): + auth = models.EventAuthorisation.objects.create(event=self.event, email="authroisation@model.test.case", + name="Test Auth", amount=self.event.total, sent_by=self.profile) + self.assertIsNotNone(auth.last_edited_at) + + +class RIGSVersionTestCase(TestCase): + def setUp(self): + models.VatRate.objects.create(rate=0.20, comment="TP V1", start_at='2013-01-01') + + self.profile = models.Profile.objects.get_or_create( + first_name='Test', + last_name='TEC User', + username='eventauthtest', + email='teccie@functional.test', + is_superuser=True # lazily grant all permissions + )[0] + with reversion.create_revision(): + reversion.set_user(self.profile) + self.person = models.Person.objects.create(name='Authorisation Test Person') + + with reversion.create_revision(): + reversion.set_user(self.profile) + self.organisation = models.Organisation.objects.create(name='Authorisation Test Organisation') + + with reversion.create_revision(): + reversion.set_user(self.profile) + self.event = models.Event.objects.create(name="AuthorisationTestCase", person=self.person, + start_date=date.today()) + + with reversion.create_revision(): + reversion.set_user(self.profile) + self.event.notes = "A new note on the event" + self.event.save() + + def test_find_parent_version(self): + # Find the most recent version + currentVersion = versioning.RIGSVersion.objects.get_for_object(self.event).latest(field_name='revision__date_created') + self.assertEqual(currentVersion._object_version.object.notes, "A new note on the event") + + # Check the prev version is loaded correctly + previousVersion = currentVersion.parent + self.assertEqual(previousVersion._object_version.object.notes, None) + + # Check that finding the parent of the first version fails gracefully + self.assertFalse(previousVersion.parent) + + def test_changes_since(self): + # Find the most recent version + currentVersion = versioning.RIGSVersion.objects.get_for_object(self.event).latest(field_name='revision__date_created') + + changes = currentVersion.changes + self.assertEqual(len(changes.field_changes), 1) + + def test_manager(self): + objs = versioning.RIGSVersion.objects.get_for_multiple_models([models.Event, models.Person, models.Organisation]) + self.assertEqual(len(objs), 4) + + def test_text_field_types(self): + with reversion.create_revision(): + reversion.set_user(self.profile) + self.event.name = "New event name" # Simple text + self.event.description = "hello world" # Long text + self.event.save() + + # Find the most recent version + currentVersion = versioning.RIGSVersion.objects.get_for_object(self.event).latest(field_name='revision__date_created') + diff = currentVersion.changes + + # There are two changes + self.assertEqual(len(diff.field_changes), 2) + self.assertFalse(currentVersion.changes.items_changed) + self.assertTrue(currentVersion.changes.fields_changed) + self.assertTrue(currentVersion.changes.anything_changed) + + # Only one has "linebreaks" + self.assertEqual(sum([x.linebreaks for x in diff.field_changes]), 1) + + # None are "long" (email address) + self.assertEqual(sum([x.long for x in diff.field_changes]), 0) + + # Try changing email field in person + with reversion.create_revision(): + reversion.set_user(self.profile) + self.person.email = "hello@world.com" + self.person.save() + + # Find the most recent version + currentVersion = versioning.RIGSVersion.objects.get_for_object(self.person).latest(field_name='revision__date_created') + diff = currentVersion.changes + + # Should be declared as long + self.assertTrue(diff.field_changes[0].long) + + def test_text_diff(self): + with reversion.create_revision(): + reversion.set_user(self.profile) + self.event.notes = "An old note on the event" # Simple text + self.event.save() + + # Find the most recent version + currentVersion = versioning.RIGSVersion.objects.get_for_object(self.event).latest(field_name='revision__date_created') + + # Check the diff is correct + self.assertEqual(currentVersion.changes.field_changes[0].diff, + [{'type': 'equal', 'text': "A"}, + {'type': 'delete', 'text': " new"}, + {'type': 'insert', 'text': "n old"}, + {'type': 'equal', 'text': " note on the event"} + ]) + + def test_choice_field(self): + with reversion.create_revision(): + reversion.set_user(self.profile) + self.event.status = models.Event.CONFIRMED + self.event.save() + + currentVersion = versioning.RIGSVersion.objects.get_for_object(self.event).latest(field_name='revision__date_created') + self.assertEqual(currentVersion.changes.field_changes[0].old, 'Provisional') + self.assertEqual(currentVersion.changes.field_changes[0].new, 'Confirmed') + + def test_creation_behaviour(self): + firstVersion = versioning.RIGSVersion.objects.get_for_object(self.event).latest(field_name='revision__date_created').parent + diff = firstVersion.changes + + # Mainly to check for exceptions: + self.assertTrue(len(diff.field_changes) > 0) + + def test_event_items(self): + with reversion.create_revision(): + reversion.set_user(self.profile) + item1 = models.EventItem.objects.create(event=self.event, name="TI I1", quantity=1, cost=1.00, order=1) + self.event.save() + + # Find the most recent version + currentVersion = versioning.RIGSVersion.objects.get_for_object(self.event).latest(field_name='revision__date_created') + + diffs = currentVersion.changes.item_changes + + self.assertEqual(len(diffs), 1) + self.assertTrue(currentVersion.changes.items_changed) + self.assertFalse(currentVersion.changes.fields_changed) + self.assertTrue(currentVersion.changes.anything_changed) + + self.assertTrue(diffs[0].old is None) + self.assertEqual(diffs[0].new.name, "TI I1") + + # Edit the item + with reversion.create_revision(): + reversion.set_user(self.profile) + item1.name = "New Name" + item1.save() + self.event.save() + + currentVersion = versioning.RIGSVersion.objects.get_for_object(self.event).latest(field_name='revision__date_created') + + diffs = currentVersion.changes.item_changes + + self.assertEqual(len(diffs), 1) + + self.assertEqual(diffs[0].old.name, "TI I1") + self.assertEqual(diffs[0].new.name, "New Name") + + # Check the diff + self.assertEqual(currentVersion.changes.item_changes[0].field_changes[0].diff, + [{'type': 'delete', 'text': "TI I1"}, + {'type': 'insert', 'text': "New Name"}, + ]) + + # Delete the item + + with reversion.create_revision(): + item1.delete() + self.event.save() + + # Find the most recent version + currentVersion = versioning.RIGSVersion.objects.get_for_object(self.event).latest(field_name='revision__date_created') + + diffs = currentVersion.changes.item_changes + + self.assertEqual(len(diffs), 1) + self.assertTrue(currentVersion.changes.items_changed) + self.assertFalse(currentVersion.changes.fields_changed) + self.assertTrue(currentVersion.changes.anything_changed) + + self.assertEqual(diffs[0].old.name, "New Name") + self.assertTrue(diffs[0].new is None) diff --git a/RIGS/test_unit.py b/RIGS/test_unit.py index 75fcca5a..0a360a10 100644 --- a/RIGS/test_unit.py +++ b/RIGS/test_unit.py @@ -1,9 +1,13 @@ -from django.core.urlresolvers import reverse -from django.test import TestCase from datetime import date -from RIGS import models from django.core.exceptions import ObjectDoesNotExist +from django.core.management import call_command +from django.urls import reverse +from django.test import TestCase +from django.test.utils import override_settings + +from RIGS import models +from reversion import revisions as reversion class TestAdminMergeObjects(TestCase): @@ -50,19 +54,19 @@ class TestAdminMergeObjects(TestCase): change_url = reverse('admin:RIGS_venue_changelist') data = { 'action': 'merge', - '_selected_action': [unicode(val.pk) for key, val in self.venues.iteritems()] + '_selected_action': [str(val.pk) for key, val in self.venues.items()] } response = self.client.post(change_url, data, follow=True) self.assertContains(response, "The following objects will be merged") - for key, venue in self.venues.iteritems(): + for key, venue in self.venues.items(): self.assertContains(response, venue.name) def test_merge_no_master(self): change_url = reverse('admin:RIGS_venue_changelist') data = {'action': 'merge', - '_selected_action': [unicode(val.pk) for key, val in self.venues.iteritems()], + '_selected_action': [str(val.pk) for key, val in self.venues.items()], 'post': 'yes', } response = self.client.post(change_url, data, follow=True) @@ -73,7 +77,7 @@ class TestAdminMergeObjects(TestCase): change_url = reverse('admin:RIGS_venue_changelist') data = {'action': 'merge', - '_selected_action': [unicode(self.venues[1].pk), unicode(self.venues[2].pk)], + '_selected_action': [str(self.venues[1].pk), str(self.venues[2].pk)], 'post': 'yes', 'master': self.venues[1].pk } @@ -92,7 +96,7 @@ class TestAdminMergeObjects(TestCase): self.assertEqual(models.Venue.objects.get(pk=self.venues[3].pk), self.venues[3]) # Check the events have been moved to the master venue - for key, event in self.events.iteritems(): + for key, event in self.events.items(): updatedEvent = models.Event.objects.get(pk=event.pk) if event.venue == self.venues[3]: # The one we left in place continue @@ -102,7 +106,7 @@ class TestAdminMergeObjects(TestCase): change_url = reverse('admin:RIGS_person_changelist') data = {'action': 'merge', - '_selected_action': [unicode(self.persons[1].pk), unicode(self.persons[2].pk)], + '_selected_action': [str(self.persons[1].pk), str(self.persons[2].pk)], 'post': 'yes', 'master': self.persons[1].pk } @@ -121,7 +125,7 @@ class TestAdminMergeObjects(TestCase): self.assertEqual(models.Person.objects.get(pk=self.persons[3].pk), self.persons[3]) # Check the events have been moved to the master person - for key, event in self.events.iteritems(): + for key, event in self.events.items(): updatedEvent = models.Event.objects.get(pk=event.pk) if event.person == self.persons[3]: # The one we left in place continue @@ -131,7 +135,7 @@ class TestAdminMergeObjects(TestCase): change_url = reverse('admin:RIGS_organisation_changelist') data = {'action': 'merge', - '_selected_action': [unicode(self.organisations[1].pk), unicode(self.organisations[2].pk)], + '_selected_action': [str(self.organisations[1].pk), str(self.organisations[2].pk)], 'post': 'yes', 'master': self.organisations[1].pk } @@ -150,8 +154,272 @@ class TestAdminMergeObjects(TestCase): self.assertEqual(models.Organisation.objects.get(pk=self.organisations[3].pk), self.organisations[3]) # Check the events have been moved to the master organisation - for key, event in self.events.iteritems(): + for key, event in self.events.items(): updatedEvent = models.Event.objects.get(pk=event.pk) if event.organisation == self.organisations[3]: # The one we left in place continue self.assertEqual(updatedEvent.organisation, self.organisations[1]) + + +class TestInvoiceDelete(TestCase): + @classmethod + def setUpTestData(cls): + cls.profile = models.Profile.objects.create(username="testuser1", email="1@test.com", is_superuser=True, is_active=True, is_staff=True) + + cls.vatrate = models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1') + + cls.events = { + 1: models.Event.objects.create(name="TE E1", start_date=date.today()), + 2: models.Event.objects.create(name="TE E2", start_date=date.today()) + } + + cls.invoices = { + 1: models.Invoice.objects.create(event=cls.events[1]), + 2: models.Invoice.objects.create(event=cls.events[2]) + } + + cls.payments = { + 1: models.Payment.objects.create(invoice=cls.invoices[1], date=date.today(), amount=12.34, method=models.Payment.CASH) + } + + def setUp(self): + self.profile.set_password('testuser') + self.profile.save() + self.assertTrue(self.client.login(username=self.profile.username, password='testuser')) + + def test_invoice_delete_allowed(self): + request_url = reverse('invoice_delete', kwargs={'pk': self.invoices[2].pk}) + + response = self.client.get(request_url, follow=True) + self.assertContains(response, "Are you sure") + + # Check the invoice still exists + self.assertTrue(models.Invoice.objects.get(pk=self.invoices[2].pk)) + + # Actually delete it + response = self.client.post(request_url, follow=True) + + # Check the invoice is deleted + self.assertRaises(ObjectDoesNotExist, models.Invoice.objects.get, pk=self.invoices[2].pk) + + def test_invoice_delete_not_allowed(self): + request_url = reverse('invoice_delete', kwargs={'pk': self.invoices[1].pk}) + + response = self.client.get(request_url, follow=True) + self.assertContains(response, "To delete an invoice, delete the payments first.") + + # Check the invoice still exists + self.assertTrue(models.Invoice.objects.get(pk=self.invoices[1].pk)) + + # Try to actually delete it + response = self.client.post(request_url, follow=True) + + # Check this didn't work + self.assertTrue(models.Invoice.objects.get(pk=self.invoices[1].pk)) + + +class TestPrintPaperwork(TestCase): + @classmethod + def setUpTestData(cls): + cls.profile = models.Profile.objects.create(username="testuser1", email="1@test.com", is_superuser=True, is_active=True, is_staff=True) + + cls.vatrate = models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1') + + cls.events = { + 1: models.Event.objects.create(name="TE E1", start_date=date.today()), + } + + cls.invoices = { + 1: models.Invoice.objects.create(event=cls.events[1]), + } + + def setUp(self): + self.profile.set_password('testuser') + self.profile.save() + self.assertTrue(self.client.login(username=self.profile.username, password='testuser')) + + def test_print_paperwork_success(self): + request_url = reverse('event_print', kwargs={'pk': self.events[1].pk}) + + response = self.client.get(request_url, follow=True) + self.assertEqual(response.status_code, 200) + + def test_print_invoice_success(self): + request_url = reverse('invoice_print', kwargs={'pk': self.invoices[1].pk}) + + response = self.client.get(request_url, follow=True) + self.assertEqual(response.status_code, 200) + + +class TestVersioningViews(TestCase): + @classmethod + def setUpTestData(cls): + cls.profile = models.Profile.objects.create(username="testuser1", email="1@test.com", is_superuser=True, is_active=True, is_staff=True) + + cls.vatrate = models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1') + + cls.events = {} + + with reversion.create_revision(): + reversion.set_user(cls.profile) + cls.events[1] = models.Event.objects.create(name="TE E1", start_date=date.today()) + + with reversion.create_revision(): + reversion.set_user(cls.profile) + cls.events[2] = models.Event.objects.create(name="TE E2", start_date='2014-03-05') + + with reversion.create_revision(): + reversion.set_user(cls.profile) + cls.events[1].description = "A test description" + cls.events[1].save() + + def setUp(self): + self.profile.set_password('testuser') + self.profile.save() + self.assertTrue(self.client.login(username=self.profile.username, password='testuser')) + + def test_event_history_loads_successfully(self): + request_url = reverse('event_history', kwargs={'pk': self.events[1].pk}) + + response = self.client.get(request_url, follow=True) + self.assertEqual(response.status_code, 200) + + def test_activity_feed_loads_successfully(self): + request_url = reverse('activity_feed') + + response = self.client.get(request_url, follow=True) + self.assertEqual(response.status_code, 200) + + def test_activity_table_loads_successfully(self): + request_url = reverse('activity_table') + + response = self.client.get(request_url, follow=True) + self.assertEqual(response.status_code, 200) + + # Some edge cases that have caused server errors in the past + def test_deleted_event(self): + request_url = reverse('activity_feed') + + self.events[2].delete() + + response = self.client.get(request_url, follow=True) + self.assertContains(response, "TE E2") + self.assertEqual(response.status_code, 200) + + def test_deleted_relation(self): + request_url = reverse('activity_feed') + + with reversion.create_revision(): + person = models.Person.objects.create(name="Test Person") + with reversion.create_revision(): + self.events[1].person = person + self.events[1].save() + + # Check response contains person + response = self.client.get(request_url, follow=True) + self.assertContains(response, "Test Person") + self.assertEqual(response.status_code, 200) + + # Delete person + person.delete() + + # Check response still contains person + response = self.client.get(request_url, follow=True) + self.assertContains(response, "Test Person") + self.assertEqual(response.status_code, 200) + + +class TestEmbeddedViews(TestCase): + @classmethod + def setUpTestData(cls): + cls.profile = models.Profile.objects.create(username="testuser1", email="1@test.com", is_superuser=True, is_active=True, is_staff=True) + + cls.events = { + 1: models.Event.objects.create(name="TE E1", start_date=date.today()), + 2: models.Event.objects.create(name="TE E2", start_date=date.today()) + } + + cls.invoices = { + 1: models.Invoice.objects.create(event=cls.events[1]), + 2: models.Invoice.objects.create(event=cls.events[2]) + } + + cls.payments = { + 1: models.Payment.objects.create(invoice=cls.invoices[1], date=date.today(), amount=12.34, method=models.Payment.CASH) + } + + def setUp(self): + self.profile.set_password('testuser') + self.profile.save() + + def testLoginRedirect(self): + request_url = reverse('event_embed', kwargs={'pk': 1}) + expected_url = "{0}?next={1}".format(reverse('login_embed'), request_url) + + # Request the page and check it redirects + response = self.client.get(request_url, follow=True) + self.assertRedirects(response, expected_url, status_code=302, target_status_code=200) + + # Now login + self.assertTrue(self.client.login(username=self.profile.username, password='testuser')) + + # And check that it no longer redirects + response = self.client.get(request_url, follow=True) + self.assertEqual(len(response.redirect_chain), 0) + + def testLoginCookieWarning(self): + login_url = reverse('login_embed') + response = self.client.post(login_url, follow=True) + self.assertContains(response, "Cookies do not seem to be enabled") + + def testXFrameHeaders(self): + event_url = reverse('event_embed', kwargs={'pk': 1}) + login_url = reverse('login_embed') + + self.assertTrue(self.client.login(username=self.profile.username, password='testuser')) + + response = self.client.get(event_url, follow=True) + with self.assertRaises(KeyError): + response._headers["X-Frame-Options"] + + response = self.client.get(login_url, follow=True) + with self.assertRaises(KeyError): + response._headers["X-Frame-Options"] + + def testOEmbed(self): + event_url = reverse('event_detail', kwargs={'pk': 1}) + event_embed_url = reverse('event_embed', kwargs={'pk': 1}) + oembed_url = reverse('event_oembed', kwargs={'pk': 1}) + + alt_oembed_url = reverse('event_oembed', kwargs={'pk': 999}) + alt_event_embed_url = reverse('event_embed', kwargs={'pk': 999}) + + # Test the meta tag is in place + response = self.client.get(event_url, follow=True, HTTP_HOST='example.com') + self.assertContains(response, ' 100) + + def test_production_exception(self): + from django.core.management.base import CommandError + + self.assertRaisesRegex(CommandError, ".*production", call_command, 'generateSampleRIGSData') diff --git a/RIGS/urls.py b/RIGS/urls.py index c76e8ea8..e3b31f2e 100644 --- a/RIGS/urls.py +++ b/RIGS/urls.py @@ -1,160 +1,201 @@ -from django.conf.urls import patterns, include, url +from django.conf.urls import url +from django.contrib.auth.views import password_reset + from django.contrib.auth.decorators import login_required from RIGS import models, views, rigboard, finance, ical, versioning, forms from django.views.generic import RedirectView +from django.views.decorators.clickjacking import xframe_options_exempt from PyRIGS.decorators import permission_required_with_403 from PyRIGS.decorators import api_key_required -urlpatterns = patterns('', - # Examples: - # url(r'^$', 'PyRIGS.views.home', name='home'), - # url(r'^blog/', include('blog.urls')), - url('^$', login_required(views.Index.as_view()), name='index'), - url(r'^closemodal/$', views.CloseModal.as_view(), name='closemodal'), +urlpatterns = [ + # Examples: + # url(r'^$', 'PyRIGS.views.home', name='home'), + # url(r'^blog/', include('blog.urls')), + url('^$', login_required(views.Index.as_view()), name='index'), + url(r'^closemodal/$', views.CloseModal.as_view(), name='closemodal'), - url(r'^search_help/$', views.SearchHelp.as_view(), name='search_help'), + url('^user/login/$', views.login, name='login'), + url('^user/login/embed/$', xframe_options_exempt(views.login_embed), name='login_embed'), - url('^user/login/$', 'RIGS.views.login', name='login'), - url(r'^user/password_reset/$', 'django.contrib.auth.views.password_reset', {'password_reset_form':forms.PasswordReset}), + url(r'^user/password_reset/$', views.PasswordResetDisabled.as_view()), - # People - url(r'^people/$', permission_required_with_403('RIGS.view_person')(views.PersonList.as_view()), - name='person_list'), - url(r'^people/add/$', - permission_required_with_403('RIGS.add_person')(views.PersonCreate.as_view()), - name='person_create'), - url(r'^people/(?P\d+)/$', - permission_required_with_403('RIGS.view_person')(views.PersonDetail.as_view()), - name='person_detail'), - url(r'^people/(?P\d+)/history/$', - permission_required_with_403('RIGS.view_person')(versioning.VersionHistory.as_view()), - name='person_history', kwargs={'model': models.Person}), - url(r'^people/(?P\d+)/edit/$', - permission_required_with_403('RIGS.change_person')(views.PersonUpdate.as_view()), - name='person_update'), + url(r'^search_help/$', views.SearchHelp.as_view(), name='search_help'), - # Organisations - url(r'^organisations/$', - permission_required_with_403('RIGS.view_organisation')(views.OrganisationList.as_view()), - name='organisation_list'), - url(r'^organisations/add/$', - permission_required_with_403('RIGS.add_organisation')(views.OrganisationCreate.as_view()), - name='organisation_create'), - url(r'^organisations/(?P\d+)/$', - permission_required_with_403('RIGS.view_organisation')(views.OrganisationDetail.as_view()), - name='organisation_detail'), - url(r'^organisations/(?P\d+)/history/$', - permission_required_with_403('RIGS.view_organisation')(versioning.VersionHistory.as_view()), - name='organisation_history', kwargs={'model': models.Organisation}), - url(r'^organisations/(?P\d+)/edit/$', - permission_required_with_403('RIGS.change_organisation')(views.OrganisationUpdate.as_view()), - name='organisation_update'), + # People + url(r'^people/$', permission_required_with_403('RIGS.view_person')(views.PersonList.as_view()), + name='person_list'), + url(r'^people/add/$', + permission_required_with_403('RIGS.add_person')(views.PersonCreate.as_view()), + name='person_create'), + url(r'^people/(?P\d+)/$', + permission_required_with_403('RIGS.view_person')(views.PersonDetail.as_view()), + name='person_detail'), + url(r'^people/(?P\d+)/history/$', + permission_required_with_403('RIGS.view_person')(versioning.VersionHistory.as_view()), + name='person_history', kwargs={'model': models.Person}), + url(r'^people/(?P\d+)/edit/$', + permission_required_with_403('RIGS.change_person')(views.PersonUpdate.as_view()), + name='person_update'), - # Venues - url(r'^venues/$', - permission_required_with_403('RIGS.view_venue')(views.VenueList.as_view()), - name='venue_list'), - url(r'^venues/add/$', - permission_required_with_403('RIGS.add_venue')(views.VenueCreate.as_view()), - name='venue_create'), - url(r'^venues/(?P\d+)/$', - permission_required_with_403('RIGS.view_venue')(views.VenueDetail.as_view()), - name='venue_detail'), - url(r'^venues/(?P\d+)/history/$', - permission_required_with_403('RIGS.view_venue')(versioning.VersionHistory.as_view()), - name='venue_history', kwargs={'model': models.Venue}), - url(r'^venues/(?P\d+)/edit/$', - permission_required_with_403('RIGS.change_venue')(views.VenueUpdate.as_view()), - name='venue_update'), + # Organisations + url(r'^organisations/$', + permission_required_with_403('RIGS.view_organisation')(views.OrganisationList.as_view()), + name='organisation_list'), + url(r'^organisations/add/$', + permission_required_with_403('RIGS.add_organisation')(views.OrganisationCreate.as_view()), + name='organisation_create'), + url(r'^organisations/(?P\d+)/$', + permission_required_with_403('RIGS.view_organisation')(views.OrganisationDetail.as_view()), + name='organisation_detail'), + url(r'^organisations/(?P\d+)/history/$', + permission_required_with_403('RIGS.view_organisation')(versioning.VersionHistory.as_view()), + name='organisation_history', kwargs={'model': models.Organisation}), + url(r'^organisations/(?P\d+)/edit/$', + permission_required_with_403('RIGS.change_organisation')(views.OrganisationUpdate.as_view()), + name='organisation_update'), - # Rigboard - url(r'^rigboard/$', login_required(rigboard.RigboardIndex.as_view()), name='rigboard'), - url(r'^rigboard/calendar/$', login_required()(rigboard.WebCalendar.as_view()), name='web_calendar'), - url(r'^rigboard/calendar/(?P(month|week|day))/$', login_required()(rigboard.WebCalendar.as_view()), name='web_calendar'), - url(r'^rigboard/calendar/(?P(month|week|day))/(?P(\d{4}-\d{2}-\d{2}))/$', login_required()(rigboard.WebCalendar.as_view()), name='web_calendar'), - url(r'^rigboard/archive/$', RedirectView.as_view(permanent=True,pattern_name='event_archive')), - url(r'^rigboard/activity/$', - permission_required_with_403('RIGS.view_event')(versioning.ActivityTable.as_view()), - name='activity_table'), - url(r'^rigboard/activity/feed/$', - permission_required_with_403('RIGS.view_event')(versioning.ActivityFeed.as_view()), - name='activity_feed'), + # Venues + url(r'^venues/$', + permission_required_with_403('RIGS.view_venue')(views.VenueList.as_view()), + name='venue_list'), + url(r'^venues/add/$', + permission_required_with_403('RIGS.add_venue')(views.VenueCreate.as_view()), + name='venue_create'), + url(r'^venues/(?P\d+)/$', + permission_required_with_403('RIGS.view_venue')(views.VenueDetail.as_view()), + name='venue_detail'), + url(r'^venues/(?P\d+)/history/$', + permission_required_with_403('RIGS.view_venue')(versioning.VersionHistory.as_view()), + name='venue_history', kwargs={'model': models.Venue}), + url(r'^venues/(?P\d+)/edit/$', + permission_required_with_403('RIGS.change_venue')(views.VenueUpdate.as_view()), + name='venue_update'), - url(r'^event/(?P\d+)/$', - permission_required_with_403('RIGS.view_event')(rigboard.EventDetail.as_view()), - name='event_detail'), - url(r'^event/(?P\d+)/print/$', - permission_required_with_403('RIGS.view_event')(rigboard.EventPrint.as_view()), - name='event_print'), - url(r'^event/create/$', - permission_required_with_403('RIGS.add_event')(rigboard.EventCreate.as_view()), - name='event_create'), - url(r'^event/(?P\d+)/edit/$', - permission_required_with_403('RIGS.change_event')(rigboard.EventUpdate.as_view()), - name='event_update'), - url(r'^event/(?P\d+)/duplicate/$', - permission_required_with_403('RIGS.add_event')(rigboard.EventDuplicate.as_view()), - name='event_duplicate'), - url(r'^event/archive/$', login_required()(rigboard.EventArchive.as_view()), - name='event_archive'), + # Rigboard + url(r'^rigboard/$', login_required(rigboard.RigboardIndex.as_view()), name='rigboard'), + url(r'^rigboard/calendar/$', login_required()(rigboard.WebCalendar.as_view()), + name='web_calendar'), + url(r'^rigboard/calendar/(?P(month|week|day))/$', + login_required()(rigboard.WebCalendar.as_view()), name='web_calendar'), + url(r'^rigboard/calendar/(?P(month|week|day))/(?P(\d{4}-\d{2}-\d{2}))/$', + login_required()(rigboard.WebCalendar.as_view()), name='web_calendar'), + url(r'^rigboard/archive/$', RedirectView.as_view(permanent=True, pattern_name='event_archive')), + url(r'^rigboard/activity/$', + permission_required_with_403('RIGS.view_event')(versioning.ActivityTable.as_view()), + name='activity_table'), + url(r'^rigboard/activity/feed/$', + permission_required_with_403('RIGS.view_event')(versioning.ActivityFeed.as_view()), + name='activity_feed'), - url(r'^event/(?P\d+)/history/$', - permission_required_with_403('RIGS.view_event')(versioning.VersionHistory.as_view()), - name='event_history', kwargs={'model': models.Event}), + url(r'^event/(?P\d+)/$', + permission_required_with_403('RIGS.view_event', oembed_view="event_oembed")( + rigboard.EventDetail.as_view()), + name='event_detail'), + url(r'^event/(?P\d+)/embed/$', + xframe_options_exempt( + login_required(login_url='/user/login/embed/')(rigboard.EventEmbed.as_view())), + name='event_embed'), + url(r'^event/(?P\d+)/oembed_json/$', + rigboard.EventOembed.as_view(), + name='event_oembed'), + url(r'^event/(?P\d+)/print/$', + permission_required_with_403('RIGS.view_event')(rigboard.EventPrint.as_view()), + name='event_print'), + url(r'^event/(?P\d+)/ra/$', + permission_required_with_403('RIGS.change_event')(rigboard.EventRA.as_view()), + name='event_ra'), + url(r'^event/create/$', + permission_required_with_403('RIGS.add_event')(rigboard.EventCreate.as_view()), + name='event_create'), + url(r'^event/(?P\d+)/edit/$', + permission_required_with_403('RIGS.change_event')(rigboard.EventUpdate.as_view()), + name='event_update'), + url(r'^event/(?P\d+)/duplicate/$', + permission_required_with_403('RIGS.add_event')(rigboard.EventDuplicate.as_view()), + name='event_duplicate'), + url(r'^event/archive/$', login_required()(rigboard.EventArchive.as_view()), + name='event_archive'), - + url(r'^event/(?P\d+)/history/$', + permission_required_with_403('RIGS.view_event')(versioning.VersionHistory.as_view()), + name='event_history', kwargs={'model': models.Event}), - # Finance - url(r'^invoice/$', - permission_required_with_403('RIGS.view_invoice')(finance.InvoiceIndex.as_view()), - name='invoice_list'), - url(r'^invoice/archive/$', - permission_required_with_403('RIGS.view_invoice')(finance.InvoiceArchive.as_view()), - name='invoice_archive'), - url(r'^invoice/waiting/$', - permission_required_with_403('RIGS.add_invoice')(finance.InvoiceWaiting.as_view()), - name='invoice_waiting'), + # Finance + url(r'^invoice/$', + permission_required_with_403('RIGS.view_invoice')(finance.InvoiceIndex.as_view()), + name='invoice_list'), + url(r'^invoice/archive/$', + permission_required_with_403('RIGS.view_invoice')(finance.InvoiceArchive.as_view()), + name='invoice_archive'), + url(r'^invoice/waiting/$', + permission_required_with_403('RIGS.add_invoice')(finance.InvoiceWaiting.as_view()), + name='invoice_waiting'), - url(r'^event/(?P\d+)/invoice/$', - permission_required_with_403('RIGS.add_invoice')(finance.InvoiceEvent.as_view()), - name='invoice_event'), + url(r'^event/(?P\d+)/invoice/$', + permission_required_with_403('RIGS.add_invoice')(finance.InvoiceEvent.as_view()), + name='invoice_event'), - url(r'^invoice/(?P\d+)/$', - permission_required_with_403('RIGS.view_invoice')(finance.InvoiceDetail.as_view()), - name='invoice_detail'), - url(r'^invoice/(?P\d+)/print/$', - permission_required_with_403('RIGS.view_invoice')(finance.InvoicePrint.as_view()), - name='invoice_print'), - url(r'^invoice/(?P\d+)/void/$', - permission_required_with_403('RIGS.change_invoice')(finance.InvoiceVoid.as_view()), - name='invoice_void'), - url(r'^payment/create/$', - permission_required_with_403('RIGS.add_payment')(finance.PaymentCreate.as_view()), - name='payment_create'), - url(r'^payment/(?P\d+)/delete/$', - permission_required_with_403('RIGS.add_payment')(finance.PaymentDelete.as_view()), - name='payment_delete'), + url(r'^invoice/(?P\d+)/$', + permission_required_with_403('RIGS.view_invoice')(finance.InvoiceDetail.as_view()), + name='invoice_detail'), + url(r'^invoice/(?P\d+)/print/$', + permission_required_with_403('RIGS.view_invoice')(finance.InvoicePrint.as_view()), + name='invoice_print'), + url(r'^invoice/(?P\d+)/void/$', + permission_required_with_403('RIGS.change_invoice')(finance.InvoiceVoid.as_view()), + name='invoice_void'), + url(r'^invoice/(?P\d+)/delete/$', + permission_required_with_403('RIGS.change_invoice')(finance.InvoiceDelete.as_view()), + name='invoice_delete'), + url(r'^payment/create/$', + permission_required_with_403('RIGS.add_payment')(finance.PaymentCreate.as_view()), + name='payment_create'), + url(r'^payment/(?P\d+)/delete/$', + permission_required_with_403('RIGS.add_payment')(finance.PaymentDelete.as_view()), + name='payment_delete'), - # User editing - url(r'^user/$', login_required(views.ProfileDetail.as_view()), name='profile_detail'), - url(r'^user/(?P\d+)/$', - permission_required_with_403('RIGS.view_profile')(views.ProfileDetail.as_view()), - name='profile_detail'), - url(r'^user/edit/$', login_required(views.ProfileUpdateSelf.as_view()), - name='profile_update_self'), - url(r'^user/reset_api_key$', login_required(views.ResetApiKey.as_view(permanent=False)), name='reset_api_key'), + # Client event authorisation + url(r'^event/(?P\d+)/auth/$', + permission_required_with_403('RIGS.change_event')( + rigboard.EventAuthorisationRequest.as_view() + ), + name='event_authorise_request'), + url(r'^event/(?P\d+)/auth/preview/$', + permission_required_with_403('RIGS.change_event')( + rigboard.EventAuthoriseRequestEmailPreview.as_view() + ), + name='event_authorise_preview'), + url(r'^event/(?P\d+)/(?P[-:\w]+)/$', rigboard.EventAuthorise.as_view(), + name='event_authorise'), - # ICS Calendar - API key authentication - url(r'^ical/(?P\d+)/(?P\w+)/rigs.ics$', api_key_required(ical.CalendarICS()), name="ics_calendar"), + # User editing + url(r'^user/$', login_required(views.ProfileDetail.as_view()), name='profile_detail'), + url(r'^user/(?P\d+)/$', + permission_required_with_403('RIGS.view_profile')(views.ProfileDetail.as_view()), + name='profile_detail'), + url(r'^user/edit/$', login_required(views.ProfileUpdateSelf.as_view()), + name='profile_update_self'), + url(r'^user/reset_api_key$', login_required(views.ResetApiKey.as_view(permanent=False)), + name='reset_api_key'), - # API - url(r'^api/(?P\w+)/$', login_required(views.SecureAPIRequest.as_view()), name="api_secure"), - url(r'^api/(?P\w+)/(?P\d+)/$', login_required(views.SecureAPIRequest.as_view()), name="api_secure"), + # ICS Calendar - API key authentication + url(r'^ical/(?P\d+)/(?P\w+)/rigs.ics$', api_key_required(ical.CalendarICS()), + name="ics_calendar"), - # Legacy URL's - url(r'^rig/show/(?P\d+)/$', RedirectView.as_view(permanent=True,pattern_name='event_detail')), - url(r'^bookings/$', RedirectView.as_view(permanent=True,pattern_name='rigboard')), - url(r'^bookings/past/$', RedirectView.as_view(permanent=True,pattern_name='event_archive')), -) + # API + url(r'^api/(?P\w+)/$', login_required(views.SecureAPIRequest.as_view()), + name="api_secure"), + url(r'^api/(?P\w+)/(?P\d+)/$', login_required(views.SecureAPIRequest.as_view()), + name="api_secure"), + # Risk assessment API + url(r'^log_risk_assessment/$', rigboard.LogRiskAssessment.as_view(), name='log_risk_assessment'), + + # Legacy URL's + url(r'^rig/show/(?P\d+)/$', + RedirectView.as_view(permanent=True, pattern_name='event_detail')), + url(r'^bookings/$', RedirectView.as_view(permanent=True, pattern_name='rigboard')), + url(r'^bookings/past/$', RedirectView.as_view(permanent=True, pattern_name='event_archive')), +] diff --git a/RIGS/versioning.py b/RIGS/versioning.py index 6d524fc0..9dff4a3e 100644 --- a/RIGS/versioning.py +++ b/RIGS/versioning.py @@ -1,303 +1,258 @@ + + import logging +import datetime from django.core.exceptions import ObjectDoesNotExist from django.shortcuts import get_object_or_404 from django.views import generic - -# Versioning -import reversion -from reversion.models import Version -from django.contrib.contenttypes.models import ContentType # Used to lookup the content_type +from django.utils.functional import cached_property from django.db.models import IntegerField, EmailField, TextField +from django.contrib.contenttypes.models import ContentType + +from reversion.models import Version, VersionQuerySet from diff_match_patch import diff_match_patch from RIGS import models -import datetime logger = logging.getLogger('tec.pyrigs') -def model_compare(oldObj, newObj, excluded_keys=[]): - # recieves two objects of the same model, and compares them. Returns an array of FieldCompare objects - try: - theFields = oldObj._meta.fields # This becomes deprecated in Django 1.8!!!!!!!!!!!!! (but an alternative becomes available) - except AttributeError: - theFields = newObj._meta.fields +class FieldComparison(object): + def __init__(self, field=None, old=None, new=None): + self.field = field + self._old = old + self._new = new - class FieldCompare(object): - def __init__(self, field=None, old=None, new=None): - self.field = field - self._old = old - self._new = new + def display_value(self, value): + if isinstance(self.field, IntegerField) and len(self.field.choices) > 0: + return [x[1] for x in self.field.choices if x[0] == value][0] + if self.field.name == "risk_assessment_edit_url": + return "completed" if value else "" + return value - def display_value(self, value): - if isinstance(self.field, IntegerField) and len(self.field.choices) > 0: - return [x[1] for x in self.field.choices if x[0] == value][0] - return value + @property + def old(self): + return self.display_value(self._old) - @property - def old(self): - return self.display_value(self._old) + @property + def new(self): + return self.display_value(self._new) - @property - def new(self): - return self.display_value(self._new) - - @property - def long(self): - if isinstance(self.field, EmailField): - return True - return False - - @property - def linebreaks(self): - if isinstance(self.field, TextField): - return True - return False - - @property - def diff(self): - oldText = unicode(self.display_value(self._old)) or "" - newText = unicode(self.display_value(self._new)) or "" - dmp = diff_match_patch() - diffs = dmp.diff_main(oldText, newText) - dmp.diff_cleanupSemantic(diffs) - - outputDiffs = [] - - for (op, data) in diffs: - if op == dmp.DIFF_INSERT: - outputDiffs.append({'type': 'insert', 'text': data}) - elif op == dmp.DIFF_DELETE: - outputDiffs.append({'type': 'delete', 'text': data}) - elif op == dmp.DIFF_EQUAL: - outputDiffs.append({'type': 'equal', 'text': data}) - return outputDiffs - - changes = [] - - for thisField in theFields: - name = thisField.name - - if name in excluded_keys: - continue # if we're excluding this field, skip over it - - try: - oldValue = getattr(oldObj, name, None) - except ObjectDoesNotExist: - oldValue = None - - try: - newValue = getattr(newObj, name, None) - except ObjectDoesNotExist: - newValue = None - - try: - bothBlank = (not oldValue) and (not newValue) - if oldValue != newValue and not bothBlank: - compare = FieldCompare(thisField, oldValue, newValue) - changes.append(compare) - except TypeError: # logs issues with naive vs tz-aware datetimes - logger.error('TypeError when comparing models') - - return changes - - -def compare_event_items(old, new): - # Recieves two event version objects and compares their items, returns an array of ItemCompare objects - - item_type = ContentType.objects.get_for_model(models.EventItem) - old_item_versions = old.revision.version_set.filter(content_type=item_type) - new_item_versions = new.revision.version_set.filter(content_type=item_type) - - class ItemCompare(object): - def __init__(self, old=None, new=None, changes=None): - self.old = old - self.new = new - self.changes = changes - - # Build some dicts of what we have - item_dict = {} # build a list of items, key is the item_pk - for version in old_item_versions: # put all the old versions in a list - if version.field_dict["event"] == old.object_id_int: - compare = ItemCompare(old=version.object_version.object) - item_dict[version.object_id] = compare - - for version in new_item_versions: # go through the new versions - if version.field_dict["event"] == new.object_id_int: - try: - compare = item_dict[version.object_id] # see if there's a matching old version - compare.new = version.object_version.object # then add the new version to the dictionary - except KeyError: # there's no matching old version, so add this item to the dictionary by itself - compare = ItemCompare(new=version.object_version.object) - - item_dict[version.object_id] = compare # update the dictionary with the changes - - changes = [] - for (_, compare) in item_dict.items(): - compare.changes = model_compare(compare.old, compare.new, ['id', 'event', 'order']) # see what's changed - if len(compare.changes) >= 1: - changes.append(compare) # transfer into a sequential array to make it easier to deal with later - - return changes - - -def get_versions_for_model(models): - content_types = [] - for model in models: - content_types.append(ContentType.objects.get_for_model(model)) - - versions = reversion.models.Version.objects.filter( - content_type__in=content_types, - ).select_related("revision").order_by("-pk") - - return versions - - -def get_previous_version(version): - thisId = version.object_id - thisVersionId = version.pk - - versions = reversion.get_for_object_reference(version.content_type.model_class(), thisId) - - try: - previousVersions = versions.filter(revision_id__lt=version.revision_id).latest( - field_name='revision__date_created') - except ObjectDoesNotExist: + @property + def long(self): + if isinstance(self.field, EmailField): + return True return False - return previousVersions + @property + def linebreaks(self): + if isinstance(self.field, TextField): + return True + return False + + @property + def diff(self): + oldText = str(self.display_value(self._old)) or "" + newText = str(self.display_value(self._new)) or "" + dmp = diff_match_patch() + diffs = dmp.diff_main(oldText, newText) + dmp.diff_cleanupSemantic(diffs) + + outputDiffs = [] + + for (op, data) in diffs: + if op == dmp.DIFF_INSERT: + outputDiffs.append({'type': 'insert', 'text': data}) + elif op == dmp.DIFF_DELETE: + outputDiffs.append({'type': 'delete', 'text': data}) + elif op == dmp.DIFF_EQUAL: + outputDiffs.append({'type': 'equal', 'text': data}) + return outputDiffs -def get_changes_for_version(newVersion, oldVersion=None): - # Pass in a previous version if you already know it (for efficiancy) - # if not provided then it will be looked up in the database +class ModelComparison(object): - if oldVersion == None: - oldVersion = get_previous_version(newVersion) + def __init__(self, old=None, new=None, version=None, excluded_keys=[]): + # recieves two objects of the same model, and compares them. Returns an array of FieldCompare objects + try: + self.fields = old._meta.get_fields() + except AttributeError: + self.fields = new._meta.get_fields() - modelClass = newVersion.content_type.model_class() + self.old = old + self.new = new + self.excluded_keys = excluded_keys + self.version = version - compare = { - 'revision': newVersion.revision, - 'new': newVersion.object_version.object, - 'current': modelClass.objects.filter(pk=newVersion.pk).first(), - 'version': newVersion, + @cached_property + def revision(self): + return self.version.revision - # Old things that may not be used - 'old': None, - 'field_changes': None, - 'item_changes': None, - } + @cached_property + def field_changes(self): + changes = [] + for field in self.fields: + field_name = field.name - if oldVersion: - compare['old'] = oldVersion.object_version.object - compare['field_changes'] = model_compare(compare['old'], compare['new']) - compare['item_changes'] = compare_event_items(oldVersion, newVersion) + if field_name in self.excluded_keys: + continue # if we're excluding this field, skip over it - return compare + try: + oldValue = getattr(self.old, field_name, None) + except ObjectDoesNotExist: + oldValue = None + + try: + newValue = getattr(self.new, field_name, None) + except ObjectDoesNotExist: + newValue = None + + bothBlank = (not oldValue) and (not newValue) + if oldValue != newValue and not bothBlank: + comparison = FieldComparison(field, oldValue, newValue) + changes.append(comparison) + + return changes + + @cached_property + def fields_changed(self): + return len(self.field_changes) > 0 + + @cached_property + def item_changes(self): + # Recieves two event version objects and compares their items, returns an array of ItemCompare objects + + item_type = ContentType.objects.get_for_model(models.EventItem) + old_item_versions = self.version.parent.revision.version_set.filter(content_type=item_type) + new_item_versions = self.version.revision.version_set.filter(content_type=item_type) + + comparisonParams = {'excluded_keys': ['id', 'event', 'order']} + + # Build some dicts of what we have + item_dict = {} # build a list of items, key is the item_pk + for version in old_item_versions: # put all the old versions in a list + if version.field_dict["event_id"] == int(self.new.pk): + compare = ModelComparison(old=version._object_version.object, **comparisonParams) + item_dict[version.object_id] = compare + + for version in new_item_versions: # go through the new versions + if version.field_dict["event_id"] == int(self.new.pk): + try: + compare = item_dict[version.object_id] # see if there's a matching old version + compare.new = version._object_version.object # then add the new version to the dictionary + except KeyError: # there's no matching old version, so add this item to the dictionary by itself + compare = ModelComparison(new=version._object_version.object, **comparisonParams) + + item_dict[version.object_id] = compare # update the dictionary with the changes + + changes = [] + for (_, compare) in list(item_dict.items()): + if compare.fields_changed: + changes.append(compare) + + return changes + + @cached_property + def items_changed(self): + return len(self.item_changes) > 0 + + @cached_property + def anything_changed(self): + return self.fields_changed or self.items_changed + + +class RIGSVersionManager(VersionQuerySet): + def get_for_multiple_models(self, model_array): + content_types = [] + for model in model_array: + content_types.append(ContentType.objects.get_for_model(model)) + + return self.filter(content_type__in=content_types).select_related("revision").order_by("-revision__date_created") + + +class RIGSVersion(Version): + class Meta: + proxy = True + + objects = RIGSVersionManager.as_manager() + + @cached_property + def parent(self): + thisId = self.object_id + + versions = RIGSVersion.objects.get_for_object_reference(self.content_type.model_class(), thisId).select_related("revision", "revision__user").all() + + try: + previousVersion = versions.filter(revision_id__lt=self.revision_id).latest( + field_name='revision__date_created') + except ObjectDoesNotExist: + return False + + return previousVersion + + @cached_property + def changes(self): + return ModelComparison( + version=self, + new=self._object_version.object, + old=self.parent._object_version.object if self.parent else None + ) class VersionHistory(generic.ListView): - model = reversion.revisions.Version + model = RIGSVersion template_name = "RIGS/version_history.html" paginate_by = 25 def get_queryset(self, **kwargs): - thisModel = self.kwargs['model'] + return RIGSVersion.objects.get_for_object(self.get_object()).select_related("revision", "revision__user").all().order_by("-revision__date_created") - # thisObject = get_object_or_404(thisModel, pk=self.kwargs['pk']) - versions = reversion.get_for_object_reference(thisModel, self.kwargs['pk']) - - return versions + def get_object(self, **kwargs): + return get_object_or_404(self.kwargs['model'], pk=self.kwargs['pk']) def get_context_data(self, **kwargs): - thisModel = self.kwargs['model'] - context = super(VersionHistory, self).get_context_data(**kwargs) - - versions = context['object_list'] - thisObject = get_object_or_404(thisModel, pk=self.kwargs['pk']) - - items = [] - - for versionNo, thisVersion in enumerate(versions): - if versionNo >= len(versions) - 1: - thisItem = get_changes_for_version(thisVersion, None) - else: - thisItem = get_changes_for_version(thisVersion, versions[versionNo + 1]) - - items.append(thisItem) - - context['object_list'] = items - context['object'] = thisObject + context['object'] = self.get_object() return context class ActivityTable(generic.ListView): - model = reversion.revisions.Version + model = RIGSVersion template_name = "RIGS/activity_table.html" paginate_by = 25 def get_queryset(self): - versions = get_versions_for_model([models.Event, models.Venue, models.Person, models.Organisation]) - return versions - - def get_context_data(self, **kwargs): - # Call the base implementation first to get a context - context = super(ActivityTable, self).get_context_data(**kwargs) - - items = [] - - for thisVersion in context['object_list']: - thisItem = get_changes_for_version(thisVersion, None) - items.append(thisItem) - - context['object_list'] = items - - return context + versions = RIGSVersion.objects.get_for_multiple_models([models.Event, models.Venue, models.Person, models.Organisation, models.EventAuthorisation]) + return versions.order_by("-revision__date_created") class ActivityFeed(generic.ListView): - model = reversion.revisions.Version + model = RIGSVersion template_name = "RIGS/activity_feed_data.html" paginate_by = 25 def get_queryset(self): - versions = get_versions_for_model([models.Event, models.Venue, models.Person, models.Organisation]) - return versions + versions = RIGSVersion.objects.get_for_multiple_models([models.Event, models.Venue, models.Person, models.Organisation, models.EventAuthorisation]) + return versions.order_by("-revision__date_created") def get_context_data(self, **kwargs): - maxTimeDelta = [] - - maxTimeDelta.append({'maxAge': datetime.timedelta(days=1), 'group': datetime.timedelta(hours=1)}) - maxTimeDelta.append({'maxAge': None, 'group': datetime.timedelta(days=1)}) - # Call the base implementation first to get a context context = super(ActivityFeed, self).get_context_data(**kwargs) + maxTimeDelta = datetime.timedelta(hours=1) + items = [] for thisVersion in context['object_list']: - thisItem = get_changes_for_version(thisVersion, None) - if thisItem['item_changes'] or thisItem['field_changes'] or thisItem['old'] == None: - thisItem['withPrevious'] = False - if len(items) >= 1: - timeAgo = datetime.datetime.now(thisItem['revision'].date_created.tzinfo) - thisItem[ - 'revision'].date_created - timeDiff = items[-1]['revision'].date_created - thisItem['revision'].date_created - timeTogether = False - for params in maxTimeDelta: - if params['maxAge'] is None or timeAgo <= params['maxAge']: - timeTogether = timeDiff < params['group'] - break + thisVersion.withPrevious = False + if len(items) >= 1: + timeDiff = items[-1].revision.date_created - thisVersion.revision.date_created + timeTogether = timeDiff < maxTimeDelta + sameUser = thisVersion.revision.user_id == items[-1].revision.user_id + thisVersion.withPrevious = timeTogether & sameUser - sameUser = thisItem['revision'].user == items[-1]['revision'].user - thisItem['withPrevious'] = timeTogether & sameUser - - items.append(thisItem) - - context['object_list'] = items + items.append(thisVersion) return context diff --git a/RIGS/views.py b/RIGS/views.py index de47db5f..7d9b4c73 100644 --- a/RIGS/views.py +++ b/RIGS/views.py @@ -1,7 +1,7 @@ from django.core.exceptions import PermissionDenied from django.http.response import HttpResponseRedirect from django.http import HttpResponse -from django.core.urlresolvers import reverse_lazy, reverse, NoReverseMatch +from django.urls import reverse_lazy, reverse, NoReverseMatch from django.views import generic from django.db.models import Q from django.shortcuts import get_object_or_404 @@ -9,15 +9,22 @@ from django.core import serializers from django.conf import settings import simplejson from django.contrib import messages -import datetime, pytz +import datetime +import pytz import operator from registration.views import RegistrationView +from django.views.decorators.csrf import csrf_exempt + from RIGS import models, forms +from assets import models as asset_models +from functools import reduce """ Displays the current rig count along with a few other bits and pieces """ + + class Index(generic.TemplateView): template_name = 'RIGS/index.html' @@ -26,23 +33,50 @@ class Index(generic.TemplateView): context['rig_count'] = models.Event.objects.rig_count() return context + def login(request, **kwargs): - if request.user.is_authenticated(): - next = request.REQUEST.get('next', '/') - return HttpResponseRedirect(request.REQUEST.get('next', '/')) + if request.user.is_authenticated: + next = request.GET.get('next', '/') + return HttpResponseRedirect(next) else: from django.contrib.auth.views import login return login(request) + class SearchHelp(generic.TemplateView): template_name = 'RIGS/search_help.html' + +# This view should be exempt from requiring CSRF token. +# Then we can check for it and show a nice error +# Don't worry, django.contrib.auth.views.login will +# check for it before logging the user in +@csrf_exempt +def login_embed(request, **kwargs): + if request.user.is_authenticated: + next = request.GET.get('next', '/') + return HttpResponseRedirect(next) + else: + from django.contrib.auth.views import login + + if request.method == "POST": + csrf_cookie = request.COOKIES.get('csrftoken', None) + + if csrf_cookie is None: + messages.warning(request, 'Cookies do not seem to be enabled. Try logging in using a new tab.') + request.method = 'GET' # Render the page without trying to login + + return login(request, template_name="registration/login_embed.html", authentication_form=forms.EmbeddedAuthenticationForm) + + """ Called from a modal window (e.g. when an item is submitted to an event/invoice). May optionally also include some javascript in a success message to cause a load of the new information onto the page. """ + + class CloseModal(generic.TemplateView): template_name = 'closemodal.html' @@ -66,9 +100,9 @@ class PersonList(generic.ListView): except: #not an integer pass - + object_list = self.model.objects.filter(filter) - + orderBy = self.request.GET.get('orderBy', 'name') if orderBy is not None: object_list = object_list.order_by(orderBy) @@ -81,16 +115,16 @@ class PersonDetail(generic.DetailView): class PersonCreate(generic.CreateView): model = models.Person - fields = ['name','phone','email','address','notes'] + fields = ['name', 'phone', 'email', 'address', 'notes'] def get_success_url(self): if self.request.is_ajax(): url = reverse_lazy('closemodal') - update_url = str(reverse_lazy('person_update',kwargs={'pk':self.object.pk})) - messages.info(self.request, "modalobject="+serializers.serialize("json", [self.object])) - messages.info(self.request, "modalobject[0]['update_url']='"+update_url+"'") + update_url = str(reverse_lazy('person_update', kwargs={'pk': self.object.pk})) + messages.info(self.request, "modalobject=" + serializers.serialize("json", [self.object])) + messages.info(self.request, "modalobject[0]['update_url']='" + update_url + "'") else: - url = reverse_lazy('person_detail', kwargs={ + url = reverse_lazy('person_detail', kwargs={ 'pk': self.object.pk, }) return url @@ -98,16 +132,16 @@ class PersonCreate(generic.CreateView): class PersonUpdate(generic.UpdateView): model = models.Person - fields = ['name','phone','email','address','notes'] + fields = ['name', 'phone', 'email', 'address', 'notes'] def get_success_url(self): if self.request.is_ajax(): url = reverse_lazy('closemodal') - update_url = str(reverse_lazy('person_update',kwargs={'pk':self.object.pk})) - messages.info(self.request, "modalobject="+serializers.serialize("json", [self.object])) - messages.info(self.request, "modalobject[0]['update_url']='"+update_url+"'") + update_url = str(reverse_lazy('person_update', kwargs={'pk': self.object.pk})) + messages.info(self.request, "modalobject=" + serializers.serialize("json", [self.object])) + messages.info(self.request, "modalobject[0]['update_url']='" + update_url + "'") else: - url = reverse_lazy('person_detail', kwargs={ + url = reverse_lazy('person_detail', kwargs={ 'pk': self.object.pk, }) return url @@ -119,7 +153,7 @@ class OrganisationList(generic.ListView): def get_queryset(self): q = self.request.GET.get('q', "") - + filter = Q(name__icontains=q) | Q(email__icontains=q) | Q(address__icontains=q) | Q(notes__icontains=q) | Q(phone__startswith=q) | Q(phone__endswith=q) #try and parse an int @@ -129,7 +163,7 @@ class OrganisationList(generic.ListView): except: #not an integer pass - + object_list = self.model.objects.filter(filter) orderBy = self.request.GET.get('orderBy', "name") @@ -144,16 +178,16 @@ class OrganisationDetail(generic.DetailView): class OrganisationCreate(generic.CreateView): model = models.Organisation - fields = ['name','phone','email','address','notes','union_account'] + fields = ['name', 'phone', 'email', 'address', 'notes', 'union_account'] def get_success_url(self): if self.request.is_ajax(): url = reverse_lazy('closemodal') - update_url = str(reverse_lazy('organisation_update',kwargs={'pk':self.object.pk})) - messages.info(self.request, "modalobject="+serializers.serialize("json", [self.object])) - messages.info(self.request, "modalobject[0]['update_url']='"+update_url+"'") + update_url = str(reverse_lazy('organisation_update', kwargs={'pk': self.object.pk})) + messages.info(self.request, "modalobject=" + serializers.serialize("json", [self.object])) + messages.info(self.request, "modalobject[0]['update_url']='" + update_url + "'") else: - url = reverse_lazy('organisation_detail', kwargs={ + url = reverse_lazy('organisation_detail', kwargs={ 'pk': self.object.pk, }) return url @@ -161,16 +195,16 @@ class OrganisationCreate(generic.CreateView): class OrganisationUpdate(generic.UpdateView): model = models.Organisation - fields = ['name','phone','email','address','notes','union_account'] + fields = ['name', 'phone', 'email', 'address', 'notes', 'union_account'] def get_success_url(self): if self.request.is_ajax(): url = reverse_lazy('closemodal') - update_url = str(reverse_lazy('organisation_update',kwargs={'pk':self.object.pk})) - messages.info(self.request, "modalobject="+serializers.serialize("json", [self.object])) - messages.info(self.request, "modalobject[0]['update_url']='"+update_url+"'") + update_url = str(reverse_lazy('organisation_update', kwargs={'pk': self.object.pk})) + messages.info(self.request, "modalobject=" + serializers.serialize("json", [self.object])) + messages.info(self.request, "modalobject[0]['update_url']='" + update_url + "'") else: - url = reverse_lazy('organisation_detail', kwargs={ + url = reverse_lazy('organisation_detail', kwargs={ 'pk': self.object.pk, }) return url @@ -182,7 +216,7 @@ class VenueList(generic.ListView): def get_queryset(self): q = self.request.GET.get('q', "") - + filter = Q(name__icontains=q) | Q(email__icontains=q) | Q(address__icontains=q) | Q(notes__icontains=q) | Q(phone__startswith=q) | Q(phone__endswith=q) #try and parse an int @@ -192,7 +226,7 @@ class VenueList(generic.ListView): except: #not an integer pass - + object_list = self.model.objects.filter(filter) orderBy = self.request.GET.get('orderBy', "name") @@ -207,16 +241,16 @@ class VenueDetail(generic.DetailView): class VenueCreate(generic.CreateView): model = models.Venue - fields = ['name','phone','email','address','notes','three_phase_available'] + fields = ['name', 'phone', 'email', 'address', 'notes', 'three_phase_available'] def get_success_url(self): if self.request.is_ajax(): url = reverse_lazy('closemodal') - update_url = str(reverse_lazy('venue_update',kwargs={'pk':self.object.pk})) - messages.info(self.request, "modalobject="+serializers.serialize("json", [self.object])) - messages.info(self.request, "modalobject[0]['update_url']='"+update_url+"'") + update_url = str(reverse_lazy('venue_update', kwargs={'pk': self.object.pk})) + messages.info(self.request, "modalobject=" + serializers.serialize("json", [self.object])) + messages.info(self.request, "modalobject[0]['update_url']='" + update_url + "'") else: - url = reverse_lazy('venue_detail', kwargs={ + url = reverse_lazy('venue_detail', kwargs={ 'pk': self.object.pk, }) return url @@ -224,16 +258,16 @@ class VenueCreate(generic.CreateView): class VenueUpdate(generic.UpdateView): model = models.Venue - fields = ['name','phone','email','address','notes','three_phase_available'] + fields = ['name', 'phone', 'email', 'address', 'notes', 'three_phase_available'] def get_success_url(self): if self.request.is_ajax(): url = reverse_lazy('closemodal') - update_url = str(reverse_lazy('venue_update',kwargs={'pk':self.object.pk})) - messages.info(self.request, "modalobject="+serializers.serialize("json", [self.object])) - messages.info(self.request, "modalobject[0]['update_url']='"+update_url+"'") + update_url = str(reverse_lazy('venue_update', kwargs={'pk': self.object.pk})) + messages.info(self.request, "modalobject=" + serializers.serialize("json", [self.object])) + messages.info(self.request, "modalobject[0]['update_url']='" + update_url + "'") else: - url = reverse_lazy('venue_detail', kwargs={ + url = reverse_lazy('venue_detail', kwargs={ 'pk': self.object.pk, }) return url @@ -246,6 +280,7 @@ class SecureAPIRequest(generic.View): 'organisation': models.Organisation, 'profile': models.Profile, 'event': models.Event, + 'supplier': asset_models.Supplier } perms = { @@ -254,6 +289,7 @@ class SecureAPIRequest(generic.View): 'organisation': 'RIGS.view_organisation', 'profile': 'RIGS.view_profile', 'event': None, + 'supplier': None } ''' @@ -291,9 +327,9 @@ class SecureAPIRequest(generic.View): # Supply data for autocomplete ajax request in json form term = request.GET.get('term', None) if term: - if fields is None: # Default to just name + if fields is None: # Default to just name fields = ['name'] - + # Build a list of Q objects for use later queries = [] for part in term.split(" "): @@ -303,7 +339,6 @@ class SecureAPIRequest(generic.View): qs.append(q) queries.append(reduce(operator.or_, qs)) - # Build the data response list results = [] query = reduce(operator.and_, queries) @@ -314,7 +349,7 @@ class SecureAPIRequest(generic.View): 'value': o.pk, 'label': o.name, } - try: # See if there is a valid update URL + try: # See if there is a valid update URL data['update'] = reverse("%s_update" % model, kwargs={'pk': o.pk}) except NoReverseMatch: pass @@ -329,10 +364,10 @@ class SecureAPIRequest(generic.View): if model == "event" and start and end: # Probably a calendar request - start_datetime = datetime.datetime.strptime( start, "%Y-%m-%dT%H:%M:%S" ) - end_datetime = datetime.datetime.strptime( end, "%Y-%m-%dT%H:%M:%S" ) + start_datetime = datetime.datetime.strptime(start, "%Y-%m-%dT%H:%M:%S") + end_datetime = datetime.datetime.strptime(end, "%Y-%m-%dT%H:%M:%S") - objects = self.models[model].objects.events_in_bounds(start_datetime,end_datetime) + objects = self.models[model].objects.events_in_bounds(start_datetime, end_datetime) results = [] for item in objects: @@ -352,6 +387,7 @@ class SecureAPIRequest(generic.View): return HttpResponse(model) + class ProfileDetail(generic.DetailView): model = models.Profile @@ -364,6 +400,7 @@ class ProfileDetail(generic.DetailView): return self.model.objects.filter(pk=pk) + class ProfileUpdateSelf(generic.UpdateView): model = models.Profile fields = ['first_name', 'last_name', 'email', 'initials', 'phone'] @@ -375,13 +412,18 @@ class ProfileUpdateSelf(generic.UpdateView): return self.model.objects.filter(pk=pk) def get_success_url(self): - url = reverse_lazy('profile_detail') + url = reverse_lazy('profile_detail') return url + class ResetApiKey(generic.RedirectView): def get_redirect_url(self, *args, **kwargs): self.request.user.api_key = self.request.user.make_api_key() - + self.request.user.save() return reverse_lazy('profile_detail') + + +class PasswordResetDisabled(generic.TemplateView): + template_name = "RIGS/password_reset_disable.html" diff --git a/app.json b/app.json new file mode 100644 index 00000000..737e5eb0 --- /dev/null +++ b/app.json @@ -0,0 +1,53 @@ +{ + "name": "PyRIGS", + "description": "", + "scripts": { + "postdeploy": "python manage.py migrate && python manage.py generateSampleData" + }, + "env": { + "DEBUG": { + "required": true + }, + "STAGING": "1", + "EMAIL_FROM": { + "required": true + }, + "EMAIL_HOST": { + "required": true + }, + "EMAIL_HOST_PASSWORD": { + "required": true + }, + "EMAIL_HOST_USER": { + "required": true + }, + "EMAIL_PORT": { + "required": true + }, + "EMAIL_USE_SSL": { + "required": true + }, + "RECAPTCHA_PRIVATE_KEY": { + "required": true + }, + "RECAPTCHA_PUBLIC_KEY": { + "required": true + }, + "SECRET_KEY": { + "generator": "secret" + } + }, + "formation": { + "web": { + "quantity": 1 + } + }, + "addons": [ + "heroku-postgresql" + ], + "buildpacks": [ + { + "url": "heroku/python" + } + ] +} \ No newline at end of file diff --git a/assets/__init__.py b/assets/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/assets/admin.py b/assets/admin.py new file mode 100644 index 00000000..3e6c9d58 --- /dev/null +++ b/assets/admin.py @@ -0,0 +1,32 @@ +from django.contrib import admin +from assets import models as assets + + +@admin.register(assets.AssetCategory) +class AssetCategoryAdmin(admin.ModelAdmin): + list_display = ['id', 'name'] + ordering = ['id'] + + +@admin.register(assets.AssetStatus) +class AssetStatusAdmin(admin.ModelAdmin): + list_display = ['id', 'name'] + ordering = ['id'] + + +@admin.register(assets.Supplier) +class SupplierAdmin(admin.ModelAdmin): + list_display = ['id', 'name'] + ordering = ['id'] + + +@admin.register(assets.Asset) +class AssetAdmin(admin.ModelAdmin): + list_display = ['id', 'asset_id', 'description', 'category', 'status'] + list_filter = ['is_cable', 'category'] + search_fields = ['id', 'asset_id', 'description'] + + +@admin.register(assets.Connector) +class ConnectorAdmin(admin.ModelAdmin): + list_display = ['id', '__str__', 'current_rating', 'voltage_rating', 'num_pins'] diff --git a/assets/apps.py b/assets/apps.py new file mode 100644 index 00000000..5569d303 --- /dev/null +++ b/assets/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class AssetsConfig(AppConfig): + name = 'assets' diff --git a/assets/filters.py b/assets/filters.py new file mode 100644 index 00000000..b7cc8ffa --- /dev/null +++ b/assets/filters.py @@ -0,0 +1,9 @@ +import django_filters + +from assets import models + + +class AssetFilter(django_filters.FilterSet): + class Meta: + model = models.Asset + fields = ['asset_id', 'description', 'serial_number', 'category', 'status'] diff --git a/assets/forms.py b/assets/forms.py new file mode 100644 index 00000000..ea05efd3 --- /dev/null +++ b/assets/forms.py @@ -0,0 +1,36 @@ +from django import forms + +from assets import models + + +class AssetForm(forms.ModelForm): + related_models = { + 'asset': models.Asset, + 'supplier': models.Supplier + } + + class Meta: + model = models.Asset + fields = '__all__' + exclude = ['asset_id_prefix', 'asset_id_number'] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['date_sold'].widget.format = '%Y-%m-%d' + self.fields['date_acquired'].widget.format = '%Y-%m-%d' + + +class AssetSearchForm(forms.Form): + query = forms.CharField(required=False) + category = forms.ModelMultipleChoiceField(models.AssetCategory.objects.all(), required=False) + status = forms.ModelMultipleChoiceField(models.AssetStatus.objects.all(), required=False) + + +class SupplierForm(forms.ModelForm): + class Meta: + model = models.Supplier + fields = '__all__' + + +class SupplierSearchForm(forms.Form): + query = forms.CharField(required=False) diff --git a/assets/management/__init__.py b/assets/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/assets/management/commands/__init__.py b/assets/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/assets/management/commands/deleteSampleData.py b/assets/management/commands/deleteSampleData.py new file mode 100644 index 00000000..cdf34ce9 --- /dev/null +++ b/assets/management/commands/deleteSampleData.py @@ -0,0 +1,23 @@ +from django.core.management.base import BaseCommand, CommandError + +from assets import models + + +class Command(BaseCommand): + help = 'Deletes testing sample data' + + def handle(self, *args, **kwargs): + from django.conf import settings + + if not (settings.DEBUG): + raise CommandError('You cannot run this command in production') + + self.delete_objects(models.AssetCategory) + self.delete_objects(models.AssetStatus) + self.delete_objects(models.Supplier) + self.delete_objects(models.Connector) + self.delete_objects(models.Asset) + + def delete_objects(self, model): + for object in model.objects.all(): + object.delete() diff --git a/assets/management/commands/generateSampleAssetsData.py b/assets/management/commands/generateSampleAssetsData.py new file mode 100644 index 00000000..258551c2 --- /dev/null +++ b/assets/management/commands/generateSampleAssetsData.py @@ -0,0 +1,120 @@ +import random +from django.core.management.base import BaseCommand, CommandError +from django.utils import timezone +from assets import models + + +class Command(BaseCommand): + help = 'Creates some sample data for testing' + + def handle(self, *args, **kwargs): + from django.conf import settings + + if not (settings.DEBUG or settings.STAGING): + raise CommandError('You cannot run this command in production') + + random.seed('Some object to see the random number generator') + + self.create_categories() + self.create_statuses() + self.create_suppliers() + self.create_assets() + self.create_connectors() + self.create_cables() + + def create_categories(self): + categories = ['Case', 'Video', 'General', 'Sound', 'Lighting', 'Rigging'] + + for cat in categories: + models.AssetCategory.objects.create(name=cat) + + def create_statuses(self): + statuses = [('In Service', True), ('Lost', False), ('Binned', False), ('Sold', False), ('Broken', False)] + + for stat in statuses: + models.AssetStatus.objects.create(name=stat[0], should_show=stat[1]) + + def create_suppliers(self): + suppliers = ["Acme, inc.", "Widget Corp", "123 Warehousing", "Demo Company", "Smith and Co.", "Foo Bars", "ABC Telecom", "Fake Brothers", "QWERTY Logistics", "Demo, inc.", "Sample Company", "Sample, inc", "Acme Corp", "Allied Biscuit", "Ankh-Sto Associates", "Extensive Enterprise", "Galaxy Corp", "Globo-Chem", "Mr. Sparkle", "Globex Corporation", "LexCorp", "LuthorCorp", "North Central Positronics", "Omni Consimer Products", "Praxis Corporation", "Sombra Corporation", "Sto Plains Holdings", "Tessier-Ashpool", "Wayne Enterprises", "Wentworth Industries", "ZiffCorp", "Bluth Company", "Strickland Propane", "Thatherton Fuels", "Three Waters", "Water and Power", "Western Gas & Electric", "Mammoth Pictures", "Mooby Corp", "Gringotts", "Thrift Bank", "Flowers By Irene", "The Legitimate Businessmens Club", "Osato Chemicals", "Transworld Consortium", "Universal Export", "United Fried Chicken", "Virtucon", "Kumatsu Motors", "Keedsler Motors", "Powell Motors", "Industrial Automation", "Sirius Cybernetics Corporation", "U.S. Robotics and Mechanical Men", "Colonial Movers", "Corellian Engineering Corporation", "Incom Corporation", "General Products", "Leeding Engines Ltd.", "Blammo", # noqa + "Input, Inc.", "Mainway Toys", "Videlectrix", "Zevo Toys", "Ajax", "Axis Chemical Co.", "Barrytron", "Carrys Candles", "Cogswell Cogs", "Spacely Sprockets", "General Forge and Foundry", "Duff Brewing Company", "Dunder Mifflin", "General Services Corporation", "Monarch Playing Card Co.", "Krustyco", "Initech", "Roboto Industries", "Primatech", "Sonky Rubber Goods", "St. Anky Beer", "Stay Puft Corporation", "Vandelay Industries", "Wernham Hogg", "Gadgetron", "Burleigh and Stronginthearm", "BLAND Corporation", "Nordyne Defense Dynamics", "Petrox Oil Company", "Roxxon", "McMahon and Tate", "Sixty Second Avenue", "Charles Townsend Agency", "Spade and Archer", "Megadodo Publications", "Rouster and Sideways", "C.H. Lavatory and Sons", "Globo Gym American Corp", "The New Firm", "SpringShield", "Compuglobalhypermeganet", "Data Systems", "Gizmonic Institute", "Initrode", "Taggart Transcontinental", "Atlantic Northern", "Niagular", "Plow King", "Big Kahuna Burger", "Big T Burgers and Fries", "Chez Quis", "Chotchkies", "The Frying Dutchman", "Klimpys", "The Krusty Krab", "Monks Diner", "Milliways", "Minuteman Cafe", "Taco Grande", "Tip Top Cafe", "Moes Tavern", "Central Perk", "Chasers"] # noqa + + for supplier in suppliers: + models.Supplier.objects.create(name=supplier) + + def create_assets(self): + asset_description = ['Large cable', 'Shiny thing', 'New lights', 'Really expensive microphone', 'Box of fuse flaps', 'Expensive tool we didn\'t agree to buy', 'Cable drums', 'Boring amount of tape', 'Video stuff no one knows how to use', 'More amplifiers', 'Heatshrink'] + + categories = models.AssetCategory.objects.all() + statuses = models.AssetStatus.objects.all() + suppliers = models.Supplier.objects.all() + + for i in range(100): + asset = models.Asset( + asset_id='{}'.format(models.Asset.get_available_asset_id()), + description=random.choice(asset_description), + category=random.choice(categories), + status=random.choice(statuses), + date_acquired=timezone.now().date() + ) + + if i % 4 == 0: + asset.parent = models.Asset.objects.order_by('?').first() + + if i % 3 == 0: + asset.purchased_from = random.choice(suppliers) + asset.clean() + asset.save() + + def create_cables(self): + asset_description = ['The worm', 'Harting without a cap', 'Heavy cable', 'Extension lead', 'IEC cable that we should remember to prep'] + asset_prefixes = ["C", "C4P", "CBNC", "CDMX", "CDV", "CRCD", "CSOCA", "CXLR"] + + csas = [0.75, 1.00, 1.25, 2.5, 4] + lengths = [1, 2, 5, 10, 15, 20, 25, 30, 50, 100] + cores = [3, 5] + circuits = [1, 2, 3, 6] + categories = models.AssetCategory.objects.all() + statuses = models.AssetStatus.objects.all() + suppliers = models.Supplier.objects.all() + connectors = models.Connector.objects.all() + + for i in range(100): + asset = models.Asset( + asset_id='{}'.format(models.Asset.get_available_asset_id()), + description=random.choice(asset_description), + category=random.choice(categories), + status=random.choice(statuses), + date_acquired=timezone.now().date(), + + is_cable=True, + plug=random.choice(connectors), + socket=random.choice(connectors), + csa=random.choice(csas), + length=random.choice(lengths), + circuits=random.choice(circuits), + cores=random.choice(circuits) + ) + + if i % 5 == 0: + prefix = random.choice(asset_prefixes) + asset.asset_id = prefix + str(models.Asset.get_available_asset_id(wanted_prefix=prefix)) + + if i % 4 == 0: + asset.parent = models.Asset.objects.order_by('?').first() + + if i % 3 == 0: + asset.purchased_from = random.choice(suppliers) + + asset.clean() + asset.save() + + def create_connectors(self): + connectors = [ + {"description": "13A UK", "current_rating": 13, "voltage_rating": 230, "num_pins": 3}, + {"description": "16A", "current_rating": 16, "voltage_rating": 230, "num_pins": 3}, + {"description": "32/3", "current_rating": 32, "voltage_rating": 400, "num_pins": 5}, + {"description": "Socapex", "current_rating": 23, "voltage_rating": 600, "num_pins": 19}, + ] + for connector in connectors: + conn = models.Connector.objects.create(** connector) + conn.save() diff --git a/assets/management/commands/import_old_db.py b/assets/management/commands/import_old_db.py new file mode 100644 index 00000000..0fcff787 --- /dev/null +++ b/assets/management/commands/import_old_db.py @@ -0,0 +1,229 @@ +import os +import datetime +import xml.etree.ElementTree as ET +from django.core.management.base import BaseCommand +from django.conf import settings + +from assets import models + + +class Command(BaseCommand): + help = 'Imports old db from XML dump' + + epoch = datetime.date(1970, 1, 1) + + def handle(self, *args, **options): + self.import_categories() + self.import_statuses() + self.import_suppliers() + self.import_collections() + self.import_assets() + self.import_cables() + + @staticmethod + def xml_path(file): + return os.path.join(settings.BASE_DIR, 'data/DB_Dump/{}'.format(file)) + + @staticmethod + def parse_xml(file): + tree = ET.parse(file) + + return tree.getroot() + + def import_categories(self): + # 0: updated, 1: created + tally = [0, 0] + root = self.parse_xml(self.xml_path('TEC_Asset_Categories.xml')) + + for child in root: + obj, created = models.AssetCategory.objects.update_or_create( + pk=int(child.find('AssetCategoryID').text), + name=child.find('AssetCategory').text + ) + + if created: + tally[1] += 1 + else: + tally[0] += 1 + + print('Categories - Updated: {}, Created: {}'.format(tally[0], tally[1])) + + def import_statuses(self): + # 0: updated, 1: created + tally = [0, 0] + root = self.parse_xml(self.xml_path('TEC_Asset_Status_new.xml')) + + for child in root: + obj, created = models.AssetStatus.objects.update_or_create( + pk=int(child.find('StatusID').text), + name=child.find('Status').text + ) + + if created: + tally[1] += 1 + else: + tally[0] += 1 + + print('Statuses - Updated: {}, Created: {}'.format(tally[0], tally[1])) + + def import_suppliers(self): + # 0: updated, 1: created + tally = [0, 0] + root = self.parse_xml(self.xml_path('TEC_Asset_Suppliers_new.xml')) + + for child in root: + obj, created = models.Supplier.objects.update_or_create( + pk=int(child.find('Supplier_x0020_Id').text), + name=child.find('Supplier_x0020_Name').text + ) + + if created: + tally[1] += 1 + else: + tally[0] += 1 + + print('Suppliers - Updated: {}, Created: {}'.format(tally[0], tally[1])) + + def import_assets(self): + # 0: updated, 1: created + tally = [0, 0] + root = self.parse_xml(self.xml_path('TEC_Assets.xml')) + + for child in root: + defaults = dict() + + # defaults['pk'] = int(child.find('ID').text) + defaults['asset_id'] = child.find('AssetID').text + + try: + defaults['description'] = child.find('AssetDescription').text + except AttributeError: + defaults['description'] = 'None' + + defaults['category'] = models.AssetCategory.objects.get(pk=int(child.find('AssetCategoryID').text)) + defaults['status'] = models.AssetStatus.objects.get(pk=int(child.find('StatusID').text)) + + try: + defaults['serial_number'] = child.find('SerialNumber').text + except AttributeError: + pass + + try: + defaults['purchased_from'] = models.Supplier.objects.get(pk=int(child.find('Supplier_x0020_Id').text)) + except AttributeError: + pass + + try: + defaults['date_acquired'] = datetime.datetime.strptime(child.find('DateAcquired').text, '%d/%m/%Y').date() + except AttributeError: + defaults['date_acquired'] = self.epoch + + try: + defaults['date_sold'] = datetime.datetime.strptime(child.find('DateSold').text, '%d/%m/%Y').date() + except AttributeError: + pass + + try: + defaults['purchase_price'] = float(child.find('Replacement_x0020_Value').text) + except AttributeError: + pass + + try: + defaults['salvage_value'] = float(child.find('SalvageValue').text) + except AttributeError: + pass + + try: + defaults['comments'] = child.find('Comments').text + except AttributeError: + pass + + try: + date = child.find('NextSchedMaint').text.split('T')[0] + defaults['next_sched_maint'] = datetime.datetime.strptime(date, '%Y-%m-%d').date() + except AttributeError: + pass + + print(defaults) + + obj, created = models.Asset.objects.update_or_create(**defaults) + + if created: + tally[1] += 1 + else: + tally[0] += 1 + + print('Assets - Updated: {}, Created: {}'.format(tally[0], tally[1])) + + def import_collections(self): + tally = [0, 0] + root = self.parse_xml(self.xml_path('TEC_Cable_Collections.xml')) + + for child in root: + defaults = dict() + + defaults['pk'] = int(child.find('ID').text) + defaults['name'] = child.find('Cable_x0020_Trunk').text + + obj, created = models.Collection.objects.update_or_create(**defaults) + + if created: + tally[1] += 1 + else: + tally[0] += 1 + + print('Collections - Updated: {}, Created: {}'.format(tally[0], tally[1])) + + def import_cables(self): + tally = [0, 0] + root = self.parse_xml(self.xml_path('TEC_Cables.xml')) + + for child in root: + defaults = dict() + + defaults['asset_id'] = child.find('Asset_x0020_Number').text + + try: + defaults['description'] = child.find('Type_x0020_of_x0020_Cable').text + except AttributeError: + defaults['description'] = 'None' + + defaults['is_cable'] = True + defaults['category'] = models.AssetCategory.objects.get(pk=9) + + try: + defaults['length'] = child.find('Length_x0020__x0028_m_x0029_').text + except AttributeError: + pass + + defaults['status'] = models.AssetStatus.objects.get(pk=int(child.find('Status').text)) + + try: + defaults['comments'] = child.find('Comments').text + except AttributeError: + pass + + try: + collection_id = int(child.find('Collection').text) + if collection_id != 0: + defaults['collection'] = models.Collection.objects.get(pk=collection_id) + except AttributeError: + pass + + try: + defaults['purchase_price'] = float(child.find('Purchase_x0020_Price').text) + except AttributeError: + pass + + defaults['date_acquired'] = self.epoch + + print(defaults) + + obj, created = models.Asset.objects.update_or_create(**defaults) + + if created: + tally[1] += 1 + else: + tally[0] += 1 + + print('Collections - Updated: {}, Created: {}'.format(tally[0], tally[1])) diff --git a/assets/management/commands/update_old_db_file.py b/assets/management/commands/update_old_db_file.py new file mode 100644 index 00000000..bff0fe22 --- /dev/null +++ b/assets/management/commands/update_old_db_file.py @@ -0,0 +1,110 @@ +import os +import datetime +import xml.etree.ElementTree as ET +from django.core.management.base import BaseCommand +from django.conf import settings + + +class Command(BaseCommand): + help = 'Imports old db from XML dump' + + epoch = datetime.date(1970, 1, 1) + + def handle(self, *args, **options): + # self.update_statuses() + # self.update_suppliers() + self.update_cable_statuses() + + @staticmethod + def xml_path(file): + return os.path.join(settings.BASE_DIR, 'data/DB_Dump/{}'.format(file)) + + @staticmethod + def parse_xml(file): + tree = ET.parse(file) + + return tree.getroot() + + def update_statuses(self): + file = self.xml_path('TEC_Assets.xml') + tree = ET.parse(file) + root = tree.getroot() + + # map old status pk to new status pk + status_map = { + 2: 2, + 3: 4, + 4: 3, + 5: 5, + 6: 1 + } + + for child in root: + status = int(child.find('StatusID').text) + child.find('StatusID').text = str(status_map[status]) + + tree.write(file) + + def update_suppliers(self): + old_file = self.xml_path('TEC_Asset_Suppliers.xml') + old_tree = ET.parse(old_file) + old_root = old_tree.getroot() + + new_file = self.xml_path('TEC_Asset_Suppliers_new.xml') + new_tree = ET.parse(new_file) + new_root = new_tree.getroot() + + # map old supplier pk to new supplier pk + supplier_map = dict() + + def find_in_old(name, root): + for child in root: + found_id = child.find('Supplier_x0020_Id').text + found_name = child.find('Supplier_x0020_Name').text + + if found_name == name: + return found_id + + for new_child in new_root: + new_id = new_child.find('Supplier_x0020_Id').text + new_name = new_child.find('Supplier_x0020_Name').text + + old_id = find_in_old(new_name, old_root) + + supplier_map[int(old_id)] = int(new_id) + + file = self.xml_path('TEC_Assets.xml') + tree = ET.parse(file) + root = tree.getroot() + + for child in root: + try: + supplier = int(child.find('Supplier_x0020_Id').text) + child.find('Supplier_x0020_Id').text = str(supplier_map[supplier]) + except AttributeError: + pass + + tree.write(file) + + def update_cable_statuses(self): + file = self.xml_path('TEC_Cables.xml') + tree = ET.parse(file) + root = tree.getroot() + + # map old status pk to new status pk + status_map = { + 0: 7, + 1: 3, + 3: 2, + 4: 5, + 6: 6, + 7: 1, + 8: 4, + 9: 2, + } + + for child in root: + status = int(child.find('Status').text) + child.find('Status').text = str(status_map[status]) + + tree.write(file) diff --git a/assets/migrations/0001_initial.py b/assets/migrations/0001_initial.py new file mode 100644 index 00000000..c1951b19 --- /dev/null +++ b/assets/migrations/0001_initial.py @@ -0,0 +1,86 @@ +# Generated by Django 2.0.2 on 2018-02-28 16:06 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Asset', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('asset_id', models.IntegerField()), + ('description', models.CharField(max_length=120)), + ('serial_number', models.CharField(blank=True, max_length=150, null=True)), + ('date_acquired', models.DateField()), + ('date_sold', models.DateField(blank=True, null=True)), + ('purchase_price', models.IntegerField()), + ('salvage_value', models.IntegerField(blank=True, null=True)), + ('comments', models.TextField(blank=True, null=True)), + ('next_sched_maint', models.DateField(blank=True, null=True)), + ], + ), + migrations.CreateModel( + name='AssetCategory', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=80)), + ], + options={ + 'verbose_name': 'Asset Category', + 'verbose_name_plural': 'Asset Categories', + }, + ), + migrations.CreateModel( + name='AssetStatus', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=80)), + ], + options={ + 'verbose_name': 'Asset Status', + 'verbose_name_plural': 'Asset Statuses', + }, + ), + migrations.CreateModel( + name='Collection', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=80)), + ], + ), + migrations.CreateModel( + name='Supplier', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=80)), + ], + ), + migrations.AddField( + model_name='asset', + name='category', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.AssetCategory'), + ), + migrations.AddField( + model_name='asset', + name='collection', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.Collection'), + ), + migrations.AddField( + model_name='asset', + name='purchased_from', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='assets.Supplier'), + ), + migrations.AddField( + model_name='asset', + name='status', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.AssetStatus'), + ), + ] diff --git a/assets/migrations/0002_auto_20180301_1654.py b/assets/migrations/0002_auto_20180301_1654.py new file mode 100644 index 00000000..915a7151 --- /dev/null +++ b/assets/migrations/0002_auto_20180301_1654.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.2 on 2018-03-01 16:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='asset', + name='asset_id', + field=models.IntegerField(blank=True), + ), + ] diff --git a/assets/migrations/0003_auto_20180301_1700.py b/assets/migrations/0003_auto_20180301_1700.py new file mode 100644 index 00000000..feef9a6c --- /dev/null +++ b/assets/migrations/0003_auto_20180301_1700.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.2 on 2018-03-01 17:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0002_auto_20180301_1654'), + ] + + operations = [ + migrations.AlterField( + model_name='asset', + name='purchase_price', + field=models.IntegerField(blank=True, null=True), + ), + ] diff --git a/assets/migrations/0004_auto_20180301_1711.py b/assets/migrations/0004_auto_20180301_1711.py new file mode 100644 index 00000000..4c884e93 --- /dev/null +++ b/assets/migrations/0004_auto_20180301_1711.py @@ -0,0 +1,19 @@ +# Generated by Django 2.0.2 on 2018-03-01 17:11 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0003_auto_20180301_1700'), + ] + + operations = [ + migrations.AlterField( + model_name='asset', + name='collection', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='assets.Collection'), + ), + ] diff --git a/assets/migrations/0005_auto_20180301_1725.py b/assets/migrations/0005_auto_20180301_1725.py new file mode 100644 index 00000000..49fae9c6 --- /dev/null +++ b/assets/migrations/0005_auto_20180301_1725.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.2 on 2018-03-01 17:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0004_auto_20180301_1711'), + ] + + operations = [ + migrations.AlterField( + model_name='asset', + name='asset_id', + field=models.IntegerField(blank=True, null=True), + ), + ] diff --git a/assets/migrations/0006_auto_20180728_1451_squashed_0021_auto_20190105_1156.py b/assets/migrations/0006_auto_20180728_1451_squashed_0021_auto_20190105_1156.py new file mode 100644 index 00000000..f15341e1 --- /dev/null +++ b/assets/migrations/0006_auto_20180728_1451_squashed_0021_auto_20190105_1156.py @@ -0,0 +1,148 @@ +# Generated by Django 2.1.5 on 2019-01-05 19:54 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + replaces = [('assets', '0006_auto_20180728_1451'), ('assets', '0007_auto_20181215_1447'), ('assets', '0008_auto_20181215_1448'), ('assets', '0009_auto_20181215_1640'), ('assets', '0010_auto_20181215_1640'), ('assets', '0011_auto_20181215_1749'), ('assets', '0012_auto_20181215_1813'), ('assets', '0013_asset_parent'), ('assets', '0014_auto_20190103_1615'), ('assets', '0015_auto_20190103_1617'), ('assets', '0016_remove_asset_collection'), ('assets', '0017_delete_collection'), ('assets', '0018_auto_20190103_1708'), ('assets', '0019_auto_20190103_1723'), ('assets', '0020_auto_20190103_1729'), ('assets', '0021_auto_20190105_1156')] + + dependencies = [ + ('assets', '0005_auto_20180301_1725'), + ] + + operations = [ + migrations.AlterField( + model_name='asset', + name='asset_id', + field=models.IntegerField(blank=True, null=True, unique=True), + ), + migrations.AlterField( + model_name='asset', + name='purchase_price', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True), + ), + migrations.AlterField( + model_name='asset', + name='salvage_value', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True), + ), + migrations.AlterField( + model_name='asset', + name='asset_id', + field=models.IntegerField(blank=True, null=True), + ), + migrations.AddField( + model_name='asset', + name='is_cable', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='asset', + name='length', + field=models.DecimalField(blank=True, decimal_places=1, max_digits=10, null=True), + ), + migrations.AlterField( + model_name='asset', + name='asset_id', + field=models.CharField(blank=True, max_length=10, null=True), + ), + migrations.AlterField( + model_name='asset', + name='asset_id', + field=models.CharField(default='', max_length=10), + preserve_default=False, + ), + migrations.AddField( + model_name='asset', + name='parent', + field=models.ForeignKey(blank=True, null=True, on_delete=None, related_name='asset_parent', to='assets.Asset'), + ), + migrations.RemoveField( + model_name='asset', + name='collection', + ), + migrations.DeleteModel( + name='Collection', + ), + migrations.CreateModel( + name='Cable', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('asset_id', models.CharField(max_length=10)), + ('description', models.CharField(max_length=120)), + ('serial_number', models.CharField(blank=True, max_length=150, null=True)), + ('date_acquired', models.DateField()), + ('date_sold', models.DateField(blank=True, null=True)), + ('purchase_price', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)), + ('salvage_value', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)), + ('comments', models.TextField(blank=True, null=True)), + ('next_sched_maint', models.DateField(blank=True, null=True)), + ('is_cable', models.BooleanField(default=False)), + ('length', models.DecimalField(blank=True, decimal_places=1, help_text='m', max_digits=10, null=True)), + ('csa', models.DecimalField(blank=True, decimal_places=2, help_text='mm^2', max_digits=10, null=True)), + ('circuits', models.IntegerField(blank=True, null=True)), + ('cores', models.IntegerField(blank=True, null=True)), + ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.AssetCategory')), + ('parent', models.ForeignKey(blank=True, null=True, on_delete=None, related_name='asset_parent', to='assets.Cable')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Connector', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('description', models.CharField(max_length=80)), + ('current_rating', models.DecimalField(decimal_places=2, help_text='Amps', max_digits=10)), + ('voltage_rating', models.IntegerField(default=0, help_text='Volts')), + ('num_pins', models.IntegerField(blank=True, null=True)), + ], + ), + migrations.AddField( + model_name='cable', + name='plug', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='plug', to='assets.Connector'), + ), + migrations.AddField( + model_name='cable', + name='purchased_from', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='assets.Supplier'), + ), + migrations.AddField( + model_name='cable', + name='socket', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='socket', to='assets.Connector'), + ), + migrations.AddField( + model_name='cable', + name='status', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.AssetStatus'), + ), + migrations.AlterField( + model_name='asset', + name='comments', + field=models.TextField(blank=True, default=''), + preserve_default=False, + ), + migrations.AlterField( + model_name='asset', + name='serial_number', + field=models.CharField(blank=True, default='', max_length=150), + preserve_default=False, + ), + migrations.AlterField( + model_name='cable', + name='comments', + field=models.TextField(blank=True, default=''), + preserve_default=False, + ), + migrations.AlterField( + model_name='cable', + name='serial_number', + field=models.CharField(blank=True, default='', max_length=150), + preserve_default=False, + ), + ] diff --git a/assets/migrations/0007_auto_20190108_0202_squashed_0014_auto_20191017_2052.py b/assets/migrations/0007_auto_20190108_0202_squashed_0014_auto_20191017_2052.py new file mode 100644 index 00000000..116fa1d2 --- /dev/null +++ b/assets/migrations/0007_auto_20190108_0202_squashed_0014_auto_20191017_2052.py @@ -0,0 +1,176 @@ +# Generated by Django 2.0.13 on 2019-12-04 17:37 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + replaces = [('assets', '0007_auto_20190108_0202'), ('assets', '0008_auto_20191002_1931'), ('assets', '0009_auto_20191008_2148'), ('assets', '0010_auto_20191013_2123'), ('assets', '0011_auto_20191013_2247'), ('assets', '0012_auto_20191014_0012'), ('assets', '0013_auto_20191016_1446'), ('assets', '0014_auto_20191017_2052')] + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('assets', '0006_auto_20180728_1451_squashed_0021_auto_20190105_1156'), + ] + + operations = [ + migrations.AlterField( + model_name='asset', + name='parent', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='asset_parent', to='assets.Asset'), + ), + migrations.AlterField( + model_name='cable', + name='parent', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='asset_parent', to='assets.Cable'), + ), + migrations.AlterField( + model_name='connector', + name='voltage_rating', + field=models.IntegerField(help_text='Volts'), + ), + migrations.AlterModelOptions( + name='asset', + options={'base_manager_name': 'objects'}, + ), + migrations.AlterModelOptions( + name='cable', + options={'base_manager_name': 'objects'}, + ), + migrations.RemoveField( + model_name='asset', + name='length', + ), + migrations.RemoveField( + model_name='cable', + name='asset_id', + ), + migrations.RemoveField( + model_name='cable', + name='category', + ), + migrations.RemoveField( + model_name='cable', + name='comments', + ), + migrations.RemoveField( + model_name='cable', + name='date_acquired', + ), + migrations.RemoveField( + model_name='cable', + name='date_sold', + ), + migrations.RemoveField( + model_name='cable', + name='description', + ), + migrations.RemoveField( + model_name='cable', + name='id', + ), + migrations.RemoveField( + model_name='cable', + name='is_cable', + ), + migrations.RemoveField( + model_name='cable', + name='next_sched_maint', + ), + migrations.RemoveField( + model_name='cable', + name='parent', + ), + migrations.RemoveField( + model_name='cable', + name='purchase_price', + ), + migrations.RemoveField( + model_name='cable', + name='purchased_from', + ), + migrations.RemoveField( + model_name='cable', + name='salvage_value', + ), + migrations.RemoveField( + model_name='cable', + name='serial_number', + ), + migrations.RemoveField( + model_name='cable', + name='status', + ), + migrations.AddField( + model_name='asset', + name='polymorphic_ctype', + field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_assets.asset_set+', to='contenttypes.ContentType'), + ), + migrations.AlterField( + model_name='asset', + name='asset_id', + field=models.CharField(max_length=10, unique=True), + ), + migrations.RemoveField( + model_name='cable', + name='plug', + ), + migrations.RemoveField( + model_name='cable', + name='socket', + ), + migrations.AlterModelOptions( + name='asset', + options={}, + ), + migrations.RemoveField( + model_name='asset', + name='polymorphic_ctype', + ), + migrations.AddField( + model_name='asset', + name='circuits', + field=models.IntegerField(blank=True, null=True), + ), + migrations.AddField( + model_name='asset', + name='cores', + field=models.IntegerField(blank=True, null=True), + ), + migrations.AddField( + model_name='asset', + name='csa', + field=models.DecimalField(blank=True, decimal_places=2, help_text='mm^2', max_digits=10, null=True), + ), + migrations.AddField( + model_name='asset', + name='length', + field=models.DecimalField(blank=True, decimal_places=1, help_text='m', max_digits=10, null=True), + ), + migrations.AddField( + model_name='asset', + name='plug', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='plug', to='assets.Connector'), + ), + migrations.AddField( + model_name='asset', + name='socket', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='socket', to='assets.Connector'), + ), + migrations.DeleteModel( + name='Cable', + ), + migrations.AlterModelOptions( + name='asset', + options={'ordering': ['asset_id'], 'permissions': (('asset_finance', 'Can see financial data for assets'), ('view_asset', 'Can view an asset'))}, + ), + migrations.AddField( + model_name='assetstatus', + name='should_show', + field=models.BooleanField(default=True, help_text='Should this be shown by default in the asset list.'), + ), + migrations.AlterModelOptions( + name='supplier', + options={'permissions': (('view_supplier', 'Can view a supplier'),)}, + ), + ] diff --git a/assets/migrations/0008_auto_20191206_2124.py b/assets/migrations/0008_auto_20191206_2124.py new file mode 100644 index 00000000..f9002182 --- /dev/null +++ b/assets/migrations/0008_auto_20191206_2124.py @@ -0,0 +1,51 @@ +# Generated by Django 2.0.13 on 2019-12-06 21:24 + +from django.db import migrations, models, transaction +import re + +def forwards(apps, schema_editor): + AssetModel = apps.get_model('assets', 'Asset') + with transaction.atomic(): + for row in AssetModel.objects.all(): + + row.asset_id = row.asset_id.upper() + asset_search = re.search("^([A-Z0-9]*?[A-Z]?)([0-9]+)$", row.asset_id) + if asset_search is None: # If the asset_id doesn't have a number at the end + row.asset_id += "1" + + asset_search = re.search("^([A-Z0-9]*?[A-Z]?)([0-9]+)$", row.asset_id) + row.asset_id_prefix = asset_search.group(1) + row.asset_id_number = int(asset_search.group(2)) + + row.save(update_fields=['asset_id', 'asset_id_prefix', 'asset_id_number']) +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0007_auto_20190108_0202_squashed_0014_auto_20191017_2052'), + ] + + operations = [ + migrations.AlterModelOptions( + name='asset', + options={'ordering': ['asset_id_prefix', 'asset_id_number'], 'permissions': (('asset_finance', 'Can see financial data for assets'), ('view_asset', 'Can view an asset'))}, + ), + migrations.AddField( + model_name='asset', + name='asset_id_number', + field=models.IntegerField(default=1), + ), + migrations.AddField( + model_name='asset', + name='asset_id_prefix', + field=models.CharField(default='', max_length=8), + ), + migrations.AlterField( + model_name='asset', + name='asset_id', + field=models.CharField(max_length=15, unique=True), + ), + migrations.RunPython( + code=forwards, + reverse_code=migrations.operations.special.RunPython.noop, + ), + ] diff --git a/assets/migrations/0009_auto_20200103_2215.py b/assets/migrations/0009_auto_20200103_2215.py new file mode 100644 index 00000000..5408db0e --- /dev/null +++ b/assets/migrations/0009_auto_20200103_2215.py @@ -0,0 +1,32 @@ +# Generated by Django 2.0.13 on 2020-01-03 22:15 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0008_auto_20191206_2124'), + ] + + operations = [ + migrations.AlterModelOptions( + name='assetcategory', + options={'ordering': ['name'], 'verbose_name': 'Asset Category', 'verbose_name_plural': 'Asset Categories'}, + ), + migrations.AlterModelOptions( + name='assetstatus', + options={'ordering': ['name'], 'verbose_name': 'Asset Status', 'verbose_name_plural': 'Asset Statuses'}, + ), + migrations.AddField( + model_name='assetstatus', + name='display_class', + field=models.CharField(blank=True, help_text='HTML class to be appended to alter display of assets with this status, such as in the list.', max_length=80, null=True), + ), + migrations.AlterField( + model_name='asset', + name='purchased_from', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='assets', to='assets.Supplier'), + ), + ] diff --git a/assets/migrations/__init__.py b/assets/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/assets/models.py b/assets/models.py new file mode 100644 index 00000000..122fdcb2 --- /dev/null +++ b/assets/models.py @@ -0,0 +1,179 @@ +import re +from django.core.exceptions import ValidationError +from django.db import models, connection +from django.urls import reverse + +from django.db.models.signals import pre_save +from django.dispatch.dispatcher import receiver + +from reversion import revisions as reversion +from reversion.models import Version + +from RIGS.models import RevisionMixin + + +class AssetCategory(models.Model): + class Meta: + verbose_name = 'Asset Category' + verbose_name_plural = 'Asset Categories' + ordering = ['name'] + + name = models.CharField(max_length=80) + + def __str__(self): + return self.name + + +class AssetStatus(models.Model): + class Meta: + verbose_name = 'Asset Status' + verbose_name_plural = 'Asset Statuses' + ordering = ['name'] + + name = models.CharField(max_length=80) + should_show = models.BooleanField( + default=True, help_text="Should this be shown by default in the asset list.") + display_class = models.CharField(max_length=80, blank=True, null=True, help_text="HTML class to be appended to alter display of assets with this status, such as in the list.") + + def __str__(self): + return self.name + + +@reversion.register +class Supplier(models.Model, RevisionMixin): + name = models.CharField(max_length=80) + + class Meta: + permissions = ( + ('view_supplier', 'Can view a supplier'), + ) + + def get_absolute_url(self): + return reverse('supplier_list') + + def __str__(self): + return self.name + + +class Connector(models.Model): + description = models.CharField(max_length=80) + current_rating = models.DecimalField(decimal_places=2, max_digits=10, help_text='Amps') + voltage_rating = models.IntegerField(help_text='Volts') + num_pins = models.IntegerField(blank=True, null=True) + + def __str__(self): + return self.description + + +@reversion.register +class Asset(models.Model, RevisionMixin): + class Meta: + ordering = ['asset_id_prefix', 'asset_id_number'] + permissions = ( + ('asset_finance', 'Can see financial data for assets'), + ('view_asset', 'Can view an asset') + ) + + parent = models.ForeignKey(to='self', related_name='asset_parent', + blank=True, null=True, on_delete=models.SET_NULL) + asset_id = models.CharField(max_length=15, unique=True) + description = models.CharField(max_length=120) + category = models.ForeignKey(to=AssetCategory, on_delete=models.CASCADE) + status = models.ForeignKey(to=AssetStatus, on_delete=models.CASCADE) + serial_number = models.CharField(max_length=150, blank=True) + purchased_from = models.ForeignKey(to=Supplier, on_delete=models.CASCADE, blank=True, null=True, related_name="assets") + date_acquired = models.DateField() + date_sold = models.DateField(blank=True, null=True) + purchase_price = models.DecimalField(blank=True, null=True, decimal_places=2, max_digits=10) + salvage_value = models.DecimalField(blank=True, null=True, decimal_places=2, max_digits=10) + comments = models.TextField(blank=True) + next_sched_maint = models.DateField(blank=True, null=True) + + # Cable assets + is_cable = models.BooleanField(default=False) + plug = models.ForeignKey(Connector, on_delete=models.SET_NULL, + related_name='plug', blank=True, null=True) + socket = models.ForeignKey(Connector, on_delete=models.SET_NULL, + related_name='socket', blank=True, null=True) + length = models.DecimalField(decimal_places=1, max_digits=10, + blank=True, null=True, help_text='m') + csa = models.DecimalField(decimal_places=2, max_digits=10, + blank=True, null=True, help_text='mm^2') + circuits = models.IntegerField(blank=True, null=True) + cores = models.IntegerField(blank=True, null=True) + + # Hidden asset_id components + # For example, if asset_id was "C1001" then asset_id_prefix would be "C" and number "1001" + asset_id_prefix = models.CharField(max_length=8, default="") + asset_id_number = models.IntegerField(default=1) + + def get_available_asset_id(wanted_prefix=""): + sql = """ + SELECT a.asset_id_number+1 + FROM assets_asset a + LEFT OUTER JOIN assets_asset b ON + (a.asset_id_number + 1 = b.asset_id_number AND + a.asset_id_prefix = b.asset_id_prefix) + WHERE b.asset_id IS NULL AND a.asset_id_number >= %s AND a.asset_id_prefix = %s; + """ + with connection.cursor() as cursor: + cursor.execute(sql, [9000, wanted_prefix]) + row = cursor.fetchone() + if row is None or row[0] is None: + return 9000 + else: + return row[0] + + def get_absolute_url(self): + return reverse('asset_detail', kwargs={'pk': self.asset_id}) + + def __str__(self): + out = str(self.asset_id) + ' - ' + self.description + if self.is_cable: + out += '{} - {}m - {}'.format(self.plug, self.length, self.socket) + return out + + def clean(self): + errdict = {} + if self.date_sold and self.date_acquired > self.date_sold: + errdict["date_sold"] = ["Cannot sell an item before it is acquired"] + + self.asset_id = self.asset_id.upper() + asset_search = re.search("^([a-zA-Z0-9]*?[a-zA-Z]?)([0-9]+)$", self.asset_id) + if asset_search is None: + errdict["asset_id"] = [ + "An Asset ID can only consist of letters and numbers, with a final number"] + + if self.purchase_price and self.purchase_price < 0: + errdict["purchase_price"] = ["A price cannot be negative"] + + if self.salvage_value and self.salvage_value < 0: + errdict["salvage_value"] = ["A price cannot be negative"] + + if self.is_cable: + if not self.length or self.length <= 0: + errdict["length"] = ["The length of a cable must be more than 0"] + if not self.csa or self.csa <= 0: + errdict["csa"] = ["The CSA of a cable must be more than 0"] + if not self.circuits or self.circuits <= 0: + errdict["circuits"] = ["There must be at least one circuit in a cable"] + if not self.cores or self.cores <= 0: + errdict["cores"] = ["There must be at least one core in a cable"] + if self.socket is None: + errdict["socket"] = ["A cable must have a socket"] + if self.plug is None: + errdict["plug"] = ["A cable must have a plug"] + + if errdict != {}: # If there was an error when validation + raise ValidationError(errdict) + + +@receiver(pre_save, sender=Asset) +def pre_save_asset(sender, instance, **kwargs): + """Automatically fills in hidden members on database access""" + asset_search = re.search("^([a-zA-Z0-9]*?[a-zA-Z]?)([0-9]+)$", instance.asset_id) + if asset_search is None: + instance.asset_id += "1" + asset_search = re.search("^([a-zA-Z0-9]*?[a-zA-Z]?)([0-9]+)$", instance.asset_id) + instance.asset_id_prefix = asset_search.group(1) + instance.asset_id_number = int(asset_search.group(2)) diff --git a/assets/static/js/csrf.js b/assets/static/js/csrf.js new file mode 100644 index 00000000..895ce31e --- /dev/null +++ b/assets/static/js/csrf.js @@ -0,0 +1,23 @@ +$.ajaxSetup({ + beforeSend: function(xhr, settings) { + function getCookie(name) { + var cookieValue = null; + if (document.cookie && document.cookie != '') { + var cookies = document.cookie.split(';'); + for (var i = 0; i < cookies.length; i++) { + var cookie = jQuery.trim(cookies[i]); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) == (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; + } + if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) { + // Only send the token to relative URLs i.e. locally. + xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken')); + } + } +}); \ No newline at end of file diff --git a/assets/templates/asset_activity_table.html b/assets/templates/asset_activity_table.html new file mode 100644 index 00000000..aca6142c --- /dev/null +++ b/assets/templates/asset_activity_table.html @@ -0,0 +1,92 @@ +{% extends request.is_ajax|yesno:"base_ajax.html,base_assets.html" %} +{% load static %} +{% load paginator from filters %} +{% load to_class_name from filters %} + +{% block title %}Asset Activity Stream{% endblock %} + +{# TODO: Find a way to reduce code duplication...can't just include the content because of the IDs... #} + +{% block js %} + + + + +{% endblock %} + +{% block content %} +
    +
    +
    +

    Asset Activity Stream

    +
    +
    {% paginator %}
    +
    +
    + + + + + + + + + + + + + + {% for version in object_list %} + + + + + + + + + + + {% endfor %} + + +
    DateObjectVersion IDUserChangesComment
    {{ version.revision.date_created }}{{version.changes.new|to_class_name}} {{ version.changes.new.asset_id|default:version.changes.new.pk }}{{ version.pk }}|{{ version.revision.pk }}{{ version.revision.user.name }} + {% if version.changes.old == None %} + {{version.changes.new|to_class_name}} Created + {% else %} + {% include 'RIGS/version_changes.html' %} + {% endif %} {{ version.changes.revision.comment }}
    + +
    +
    {% paginator %}
    +
    +{% endblock %} diff --git a/assets/templates/asset_create.html b/assets/templates/asset_create.html new file mode 100644 index 00000000..bc953d2d --- /dev/null +++ b/assets/templates/asset_create.html @@ -0,0 +1,61 @@ +{% extends 'base_assets.html' %} +{% load widget_tweaks %} +{% load asset_templatetags %} +{% block title %}Asset {{ object.asset_id }}{% endblock %} + +{% block content %} + + +{% if duplicate %} +
    + {% else %} + + {% endif %} + {% include 'form_errors.html' %} + {% csrf_token %} + +
    +
    + {% include 'partials/asset_form.html' %} +
    +
    +
    +
    + {% include 'partials/purchasedetails_form.html' %} +
    + +
    + {% include 'partials/parent_form.html' %} +
    +
    +
    +
    + {% include 'partials/asset_buttons.html' %} +
    +
    +
    + + {% endblock %} + + {% block js%} + + {%endblock%} diff --git a/assets/templates/asset_embed.html b/assets/templates/asset_embed.html new file mode 100644 index 00000000..c4a40187 --- /dev/null +++ b/assets/templates/asset_embed.html @@ -0,0 +1,40 @@ +{% extends 'base_embed.html' %} +{% load static from staticfiles %} + +{% block content %} + +
    + + +
    +

    Asset: {{ object.asset_id }} | {{ object.description }}

    +

    + + Category: + {{ object.category }} + +   + + Status: + {{ object.status }} + +

    + {% if object.serial_number %} +
    Serial Number:
    +
    {{ object.serial_number }}
    + {% endif %} + {% if object.comments %} +
    Comments:
    +
    {{ object.comments|linebreaksbr }}
    + {% endif %} + + +
    +
    + + +{% endblock %} diff --git a/assets/templates/asset_list.html b/assets/templates/asset_list.html new file mode 100644 index 00000000..90bb2346 --- /dev/null +++ b/assets/templates/asset_list.html @@ -0,0 +1,65 @@ +{% extends 'base_assets.html' %} +{% block title %}Asset List{% endblock %} +{% load paginator from filters %} +{% load widget_tweaks %} + +{% block content %} + + + +
    +
    + {% render_field form.query|add_class:'form-control' placeholder='Search by Asset ID/Desc/Serial' style="width: 250px"%} + + +
    +
    +
    +
    + + {% render_field form.category|attr:'multiple'|add_class:'form-control selectpicker' data-none-selected-text="Categories" data-header="Categories" data-actions-box="true" %} +
    +
    + + {% render_field form.status|attr:'multiple'|add_class:'form-control selectpicker' data-none-selected-text="Statuses" data-header="Statuses" data-actions-box="true" %} +
    + + +
    +
    + + + + + + + + + + + + + {% include 'partials/asset_list_table_body.html' %} + +
    Asset IDDescriptionCategoryStatus
    + +{% if is_paginated %} +
    + {% paginator %} +
    +{% endif %} + +{% endblock %} + +{% load static %} +{% block css %} + + +{% endblock %} + +{% block preload_js %} + + +{% endblock %} diff --git a/assets/templates/asset_update.html b/assets/templates/asset_update.html new file mode 100644 index 00000000..6e950887 --- /dev/null +++ b/assets/templates/asset_update.html @@ -0,0 +1,72 @@ +{% extends 'base_assets.html' %} +{% load widget_tweaks %} +{% load asset_templatetags %} +{% block title %}Asset {{ object.asset_id }}{% endblock %} + +{% block content %} + + +
    + {% include 'form_errors.html' %} + {% csrf_token %} + +
    +
    + {% include 'partials/asset_form.html' %} +
    +
    +
    + {% if perms.assets.asset_finance %} +
    + {% include 'partials/purchasedetails_form.html' %} +
    + {%endif%} + +
    + {% include 'partials/parent_form.html' %} +
    +
    +
    +
    + {% include 'partials/asset_buttons.html' %} +
    +
    +
    + +{% if not edit and perms.assets.view_asset %} + +{% endif %} + +{% endblock %} + +{% block js%} +{% if edit %} + +{% endif %} +{% endblock %} diff --git a/assets/templates/asset_update_search_results.html b/assets/templates/asset_update_search_results.html new file mode 100644 index 00000000..edd156f1 --- /dev/null +++ b/assets/templates/asset_update_search_results.html @@ -0,0 +1,16 @@ +{% for asset in object_list %} + + {{ asset.asset_id }} - {{ asset.description }} + +
    + {% empty %} + No assets match given ID +{% endfor %} + + \ No newline at end of file diff --git a/assets/templates/asset_version_history.html b/assets/templates/asset_version_history.html new file mode 100644 index 00000000..89223294 --- /dev/null +++ b/assets/templates/asset_version_history.html @@ -0,0 +1,68 @@ +{% extends request.is_ajax|yesno:"base_ajax.html,base_assets.html" %} +{% load to_class_name from filters %} +{% load paginator from filters %} +{% load static %} + +{% block title %}{{object|to_class_name}} {{ object.asset_id }} - Revision History{% endblock %} + +{% block js %} + + + +{% endblock %} + +{% block content %} +
    +
    + +
    {% paginator %}
    +
    +
    + + + + + + + + + + + + {% for version in object_list %} + + + + + + + + + + + {% endfor %} + + +
    DateVersion IDUserChangesComment
    {{ version.revision.date_created }}{{ version.pk }}|{{ version.revision.pk }}{{ version.revision.user.name }} + {% if version.changes.old is None %} + {{object|to_class_name}} Created + {% else %} + {% include 'RIGS/version_changes.html' %} + {% endif %} + + {{ version.revision.comment }} +
    +
    +
    {% paginator %}
    +
    +{% endblock %} diff --git a/assets/templates/partials/asset_buttons.html b/assets/templates/partials/asset_buttons.html new file mode 100644 index 00000000..39402cbe --- /dev/null +++ b/assets/templates/partials/asset_buttons.html @@ -0,0 +1,25 @@ +{% if edit and object %} + + + Duplicate +{% elif duplicate %} + + +{% elif create %} + + +{% else %} + + +{% endif %} +{% if create or edit or duplicate %} +
    + +{% endif %} diff --git a/assets/templates/partials/asset_form.html b/assets/templates/partials/asset_form.html new file mode 100644 index 00000000..08f82d18 --- /dev/null +++ b/assets/templates/partials/asset_form.html @@ -0,0 +1,61 @@ +{% load widget_tweaks %} +{% load asset_templatetags %} +
    +
    + Asset Details +
    +
    + {% if create or edit or duplicate %} +
    + + {% if duplicate %} + {% render_field form.asset_id|add_class:'form-control' value=object.asset_id %} + {% elif object.asset_id %} + {% render_field form.asset_id|attr:'readonly'|add_class:'disabled_input form-control' value=object.asset_id %} + {% else %} + {% render_field form.asset_id|add_class:'form-control' %} + {% endif %} +
    +
    + + {% render_field form.description|add_class:'form-control' value=object.description %} +
    +
    + + {% render_field form.category|add_class:'form-control'%} +
    + {% render_field form.is_cable|attr:'onchange=checkIfCableHidden()' %} +
    + + {% render_field form.status|add_class:'form-control'%} +
    +
    + + {% render_field form.serial_number|add_class:'form-control' value=object.serial_number %} +
    + +
    + + {% render_field form.comments|add_class:'form-control' %} +
    + {% else %} +
    Asset ID
    +
    {{ object.asset_id }}
    + +
    Description
    +
    {{ object.description }}
    + +
    Category
    +
    {{ object.category }}
    + +
    Status
    +
    {{ object.status }}
    + +
    Serial Number
    +
    {{ object.serial_number|default:'-' }}
    + +
    Comments
    +
    {{ object.comments|default:'-'|linebreaksbr }}
    + {% endif %} +
    +
    diff --git a/assets/templates/partials/asset_list_table_body.html b/assets/templates/partials/asset_list_table_body.html new file mode 100644 index 00000000..c952159d --- /dev/null +++ b/assets/templates/partials/asset_list_table_body.html @@ -0,0 +1,18 @@ +{% for item in object_list %} + {#
  • {{ item.asset_id }} - {{ item.description }}
  • #} + + {{ item.asset_id }} + {{ item.description }} + {{ item.category }} + {{ item.status }} + +
    + View + {% if perms.assets.change_asset %} + Edit + Duplicate + {% endif %} +
    + + +{% endfor %} diff --git a/assets/templates/partials/asset_picker.html b/assets/templates/partials/asset_picker.html new file mode 100644 index 00000000..fd63fe86 --- /dev/null +++ b/assets/templates/partials/asset_picker.html @@ -0,0 +1,65 @@ + + +{% load static %} +{% block css %} + + +{% endblock %} + +{% block preload_js %} + + +{% endblock %} + +{% block js %} +{{ js.super }} + +{% endblock js %} \ No newline at end of file diff --git a/assets/templates/partials/cable_form.html b/assets/templates/partials/cable_form.html new file mode 100644 index 00000000..9390a73c --- /dev/null +++ b/assets/templates/partials/cable_form.html @@ -0,0 +1,61 @@ +{% load widget_tweaks %} +{% load asset_templatetags %} +
    +
    + Cable Details +
    +
    + {% if create or edit or duplicate %} +
    + + {% render_field form.plug|add_class:'form-control'%} +
    +
    + + {% render_field form.socket|add_class:'form-control'%} +
    +
    + +
    + {% render_field form.length|add_class:'form-control' %} + {{ form.length.help_text }} +
    +
    +
    + +
    + {% render_field form.csa|add_class:'form-control' value=object.csa %} + {{ form.csa.help_text }} +
    +
    +
    + + {% render_field form.circuits|add_class:'form-control' value=object.circuits %} +
    +
    + + {% render_field form.cores|add_class:'form-control' value=object.cores %} +
    + {% else %} +
    +
    Socket
    +
    {{ object.socket|default_if_none:'-' }}
    + +
    Plug
    +
    {{ object.plug|default_if_none:'-' }}
    + +
    Length
    +
    {{ object.length|default_if_none:'-' }}m
    + +
    Cross Sectional Area
    +
    {{ object.csa|default_if_none:'-' }}m^2
    + +
    Circuits
    +
    {{ object.circuits|default_if_none:'-' }}
    + +
    Cores
    +
    {{ object.cores|default_if_none:'-' }}
    +
    + {% endif %} +
    +
    diff --git a/assets/templates/partials/parent_form.html b/assets/templates/partials/parent_form.html new file mode 100644 index 00000000..5f29240d --- /dev/null +++ b/assets/templates/partials/parent_form.html @@ -0,0 +1,41 @@ +{% load widget_tweaks %} +{% load asset_templatetags %} +
    +
    + Collection Details +
    +
    + {% if create or edit or duplicate %} +
    + + {% include 'partials/asset_picker.html' %} +
    + {% else %} +
    +
    Parent
    +
    + {% if object.parent %} + + {{ object.parent.asset_id }} - {{ object.parent.description }} + + {% else %} + - + {% endif %} +
    + +
    Children
    + {% if object.asset_parent.all %} + {% for child in object.asset_parent.all %} +
    + + {{ child.asset_id }} - {{ child.description }} + +
    + {% endfor %} + {% else %} +
    -
    + {% endif %} +
    + {% endif%} +
    +
    diff --git a/assets/templates/partials/purchasedetails_form.html b/assets/templates/partials/purchasedetails_form.html new file mode 100644 index 00000000..0e2f1aa6 --- /dev/null +++ b/assets/templates/partials/purchasedetails_form.html @@ -0,0 +1,89 @@ +{% load widget_tweaks %} +{% load asset_templatetags %} +{% load static %} + + +{% block css %} + + +{% endblock %} + +{% block preload_js %} + + +{% endblock %} + +{% block js %} + +{% endblock %} + +
    +
    + Purchase Details +
    +
    + {% if create or edit or duplicate %} +
    + + +
    + +
    + +
    + £ + {% render_field form.purchase_price|add_class:'form-control' value=object.purchase_price %} +
    +
    + +
    + +
    + £ + {% render_field form.salvage_value|add_class:'form-control' value=object.salvage_value %} +
    +
    + +
    + + {% if object.date_acquired%} + {% with date_acq=object.date_acquired|date:"Y-m-d" %} + {% render_field form.date_acquired|add_class:'form-control'|attr:'type="date"' value=date_acq %} + {% endwith %} + {% else %} + + {% endif %} +
    + +
    + + {% with date_sol=object.form.date_sold|date:"Y-m-d" %} + {% render_field form.date_sold|add_class:'form-control'|attr:'type="date"' value=date_sol %} + {% endwith %} +
    + {% else %} +
    +
    Purchased From
    +
    {{ object.purchased_from|default_if_none:'-' }}
    + +
    Purchase Price
    +
    £{{ object.purchase_price|default_if_none:'-' }}
    + +
    Salvage Value
    +
    £{{ object.salvage_value|default_if_none:'-' }}
    + +
    Date Acquired
    +
    {{ object.date_acquired|default_if_none:'-' }}
    + {% if object.date_sold %} +
    Date Sold
    +
    {{ object.date_sold|default_if_none:'-' }}
    + {% endif %} +
    + {% endif %} +
    +
    diff --git a/assets/templates/partials/render_field.html b/assets/templates/partials/render_field.html new file mode 100644 index 00000000..58744530 --- /dev/null +++ b/assets/templates/partials/render_field.html @@ -0,0 +1,16 @@ +{% load widget_tweaks %} + + +
    + + {% if css %} + {% render_field field|add_class:css %} + {% elif disable_if_filled and field.value %} + {% render_field field|attr:'disabled' %} + {% elif css and disable_if_filled %} + {% render_field field|add_class:css|attr:'disabled' %} + {% else %} + {{ field }} + {% endif %} + +
    diff --git a/assets/templates/supplier_detail.html b/assets/templates/supplier_detail.html new file mode 100644 index 00000000..a7889611 --- /dev/null +++ b/assets/templates/supplier_detail.html @@ -0,0 +1,73 @@ +{% extends 'base_assets.html' %} +{% block title %}Supplier | {{ object.name }}{% endblock %} + +{% block content %} +
    + {% if not request.is_ajax %} +
    +

    Supplier | {{ object.name }}

    +
    + +
    +
    + Edit +
    +
    + {% endif %} +
    +
    +
    Supplier Details
    +
    +
    +
    Name
    +
    {{ object.name }}
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    Associated Assets
    +
    + + + + + + + + + + + + {% with object.assets.all as object_list %} + {% include 'partials/asset_list_table_body.html' %} + {% endwith %} + +
    Asset IDDescriptionCategoryStatus
    +
    +
    +
    +
    + + + {% if not request.is_ajax %} + + {% endif %} +{% endblock %} diff --git a/assets/templates/supplier_list.html b/assets/templates/supplier_list.html new file mode 100644 index 00000000..afc6d61d --- /dev/null +++ b/assets/templates/supplier_list.html @@ -0,0 +1,47 @@ +{% extends 'base_assets.html' %} +{% block title %}Supplier List{% endblock %} +{% load paginator from filters %} +{% load widget_tweaks %} + +{% block content %} + + + +
    + {% csrf_token %} +
    + {% render_field form.query|add_class:'form-control' placeholder='Search by Name' style="width: 250px"%} + + +
    +
    + + + + + + + + + + {% for item in object_list %} + + + + + {% endfor %} + +
    SupplierQuick Links
    {{ item.name }} + View + Edit +
    + +{% if is_paginated %} +
    + {% paginator %} +
    +{% endif %} + +{% endblock %} diff --git a/assets/templates/supplier_update.html b/assets/templates/supplier_update.html new file mode 100644 index 00000000..dfc8cc54 --- /dev/null +++ b/assets/templates/supplier_update.html @@ -0,0 +1,20 @@ +{% extends 'base_assets.html' %} +{% block title %}Edit{% endblock %} + +{% block content %} + + +
    + {% csrf_token %} + {{ form }} + + +
    +{% endblock %} diff --git a/assets/templatetags/__init__.py b/assets/templatetags/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/assets/templatetags/asset_templatetags.py b/assets/templatetags/asset_templatetags.py new file mode 100644 index 00000000..905f9ce2 --- /dev/null +++ b/assets/templatetags/asset_templatetags.py @@ -0,0 +1,21 @@ +from django import template +from django.template.defaultfilters import stringfilter +from django.utils.safestring import SafeData, mark_safe +from django.utils.text import normalize_newlines +from django.utils.html import escape + +register = template.Library() + + +@register.filter(is_safe=True, needs_autoescape=True) +@stringfilter +def linebreaksn(value, autoescape=True): + """ + Convert all newlines in a piece of plain text to jQuery line breaks + (`\n`). + """ + autoescape = autoescape and not isinstance(value, SafeData) + value = normalize_newlines(value) + if autoescape: + value = escape(value) + return mark_safe(value.replace('\n', '\\n')) diff --git a/assets/urls.py b/assets/urls.py new file mode 100644 index 00000000..78e60592 --- /dev/null +++ b/assets/urls.py @@ -0,0 +1,45 @@ +from django.conf.urls import url +from django.urls import path +from assets import views, models +from RIGS import versioning + +from django.contrib.auth.decorators import login_required +from django.views.decorators.clickjacking import xframe_options_exempt +from PyRIGS.decorators import has_oembed, permission_required_with_403 + +urlpatterns = [ + path('', views.AssetList.as_view(), name='asset_index'), + path('asset/list/', views.AssetList.as_view(), name='asset_list'), + # Lazy way to enable the oembed redirect... + path('asset/id//', has_oembed(oembed_view="asset_oembed")(views.AssetDetail.as_view()), name='asset_detail'), + path('asset/create/', permission_required_with_403('assets.add_asset') + (views.AssetCreate.as_view()), name='asset_create'), + path('asset/id//edit/', permission_required_with_403('assets.change_asset') + (views.AssetEdit.as_view()), name='asset_update'), + path('asset/id//duplicate/', permission_required_with_403('assets.add_asset') + (views.AssetDuplicate.as_view()), name='asset_duplicate'), + path('asset/id//history/', permission_required_with_403('assets.view_asset')(views.AssetVersionHistory.as_view()), + name='asset_history', kwargs={'model': models.Asset}), + path('activity', permission_required_with_403('assets.view_asset') + (views.ActivityTable.as_view()), name='asset_activity_table'), + + path('asset/search/', views.AssetSearch.as_view(), name='asset_search_json'), + path('asset/id//embed/', + xframe_options_exempt( + login_required(login_url='/user/login/embed/')(views.AssetEmbed.as_view())), + name='asset_embed'), + path('asset/id//oembed_json/', + views.AssetOembed.as_view(), + name='asset_oembed'), + + path('supplier/list', views.SupplierList.as_view(), name='supplier_list'), + path('supplier/', views.SupplierDetail.as_view(), name='supplier_detail'), + path('supplier/create', permission_required_with_403('assets.add_supplier') + (views.SupplierCreate.as_view()), name='supplier_create'), + path('supplier//edit', permission_required_with_403('assets.change_supplier') + (views.SupplierUpdate.as_view()), name='supplier_update'), + path('supplier//history/', views.SupplierVersionHistory.as_view(), + name='supplier_history', kwargs={'model': models.Supplier}), + + path('supplier/search/', views.SupplierSearch.as_view(), name='supplier_search_json'), +] diff --git a/assets/views.py b/assets/views.py new file mode 100644 index 00000000..96e8940d --- /dev/null +++ b/assets/views.py @@ -0,0 +1,256 @@ +from django.contrib.auth.mixins import LoginRequiredMixin +from django.http import JsonResponse +from django.http import HttpResponse +from django.views import generic +from django.views.decorators.csrf import csrf_exempt +from django.utils.decorators import method_decorator +from django.urls import reverse +from django.db.models import Q +from django.shortcuts import get_object_or_404 +from assets import models, forms +from RIGS import versioning + +import simplejson + + +@method_decorator(csrf_exempt, name='dispatch') +class AssetList(LoginRequiredMixin, generic.ListView): + model = models.Asset + template_name = 'asset_list.html' + paginate_by = 40 + ordering = ['-pk'] + hide_hidden_status = True + + def get_initial(self): + initial = {'status': models.AssetStatus.objects.filter(should_show=True)} + return initial + + def get_queryset(self): + if self.request.method == 'POST': + self.form = forms.AssetSearchForm(data=self.request.POST) + elif self.request.method == 'GET' and len(self.request.GET) > 0: + self.form = forms.AssetSearchForm(data=self.request.GET) + else: + self.form = forms.AssetSearchForm(data=self.get_initial()) + form = self.form + if not form.is_valid(): + return self.model.objects.none() + + # TODO Feedback to user when search fails + query_string = form.cleaned_data['query'] or "" + if len(query_string) == 0: + queryset = self.model.objects.all() + elif len(query_string) >= 3: + queryset = self.model.objects.filter( + Q(asset_id__exact=query_string) | Q(description__icontains=query_string) | Q(serial_number__exact=query_string)) + else: + queryset = self.model.objects.filter(Q(asset_id__exact=query_string)) + + if form.cleaned_data['category']: + queryset = queryset.filter(category__in=form.cleaned_data['category']) + + if len(form.cleaned_data['status']) > 0: + queryset = queryset.filter(status__in=form.cleaned_data['status']) + elif self.hide_hidden_status: + queryset = queryset.filter( + status__in=models.AssetStatus.objects.filter(should_show=True)) + + return queryset + + def get_context_data(self, **kwargs): + context = super(AssetList, self).get_context_data(**kwargs) + context["form"] = self.form + + context["categories"] = models.AssetCategory.objects.all() + + context["statuses"] = models.AssetStatus.objects.all() + return context + + +class AssetSearch(AssetList): + hide_hidden_status = False + + def render_to_response(self, context, **response_kwargs): + result = [] + + for asset in context["object_list"]: + result.append({"id": asset.pk, "label": (asset.asset_id + " | " + asset.description)}) + + return JsonResponse(result, safe=False) + + +class AssetIDUrlMixin: + def get_object(self, queryset=None): + pk = self.kwargs.get(self.pk_url_kwarg) + queryset = models.Asset.objects.filter(asset_id=pk) + try: + # Get the single item from the filtered queryset + obj = queryset.get() + except queryset.model.DoesNotExist: + raise Http404(_("No %(verbose_name)s found matching the query") % + {'verbose_name': queryset.model._meta.verbose_name}) + return obj + + +class AssetDetail(LoginRequiredMixin, AssetIDUrlMixin, generic.DetailView): + model = models.Asset + template_name = 'asset_update.html' + + +class AssetEdit(LoginRequiredMixin, AssetIDUrlMixin, generic.UpdateView): + template_name = 'asset_update.html' + model = models.Asset + form_class = forms.AssetForm + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['edit'] = True + context["connectors"] = models.Connector.objects.all() + + return context + + def get_success_url(self): + return reverse("asset_detail", kwargs={"pk": self.object.asset_id}) + + +class AssetCreate(LoginRequiredMixin, generic.CreateView): + template_name = 'asset_create.html' + model = models.Asset + form_class = forms.AssetForm + + def get_context_data(self, **kwargs): + context = super(AssetCreate, self).get_context_data(**kwargs) + context["create"] = True + context["connectors"] = models.Connector.objects.all() + + return context + + def get_initial(self, *args, **kwargs): + initial = super().get_initial(*args, **kwargs) + initial["asset_id"] = models.Asset.get_available_asset_id() + return initial + + def get_success_url(self): + return reverse("asset_detail", kwargs={"pk": self.object.asset_id}) + + +class DuplicateMixin: + def get(self, request, *args, **kwargs): + self.object = self.get_object() + self.object.pk = None + return self.render_to_response(self.get_context_data()) + + +class AssetDuplicate(DuplicateMixin, AssetIDUrlMixin, AssetCreate): + model = models.Asset + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["create"] = None + context["duplicate"] = True + context['previous_asset_id'] = self.get_object().asset_id + return context + + +class AssetOembed(generic.View): + model = models.Asset + + def get(self, request, pk=None): + embed_url = reverse('asset_embed', args=[pk]) + full_url = "{0}://{1}{2}".format(request.scheme, request.META['HTTP_HOST'], embed_url) + + data = { + 'html': ''.format(full_url), + 'version': '1.0', + 'type': 'rich', + 'height': '250' + } + + json = simplejson.JSONEncoderForHTML().encode(data) + return HttpResponse(json, content_type="application/json") + + +class AssetEmbed(AssetDetail): + template_name = 'asset_embed.html' + + +class SupplierList(generic.ListView): + model = models.Supplier + template_name = 'supplier_list.html' + paginate_by = 40 + ordering = ['name'] + + def get_queryset(self): + if self.request.method == 'POST': + self.form = forms.SupplierSearchForm(data=self.request.POST) + elif self.request.method == 'GET': + self.form = forms.SupplierSearchForm(data=self.request.GET) + else: + self.form = forms.SupplierSearchForm(data={}) + form = self.form + if not form.is_valid(): + return self.model.objects.none() + + query_string = form.cleaned_data['query'] or "" + if len(query_string) == 0: + queryset = self.model.objects.all() + else: + queryset = self.model.objects.filter(Q(name__icontains=query_string)) + + return queryset + + def get_context_data(self, **kwargs): + context = super(SupplierList, self).get_context_data(**kwargs) + context["form"] = self.form + return context + + +class SupplierSearch(SupplierList): + hide_hidden_status = False + + def render_to_response(self, context, **response_kwargs): + result = [] + + for supplier in context["object_list"]: + result.append({"id": supplier.pk, "name": supplier.name}) + + return JsonResponse(result, safe=False) + + +class SupplierDetail(generic.DetailView): + model = models.Supplier + template_name = 'supplier_detail.html' + + +class SupplierCreate(generic.CreateView): + model = models.Supplier + form_class = forms.SupplierForm + template_name = 'supplier_update.html' + + +class SupplierUpdate(generic.UpdateView): + model = models.Supplier + form_class = forms.SupplierForm + template_name = 'supplier_update.html' + + +class SupplierVersionHistory(versioning.VersionHistory): + template_name = "asset_version_history.html" + + +class AssetVersionHistory(versioning.VersionHistory): + template_name = "asset_version_history.html" + + def get_object(self, **kwargs): + return get_object_or_404(models.Asset, asset_id=self.kwargs['pk']) + + +class ActivityTable(versioning.ActivityTable): + model = versioning.RIGSVersion + template_name = "asset_activity_table.html" + paginate_by = 25 + + def get_queryset(self): + versions = versioning.RIGSVersion.objects.get_for_multiple_models( + [models.Asset, models.Supplier]) + return versions diff --git a/db.sqlite3 b/db.sqlite3 deleted file mode 100644 index 07462b3f..00000000 Binary files a/db.sqlite3 and /dev/null differ diff --git a/importer.py b/importer.py index c7f3c9e0..44df4c15 100644 --- a/importer.py +++ b/importer.py @@ -19,6 +19,7 @@ from multiprocessing import Process # Slight fix for needing to restablish the connection connection.close() + def fix_email(email): if not (email is None or email is "") and ("@" not in email): email += "@nottingham.ac.uk" @@ -31,7 +32,7 @@ def setup_cursor(): return cursor except ConnectionDoesNotExist: print("Legacy database is not configured") - print(connections._databases) + print((connections._databases)) return None @@ -55,13 +56,13 @@ def import_users(delete=False): object.initials = row[6] object.phone = row[7] object.save() - print("Updated " + str(object)) + print(("Updated " + str(object))) except ObjectDoesNotExist: object = models.Profile(pk=row[0], username=row[1], email=row[2], first_name=row[3], last_name=row[4], is_active=row[5], initials=row[6], phone=row[7]) object.set_password(uuid.uuid4().hex) object.save() - print("Created " + str(object)) + print(("Created " + str(object))) def import_people(delete=False): @@ -85,15 +86,15 @@ def import_people(delete=False): if row[5] != "Normal": notes = row[5] - print("Trying %s %s %s" % (pk, name, phone)) + print(("Trying %s %s %s" % (pk, name, phone))) person, created = models.Person.objects.get_or_create(pk=pk, name=name, phone=phone, email=email, address=address, notes=notes) if created: - print("Created: " + person.__str__()) + print(("Created: " + person.__str__())) with reversion.create_revision(): person.save() else: - print("Found: " + person.__str__()) + print(("Found: " + person.__str__())) def import_organisations(delete=False): @@ -113,11 +114,11 @@ def import_organisations(delete=False): address=row[3], union_account=row[4], notes=notes) if created: - print("Created: " + object.__str__()) + print(("Created: " + object.__str__())) with reversion.create_revision(): object.save() else: - print("Found: " + object.__str__()) + print(("Found: " + object.__str__())) def import_vat_rates(delete=False): @@ -133,11 +134,11 @@ def import_vat_rates(delete=False): object, created = models.VatRate.objects.get_or_create(pk=row[0], start_at=start_at, comment=row[3], rate=row[4]) if created: - print("Created: " + object.__str__()) + print(("Created: " + object.__str__())) with reversion.create_revision(): object.save() else: - print("Found: " + object.__str__()) + print(("Found: " + object.__str__())) def import_venues(delete=False): @@ -258,7 +259,7 @@ def import_nonrigs(delete=False): if (delete): try: models.Event.objects.filter(is_rig=False).delete() - except: + except BaseException: pass cursor = setup_cursor() if cursor is None: @@ -286,7 +287,7 @@ def import_invoices(delete=False): try: models.Invoice.objects.all().delete() models.Payment.objects.all().delete() - except: + except BaseException: pass cursor = setup_cursor() if cursor is None: @@ -295,11 +296,11 @@ def import_invoices(delete=False): cursor.execute(sql) for row in cursor.fetchall(): print(row) - print row[1] + print(row[1]) try: event = models.Event.objects.get(pk=row[1]) except ObjectDoesNotExist: - print "Event %d not found" % row[1] + print("Event %d not found" % row[1]) continue print(event) @@ -332,6 +333,7 @@ def import_invoices(delete=False): p2.date = datetime.date.today() p2.save() + @transaction.atomic def main(): # processs = [] @@ -340,7 +342,7 @@ def main(): # processs.append(Process(target=import_organisations, args=(True,))) # processs.append(Process(target=import_vat_rates, args=(True,))) # processs.append(Process(target=import_venues, args=(True,))) - + # # Start all processs # [x.start() for x in processs] # # Wait for all processs to finish @@ -351,7 +353,7 @@ def main(): import_organisations(True) import_vat_rates(True) import_venues(True) - + import_rigs(True) import_eventitem(True) import_invoices(True) @@ -368,5 +370,6 @@ def main(): cursor = connections['default'].cursor() cursor.execute(sql) + if __name__ == "__main__": main() diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..95b8798b --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +DJANGO_SETTINGS_MODULE = myproject.settings \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 532cadea..96e43b97 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,33 +1,40 @@ +beautifulsoup4==4.6.0 +contextlib2==0.5.5 diff-match-patch==20121119 -dj-database-url==0.3.0 +dj-database-url==0.5.0 dj-static==0.0.6 -Django==1.8.2 -django-debug-toolbar==1.3.0 -django-ical==1.3 -django-recaptcha==1.0.4 -django-registration-redux==1.2 -django-reversion==1.8.7 +Django==2.0.13 +django-filter==2.0.0 +django-widget-tweaks==1.4.3 +django-debug-toolbar==1.9.1 +django-ical==1.4 +django-recaptcha==1.4.0 +django-registration-redux==2.4 +django-reversion==2.0.13 django-toolbelt==0.0.1 -django-widget-tweaks==1.3 -gunicorn==19.3.0 -icalendar==3.9.0 -lxml==3.4.4 -Pillow==2.8.1 -psycopg2==2.6 -Pygments==2.0.2 -PyPDF2==1.24 -python-dateutil==2.4.2 -pytz==2015.4 -raven==5.8.1 -reportlab==3.1.44 -selenium==2.53.1 -simplejson==3.7.2 -six==1.9.0 -sqlparse==0.1.15 -static3==0.6.1 +premailer==3.2.0 +git+git://github.com/jazzband/django-widget-tweaks.git@1.4.2 +gunicorn==19.8.1 +icalendar==4.0.1 +lxml==4.2.1 +Markdown==2.6.11 +Pillow==5.1.0 +psycopg2==2.7.4 +Pygments==2.2.0 +PyPDF2==1.26.0 +python-dateutil==2.7.3 +pytz==2018.4 +raven==6.8.0 +reportlab==3.4.0 +selenium==3.12.0 +simplejson==3.15.0 +six==1.11.0 +sqlparse==0.2.4 +static3==0.7.0 svg2rlg==0.3 yolk==0.4.3 -z3c.rml==2.8.1 -zope.event==4.0.3 -zope.interface==4.1.2 -zope.schema==4.4.2 +whitenoise==4.1.2 +z3c.rml==3.5.0 +zope.event==4.3.0 +zope.interface==4.5.0 +zope.schema==4.5.0 diff --git a/runtime.txt b/runtime.txt new file mode 100644 index 00000000..5871d65b --- /dev/null +++ b/runtime.txt @@ -0,0 +1 @@ +python-3.6.1 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..734a17f5 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[pep8] +max-line-length = 320 \ No newline at end of file diff --git a/templates/400.html b/templates/400.html index 5dd0c30b..dda8d561 100644 --- a/templates/400.html +++ b/templates/400.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'base_rigs.html' %} {% load staticfiles %} {% block title %}Bad Request{% endblock %} diff --git a/templates/401.html b/templates/401.html index 9b9b019f..7fc7dfe0 100644 --- a/templates/401.html +++ b/templates/401.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'base_rigs.html' %} {% load staticfiles %} {% block title %}Unauthorized{% endblock %} diff --git a/templates/403.html b/templates/403.html index 5f59c351..7fdb8f24 100644 --- a/templates/403.html +++ b/templates/403.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'base_rigs.html' %} {% load staticfiles %} {% block title %}Forbidden{% endblock %} diff --git a/templates/404.html b/templates/404.html index 5b347c1b..5a4ef0bf 100644 --- a/templates/404.html +++ b/templates/404.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'base_rigs.html' %} {% load staticfiles %} {% block title %}Page Not Found{% endblock %} diff --git a/templates/500.html b/templates/500.html index 2fc2db83..26fd115d 100644 --- a/templates/500.html +++ b/templates/500.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'base_rigs.html' %} {% load staticfiles %} {% block title %}Server error{% endblock %} diff --git a/templates/base.html b/templates/base.html index 24cc30bc..53238def 100644 --- a/templates/base.html +++ b/templates/base.html @@ -43,58 +43,13 @@ - RIGS + {% block titleheader %} + {% endblock %} - \ No newline at end of file + diff --git a/templates/base_assets.html b/templates/base_assets.html new file mode 100644 index 00000000..636c70df --- /dev/null +++ b/templates/base_assets.html @@ -0,0 +1,39 @@ +{% extends 'base.html' %} + +{% block extrahead %} + +{% endblock %} + +{% block titleheader %} + RIGS + Assets +{% endblock %} + +{% block titleelements %} + {# % if perms.assets.view_asset % #} + + {# % endif % #} + {# % if perms.assets.view_supplier % #} + + {# % endif % #} + {% if perms.assets.view_asset %} +
  • Recent Changes
  • + {% endif %} +{% endblock %} diff --git a/templates/base_client.html b/templates/base_client.html new file mode 100644 index 00000000..824e9f88 --- /dev/null +++ b/templates/base_client.html @@ -0,0 +1,125 @@ +{% load static from staticfiles %} +{% load raven %} + + + + + + {% block title %}{% endblock %} | Rig Information Gathering System + + + + + + + + + + {% block css %} + {% endblock %} + + + + + {% block preload_js %} + {% endblock %} + + {% block extra-head %}{% endblock %} + + + +{% include "analytics.html" %} + + +
    +
    + {% block content-header %} + {% if error %} +
    {{ error }}
    {% endif %} + {% if info %} +
    {{ info }}
    {% endif %} + {% if messages %} + {% for message in messages %} + + {% endfor %} + {% endif %} + {% endblock %} + + {% block content %}{% endblock %} +
    + + +
    + + + + + + + + + + + + + +{% block js %} +{% endblock %} + + diff --git a/templates/base_client_email.html b/templates/base_client_email.html new file mode 100644 index 00000000..f6f5e255 --- /dev/null +++ b/templates/base_client_email.html @@ -0,0 +1,58 @@ +{% load static from staticfiles %} +{% load raven %} + + + + + + + + + + + + + + + + + + + +
    + + + + + + +
    + + + +
    + +
    + +
    + {% block content %}{% endblock %} +
    + +
    + + + + + diff --git a/templates/base_embed.html b/templates/base_embed.html new file mode 100644 index 00000000..6a7a8741 --- /dev/null +++ b/templates/base_embed.html @@ -0,0 +1,49 @@ +{% load static from staticfiles %} +{% load raven %} + + + + + + + + + + + + + + + + + + +{% include "analytics.html" %} + +
    +
    + {% if messages %} + {% for message in messages %} + + {% endfor %} + {% endif %} + + {% block content %} + {% endblock %} +
    +
    + +{% block js %} +{% endblock %} + + diff --git a/templates/base_rigs.html b/templates/base_rigs.html new file mode 100644 index 00000000..febaba98 --- /dev/null +++ b/templates/base_rigs.html @@ -0,0 +1,54 @@ +{% extends 'base.html' %} +{% block titleheader %} + RIGS +{% endblock %} + +{% block titleelements %} + {% if user.is_authenticated %} +
  • Home
  • + + {% endif %} + {% if perms.RIGS.view_invoice %} + + {% endif %} + {% if perms.RIGS.view_person %} +
  • People
  • + {% endif %} + {% if perms.RIGS.view_organisation %} +
  • Organisations
  • + {% endif %} + {% if perms.RIGS.view_venue %} +
  • Venues
  • + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/templates/login_redirect.html b/templates/login_redirect.html new file mode 100644 index 00000000..6b69cdfd --- /dev/null +++ b/templates/login_redirect.html @@ -0,0 +1,24 @@ +{% extends 'base_rigs.html' %} +{% load staticfiles %} +{% block title %}Login Required{% endblock %} + +{% block js %} + +{% endblock %} + +{% block extra-head %} + {% if oembed_url %} + + {% endif %} +{% endblock %} + +{% block content %} +
    +

    Login is required for this page

    + Login +
    +{% endblock %} diff --git a/templates/registration/activation_complete.html b/templates/registration/activation_complete.html index 4797ef69..5aed33e9 100644 --- a/templates/registration/activation_complete.html +++ b/templates/registration/activation_complete.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'base_rigs.html' %} {% block title %}Activation Complete{% endblock %} diff --git a/templates/registration/activation_email_subject.txt b/templates/registration/activation_email_subject.txt index 2237d0a8..1f707f9e 100644 --- a/templates/registration/activation_email_subject.txt +++ b/templates/registration/activation_email_subject.txt @@ -1 +1 @@ -{{ user }} activation required \ No newline at end of file +{{ user|safe }} activation required diff --git a/templates/registration/logged_out.html b/templates/registration/logged_out.html index 47a0abc6..058e2127 100644 --- a/templates/registration/logged_out.html +++ b/templates/registration/logged_out.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'base_rigs.html' %} {% block title %}Logout Successful{% endblock %} diff --git a/templates/registration/login.html b/templates/registration/login.html index ace80d16..be02db72 100644 --- a/templates/registration/login.html +++ b/templates/registration/login.html @@ -1,7 +1,10 @@ -{% extends 'base.html' %} +{% extends 'base_rigs.html' %} {% block title %}Login{% endblock %} {% block content %} +
    +

    Rig Information Gathering System

    +
    {% include 'registration/loginform.html' %} {% endblock %} \ No newline at end of file diff --git a/templates/registration/login_embed.html b/templates/registration/login_embed.html new file mode 100644 index 00000000..bddb7333 --- /dev/null +++ b/templates/registration/login_embed.html @@ -0,0 +1,34 @@ +{% extends 'base_embed.html' %} +{% load widget_tweaks %} + +{% block title %}Login{% endblock %} + +{% block content %} +
    +

    Rig Information Gathering System

    +
    + + +{% include 'form_errors.html' %} + + +
    + +
    {% csrf_token %} +
    + + {% render_field form.username class+="form-control" placeholder=form.username.label %} +
    +
    + + {% render_field form.password class+="form-control" placeholder=form.password.label %} +
    +
    + +
    + + +
    +
    + +{% endblock %} diff --git a/templates/registration/loginform.html b/templates/registration/loginform.html index 74a3787b..ad7e33d3 100644 --- a/templates/registration/loginform.html +++ b/templates/registration/loginform.html @@ -3,7 +3,7 @@ {% include 'form_errors.html' %}
    -
    {% csrf_token %} + {% csrf_token %}
    {% render_field form.username class+="form-control" placeholder=form.username.label autofocus="" %} @@ -12,9 +12,11 @@ {% render_field form.password class+="form-control" placeholder=form.password.label %}
    - Register - Forgotten Password - - +
    -
    \ No newline at end of file + diff --git a/templates/registration/password_change_done.html b/templates/registration/password_change_done.html index 8db9ea9a..e306a5fa 100644 --- a/templates/registration/password_change_done.html +++ b/templates/registration/password_change_done.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "base_rigs.html" %} {% load i18n %} diff --git a/templates/registration/password_change_form.html b/templates/registration/password_change_form.html index fbe95177..bae74452 100644 --- a/templates/registration/password_change_form.html +++ b/templates/registration/password_change_form.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "base_rigs.html" %} {% load widget_tweaks %} diff --git a/templates/registration/password_reset_complete.html b/templates/registration/password_reset_complete.html index 7543d38f..88bc4d77 100644 --- a/templates/registration/password_reset_complete.html +++ b/templates/registration/password_reset_complete.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "base_rigs.html" %} {% load i18n %} diff --git a/templates/registration/password_reset_confirm.html b/templates/registration/password_reset_confirm.html index 71451fcf..16b43cb0 100644 --- a/templates/registration/password_reset_confirm.html +++ b/templates/registration/password_reset_confirm.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "base_rigs.html" %} {% load i18n %} {% load widget_tweaks %} diff --git a/templates/registration/password_reset_done.html b/templates/registration/password_reset_done.html index cb0dbf02..062cba6f 100644 --- a/templates/registration/password_reset_done.html +++ b/templates/registration/password_reset_done.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "base_rigs.html" %} {% load i18n %} diff --git a/templates/registration/password_reset_form.html b/templates/registration/password_reset_form.html index b9358488..6fb3fb8a 100644 --- a/templates/registration/password_reset_form.html +++ b/templates/registration/password_reset_form.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'base_rigs.html' %} {% load i18n %} {% load widget_tweaks %} diff --git a/templates/registration/registration_complete.html b/templates/registration/registration_complete.html index c7b02238..f065ff21 100644 --- a/templates/registration/registration_complete.html +++ b/templates/registration/registration_complete.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'base_rigs.html' %} {% block title %}Registration complete{% endblock %} diff --git a/templates/registration/registration_form.html b/templates/registration/registration_form.html index a73bfaed..5022a161 100644 --- a/templates/registration/registration_form.html +++ b/templates/registration/registration_form.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'base_rigs.html' %} {% load widget_tweaks %} {% block title %}Registration{% endblock %}