From 39857d1b565ea8c76cce388f6200cae697345331 Mon Sep 17 00:00:00 2001 From: virusdefender Date: Sun, 30 Oct 2016 02:17:35 +0800 Subject: [PATCH] add some tests --- account/decorators.py | 23 +- account/middleware.py | 15 +- account/migrations/0005_auto_20161029_2255.py | 39 + account/models.py | 29 +- account/serializers.py | 11 +- account/tests.py | 138 +- account/views/admin.py | 29 +- account/views/oj.py | 38 +- announcement/tests.py | 30 + announcement/views.py | 20 +- conf/models.py | 24 +- dockerfiles/judger/Dockerfile | 20 - dockerfiles/judger/docker-compose.example.yml | 11 - dockerfiles/judger/policy | 3 - dockerfiles/judger/run.sh | 3 - dockerfiles/judger/sources.list | 10 - dockerfiles/judger/supervisord.conf | 23 - dockerfiles/mysql/Dockerfile | 2 - dockerfiles/mysql/README.md | 2 - dockerfiles/oj_web_server/Dockerfile | 15 - .../oj_web_server/docker-compose.example.yml | 26 - dockerfiles/oj_web_server/gunicorn.conf | 12 - dockerfiles/oj_web_server/requirements.txt | 15 - dockerfiles/oj_web_server/run.sh | 10 - dockerfiles/oj_web_server/sources.list | 8 - dockerfiles/oj_web_server/supervisord.conf | 15 - dockerfiles/oj_web_server/task_queue.conf | 12 - dockerfiles/test_case_rsync/Dockerfile | 3 - .../docker-compose.example.yml | 21 - dockerfiles/test_case_rsync/rsyncd.conf | 11 - .../test_case_rsync/rsyncd.example.passwd | 1 - dockerfiles/test_case_rsync/rsyncd_slave.sh | 6 - dockerfiles/test_case_rsync/run.sh | 16 - frontend/admin/.babelrc | 5 - frontend/admin/config/dev.env.js | 6 - frontend/admin/config/index.js | 37 - frontend/admin/config/prod.env.js | 3 - frontend/admin/config/test.env.js | 6 - frontend/admin/index.html | 36 - frontend/admin/package.json | 56 - frontend/admin/src/App.vue | 51 - frontend/admin/src/components/back.vue | 9 - frontend/admin/src/components/codeMirror.vue | 104 - frontend/admin/src/components/help.vue | 29 - frontend/admin/src/components/helpLink.vue | 14 - frontend/admin/src/components/pager.vue | 93 - .../admin/src/components/problemSample.vue | 71 - frontend/admin/src/components/simditor.vue | 38 - frontend/admin/src/components/tagInput.vue | 115 - .../admin/src/components/testCaseMgnt.vue | 107 - frontend/admin/src/components/uploader.vue | 62 - frontend/admin/src/locales.js | 136 - frontend/admin/src/main.js | 155 - frontend/admin/src/utils/cookie.js | 10 - frontend/admin/src/views/account/editUser.vue | 165 - frontend/admin/src/views/account/userList.vue | 113 - .../views/announcement/announcementList.vue | 74 - .../views/announcement/createAnnouncement.vue | 49 - .../views/announcement/editAnnouncement.vue | 68 - frontend/admin/src/views/nav/leftNav.vue | 35 - frontend/admin/src/views/nav/topNav.vue | 25 - .../admin/src/views/problem/createProblem.vue | 150 - .../admin/src/views/problem/problemList.vue | 93 - .../admin/src/views/problem/specialJudge.vue | 78 - frontend/admin/static/img/favicon.ico | Bin 67646 -> 0 bytes frontend/static/css/CodeMirror.css | 347 - frontend/static/css/bootstrap-theme.css | 587 -- frontend/static/css/bootstrap-theme.css.map | 1 - frontend/static/css/bootstrap-theme.min.css | 6 - .../static/css/bootstrap-theme.min.css.map | 1 - frontend/static/css/bootstrap.css | 6760 ----------------- frontend/static/css/bootstrap.css.map | 1 - frontend/static/css/bootstrap.min.css | 6 - frontend/static/css/bootstrap.min.css.map | 1 - frontend/static/css/loading.css | 564 -- frontend/static/css/simditor-markdown.css | 28 - frontend/static/css/simditor.css | 746 -- frontend/static/css/todc-bootstrap.css | 4256 ----------- frontend/static/css/todc-bootstrap.css.map | 1 - frontend/static/css/todc-bootstrap.min.css | 6 - .../static/css/todc-bootstrap.min.css.map | 1 - frontend/static/css/webuploader.css | 28 - .../fonts/glyphicons-halflings-regular.eot | Bin 20127 -> 0 bytes .../fonts/glyphicons-halflings-regular.svg | 288 - .../fonts/glyphicons-halflings-regular.ttf | Bin 45404 -> 0 bytes .../fonts/glyphicons-halflings-regular.woff | Bin 23424 -> 0 bytes .../fonts/glyphicons-halflings-regular.woff2 | Bin 18028 -> 0 bytes frontend/static/img/checkmark.png | Bin 169 -> 0 bytes frontend/static/img/loading.jpg | Bin 66451 -> 0 bytes frontend/static/js/bootstrap.js | 2363 ------ frontend/static/js/bootstrap.min.js | 7 - oj/__init__.py | 7 +- oj/celery.py | 19 - oj/custom_settings.example.py | 19 - tools/create_db.py | 23 - tools/release_static.py | 70 - tools/runserver.sh | 2 - tools/runtest.sh | 7 - tools/template_js_path.py | 37 - tools/ubuntu-run.sh | 8 - utils/cache.py | 9 - utils/mail.py | 15 - utils/management/commands/cleantestcase.py | 36 - utils/management/commands/initinstall.py | 21 - utils/management/commands/inituserrank.py | 19 - utils/shortcuts.py | 105 +- utils/signal2str.py | 26 - utils/templatetags/__init__.py | 1 - utils/templatetags/announcement_list.py | 11 - utils/templatetags/contest.py | 73 - utils/templatetags/problem.py | 27 - utils/templatetags/submission.py | 44 - utils/templatetags/user.py | 17 - utils/templatetags/website_info.py | 9 - utils/test_urls.py | 9 - utils/tests.py | 80 +- utils/throttling.py | 94 - utils/views.py | 37 - 118 files changed, 326 insertions(+), 19131 deletions(-) create mode 100644 account/migrations/0005_auto_20161029_2255.py delete mode 100644 dockerfiles/judger/Dockerfile delete mode 100644 dockerfiles/judger/docker-compose.example.yml delete mode 100644 dockerfiles/judger/policy delete mode 100644 dockerfiles/judger/run.sh delete mode 100644 dockerfiles/judger/sources.list delete mode 100644 dockerfiles/judger/supervisord.conf delete mode 100644 dockerfiles/mysql/Dockerfile delete mode 100644 dockerfiles/mysql/README.md delete mode 100644 dockerfiles/oj_web_server/Dockerfile delete mode 100644 dockerfiles/oj_web_server/docker-compose.example.yml delete mode 100644 dockerfiles/oj_web_server/gunicorn.conf delete mode 100644 dockerfiles/oj_web_server/requirements.txt delete mode 100644 dockerfiles/oj_web_server/run.sh delete mode 100644 dockerfiles/oj_web_server/sources.list delete mode 100644 dockerfiles/oj_web_server/supervisord.conf delete mode 100644 dockerfiles/oj_web_server/task_queue.conf delete mode 100644 dockerfiles/test_case_rsync/Dockerfile delete mode 100644 dockerfiles/test_case_rsync/docker-compose.example.yml delete mode 100644 dockerfiles/test_case_rsync/rsyncd.conf delete mode 100644 dockerfiles/test_case_rsync/rsyncd.example.passwd delete mode 100644 dockerfiles/test_case_rsync/rsyncd_slave.sh delete mode 100644 dockerfiles/test_case_rsync/run.sh delete mode 100644 frontend/admin/.babelrc delete mode 100644 frontend/admin/config/dev.env.js delete mode 100644 frontend/admin/config/index.js delete mode 100644 frontend/admin/config/prod.env.js delete mode 100644 frontend/admin/config/test.env.js delete mode 100644 frontend/admin/index.html delete mode 100644 frontend/admin/package.json delete mode 100644 frontend/admin/src/App.vue delete mode 100644 frontend/admin/src/components/back.vue delete mode 100644 frontend/admin/src/components/codeMirror.vue delete mode 100644 frontend/admin/src/components/help.vue delete mode 100644 frontend/admin/src/components/helpLink.vue delete mode 100644 frontend/admin/src/components/pager.vue delete mode 100644 frontend/admin/src/components/problemSample.vue delete mode 100644 frontend/admin/src/components/simditor.vue delete mode 100644 frontend/admin/src/components/tagInput.vue delete mode 100644 frontend/admin/src/components/testCaseMgnt.vue delete mode 100644 frontend/admin/src/components/uploader.vue delete mode 100644 frontend/admin/src/locales.js delete mode 100644 frontend/admin/src/main.js delete mode 100644 frontend/admin/src/utils/cookie.js delete mode 100644 frontend/admin/src/views/account/editUser.vue delete mode 100644 frontend/admin/src/views/account/userList.vue delete mode 100644 frontend/admin/src/views/announcement/announcementList.vue delete mode 100644 frontend/admin/src/views/announcement/createAnnouncement.vue delete mode 100644 frontend/admin/src/views/announcement/editAnnouncement.vue delete mode 100644 frontend/admin/src/views/nav/leftNav.vue delete mode 100644 frontend/admin/src/views/nav/topNav.vue delete mode 100644 frontend/admin/src/views/problem/createProblem.vue delete mode 100644 frontend/admin/src/views/problem/problemList.vue delete mode 100644 frontend/admin/src/views/problem/specialJudge.vue delete mode 100644 frontend/admin/static/img/favicon.ico delete mode 100644 frontend/static/css/CodeMirror.css delete mode 100644 frontend/static/css/bootstrap-theme.css delete mode 100644 frontend/static/css/bootstrap-theme.css.map delete mode 100644 frontend/static/css/bootstrap-theme.min.css delete mode 100644 frontend/static/css/bootstrap-theme.min.css.map delete mode 100644 frontend/static/css/bootstrap.css delete mode 100644 frontend/static/css/bootstrap.css.map delete mode 100644 frontend/static/css/bootstrap.min.css delete mode 100644 frontend/static/css/bootstrap.min.css.map delete mode 100644 frontend/static/css/loading.css delete mode 100644 frontend/static/css/simditor-markdown.css delete mode 100644 frontend/static/css/simditor.css delete mode 100644 frontend/static/css/todc-bootstrap.css delete mode 100644 frontend/static/css/todc-bootstrap.css.map delete mode 100644 frontend/static/css/todc-bootstrap.min.css delete mode 100644 frontend/static/css/todc-bootstrap.min.css.map delete mode 100644 frontend/static/css/webuploader.css delete mode 100644 frontend/static/fonts/glyphicons-halflings-regular.eot delete mode 100644 frontend/static/fonts/glyphicons-halflings-regular.svg delete mode 100644 frontend/static/fonts/glyphicons-halflings-regular.ttf delete mode 100644 frontend/static/fonts/glyphicons-halflings-regular.woff delete mode 100644 frontend/static/fonts/glyphicons-halflings-regular.woff2 delete mode 100644 frontend/static/img/checkmark.png delete mode 100644 frontend/static/img/loading.jpg delete mode 100644 frontend/static/js/bootstrap.js delete mode 100644 frontend/static/js/bootstrap.min.js delete mode 100644 oj/celery.py delete mode 100644 tools/create_db.py delete mode 100644 tools/release_static.py delete mode 100644 tools/runserver.sh delete mode 100755 tools/runtest.sh delete mode 100644 tools/template_js_path.py delete mode 100644 tools/ubuntu-run.sh delete mode 100644 utils/cache.py delete mode 100644 utils/mail.py delete mode 100644 utils/management/commands/cleantestcase.py delete mode 100644 utils/management/commands/initinstall.py delete mode 100644 utils/management/commands/inituserrank.py delete mode 100644 utils/signal2str.py delete mode 100644 utils/templatetags/__init__.py delete mode 100644 utils/templatetags/announcement_list.py delete mode 100644 utils/templatetags/contest.py delete mode 100644 utils/templatetags/problem.py delete mode 100644 utils/templatetags/submission.py delete mode 100644 utils/templatetags/user.py delete mode 100644 utils/templatetags/website_info.py delete mode 100644 utils/test_urls.py delete mode 100644 utils/throttling.py delete mode 100644 utils/views.py diff --git a/account/decorators.py b/account/decorators.py index 2efdb99e..d68dda7f 100644 --- a/account/decorators.py +++ b/account/decorators.py @@ -1,12 +1,13 @@ # coding=utf-8 from __future__ import unicode_literals import urllib +import json import functools -from django.http import HttpResponseRedirect +from django.http import HttpResponse from django.utils.translation import ugettext as _ -from utils.shortcuts import error_response, error_page, redirect_to_login +from utils.shortcuts import JSONResponse from .models import AdminType @@ -17,24 +18,18 @@ class BasePermissionDecorator(object): def __get__(self, obj, obj_type): return functools.partial(self.__call__, obj) + def error(self, data): + return JSONResponse({"error": "permission-denied", "data": data}) + def __call__(self, *args, **kwargs): - if len(args) == 2: - self.request = args[1] - else: - self.request = args[0] + self.request = args[1] if self.check_permission(): if self.request.user.is_disabled: - if self.request.is_ajax(): - return error_response(_("Your account is disabled")) - else: - return error_page(self.request, _("Your account is disabled")) + return self.error(_("Your account is disabled")) return self.func(*args, **kwargs) else: - if self.request.is_ajax(): - return error_response(_("Please login in first")) - else: - return redirect_to_login(self.request) + return self.error(_("Please login in first")) def check_permission(self): raise NotImplementedError() diff --git a/account/middleware.py b/account/middleware.py index 64ee3dca..bc1c089d 100644 --- a/account/middleware.py +++ b/account/middleware.py @@ -6,7 +6,7 @@ from django.http import HttpResponse from django.utils.translation import ugettext as _ from django.contrib import auth -from utils.shortcuts import redirect_to_login +from utils.shortcuts import JSONResponse from .models import AdminType @@ -17,11 +17,7 @@ class SessionSecurityMiddleware(object): # 24 hours passed since last visit if time.time() - request.session["last_activity"] >= 24 * 60 * 60: auth.logout(request) - if request.is_ajax(): - return HttpResponse(json.dumps({"code": 1, "data": _("Please login in first")}), - content_type="application/json") - else: - return redirect_to_login(request) + return JSONResponse({"error": "login-required", "data": _("Please login in first")}) # update last active time request.session["last_activity"] = time.time() @@ -31,9 +27,4 @@ class AdminRequiredMiddleware(object): path = request.path_info if path.startswith("/admin/") or path.startswith("/api/admin/"): if not(request.user.is_authenticated() and request.user.is_admin()): - if request.is_ajax(): - return HttpResponse(json.dumps({"code": 1, "data": _("Please login in first")}), - content_type="application/json") - else: - return HttpResponse(json.dumps({"code": 1, "data": _("Admin required")}), - content_type="application/json") \ No newline at end of file + return JSONResponse({"error": "login-required", "data": _("Please login in first")}) \ No newline at end of file diff --git a/account/migrations/0005_auto_20161029_2255.py b/account/migrations/0005_auto_20161029_2255.py new file mode 100644 index 00000000..48457156 --- /dev/null +++ b/account/migrations/0005_auto_20161029_2255.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.5 on 2016-10-29 14:55 +from __future__ import unicode_literals + +from django.db import migrations, models +import jsonfield.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0004_auto_20160925_1649'), + ] + + operations = [ + migrations.RenameField( + model_name='user', + old_name='reset_password_token_create_time', + new_name='reset_password_token_expire_time', + ), + migrations.RemoveField( + model_name='user', + name='admin_extra_permission', + ), + migrations.RemoveField( + model_name='user', + name='problems_status', + ), + migrations.AddField( + model_name='userprofile', + name='problems_status', + field=jsonfield.fields.JSONField(default={}), + ), + migrations.AlterField( + model_name='user', + name='admin_type', + field=models.CharField(default='regular_user', max_length=24), + ), + ] diff --git a/account/models.py b/account/models.py index 7a86510b..79ad34a8 100644 --- a/account/models.py +++ b/account/models.py @@ -6,9 +6,9 @@ from jsonfield import JSONField class AdminType(object): - REGULAR_USER = 0 - ADMIN = 1 - SUPER_ADMIN = 2 + REGULAR_USER = "regular_user" + ADMIN = "admin" + SUPER_ADMIN = "super_admin" class ProblemSolutionStatus(object): @@ -16,15 +16,6 @@ class ProblemSolutionStatus(object): PENDING = 2 -class AdminExtraPermission(object): - CREATE_PUBLIC_CONTEST = 1 - MANAGE_ALL_CONTEST = 2 - # 3 and 4 are mutually exclusive - MANAGE_ALL_PROBLEM = 3 - # Manage public problem user created - MANAGE_OWN_PROBLEM = 4 - - class UserManager(models.Manager): use_in_migrations = True @@ -38,14 +29,9 @@ class User(AbstractBaseUser): email = models.EmailField(max_length=254, null=True) create_time = models.DateTimeField(auto_now_add=True, null=True) # One of UserType - admin_type = models.IntegerField(default=0) - # List of items in AdminExtraPermission - admin_extra_permission = JSONField(default=[]) - # Store user problem solution status with json string format - # {"problems": {1: ProblemSolutionStatus.ACCEPTED}, "contest_problems": {20: ProblemSolutionStatus.PENDING)} - problems_status = JSONField(default={}) + admin_type = models.CharField(max_length=24, default=AdminType.REGULAR_USER) reset_password_token = models.CharField(max_length=40, null=True) - reset_password_token_create_time = models.DateTimeField(null=True) + reset_password_token_expire_time = models.DateTimeField(null=True) # SSO auth token auth_token = models.CharField(max_length=40, null=True) two_factor_auth = models.BooleanField(default=False) @@ -61,7 +47,7 @@ class User(AbstractBaseUser): objects = UserManager() def is_admin(self): - return self.admin_type > AdminType.REGULAR_USER + return self.admin_type in [AdminType.ADMIN, AdminType.SUPER_ADMIN] class Meta: db_table = "user" @@ -74,6 +60,9 @@ def _random_avatar(): class UserProfile(models.Model): user = models.OneToOneField(User) + # Store user problem solution status with json string format + # {"problems": {1: ProblemSolutionStatus.ACCEPTED}, "contest_problems": {20: ProblemSolutionStatus.PENDING)} + problems_status = JSONField(default={}) avatar = models.CharField(max_length=50, default=_random_avatar) blog = models.URLField(blank=True, null=True) mood = models.CharField(max_length=200, blank=True, null=True) diff --git a/account/serializers.py b/account/serializers.py index 9be94a36..2dbd34a7 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -2,13 +2,13 @@ from rest_framework import serializers from utils.serializers import DateTimeTZField -from .models import User +from .models import User, AdminType class UserLoginSerializer(serializers.Serializer): username = serializers.CharField(max_length=30) password = serializers.CharField(max_length=30) - tfa_code = serializers.CharField(min_length=6, max_length=6, required=False) + tfa_code = serializers.CharField(min_length=6, max_length=6, required=False, allow_null=True) class UserRegisterSerializer(serializers.Serializer): @@ -27,11 +27,10 @@ class UserChangePasswordSerializer(serializers.Serializer): class UserSerializer(serializers.ModelSerializer): create_time = DateTimeTZField() last_login = DateTimeTZField() - admin_extra_permission = serializers.ListField() class Meta: model = User - fields = ["id", "username", "real_name", "email", "admin_type", "admin_extra_permission", + fields = ["id", "username", "real_name", "email", "admin_type", "create_time", "last_login", "two_factor_auth", "open_api", "is_disabled"] @@ -41,9 +40,7 @@ class EditUserSerializer(serializers.Serializer): real_name = serializers.CharField(max_length=30) password = serializers.CharField(max_length=30, min_length=6, required=False, default=None) email = serializers.EmailField(max_length=254) - admin_type = serializers.IntegerField(default=0) + admin_type = serializers.ChoiceField(choices=(AdminType.REGULAR_USER, AdminType.ADMIN, AdminType.SUPER_ADMIN)) open_api = serializers.BooleanField() two_factor_auth = serializers.BooleanField() is_disabled = serializers.BooleanField() - admin_extra_permission = serializers.ListField(required=False, default=[], - child=serializers.IntegerField()) diff --git a/account/tests.py b/account/tests.py index d2512aa6..a3d55efd 100644 --- a/account/tests.py +++ b/account/tests.py @@ -1,17 +1,18 @@ # coding=utf-8 from __future__ import unicode_literals -import time -import mock +import time + +import mock from django.contrib import auth from django.core.urlresolvers import reverse from django.utils.translation import ugettext as _ +from rest_framework.test import APIClient -from rest_framework.test import APIClient, APITestCase - -from utils.shortcuts import rand_str from utils.otp_auth import OtpAuth -from .models import User +from utils.shortcuts import rand_str +from utils.tests import APITestCase +from .models import User, AdminType class PermissionDecoratorTest(APITestCase): @@ -34,12 +35,8 @@ class PermissionDecoratorTest(APITestCase): class UserLoginAPITest(APITestCase): def setUp(self): - self.username = "testuser" - self.password = "testuserpassword" - self.user = User.objects.create(username=self.username) - self.user.set_password(self.password) - self.user.save() - + self.username = self.password = "test" + self.user = self.create_user(username=self.username, password=self.password) self.login_url = reverse("user_login_api") def _set_tfa(self): @@ -52,7 +49,7 @@ class UserLoginAPITest(APITestCase): def test_login_with_correct_info(self): response = self.client.post(self.login_url, data={"username": self.username, "password": self.password}) - self.assertDictEqual(response.data, {"code": 0, "data": _("Succeeded")}) + self.assertDictEqual(response.data, {"error": None, "data": _("Succeeded")}) user = auth.get_user(self.client) self.assertTrue(user.is_authenticated()) @@ -60,8 +57,7 @@ class UserLoginAPITest(APITestCase): def test_login_with_wrong_info(self): response = self.client.post(self.login_url, data={"username": self.username, "password": "invalid_password"}) - - self.assertDictEqual(response.data, {"code": 1, "data": _("Invalid username or password")}) + self.assertDictEqual(response.data, {"error": "error", "data": _("Invalid username or password")}) user = auth.get_user(self.client) self.assertFalse(user.is_authenticated()) @@ -75,7 +71,7 @@ class UserLoginAPITest(APITestCase): data={"username": self.username, "password": self.password, "tfa_code": code}) - self.assertDictEqual(response.data, {"code": 0, "data": _("Succeeded")}) + self.assertDictEqual(response.data, {"error": None, "data": _("Succeeded")}) user = auth.get_user(self.client) self.assertTrue(user.is_authenticated()) @@ -86,7 +82,7 @@ class UserLoginAPITest(APITestCase): data={"username": self.username, "password": self.password, "tfa_code": "qqqqqq"}) - self.assertDictEqual(response.data, {"code": 1, "data": _("Invalid two factor verification code")}) + self.assertDictEqual(response.data, {"error": "error", "data": _("Invalid two factor verification code")}) user = auth.get_user(self.client) self.assertFalse(user.is_authenticated()) @@ -96,7 +92,7 @@ class UserLoginAPITest(APITestCase): response = self.client.post(self.login_url, data={"username": self.username, "password": self.password}) - self.assertDictEqual(response.data, {"code": 0, "data": "tfa_required"}) + self.assertDictEqual(response.data, {"error": None, "data": "tfa_required"}) user = auth.get_user(self.client) self.assertFalse(user.is_authenticated()) @@ -124,15 +120,15 @@ class UserRegisterAPITest(CaptchaTest): def test_invalid_captcha(self): self.data["captcha"] = "****" response = self.client.post(self.register_url, data=self.data) - self.assertDictEqual(response.data, {"code": 1, "data": _("Invalid captcha")}) + self.assertDictEqual(response.data, {"error": "error", "data": _("Invalid captcha")}) self.data.pop("captcha") response = self.client.post(self.register_url, data=self.data) - self.assertEqual(response.data["code"], 1) + self.assertTrue(response.data["error"] is not None) def test_register_with_correct_info(self): response = self.client.post(self.register_url, data=self.data) - self.assertDictEqual(response.data, {"code": 0, "data": _("Succeeded")}) + self.assertDictEqual(response.data, {"error": None, "data": _("Succeeded")}) def test_username_already_exists(self): self.test_register_with_correct_info() @@ -140,7 +136,7 @@ class UserRegisterAPITest(CaptchaTest): self.data["captcha"] = self._set_captcha(self.client.session) self.data["email"] = "test1@qduoj.com" response = self.client.post(self.register_url, data=self.data) - self.assertDictEqual(response.data, {"code": 1, "data": _("Username already exists")}) + self.assertDictEqual(response.data, {"error": "error", "data": _("Username already exists")}) def test_email_already_exists(self): self.test_register_with_correct_info() @@ -148,7 +144,7 @@ class UserRegisterAPITest(CaptchaTest): self.data["captcha"] = self._set_captcha(self.client.session) self.data["username"] = "test_user1" response = self.client.post(self.register_url, data=self.data) - self.assertDictEqual(response.data, {"code": 1, "data": _("Email already exists")}) + self.assertDictEqual(response.data, {"error": "error", "data": _("Email already exists")}) class UserChangePasswordAPITest(CaptchaTest): @@ -160,45 +156,99 @@ class UserChangePasswordAPITest(CaptchaTest): self.username = "test_user" self.old_password = "testuserpassword" self.new_password = "new_password" - register_data = {"username": self.username, "password": self.old_password, - "real_name": "real_name", "email": "test@qduoj.com", - "captcha": self._set_captcha(self.client.session)} - - response = self.client.post(reverse("user_register_api"), data=register_data) - self.assertDictEqual(response.data, {"code": 0, "data": _("Succeeded")}) + self.create_user(username=self.username, password=self.old_password) self.data = {"old_password": self.old_password, "new_password": self.new_password, "captcha": self._set_captcha(self.client.session)} def test_login_required(self): - response = self.client.post(self.url, data=self.data, HTTP_X_REQUESTED_WITH="XMLHttpRequest") - self.assertEqual(response.data, {"code": 1, "data": _("Please login in first")}) + response = self.client.post(self.url, data=self.data) + self.assertEqual(response.data, {"error": "permission-denied", "data": _("Please login in first")}) def test_valid_ola_password(self): self.assertTrue(self.client.login(username=self.username, password=self.old_password)) - response = self.client.post(self.url, data=self.data, HTTP_X_REQUESTED_WITH="XMLHttpRequest") - self.assertEqual(response.data, {"code": 0, "data": _("Succeeded")}) + response = self.client.post(self.url, data=self.data) + self.assertEqual(response.data, {"error": None, "data": _("Succeeded")}) self.assertTrue(self.client.login(username=self.username, password=self.new_password)) def test_invalid_old_password(self): self.assertTrue(self.client.login(username=self.username, password=self.old_password)) self.data["old_password"] = "invalid" - response = self.client.post(self.url, data=self.data, HTTP_X_REQUESTED_WITH="XMLHttpRequest") - self.assertEqual(response.data, {"code": 1, "data": _("Invalid old password")}) + response = self.client.post(self.url, data=self.data) + self.assertEqual(response.data, {"error": "error", "data": _("Invalid old password")}) -class AdminEditUserTest(APITestCase): +class AdminUserTest(APITestCase): def setUp(self): - pass + self.user = self.create_super_admin(login=True) + self.username = self.password = "test" + self.regular_user = self.create_user(username=self.username, password=self.password) + self.url = reverse("user_admin_api") + self.data = {"id": self.regular_user.id, "username": self.username, "real_name": "test_name", + "email": "test@qq.com", "admin_type": AdminType.REGULAR_USER, + "open_api": True, "two_factor_auth": False, "is_disabled": False} + + def test_user_list(self): + response = self.client.get(self.url) + self.assertSuccess(response) def test_edit_user_successfully(self): - pass + response = self.client.put(self.url, data=self.data) + self.assertSuccess(response) + resp_data = response.data["data"] + self.assertEqual(resp_data["username"], self.username) + self.assertEqual(resp_data["email"], "test@qq.com") + self.assertEqual(resp_data["real_name"], "test_name") + self.assertEqual(resp_data["open_api"], True) + self.assertEqual(resp_data["two_factor_auth"], False) + self.assertEqual(resp_data["is_disabled"], False) - def test_change_user_admin_type(self): - pass + self.assertTrue(self.regular_user.check_password("test")) - def test_change_user_permission(self): - pass + def test_edit_user_password(self): + data = self.data + new_password = "testpassword" + data["password"] = new_password + response = self.client.put(self.url, data=data) + self.assertSuccess(response) + user = User.objects.get(id=self.regular_user.id) + self.assertFalse(user.check_password(self.password)) + self.assertTrue(user.check_password(new_password)) - def test_change_user_password(self): - pass + def test_edit_user_tfa(self): + data = self.data + self.assertIsNone(self.regular_user.tfa_token) + data["two_factor_auth"] = True + response = self.client.put(self.url, data=data) + self.assertSuccess(response) + resp_data = response.data["data"] + # if `tfa_token` is None, a new value will be generated + self.assertTrue(resp_data["two_factor_auth"]) + token = User.objects.get(id=self.regular_user.id).tfa_token + self.assertIsNotNone(token) + + response = self.client.put(self.url, data=data) + self.assertSuccess(response) + resp_data = response.data["data"] + # if `tfa_token` is not None, the value is not changed + self.assertTrue(resp_data["two_factor_auth"]) + self.assertEqual(User.objects.get(id=self.regular_user.id).tfa_token, token) + + def test_edit_user_openapi(self): + data = self.data + self.assertIsNone(self.regular_user.open_api_appkey) + data["open_api"] = True + response = self.client.put(self.url, data=data) + self.assertSuccess(response) + resp_data = response.data["data"] + # if `open_api_appkey` is None, a new value will be generated + self.assertTrue(resp_data["open_api"]) + key = User.objects.get(id=self.regular_user.id).open_api_appkey + self.assertIsNotNone(key) + + response = self.client.put(self.url, data=data) + self.assertSuccess(response) + resp_data = response.data["data"] + # if `openapi_app_key` is not None, the value is not changed + self.assertTrue(resp_data["open_api"]) + self.assertEqual(User.objects.get(id=self.regular_user.id).open_api_appkey, key) diff --git a/account/views/admin.py b/account/views/admin.py index 521ed47e..62be6b3f 100644 --- a/account/views/admin.py +++ b/account/views/admin.py @@ -4,10 +4,8 @@ from __future__ import unicode_literals from django.core.exceptions import MultipleObjectsReturned from django.db.models import Q from django.utils.translation import ugettext as _ -from rest_framework.views import APIView -from utils.shortcuts import (serializer_invalid_response, error_response, - success_response, paginate, rand_str) +from utils.shortcuts import (APIView, paginate_data, rand_str) from ..decorators import super_admin_required from ..models import User, AdminType from ..serializers import (UserSerializer, EditUserSerializer) @@ -25,21 +23,21 @@ class UserAdminAPIView(APIView): try: user = User.objects.get(id=data["id"]) except User.DoesNotExist: - return error_response(_("User does not exist")) + return self.error(_("User does not exist")) try: user = User.objects.get(username=data["username"]) if user.id != data["id"]: - return error_response(_("Username already exists")) + return self.error(_("Username already exists")) except User.DoesNotExist: pass try: user = User.objects.get(email=data["email"]) if user.id != data["id"]: - return error_response(_("Email already exists")) + return self.error(_("Email already exists")) # Some old data has duplicate email except MultipleObjectsReturned: - return error_response(_("Email already exists")) + return self.error(_("Email already exists")) except User.DoesNotExist: pass @@ -68,15 +66,10 @@ class UserAdminAPIView(APIView): user.tfa_token = None user.two_factor_auth = data["two_factor_auth"] - if data["admin_type"] == AdminType.ADMIN: - user.admin_extra_permission = list(set(data["admin_extra_permission"])) - else: - user.admin_extra_permission = [] - user.save() - return success_response(UserSerializer(user).data) + return self.success(UserSerializer(user).data) else: - return serializer_invalid_response(serializer) + return self.invalid_serializer(serializer) @super_admin_required def get(self, request): @@ -88,8 +81,8 @@ class UserAdminAPIView(APIView): try: user = User.objects.get(id=user_id) except User.DoesNotExist: - return error_response(_("User does not exist")) - return success_response(UserSerializer(user).data) + return self.error(_("User does not exist")) + return self.success(UserSerializer(user).data) user = User.objects.all().order_by("-create_time") @@ -98,10 +91,10 @@ class UserAdminAPIView(APIView): try: user = user.filter(admin_type__gte=int(admin_type)) except ValueError: - return error_response(_("Invalid parameter")) + return self.error(_("Invalid parameter")) keyword = request.GET.get("keyword", None) if keyword: user = user.filter(Q(username__contains=keyword) | Q(real_name__contains=keyword) | Q(email__contains=keyword)) - return paginate(request, user, UserSerializer) + return self.success(paginate_data(request, user, UserSerializer)) diff --git a/account/views/oj.py b/account/views/oj.py index 6f64662d..853c6ae2 100644 --- a/account/views/oj.py +++ b/account/views/oj.py @@ -4,12 +4,10 @@ from __future__ import unicode_literals from django.contrib import auth from django.core.exceptions import MultipleObjectsReturned from django.utils.translation import ugettext as _ -from rest_framework.views import APIView from utils.captcha import Captcha from utils.otp_auth import OtpAuth -from utils.shortcuts import (serializer_invalid_response, error_response, - success_response) +from utils.shortcuts import (APIView, ) from ..decorators import login_required from ..models import User, UserProfile from ..serializers import (UserLoginSerializer, UserRegisterSerializer, @@ -29,26 +27,26 @@ class UserLoginAPIView(APIView): if user: if not user.two_factor_auth: auth.login(request, user) - return success_response(_("Succeeded")) + return self.success(_("Succeeded")) # `tfa_code` not in post data if user.two_factor_auth and "tfa_code" not in data: - return success_response("tfa_required") + return self.success("tfa_required") if OtpAuth(user.tfa_token).valid_totp(data["tfa_code"]): auth.login(request, user) - return success_response(_("Succeeded")) + return self.success(_("Succeeded")) else: - return error_response(_("Invalid two factor verification code")) + return self.error(_("Invalid two factor verification code")) else: - return error_response(_("Invalid username or password")) + return self.error(_("Invalid username or password")) else: - return serializer_invalid_response(serializer) + return self.invalid_serializer(serializer) # todo remove this, only for debug use def get(self, request): auth.login(request, auth.authenticate(username=request.GET["username"], password=request.GET["password"])) - return success_response({}) + return self.success({}) class UserRegisterAPIView(APIView): @@ -61,26 +59,26 @@ class UserRegisterAPIView(APIView): data = serializer.data captcha = Captcha(request) if not captcha.check(data["captcha"]): - return error_response(_("Invalid captcha")) + return self.error(_("Invalid captcha")) try: User.objects.get(username=data["username"]) - return error_response(_("Username already exists")) + return self.error(_("Username already exists")) except User.DoesNotExist: pass try: User.objects.get(email=data["email"]) - return error_response(_("Email already exists")) + return self.error(_("Email already exists")) # Some old data has duplicate email except MultipleObjectsReturned: - return error_response(_("Email already exists")) + return self.error(_("Email already exists")) except User.DoesNotExist: user = User.objects.create(username=data["username"], email=data["email"]) user.set_password(data["password"]) user.save() UserProfile.objects.create(user=user) - return success_response(_("Succeeded")) + return self.success(_("Succeeded")) else: - return serializer_invalid_response(serializer) + return self.invalid_serializer(serializer) class UserChangePasswordAPIView(APIView): @@ -94,14 +92,14 @@ class UserChangePasswordAPIView(APIView): data = serializer.data captcha = Captcha(request) if not captcha.check(data["captcha"]): - return error_response(_("Invalid captcha")) + return self.error(_("Invalid captcha")) username = request.user.username user = auth.authenticate(username=username, password=data["old_password"]) if user: user.set_password(data["new_password"]) user.save() - return success_response(_("Succeeded")) + return self.success(_("Succeeded")) else: - return error_response(_("Invalid old password")) + return self.error(_("Invalid old password")) else: - return serializer_invalid_response(serializer) + return self.invalid_serializer(serializer) diff --git a/announcement/tests.py b/announcement/tests.py index e69de29b..86037d95 100644 --- a/announcement/tests.py +++ b/announcement/tests.py @@ -0,0 +1,30 @@ +# coding=utf-8 +from django.core.urlresolvers import reverse +from utils.tests import APITestCase + + +class AnnouncementAdminTest(APITestCase): + def setUp(self): + self.user = self.create_super_admin(login=True) + self.url = reverse("announcement_admin_api") + + def test_announcement_list(self): + response = self.client.get(self.url) + self.assertSuccess(response) + + def create_announcement(self): + return self.client.post(self.url, data={"title": "test", "content": "test"}) + + def test_create_announcement(self): + response = self.create_announcement() + self.assertSuccess(response) + + def test_edit_announcement(self): + data = {"id": self.create_announcement().data["data"]["id"], "title": "ahaha", "content": "test content", + "visible": False} + response = self.client.put(self.url, data=data) + self.assertSuccess(response) + resp_data = response.data["data"] + self.assertEqual(resp_data["title"], "ahaha") + self.assertEqual(resp_data["content"], "test content") + self.assertEqual(resp_data["visible"], False) diff --git a/announcement/views.py b/announcement/views.py index 3f726f47..5ccf1cfd 100644 --- a/announcement/views.py +++ b/announcement/views.py @@ -2,11 +2,9 @@ from __future__ import unicode_literals from django.utils.translation import ugettext as _ -from rest_framework.views import APIView from account.decorators import super_admin_required -from utils.shortcuts import paginate -from utils.shortcuts import serializer_invalid_response, error_response, success_response +from utils.shortcuts import paginate_data, APIView from .models import Announcement from .serializers import (CreateAnnouncementSerializer, AnnouncementSerializer, EditAnnouncementSerializer) @@ -24,9 +22,9 @@ class AnnouncementAdminAPIView(APIView): announcement = Announcement.objects.create(title=data["title"], content=data["content"], created_by=request.user) - return success_response(AnnouncementSerializer(announcement).data) + return self.success(AnnouncementSerializer(announcement).data) else: - return serializer_invalid_response(serializer) + return self.invalid_serializer(serializer) @super_admin_required def put(self, request): @@ -39,16 +37,16 @@ class AnnouncementAdminAPIView(APIView): try: announcement = Announcement.objects.get(id=data["id"]) except Announcement.DoesNotExist: - return error_response(_("Announcement does not exist")) + return self.error(_("Announcement does not exist")) announcement.title = data["title"] announcement.content = data["content"] announcement.visible = data["visible"] announcement.save() - return success_response(AnnouncementSerializer(announcement).data) + return self.success(AnnouncementSerializer(announcement).data) else: - return serializer_invalid_response(serializer) + return self.invalid_serializer(serializer) @super_admin_required def get(self, request): @@ -59,8 +57,8 @@ class AnnouncementAdminAPIView(APIView): if announcement_id: try: announcement = Announcement.objects.get(id=announcement_id) - return success_response(AnnouncementSerializer(announcement).data) + return self.success(AnnouncementSerializer(announcement).data) except Announcement.DoesNotExist: - return error_response(_("Announcement does not exist")) + return self.error(_("Announcement does not exist")) announcement = Announcement.objects.all().order_by("-create_time") - return paginate(request, announcement, AnnouncementSerializer) + return self.success(paginate_data(request, announcement, AnnouncementSerializer)) diff --git a/conf/models.py b/conf/models.py index 7eb9257b..1ce796a5 100644 --- a/conf/models.py +++ b/conf/models.py @@ -5,4 +5,26 @@ from django.db import models class SMTPConfig(models.Model): - pass + server = models.CharField(max_length=128) + port = models.IntegerField(default=25) + email = models.CharField(max_length=128) + password = models.CharField(max_length=128) + tls = models.BooleanField() + + class Meta: + db_table = "smtp_config" + + +class WebsiteConfig(models.Model): + base_url = models.CharField(max_length=128, default=None) + name = models.CharField(max_length=32, default="Online Judge") + name_shortcut = models.CharField(max_length=32, default="oj") + website_footer = models.CharField(max_length=256, default="Online Judge") + # allow register + register = models.BooleanField(default=True) + # submission list show all user's submission + submission_list_show_all = models.BooleanField(default=False) + + class Meta: + db_table = "website_config" + diff --git a/dockerfiles/judger/Dockerfile b/dockerfiles/judger/Dockerfile deleted file mode 100644 index 3c01c3a5..00000000 --- a/dockerfiles/judger/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM ubuntu:14.04 -ENV DEBIAN_FRONTEND noninteractive -RUN rm /etc/apt/sources.list -COPY sources.list /etc/apt/ -RUN apt-get update -RUN apt-get -y install software-properties-common python-software-properties python python-dev gcc g++ git libtool python-pip libseccomp-dev -RUN add-apt-repository -y ppa:webupd8team/java -RUN echo debconf shared/accepted-oracle-license-v1-1 select true | sudo debconf-set-selections -RUN echo debconf shared/accepted-oracle-license-v1-1 seen true | sudo debconf-set-selections -RUN apt-get update -RUN apt-get install -y oracle-java7-installer -RUN cd /tmp && git clone https://github.com/QingdaoU/Judger && cd Judger && python setup.py install -RUN mkdir -p /var/judger/run/ && mkdir /var/judger/test_case/ && mkdir /var/judger/code/ -RUN chmod -R 777 /var/judger/run/ -COPY policy /var/judger/run/ -COPY supervisord.conf /etc -RUN pip install supervisor -WORKDIR /var/judger/code/judge/ -EXPOSE 8080 -CMD bash /var/judger/code/dockerfiles/judger/run.sh \ No newline at end of file diff --git a/dockerfiles/judger/docker-compose.example.yml b/dockerfiles/judger/docker-compose.example.yml deleted file mode 100644 index b3a6441a..00000000 --- a/dockerfiles/judger/docker-compose.example.yml +++ /dev/null @@ -1,11 +0,0 @@ -judger: - image: qduoj/judger - volumes: - - /home/OnlineJudge:/var/judger/code - - /home/test_case:/var/judger/test_case - - /home/log:/var/judger/code/judge/log - environment: - - TZ=Asia/Shanghai - - rpc_token={YOUR_PASSWORD} - ports: - - "0.0.0.0:8085:8080" \ No newline at end of file diff --git a/dockerfiles/judger/policy b/dockerfiles/judger/policy deleted file mode 100644 index a057b210..00000000 --- a/dockerfiles/judger/policy +++ /dev/null @@ -1,3 +0,0 @@ -grant { - permission java.io.FilePermission "/tmp", "read"; -}; \ No newline at end of file diff --git a/dockerfiles/judger/run.sh b/dockerfiles/judger/run.sh deleted file mode 100644 index b70bb2e9..00000000 --- a/dockerfiles/judger/run.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -python -m compileall /var/judger/code -exec supervisord \ No newline at end of file diff --git a/dockerfiles/judger/sources.list b/dockerfiles/judger/sources.list deleted file mode 100644 index 3f18a0f9..00000000 --- a/dockerfiles/judger/sources.list +++ /dev/null @@ -1,10 +0,0 @@ -deb http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse -deb http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse -deb http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse -deb http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse -deb http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse -deb-src http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse -deb-src http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse -deb-src http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse -deb-src http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse -deb-src http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse \ No newline at end of file diff --git a/dockerfiles/judger/supervisord.conf b/dockerfiles/judger/supervisord.conf deleted file mode 100644 index 8769c434..00000000 --- a/dockerfiles/judger/supervisord.conf +++ /dev/null @@ -1,23 +0,0 @@ -[supervisord] -logfile=/var/judger/code/judge/log/judger_supervisord.log -logfile_maxbytes=50MB -logfile_backups=10 -loglevel=info -pidfile=/var/judger/code/judge/log/judger_supervisord.pid -nodaemon=true -childlogdir=/var/judger/code/judge/log/ - -[supervisorctl] -serverurl=unix:///tmp/supervisor.sock - -[program:judger_server] -command=python server.py -directory=/var/judger/code/judge/ -numprocs=1 -stdout_logfile=/var/judger/code/judge/log/judger_server.log -stderr_logfile=/var/judger/code/judge/log/judger_server.log -autostart=true -autorestart=true -startsecs=5 -stopwaitsecs = 5 -killasgroup=true \ No newline at end of file diff --git a/dockerfiles/mysql/Dockerfile b/dockerfiles/mysql/Dockerfile deleted file mode 100644 index 131c45eb..00000000 --- a/dockerfiles/mysql/Dockerfile +++ /dev/null @@ -1,2 +0,0 @@ -FROM mysql:latest -RUN printf "\nserver-id=1\nlog-bin=binlog\nperformance_schema=OFF" >> /etc/mysql/my.cnf \ No newline at end of file diff --git a/dockerfiles/mysql/README.md b/dockerfiles/mysql/README.md deleted file mode 100644 index 14ce4b1d..00000000 --- a/dockerfiles/mysql/README.md +++ /dev/null @@ -1,2 +0,0 @@ -开启了binlog, 保证数据安全 -关闭了performance_schema=OFF, 避免MySQL占用过多的内存 见https://virusdefender.net/index.php/archives/471/ \ No newline at end of file diff --git a/dockerfiles/oj_web_server/Dockerfile b/dockerfiles/oj_web_server/Dockerfile deleted file mode 100644 index def778d6..00000000 --- a/dockerfiles/oj_web_server/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM python:2.7 -ENV PYTHONBUFFERED 1 -RUN mkdir -p /code/log /code/test_case /code/upload -WORKDIR /code -ADD requirements.txt /code/ -RUN pip install -i http://pypi.douban.com/simple -r requirements.txt --trusted-host pypi.douban.com -RUN rm /etc/apt/sources.list -ADD sources.list /etc/apt/ -RUN curl -sL https://deb.nodesource.com/setup | bash - -RUN apt-get -y install nodejs -ADD gunicorn.conf /etc -ADD supervisord.conf /etc -ADD task_queue.conf /etc -EXPOSE 8080 -CMD bash /code/dockerfiles/oj_web_server/run.sh \ No newline at end of file diff --git a/dockerfiles/oj_web_server/docker-compose.example.yml b/dockerfiles/oj_web_server/docker-compose.example.yml deleted file mode 100644 index 4caf38e1..00000000 --- a/dockerfiles/oj_web_server/docker-compose.example.yml +++ /dev/null @@ -1,26 +0,0 @@ -redis: - image: redis - volumes: - - /home/data/redis:/data -mysql: - image: mysql - volumes: - - /home/data/mysql:/var/lib/mysql - environment: - - MYSQL_ROOT_PASSWORD={YOUR_PASSWORD} -oj_web_server: - image: qduoj/oj_web_server - volumes: - - /home/OnlineJudge:/code - - /home/test_case:/code/test_case - - /home/upload:/code/upload - - /home/log:/code/log - links: - - redis:redis - - mysql:mysql - ports: - - "127.0.0.1:8080:8080" - environment: - - TZ=Asia/Shanghai - - oj_env=server - - MYSQL_ENV_MYSQL_USER=root \ No newline at end of file diff --git a/dockerfiles/oj_web_server/gunicorn.conf b/dockerfiles/oj_web_server/gunicorn.conf deleted file mode 100644 index 18dbab32..00000000 --- a/dockerfiles/oj_web_server/gunicorn.conf +++ /dev/null @@ -1,12 +0,0 @@ -[program:gunicorn] -command=gunicorn oj.wsgi:application -b 0.0.0.0:8080 --reload -directory=/code/ -user=nobody -numprocs=1 -stdout_logfile=/code/log/gunicorn.log -stderr_logfile=/code/log/gunicorn.log -autostart=true -autorestart=true -startsecs=5 -stopwaitsecs = 5 -killasgroup=true \ No newline at end of file diff --git a/dockerfiles/oj_web_server/requirements.txt b/dockerfiles/oj_web_server/requirements.txt deleted file mode 100644 index e94e007a..00000000 --- a/dockerfiles/oj_web_server/requirements.txt +++ /dev/null @@ -1,15 +0,0 @@ -django<1.10 -MySQL-python -redis -django-redis-sessions -djangorestframework -django-rest-swagger -gunicorn -coverage -django-extensions -supervisor -pillow -jsonfield -Envelopes -celery -qrcode diff --git a/dockerfiles/oj_web_server/run.sh b/dockerfiles/oj_web_server/run.sh deleted file mode 100644 index 04b6d695..00000000 --- a/dockerfiles/oj_web_server/run.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash -if [ ! -f "/code/oj/custom_settings.py" ]; then - cp /code/oj/custom_settings.example.py /code/oj/custom_settings.py - echo "SECRET_KEY=\"`cat /dev/urandom | head -1 | md5sum | head -c 32`\"" >> /code/oj/custom_settings.py -fi -find /code -name "*.pyc" -delete -python -m compileall /code -chown -R nobody:nogroup /code/log /code/test_case /code/upload -echo "Waiting MySQL and Redis to start" -exec supervisord \ No newline at end of file diff --git a/dockerfiles/oj_web_server/sources.list b/dockerfiles/oj_web_server/sources.list deleted file mode 100644 index 67caf139..00000000 --- a/dockerfiles/oj_web_server/sources.list +++ /dev/null @@ -1,8 +0,0 @@ -deb http://mirrors.aliyun.com/debian wheezy main contrib non-free -deb-src http://mirrors.aliyun.com/debian wheezy main contrib non-free - -deb http://mirrors.aliyun.com/debian wheezy-updates main contrib non-free -deb-src http://mirrors.aliyun.com/debian wheezy-updates main contrib non-free - -deb http://mirrors.aliyun.com/debian-security wheezy/updates main contrib non-free -deb-src http://mirrors.aliyun.com/debian-security wheezy/updates main contrib non-free \ No newline at end of file diff --git a/dockerfiles/oj_web_server/supervisord.conf b/dockerfiles/oj_web_server/supervisord.conf deleted file mode 100644 index 4026c19e..00000000 --- a/dockerfiles/oj_web_server/supervisord.conf +++ /dev/null @@ -1,15 +0,0 @@ -[supervisord] -logfile=/code/log/supervisord.log -logfile_maxbytes=50MB -logfile_backups=10 -loglevel=info -pidfile=/code/log/supervisord.pid -nodaemon=true -user=nobody -childlogdir=/code/log/ - -[supervisorctl] -serverurl=unix:///tmp/supervisor.sock - -[include] -files=gunicorn.conf task_queue.conf \ No newline at end of file diff --git a/dockerfiles/oj_web_server/task_queue.conf b/dockerfiles/oj_web_server/task_queue.conf deleted file mode 100644 index dd51bebc..00000000 --- a/dockerfiles/oj_web_server/task_queue.conf +++ /dev/null @@ -1,12 +0,0 @@ -[program:task_queue] -command=celery -A oj worker --autoscale=30,4 -l DEBUG -directory=/code/ -user=nobody -numprocs=1 -stdout_logfile=/code/log/task_queue.log -stderr_logfile=/code/log/task_queue.log -autostart=true -autorestart=true -startsecs=5 -stopwaitsecs = 5 -killasgroup=true \ No newline at end of file diff --git a/dockerfiles/test_case_rsync/Dockerfile b/dockerfiles/test_case_rsync/Dockerfile deleted file mode 100644 index 85dc054a..00000000 --- a/dockerfiles/test_case_rsync/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM ubuntu:14.04 -RUN apt-get update && apt-get -y install rsync -CMD /bin/bash /OnlineJudge/dockerfiles/test_case_rsync/run.sh diff --git a/dockerfiles/test_case_rsync/docker-compose.example.yml b/dockerfiles/test_case_rsync/docker-compose.example.yml deleted file mode 100644 index 06500d6d..00000000 --- a/dockerfiles/test_case_rsync/docker-compose.example.yml +++ /dev/null @@ -1,21 +0,0 @@ -oj_rsync_master: - image: oj_rsync - volumes: - - /home/OnlineJudge:/OnlineJudge - - /home/test_case:/OnlineJudge/test_case - - /home/log:/OnlineJudge/log - ports: - - "0.0.0.0:873:873" - environment: - - TZ=Asia/Shanghai - - RSYNC_MODE=master -oj_rsync_slave: - image: oj_rsync - volumes: - - /home/OnlineJudge:/OnlineJudge - - /home/test_case:/OnlineJudge/test_case - - /home/log:/OnlineJudge/log - environment: - - TZ=Asia/Shanghai - - RSYNC_MODE=slave - - RSYNC_MASTER_ADDR={YOUR_MASTER_ADDR} diff --git a/dockerfiles/test_case_rsync/rsyncd.conf b/dockerfiles/test_case_rsync/rsyncd.conf deleted file mode 100644 index 52e7a557..00000000 --- a/dockerfiles/test_case_rsync/rsyncd.conf +++ /dev/null @@ -1,11 +0,0 @@ -port = 873 -uid = root -gid = root -use chroot = yes -read only = yes -log file = /OnlineJudge/log/rsyncd.log -[testcase] -path = /OnlineJudge/test_case/ -list = yes -auth users = ojrsync -secrets file = /etc/rsyncd/rsyncd.passwd diff --git a/dockerfiles/test_case_rsync/rsyncd.example.passwd b/dockerfiles/test_case_rsync/rsyncd.example.passwd deleted file mode 100644 index 54329f0e..00000000 --- a/dockerfiles/test_case_rsync/rsyncd.example.passwd +++ /dev/null @@ -1 +0,0 @@ -YOUR_PASSWORD diff --git a/dockerfiles/test_case_rsync/rsyncd_slave.sh b/dockerfiles/test_case_rsync/rsyncd_slave.sh deleted file mode 100644 index e63e1128..00000000 --- a/dockerfiles/test_case_rsync/rsyncd_slave.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash -while true -do - rsync -avz --delete --progress --password-file=/OnlineJudge/dockerfiles/test_case_rsync/rsyncd.passwd ojrsync@$RSYNC_MASTER_ADDR::testcase /OnlineJudge/test_case >> /OnlineJudge/log/rsync_slave.log - sleep 5 -done diff --git a/dockerfiles/test_case_rsync/run.sh b/dockerfiles/test_case_rsync/run.sh deleted file mode 100644 index 7ac3e0b5..00000000 --- a/dockerfiles/test_case_rsync/run.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash -if [ "$RSYNC_MODE" = "master" ]; then - if [ ! -f "/etc/rsyncd/rsync_master.passwd" ]; then - mkdir /etc/rsyncd - (echo "ojrsync:" && cat /OnlineJudge/dockerfiles/test_case_rsync/rsyncd.passwd) | tr -d "\n" > /etc/rsyncd/rsyncd.passwd - fi - chmod 600 /etc/rsyncd/rsyncd.passwd - rsync --daemon --config=/OnlineJudge/dockerfiles/test_case_rsync/rsyncd.conf -else - chmod 600 /OnlineJudge/dockerfiles/test_case_rsync/rsyncd.passwd - /bin/bash /OnlineJudge/dockerfiles/test_case_rsync/rsyncd_slave.sh -fi -while true -do - sleep 100 -done diff --git a/frontend/admin/.babelrc b/frontend/admin/.babelrc deleted file mode 100644 index 41789cac..00000000 --- a/frontend/admin/.babelrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "presets": ["es2015", "stage-2"], - "plugins": ["transform-runtime"], - "comments": false -} diff --git a/frontend/admin/config/dev.env.js b/frontend/admin/config/dev.env.js deleted file mode 100644 index efead7c8..00000000 --- a/frontend/admin/config/dev.env.js +++ /dev/null @@ -1,6 +0,0 @@ -var merge = require('webpack-merge') -var prodEnv = require('./prod.env') - -module.exports = merge(prodEnv, { - NODE_ENV: '"development"' -}) diff --git a/frontend/admin/config/index.js b/frontend/admin/config/index.js deleted file mode 100644 index 35ac39f2..00000000 --- a/frontend/admin/config/index.js +++ /dev/null @@ -1,37 +0,0 @@ -// see http://vuejs-templates.github.io/webpack for documentation. -var path = require('path') - -module.exports = { - build: { - env: require('./prod.env'), - index: path.resolve(__dirname, '../dist/index.html'), - assetsRoot: path.resolve(__dirname, '../dist'), - assetsSubDirectory: 'static', - assetsPublicPath: '/', - productionSourceMap: true, - // Gzip off by default as many popular static hosts such as - // Surge or Netlify already gzip all static assets for you. - // Before setting to `true`, make sure to: - // npm install --save-dev compression-webpack-plugin - productionGzip: false, - productionGzipExtensions: ['js', 'css'] - }, - dev: { - env: require('./dev.env'), - port: 8080, - assetsSubDirectory: 'static', - assetsPublicPath: '/', - proxyTable: { - "/api": { - target: "http://127.0.0.1:8000", - changeOrigin: true - } - }, - // CSS Sourcemaps off by default because relative paths are "buggy" - // with this option, according to the CSS-Loader README - // (https://github.com/webpack/css-loader#sourcemaps) - // In our experience, they generally work as expected, - // just be aware of this issue when enabling this option. - cssSourceMap: false, - } -} diff --git a/frontend/admin/config/prod.env.js b/frontend/admin/config/prod.env.js deleted file mode 100644 index 773d263d..00000000 --- a/frontend/admin/config/prod.env.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - NODE_ENV: '"production"' -} diff --git a/frontend/admin/config/test.env.js b/frontend/admin/config/test.env.js deleted file mode 100644 index 89f90deb..00000000 --- a/frontend/admin/config/test.env.js +++ /dev/null @@ -1,6 +0,0 @@ -var merge = require('webpack-merge') -var devEnv = require('./dev.env') - -module.exports = merge(devEnv, { - NODE_ENV: '"testing"' -}) diff --git a/frontend/admin/index.html b/frontend/admin/index.html deleted file mode 100644 index 8ea866bc..00000000 --- a/frontend/admin/index.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - Online Judge Admin - - - - - - -
-
- - diff --git a/frontend/admin/package.json b/frontend/admin/package.json deleted file mode 100644 index 3f680f9e..00000000 --- a/frontend/admin/package.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "name": "frontend", - "version": "1.0.0", - "description": "A Vue.js project", - "author": "LiYang ", - "private": true, - "scripts": { - "dev": "node build/dev-server.js", - "build": "node build/build.js", - "test": "" - }, - "dependencies": { - "babel-runtime": "^6.0.0", - "vue": "^1.0.21" - }, - "devDependencies": { - "babel-core": "^6.0.0", - "babel-loader": "^6.0.0", - "babel-plugin-transform-runtime": "^6.0.0", - "babel-preset-es2015": "^6.0.0", - "babel-preset-stage-2": "^6.0.0", - "babel-register": "^6.0.0", - "bootbox": "^4.4.0", - "bootstrap-modal": "^2.0.0", - "codemirror": "^5.17.0", - "connect-history-api-fallback": "^1.1.0", - "css-loader": "^0.23.0", - "eventsource-polyfill": "^0.9.6", - "expose-loader": "^0.7.1", - "express": "^4.13.3", - "extract-text-webpack-plugin": "^1.0.1", - "file-loader": "^0.8.4", - "function-bind": "^1.0.2", - "html-webpack-plugin": "^2.8.1", - "http-proxy-middleware": "^0.12.0", - "jquery": "^3.1.0", - "json-loader": "^0.5.4", - "ora": "^0.2.0", - "shelljs": "^0.6.0", - "simditor-markdown": "^1.1.2", - "url-loader": "^0.5.7", - "vue-hot-reload-api": "^1.2.0", - "vue-html-loader": "^1.0.0", - "vue-i18n": "^4.1.0", - "vue-loader": "^8.3.0", - "vue-resource": "^0.9.3", - "vue-router": "^0.7.13", - "vue-strap": "^1.0.11", - "vue-style-loader": "^1.0.0", - "webpack": "^1.12.2", - "webpack-dev-middleware": "^1.4.0", - "webpack-hot-middleware": "^2.6.0", - "webpack-merge": "^0.8.3", - "webuploader": "^0.1.8" - } -} diff --git a/frontend/admin/src/App.vue b/frontend/admin/src/App.vue deleted file mode 100644 index c78412c3..00000000 --- a/frontend/admin/src/App.vue +++ /dev/null @@ -1,51 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/admin/src/components/back.vue b/frontend/admin/src/components/back.vue deleted file mode 100644 index af1003f0..00000000 --- a/frontend/admin/src/components/back.vue +++ /dev/null @@ -1,9 +0,0 @@ - \ No newline at end of file diff --git a/frontend/admin/src/components/codeMirror.vue b/frontend/admin/src/components/codeMirror.vue deleted file mode 100644 index 7cd9e5d6..00000000 --- a/frontend/admin/src/components/codeMirror.vue +++ /dev/null @@ -1,104 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/admin/src/components/help.vue b/frontend/admin/src/components/help.vue deleted file mode 100644 index efa0520a..00000000 --- a/frontend/admin/src/components/help.vue +++ /dev/null @@ -1,29 +0,0 @@ - - - \ No newline at end of file diff --git a/frontend/admin/src/components/helpLink.vue b/frontend/admin/src/components/helpLink.vue deleted file mode 100644 index c31f42e6..00000000 --- a/frontend/admin/src/components/helpLink.vue +++ /dev/null @@ -1,14 +0,0 @@ - - - \ No newline at end of file diff --git a/frontend/admin/src/components/pager.vue b/frontend/admin/src/components/pager.vue deleted file mode 100644 index 7a6a646f..00000000 --- a/frontend/admin/src/components/pager.vue +++ /dev/null @@ -1,93 +0,0 @@ - - diff --git a/frontend/admin/src/components/problemSample.vue b/frontend/admin/src/components/problemSample.vue deleted file mode 100644 index ef401cb7..00000000 --- a/frontend/admin/src/components/problemSample.vue +++ /dev/null @@ -1,71 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/admin/src/components/simditor.vue b/frontend/admin/src/components/simditor.vue deleted file mode 100644 index d7261f7c..00000000 --- a/frontend/admin/src/components/simditor.vue +++ /dev/null @@ -1,38 +0,0 @@ - - - - \ No newline at end of file diff --git a/frontend/admin/src/components/tagInput.vue b/frontend/admin/src/components/tagInput.vue deleted file mode 100644 index da7e646b..00000000 --- a/frontend/admin/src/components/tagInput.vue +++ /dev/null @@ -1,115 +0,0 @@ - - - - - diff --git a/frontend/admin/src/components/testCaseMgnt.vue b/frontend/admin/src/components/testCaseMgnt.vue deleted file mode 100644 index ec235f31..00000000 --- a/frontend/admin/src/components/testCaseMgnt.vue +++ /dev/null @@ -1,107 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/admin/src/components/uploader.vue b/frontend/admin/src/components/uploader.vue deleted file mode 100644 index c9cc6b77..00000000 --- a/frontend/admin/src/components/uploader.vue +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/frontend/admin/src/locales.js b/frontend/admin/src/locales.js deleted file mode 100644 index c4e8bd82..00000000 --- a/frontend/admin/src/locales.js +++ /dev/null @@ -1,136 +0,0 @@ -export default { - "zh-cn": { - alert: { - alert: "提示", - OK: "确定", - confirm: "确认", - cancel: "取消" - }, - nav: { - general: "通用", - userManagement: "用户管理", - announcementManagement: "公告管理", - problemManagement: "题目管理", - createProblem: "创建题目", - }, - pagination: { - firstPage: "首页", - lastPage: "末页" - }, - request: { - error: "请求失败", - succeeded: "操作成功" - }, - user: { - userList: "用户列表", - editUser: "编辑用户信息", - username: "用户名", - email: "邮箱", - realName: "真实姓名", - adminType: "用户类型", - createTime: "注册时间", - management: "管理", - - submission: "提交", - newPassword: "新密码", - leaveBlankIfDoNotChangePassword: "不需要修改密码请留空", - openAPIFunction: "OpenAPI 功能", - tfaAuth: "两步验证", - isDisabled: "禁用用户", - adminExtraPermission: "普通管理员额外权限", - showAdminOnly: "只显示管理员", - - regularUser: "普通用户", - admin: "普通管理员", - superAdmin: "超级管理员", - UserDoesNotExist: "用户不存在", - createPublicContest: "创建公开比赛", - manageAllContest: "管理所有比赛", - manageAllProblem: "管理所有题目", - manageOwnProblem: "管理自己创建的题目", - }, - announcement: { - announcementList: "公告列表", - editAnnouncement: "编辑公告", - createAnnouncement: "创建公告", - contentCanNotBeEmpty: "内容不能为空" - }, - problem: { - problemList: "题目列表", - createProblem: "创建题目", - sample: "样例", - addSample: "添加样例", - fold: "折叠", - show: "展开", - deleteThisSample: "删除这组样例?", - testCase: "测试用例", - uploadProgress: "上传进度", - mode: "模式", - OIMode: "OI模式", - ACMMode: "ACM模式", - score: "分数", - timeLimit: "时间限制", - memoryLimit: "内存限制", - tag: "标签", - hard: "难", - medium: "中等", - easy: "简单", - difficulty: "难度", - chooseLanguage: "选择语言", - submitCode: "提交代码", - switchMode: "如果更换题目模式,需要重新上传测试用例", - switchSpecialJudge: "如果切换判题模式,需要重新上传测试用例", - turnOnSpecialJudge: "使用Special Judge", - testSpecialJudge: "测试Special Judge代码", - specialJudgeTestResult: "Special Judge测试运行结果", - runResult: "运行结果", - output: "输出", - hint: "提示", - source: "题目来源", - problemDescriptionIsRequired: "请填写题目描述" - }, - tag: { - hint: "回车创建标签" - }, - adminUtils: { - search: "搜索", - inputKeyword: "输入关键词", - submit: "提交", - edit: "编辑", - - title: "标题", - description: "描述", - createTime: "创建时间", - lastUpdateTime: "最后更新", - createdBy: "创建人", - - isVisible: "是否可见", - visible: "可见", - invisible: "隐藏", - - management: "管理", - content: "内容", - - back: "返回", - saveChanges: "保存修改", - delete: "删除", - input: "输入", - output: "输出", - download: "下载", - upload: "上传", - - score: "分数", - help: "帮助", - - chooseFile: "选择文件", - - unsupportedBrowserWarningMsg: "当前网页 不支持 你正在使用的浏览器, 为了正常的访问,请到 升级你的浏览器", - CPUTime: "CPU时间", - memory: "内存" - }, - help: { - timeLimit: "1-1000ms", - memoryLimit: "最小16M, Java最小32M" - } - } -}; \ No newline at end of file diff --git a/frontend/admin/src/main.js b/frontend/admin/src/main.js deleted file mode 100644 index 29acddb8..00000000 --- a/frontend/admin/src/main.js +++ /dev/null @@ -1,155 +0,0 @@ -import Vue from 'vue' -import App from './App' -import VueRouter from "vue-router" -import VueI18n from "vue-i18n" - -import "expose?$!expose?jQuery!jquery" -import "bootstrap" -import bootbox from "bootbox" - -import locale from "./locales" -import getCookie from "./utils/cookie" - -import userList from "./views/account/userList.vue" -import editUser from "./views/account/editUser.vue" - -import announcementList from "./views/announcement/announcementList.vue" -import editAnnouncement from "./views/announcement/editAnnouncement.vue" - -import createProblem from "./views/problem/createProblem.vue" -import problemList from "./views/problem/problemList.vue" - - -// i18n settings -Vue.use(VueI18n); - -// todo: strore lang config in localstorage -var lang = "zh-cn"; -Vue.config.lang = lang; - -Object.keys(locale).forEach(function (lang) { - Vue.locale(lang, locale[lang]) -}); - -///////// - -// custom ajax -Vue.use({ - install: function (Vue, options) { - Vue.prototype.request = function (option) { - var request = new XMLHttpRequest(); - request.open(option.method, option.url, true); - request.onerror = function () { - if (option.error) { - option.error(request) - } - else { - alert(locale[lang].request.error); - } - }; - request.onload = function () { - if (request.status >= 200 && request.status < 400) { - try { - var data = JSON.parse(request.responseText); - if (data.code == 1 && data.data) { - alert(data.data); - return; - } - } - catch (err) { - request.onerror(); - } - if(option.success) { - option.success(data); - } - else { - alert(locale[lang].request.succeeded); - } - } - else { - request.onerror(); - } - }; - request.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); - if (option.method.toLowerCase() != 'get') { - request.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - request.setRequestHeader('X-CSRFToken', getCookie('csrftoken')); - request.send(JSON.stringify(option.data)); - } - else { - request.send(); - } - } - } -}); -/////// - - -Vue.use(VueRouter); -var router = new VueRouter({linkActiveClass: "active"}); - -router.map({ - "/user/:page": { - component: userList - }, - "/user/edit/:userId": { - component: editUser - }, - "/problem/create": { - component: createProblem - }, - "/problem/:page": { - component: problemList - }, - "/announcement/:page": { - component: announcementList - }, - "/announcement/edit/:announcementId": { - component: editAnnouncement - } -}); - -// override window.alert -window.alert = function bootboxAlert(content) { - bootbox.dialog({ - message: content, - title: locale[lang].alert.alert, - buttons: { - main: { - label: locale[lang].alert.OK, - className: "btn-primary" - } - } - }) -}; - -// override window.confirm -window.confirm = function bootboxConfirm(content, okCallback, cancelCallback) { - var config = { - message: content, - title: locale[lang].alert.confirm, - buttons: { - cancel: { - label: locale[lang].alert.cancel, - className: "btn-success" - }, - main: { - label: locale[lang].alert.OK, - className: "btn-warning", - callback: okCallback - } - } - }; - if (cancelCallback) { - config.buttons.cancel.callback = cancelCallback; - } - bootbox.dialog (config) -}; - -router.redirect({"/user": "/user/1"}); -router.redirect({"/announcement": "/announcement/1"}); -router.redirect({"/problem": "problem/1"}); - -// hide loading -document.getElementById("loading").style.display = "none"; -router.start(App, '#app'); diff --git a/frontend/admin/src/utils/cookie.js b/frontend/admin/src/utils/cookie.js deleted file mode 100644 index bcae3066..00000000 --- a/frontend/admin/src/utils/cookie.js +++ /dev/null @@ -1,10 +0,0 @@ -function getCookie(name) { - var value = "; " + document.cookie; - var parts = value.split("; " + name + "="); - if (parts.length == 2) { - return parts.pop().split(";").shift(); - } -} - - -module.exports = getCookie; \ No newline at end of file diff --git a/frontend/admin/src/views/account/editUser.vue b/frontend/admin/src/views/account/editUser.vue deleted file mode 100644 index 86f45f82..00000000 --- a/frontend/admin/src/views/account/editUser.vue +++ /dev/null @@ -1,165 +0,0 @@ - - \ No newline at end of file diff --git a/frontend/admin/src/views/account/userList.vue b/frontend/admin/src/views/account/userList.vue deleted file mode 100644 index ca9498de..00000000 --- a/frontend/admin/src/views/account/userList.vue +++ /dev/null @@ -1,113 +0,0 @@ - - \ No newline at end of file diff --git a/frontend/admin/src/views/announcement/announcementList.vue b/frontend/admin/src/views/announcement/announcementList.vue deleted file mode 100644 index e9b6bba4..00000000 --- a/frontend/admin/src/views/announcement/announcementList.vue +++ /dev/null @@ -1,74 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/admin/src/views/announcement/createAnnouncement.vue b/frontend/admin/src/views/announcement/createAnnouncement.vue deleted file mode 100644 index cd2ed4b4..00000000 --- a/frontend/admin/src/views/announcement/createAnnouncement.vue +++ /dev/null @@ -1,49 +0,0 @@ - - - \ No newline at end of file diff --git a/frontend/admin/src/views/announcement/editAnnouncement.vue b/frontend/admin/src/views/announcement/editAnnouncement.vue deleted file mode 100644 index 21dee579..00000000 --- a/frontend/admin/src/views/announcement/editAnnouncement.vue +++ /dev/null @@ -1,68 +0,0 @@ - - - \ No newline at end of file diff --git a/frontend/admin/src/views/nav/leftNav.vue b/frontend/admin/src/views/nav/leftNav.vue deleted file mode 100644 index d7742dcd..00000000 --- a/frontend/admin/src/views/nav/leftNav.vue +++ /dev/null @@ -1,35 +0,0 @@ - - \ No newline at end of file diff --git a/frontend/admin/src/views/nav/topNav.vue b/frontend/admin/src/views/nav/topNav.vue deleted file mode 100644 index 26eba83b..00000000 --- a/frontend/admin/src/views/nav/topNav.vue +++ /dev/null @@ -1,25 +0,0 @@ - - \ No newline at end of file diff --git a/frontend/admin/src/views/problem/createProblem.vue b/frontend/admin/src/views/problem/createProblem.vue deleted file mode 100644 index 49dac942..00000000 --- a/frontend/admin/src/views/problem/createProblem.vue +++ /dev/null @@ -1,150 +0,0 @@ - - - \ No newline at end of file diff --git a/frontend/admin/src/views/problem/problemList.vue b/frontend/admin/src/views/problem/problemList.vue deleted file mode 100644 index 5ce3b3eb..00000000 --- a/frontend/admin/src/views/problem/problemList.vue +++ /dev/null @@ -1,93 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/admin/src/views/problem/specialJudge.vue b/frontend/admin/src/views/problem/specialJudge.vue deleted file mode 100644 index fb70f8e4..00000000 --- a/frontend/admin/src/views/problem/specialJudge.vue +++ /dev/null @@ -1,78 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/admin/static/img/favicon.ico b/frontend/admin/static/img/favicon.ico deleted file mode 100644 index a8f829aac3358f12f41066cfb1fe66dd62b90908..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 67646 zcmeHw2Vhl2_WpY>jU*(b69^Ct9Rxx^1VlhYML^^yML<9VL@6SI0_hQT*Hsr$x44L| zWkqrAu7xHTu&lD`-&I$!5Csts6%j}waQ@#n_s-4DGl7)%lDtPI9Olg}Q_gqhOgl5z z;c((FFVBJh+d4WoaXJzl4o7DYF7cwe&Yz90kpqn!Xyia62O2rh$bm)azIR&&4G@1c_bMxlSo1K!9lD}fbiu}^j()<%API!O>aa2Sn@{b=sp8xX8 zujDt2h}aG8D%>$wYGDuls%w-15}yeQ2q>97>*{UAMaB7o{GEy_E5oVYv+fJ?ej@+b zr=Q7(Toy4;ps)Q6dz6Q|bvsBxLqlc5h7DlB38|Ii>y;4W-nw`?#n?z9=r010^S<@Yr!V z0h!tL?JgP6zdvxGE^tb8vu3#Vtsw(SRw|Z7X5+X?{9PT#vQL~iQDe_M-{p_M+sbsU z#y>JLQoj4{yPCHH?)aOG-?nX=1O^5w`~w1mWW~de%KJx;$!mv?%i2T5^6;S|dF+=W zdHR=P+4##b`EOyV{8%bd3>o-t_iky^y0y_3pkH|P%$afmxQQp*Vg=6EbkW|m!E+1=1m*XZnQD*$NSOEqGa#hy%nHdcBwY_ zzw^#J;$&Vy;K|1z^2guZ7!*+9-@JMAYWD-EGgX_xDv7G8iS@Inun2v=SQU2w`g9L% zepA*QE|pt;D3k?zie<)k#d5`-BDv!GLYcZ3*Y+04?T~}VzW!GJZ~9ddr1VDF7=h!3 zr`E1D5-EcmVWMuh+TefRefQb%U*+$>o8=3J|B;q~)vH%)R#xh3%(c&YejGZvxVS_P z9r{%c{P3d`>@1KcpIj?0^xqkGOp7FW8Fl-%14VKzu%5W9NG|#2s9f;P5kLHNQNfL?uDZ%d z$It<@P5zt?-a?K4uwlal^8vr%4*5%*>Hms8`qy87Ew8@%s@#3|-78~haSs?VN>={@eZT*xjQ_q!M(iq(aJ*-c9oE|i645s{LNK0%+fG#Pi1zj2JOOFmEJ5K|v-aM%zJbFs)z`e>))uVb(L}T|P|GNYGHaibV+ntA{+s-3$<{f`A+g_(cCA5_ETknvA&?lSLZB%l>_(KsV`x1Jd zh`tWTU_Rk2jNPxj_F8%Lkw@h7&p(&K!lS?%a?3x+!wD(1{-Cw2KaM|}v+qazU6}iE zIa-}Y__L__#Gieie_;&#U0vW8#U#F|L9P%9 zi-@tF9WZ8`to*S^<^umw-yKo)y&bTR-TAX5>^vxE;P>E9_e*)m{prri0zuVg#= z3LFo<_}mL7{!SBrV@zn^ZuBkrTa%Iv;207TA{{z*keRb)%G#&a%J=*BTG&$x%FsV9 zg~i2at562aJwpaq7fy%1ABt(Y75Js_I``aj(eG|;VMo5I`mJ;$`ONu|5OIdZOIT75 zi8%9oX+Cg@v>dfqVkX}%akE!T(t^jO&6M96{DdxOd*+#P&%whoXWtQpe_yowDLa3a zn1X{6Py7phmHq`sW$br_G6(&GmCzOcM*p$!SgEYMa|POiK+Fj{CBFM$Nt|?>Bwc)i z#PypXksbRM+kbzQk zTEE)4)71SatJAr|;>zjsBSYhF_u1zPAYYwLB)oMuiR?F4V#nu5>-i5z>ziJZ7U`QL zA#1b5Wo?m!%zXUbBJtS_Zm+6IKP7S6T@p8Jjzo4FDglu#kZzEgJJNEY%4O(xiHVDqY1604);G6GDdr&G z{`bEPSvBhaw$lf8tE5IBko+RNJKK=3W-+q- z-4A5`zCyVYV}FhT#{l;+y9;G3<^!f-E@08V!}1r*2j*jra4+VFhhIDb^_cxVmo!W1 zCyBr|CX-kLf6Aaq_WV&66=gyuA)hfhm`8#xikpxv&CWbeoS~EjwqdHRSF&aF`w(XR zcR5d&vAp)Kkna^V1w{Qmf3@rS+1hCE=3mrAg}^;)ITK8kn=)2llG{zt_%~)8zLo zRsX^3$iCxIhc|&|uY-5c>58ga|838)?tzl0+$PSzFbN`#sB=jjyGr^iZ_3@6?^}ML zSZ@BINN)Y9P##3RUypkK-mdRu@|9Og0Q=+6M;vFwqz#vr8E9u%_p4g}mA*oMMCIRT zlj5KgC<`o8>x>N&KmCsq(d9gG1!As(?Sg^7zpwjLB+@es{4vMUXFT})I{361b=Zby zHGf9|`zW-_37MND@j|q(sKZQyG7uONCcQ5nCG%E2Ab;NWwmkUW2Xgx}&&ibe3nd|u z))3CV*8MjFNr8?+B#YOtSc&>iN8~#&l)SuQ76<`4t=MjtaZ}jy1z@xwBJemh4Uq*-xU(x?IHA?II-DFcay-s?Ap=_rYo!CI2g*pz^($RZeQ)S|&igls zjFpi14ieO&hXl7fTOxXnl*m34Bx>MvNf@~pdAdWAXWuWa7Ck4efMxRxj6>5oF4?AZ z5Ajbh@we(daM!<$eoeka555ZdG5drL2gkyvn=|x3cprb(CDLlfUnKr~jHNpck-(;~ z-ND$)q2>TNAHcfFJ~{L_e(O1wvUwunU50H{ew%Bq<=$g_obxf_b&t=5dz3E_?1Nk( z%_Oi{Yl-PHP+|v7m*!XeLCpyyK|jPpCgPyGH0JJpY&P&8GTn$v$*N=7=>|T^Yt?@z z`$DcJ;0JMJ{nflLUyoHiweA_usCi;F_hzi4Yh1No7MG4wzh^vezwbhu;ADTyEscu9 zhMrMlIzEp)xB??2IH{+^445LVu3RatFs4a>j);LQ#9>^k#uCs4u^daDJ59AWv^PTh zH-b(V*8~Nf46@dM@%uMCCw{@f!4exAEB#@YMZ1m#3l_*7cibUQJn@7)|NQf^ZrwVy zPV^tF#eMS0C-UW&U&?OS@Niv@v}eyA`4%=@T$lOpfB!4nw{Mp>-+WUxZ{92$H*S=d zUw&C$eDOtj?z!jWnP;AnhaY}e?!5C($;il%>#*+2^;xb*oPYlL(ym=QX$G6HCQUFm zgmFS;@}=4p>JVp;1V*%!n1Pd|#hgE3-f|=Q21Yv&NBqx$oeFdb=NV9b&V~841HZWL zKM}M8WZ|v(NIxzvPA<9R61n~M+hzUw^~yfXSnD^|fu$atUX#>5*Qsc?Lz$tROqw)F zl9Q8_T{G>DTqv8-hM0X3v~LdBB!s8*k@#`TAqVTA6EIFu_+!jr=zqV)AK0dWwrQ+2 z{tUNi(?->`O`A5!5zN6fG)LS|<@n4OUwk2J)~u0hX3v(kZQDu!)*slOsWPLSEHlR! zp)I;e>j_yBfA(bv2ZPkT1-S2zE)0dr0S^wtx9U83Ghn~~dFrXBV83(7&3@uk+-}(S zDGNuB9u?Z5(k`G?t5)UP3d#cK8iFw=ti~(~|G9n$K5aa_iOPG}F$E?Sj$?%J(PcH9#Z66CLc{VVKa3Ki}(G3HD`YS$dH8oAf38p&);7!%>KXAVa=U|7qj*)mL9BJ@1{$+_^V!aGpHDGtowPpm3@3dv7R>|tKm726T!}TO3dWL8 z;aXE4lz@K7vsLdoPF@S!d0l4TM9e$6);yul`3LTUU|Y$0W71pj6G6V>*w@n-!Zbv%I79KiKI^|un{B~GIIBkI z7k&})I|_b7aeqNZRQ}GyUK>5e*ZHqcpZm#JuXX;oFRNFtUNU3G49U&Sm7JU$xdLbZ zbno6>t>^0J*iKOYHUv2)|2Rj$vvPF);C~B>1J$?VX9lcx|MABkuTQvtlQ#ALHP>7t zojP@rMT-{62OoS;ZoPr{k|?w9zyH3H3APs>fBdn!-cayo!QOlCJ=Isy?G^a`k1B%C zc|_V;cw;_s8sb%Q$@N?A&u(ar7~1=D8uNgvKAXX(gaxr;vd_ z+`k>fI_iyn1N);YPdD6fLxubI>yk%RNvk1ae6svA-_%oFH&r|*-*J{6h;_Rn$GPKG zpi&Uu;k+Ez;XUOd5s}jUY-2f&W*!@z_c%cQlFyt|2ky$o2ugiQ1%RgE+FKxdpW#V_ zJ=q3O_o(=3+dx}~M*O{py^fhYX8)D=n=-HiN}hV2*!BfI1S)4|gD~~9r{|6`5U0Zb z^2;yxYa0&D`}*{WJBj^(9XobN80}BNe-h3#T?7Ap;8CP8F@289?*&!XM&Op^_{^iA z4?!H$(MFSFdqiEKdm2|2{(t=A9~)eMxjwP-M%u)lxG!G3SVoN+B~3AJN$g1hSTm%r z@i6_*;B%g!GW>9>oEwUedmNOXy`a}XcY$Vr27oy3qpga1o;JE#HoZ~y#yD}*80x)! zZIF1olZglQz-&-;`+6yONA>5-c-&>7(lF;sXd6#Ed7fq2=seE>)~_BtdRQ2^V~@T7 z`+My7)y7fZF9JOe+6{7VPptHDufpH=-g~d-3*Q){s{il4`>xEKIa8J`Tc*w#%Y{vq zmIL5VeIE?+lw(|-L6?DU1w9M;6to{yp!Z6Q<^+x)PlV|#}P2+sq zoH=vUUevnOH&x;3ao8$_t8C?U52WNR7eDFcxeiHJZM7v#Y*=aI8p65^z{$Q+RW@erWBCpmAFQ@-? zdFgXsOMN-!je8hEq5D|pgV87H4!cW^0nI#e{@>ef1`<%|as1Cb)snHZ1rkcc^DK$Drr&)`>Oy%&$w*bT#*iZ|z=@7d7C zM0S4F^f&p;^Q35R#dd&w|KY=j*Zcl;=8N;{-d9+{i)!($a<5^&wKLvToKY*`^1y>`o9gh)P>7IA_{+=bH{lx9p-P2&#Hu z-guYgWn0K|eYl5^WlF}Gkk!$J)y)^@fgbqV-|Y3DcA0hI*sruO=Wo_~&e1&i%(`)-MZebTeljQSM!!dv)Q|`ak+xCjPY7 zr+%)j8;S@lZ1LN@tknH^AnpY?1X4PWHd-}xn$PS%-E+@9sx8rNDC2i0&co{OMSGgp z#QLqb-coD*OP4Mc+Bnhn%-+Y<`C%TJ7xww;;|ybde_H1SKJ9-3LA7=L5CLV0aZ?Vs zpKKc_50nO?e$dVu2htX!woc0d_59m!zb$|G!ynX{|J+}|xmdO{W5$e;S6+F=s$-tS zpK-Ari-{@whA;|h=dqG?+Mdz&X*0&X_HTdJw*1w7|GxX~({-PDT(3d#fIkj#unian ziaJSrV{lLLhikI@&Vt6Ux@FPN^L^GqjjN8&-M2rlf7jM$8a!_!5#Qw2`iDO2KHDB* z&hZVO(fO%unl*i&>5sws8!Drgfvl6@(73tzOQ{xq*LBthnn{>@#nm+x7U3uldVYHC;%xL;P{>V zYjhQJfc*uo;d72-*REZvzrp@abzCXpxBz`3VsFyQ%D}0k>s((_`15;Bjpu5F6Ty{28{HEt6W>*JKp_ z<~P33SEtR6@jXj^(=rg>gW|Ws$Ty4BcVW1WfbY-aOw#$#9lJTULc9Ow)@`cpbI!*k zj=>s=yB8TT%8PZ7#~ypkXZwQH(M80b-xuuHub(+?EW?xEDUAz?k}k0wWl)=;GN#=` znc8WFT-9~9qAR;yEfYFUl8ahjBzZKWxo)8N z!84)R&n2Jy&9O{8|Au`z{oUFi=)JRIQ)OPyd6LyTPqKR5415i&Gl4n7nKn}QfqPbJ zo@Co7vv;m6NL?&L+MF+u!4c30w)Vl`?fu|!LumIalD|>7Xss3UTnK-|pM86-1IEY4 zTlGEyb^fw;mq`Y2&je-n&H?2~ZW{2V9Mpi8;GJ=u#$%jUc9t=Cy8{$c5#t-sHO@yX z-f&&a-|**rAlLnM8N&mb$~5Tu9Mtu!UbiaBPFpV7z`i#4XQR#N8rj842k9LI)%SQ0 zSvaM83Qwqf>oel~N&l~0xzbqshh7d22$u2fCZT;_4qb0xu5l;!wZb1dBqc1xjz8yG ze8F)}O68mTrv0h@^ZU=7vtjx8?e=pL&y(z)%OwNtINSCtwDZ}Z92@akP4s_euPo^h z*};x~LmKa$Ob#ut zf7!{H-hl2|>q3`ada1(S*L48%&wd~0Hw~M6r;JD*jq#i@t}Daekb%>@{!>q>dP2Jm zU&o$(7nXtZJV8N0YHlXf6(%?Iyh+kAZp-MMZL9yD!#^u6TiQ2mZ|NKGbaVr%<0s2X z4~;)xyI}y-$v`2~P$Otu=-Evh%5l z%x%~Bd-8j*#KBHluJkn>_O#EvNqTQruroxi?RlMKSoj;b8|?spV=U-K72$iTYfI3t zXc^A2rnRq#^XtCiRw1LR{(I79U*oKO;;etu_m-ng18qZDrf~HA=J#4CnRfi4+cWG0 z{IzZ<>lai{C)}8pCC$TIpub`4EkfEnr`uN?ZHN`W#=&Nxwsv6}=^2j5~ z-jMO?d)~yK^FDgMpL6{Sdo9MAjmDVRn`?U7SNDwjHO9YJhQu_9H`aWyCX99A_Pzo> z@=5z~g?}>k!fK3aT0i+dvF_i$zq!tiemHnSOh%3zDcrBYy;s~z&i*FH9JIIdlzeb+ z55E`8GDWzW%Jn^Oz?y9?`T)y8Si9|&i*X>rYMmE8qfdKe?#~%9VuWyi3cvZu@!Og;Yh=TQ z4eHDa?)RWQCgp|uk7$eOE-zYcd0)@_hP#@`^wb%0UD^Vf+2b0S)N!heY&%-cZ+W4d z6ML?VYd=AjVqS=S|4j4;xxW7!s=`=cQR)(jg#Cy*>&NNj_g~whe7@wkR;kuXc&=t` z@#mSpjL#%xZ)OYY>!K-v*m&;7~$rj2{Hq11jht9m?JdEkb|n?zYz0_O^VzO_YVR49iVq08!B$vbtZ?mk-5DVNhbOPS_TX|pQ_dW1w9u@kRwRvlk+J}LB8ZV zBeP0R(@u)#`B&Ewdyc(W_o=r{`cn;=KwEXzA?ndKpfu2M&}0zhh3i%~f^G$^FzGJ% zd7y=%@*1S*lx86 zZ!7atZ$uw32Xh7(D{#$-eM7YIc5;`AnO&|?a)g4Ce;+r<0-ril4>A9w`Sa&j3xC#q zp2g|5kK&F~Ct2@L1R@9bv&p7_%ID?2fZ*(a2pd$ z>oiTh%k~ENZ?E@K)hq)OG#|JY$bAx3bv$pEdtvnZ@V^E{f_%kM22}e;8{X!@2{Nn4 z9P|l+e^25MJph}r%J82EyD}H_hs(hKbzdp8TRsyIrp{J4vOZU(AIP~Ij@$Kn@Jm3W zK)&Rt2X06GHDmyKASx(YF7JGmEQ4(zZG>qbUK#$Ev>StQD)tC*jNx$5rqWm4dSqN- z$usm%hClDI&9C4b_LV+bcvN4T={AAbbDoZNHoCu$_qTz<7?BUVS;+fg2mA9F$24&T$>AU}s0YTgQ+%8W z6Lp!nj?Og~)_2MQ&vWCqjosFE4&eE|K3?X^vfmOn*q=9L-6^qwF*2#$6>8lu7kVHI z>xpe6+bR6b=Z01mnU7qLLOxlaRhfBi9`WZGkKYo~`kp?X*$eVl>`^r3g7N=qkm3h* ze2^tg0mLi#XaiA)6ES)f!WX1bwEM_ z(Ff-Iv2pDuKVrx$_HvrPso(wq@};>wWY?pPW29$6o;2sB6ad$0q7t|V^5#$roT9raSMp$DgaR)8eMq~^gwVuh&*Eb=nL{EM>~xPpag#^ zl!wd0dNK%<>fv}Bqif_qBL^Be;LvL>_3Rhrme0VJW(b>KP+>nvxd7vt0yBtJ6y>aN zh!M7)H^Uq^E5|tD+{5f^+wWW9Wc9e5(D!Q?X8Wz2`M%e}gN^)J@mDcymCtf;T<>E(q^2j&slFPpii7eu5o8=I0V+HU7@7v`^Nwod`VBF8MU8pF$#dbdz&%1{U zZ1-_n+`~2iRE4k=P=##_Q3}u&raaqA40ioCY!h&GzpleJ2CMXI5O$h8HQvvI;>c4> zEelI&GWepynos4zD7O*bqVxcP*J1r!`EY@Gzd(rzYl?Bdz*Kxi=Jf(dxxx5k Star\n\n// Import the fonts\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('@{icon-font-path}@{icon-font-name}.eot');\n src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'),\n url('@{icon-font-path}@{icon-font-name}.woff2') format('woff2'),\n url('@{icon-font-path}@{icon-font-name}.woff') format('woff'),\n url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'),\n url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg');\n}\n\n// Catchall baseclass\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n// Individual icons\n.glyphicon-asterisk { &:before { content: \"\\002a\"; } }\n.glyphicon-plus { &:before { content: \"\\002b\"; } }\n.glyphicon-euro,\n.glyphicon-eur { &:before { content: \"\\20ac\"; } }\n.glyphicon-minus { &:before { content: \"\\2212\"; } }\n.glyphicon-cloud { &:before { content: \"\\2601\"; } }\n.glyphicon-envelope { &:before { content: \"\\2709\"; } }\n.glyphicon-pencil { &:before { content: \"\\270f\"; } }\n.glyphicon-glass { &:before { content: \"\\e001\"; } }\n.glyphicon-music { &:before { content: \"\\e002\"; } }\n.glyphicon-search { &:before { content: \"\\e003\"; } }\n.glyphicon-heart { &:before { content: \"\\e005\"; } }\n.glyphicon-star { &:before { content: \"\\e006\"; } }\n.glyphicon-star-empty { &:before { content: \"\\e007\"; } }\n.glyphicon-user { &:before { content: \"\\e008\"; } }\n.glyphicon-film { &:before { content: \"\\e009\"; } }\n.glyphicon-th-large { &:before { content: \"\\e010\"; } }\n.glyphicon-th { &:before { content: \"\\e011\"; } }\n.glyphicon-th-list { &:before { content: \"\\e012\"; } }\n.glyphicon-ok { &:before { content: \"\\e013\"; } }\n.glyphicon-remove { &:before { content: \"\\e014\"; } }\n.glyphicon-zoom-in { &:before { content: \"\\e015\"; } }\n.glyphicon-zoom-out { &:before { content: \"\\e016\"; } }\n.glyphicon-off { &:before { content: \"\\e017\"; } }\n.glyphicon-signal { &:before { content: \"\\e018\"; } }\n.glyphicon-cog { &:before { content: \"\\e019\"; } }\n.glyphicon-trash { &:before { content: \"\\e020\"; } }\n.glyphicon-home { &:before { content: \"\\e021\"; } }\n.glyphicon-file { &:before { content: \"\\e022\"; } }\n.glyphicon-time { &:before { content: \"\\e023\"; } }\n.glyphicon-road { &:before { content: \"\\e024\"; } }\n.glyphicon-download-alt { &:before { content: \"\\e025\"; } }\n.glyphicon-download { &:before { content: \"\\e026\"; } }\n.glyphicon-upload { &:before { content: \"\\e027\"; } }\n.glyphicon-inbox { &:before { content: \"\\e028\"; } }\n.glyphicon-play-circle { &:before { content: \"\\e029\"; } }\n.glyphicon-repeat { &:before { content: \"\\e030\"; } }\n.glyphicon-refresh { &:before { content: \"\\e031\"; } }\n.glyphicon-list-alt { &:before { content: \"\\e032\"; } }\n.glyphicon-lock { &:before { content: \"\\e033\"; } }\n.glyphicon-flag { &:before { content: \"\\e034\"; } }\n.glyphicon-headphones { &:before { content: \"\\e035\"; } }\n.glyphicon-volume-off { &:before { content: \"\\e036\"; } }\n.glyphicon-volume-down { &:before { content: \"\\e037\"; } }\n.glyphicon-volume-up { &:before { content: \"\\e038\"; } }\n.glyphicon-qrcode { &:before { content: \"\\e039\"; } }\n.glyphicon-barcode { &:before { content: \"\\e040\"; } }\n.glyphicon-tag { &:before { content: \"\\e041\"; } }\n.glyphicon-tags { &:before { content: \"\\e042\"; } }\n.glyphicon-book { &:before { content: \"\\e043\"; } }\n.glyphicon-bookmark { &:before { content: \"\\e044\"; } }\n.glyphicon-print { &:before { content: \"\\e045\"; } }\n.glyphicon-camera { &:before { content: \"\\e046\"; } }\n.glyphicon-font { &:before { content: \"\\e047\"; } }\n.glyphicon-bold { &:before { content: \"\\e048\"; } }\n.glyphicon-italic { &:before { content: \"\\e049\"; } }\n.glyphicon-text-height { &:before { content: \"\\e050\"; } }\n.glyphicon-text-width { &:before { content: \"\\e051\"; } }\n.glyphicon-align-left { &:before { content: \"\\e052\"; } }\n.glyphicon-align-center { &:before { content: \"\\e053\"; } }\n.glyphicon-align-right { &:before { content: \"\\e054\"; } }\n.glyphicon-align-justify { &:before { content: \"\\e055\"; } }\n.glyphicon-list { &:before { content: \"\\e056\"; } }\n.glyphicon-indent-left { &:before { content: \"\\e057\"; } }\n.glyphicon-indent-right { &:before { content: \"\\e058\"; } }\n.glyphicon-facetime-video { &:before { content: \"\\e059\"; } }\n.glyphicon-picture { &:before { content: \"\\e060\"; } }\n.glyphicon-map-marker { &:before { content: \"\\e062\"; } }\n.glyphicon-adjust { &:before { content: \"\\e063\"; } }\n.glyphicon-tint { &:before { content: \"\\e064\"; } }\n.glyphicon-edit { &:before { content: \"\\e065\"; } }\n.glyphicon-share { &:before { content: \"\\e066\"; } }\n.glyphicon-check { &:before { content: \"\\e067\"; } }\n.glyphicon-move { &:before { content: \"\\e068\"; } }\n.glyphicon-step-backward { &:before { content: \"\\e069\"; } }\n.glyphicon-fast-backward { &:before { content: \"\\e070\"; } }\n.glyphicon-backward { &:before { content: \"\\e071\"; } }\n.glyphicon-play { &:before { content: \"\\e072\"; } }\n.glyphicon-pause { &:before { content: \"\\e073\"; } }\n.glyphicon-stop { &:before { content: \"\\e074\"; } }\n.glyphicon-forward { &:before { content: \"\\e075\"; } }\n.glyphicon-fast-forward { &:before { content: \"\\e076\"; } }\n.glyphicon-step-forward { &:before { content: \"\\e077\"; } }\n.glyphicon-eject { &:before { content: \"\\e078\"; } }\n.glyphicon-chevron-left { &:before { content: \"\\e079\"; } }\n.glyphicon-chevron-right { &:before { content: \"\\e080\"; } }\n.glyphicon-plus-sign { &:before { content: \"\\e081\"; } }\n.glyphicon-minus-sign { &:before { content: \"\\e082\"; } }\n.glyphicon-remove-sign { &:before { content: \"\\e083\"; } }\n.glyphicon-ok-sign { &:before { content: \"\\e084\"; } }\n.glyphicon-question-sign { &:before { content: \"\\e085\"; } }\n.glyphicon-info-sign { &:before { content: \"\\e086\"; } }\n.glyphicon-screenshot { &:before { content: \"\\e087\"; } }\n.glyphicon-remove-circle { &:before { content: \"\\e088\"; } }\n.glyphicon-ok-circle { &:before { content: \"\\e089\"; } }\n.glyphicon-ban-circle { &:before { content: \"\\e090\"; } }\n.glyphicon-arrow-left { &:before { content: \"\\e091\"; } }\n.glyphicon-arrow-right { &:before { content: \"\\e092\"; } }\n.glyphicon-arrow-up { &:before { content: \"\\e093\"; } }\n.glyphicon-arrow-down { &:before { content: \"\\e094\"; } }\n.glyphicon-share-alt { &:before { content: \"\\e095\"; } }\n.glyphicon-resize-full { &:before { content: \"\\e096\"; } }\n.glyphicon-resize-small { &:before { content: \"\\e097\"; } }\n.glyphicon-exclamation-sign { &:before { content: \"\\e101\"; } }\n.glyphicon-gift { &:before { content: \"\\e102\"; } }\n.glyphicon-leaf { &:before { content: \"\\e103\"; } }\n.glyphicon-fire { &:before { content: \"\\e104\"; } }\n.glyphicon-eye-open { &:before { content: \"\\e105\"; } }\n.glyphicon-eye-close { &:before { content: \"\\e106\"; } }\n.glyphicon-warning-sign { &:before { content: \"\\e107\"; } }\n.glyphicon-plane { &:before { content: \"\\e108\"; } }\n.glyphicon-calendar { &:before { content: \"\\e109\"; } }\n.glyphicon-random { &:before { content: \"\\e110\"; } }\n.glyphicon-comment { &:before { content: \"\\e111\"; } }\n.glyphicon-magnet { &:before { content: \"\\e112\"; } }\n.glyphicon-chevron-up { &:before { content: \"\\e113\"; } }\n.glyphicon-chevron-down { &:before { content: \"\\e114\"; } }\n.glyphicon-retweet { &:before { content: \"\\e115\"; } }\n.glyphicon-shopping-cart { &:before { content: \"\\e116\"; } }\n.glyphicon-folder-close { &:before { content: \"\\e117\"; } }\n.glyphicon-folder-open { &:before { content: \"\\e118\"; } }\n.glyphicon-resize-vertical { &:before { content: \"\\e119\"; } }\n.glyphicon-resize-horizontal { &:before { content: \"\\e120\"; } }\n.glyphicon-hdd { &:before { content: \"\\e121\"; } }\n.glyphicon-bullhorn { &:before { content: \"\\e122\"; } }\n.glyphicon-bell { &:before { content: \"\\e123\"; } }\n.glyphicon-certificate { &:before { content: \"\\e124\"; } }\n.glyphicon-thumbs-up { &:before { content: \"\\e125\"; } }\n.glyphicon-thumbs-down { &:before { content: \"\\e126\"; } }\n.glyphicon-hand-right { &:before { content: \"\\e127\"; } }\n.glyphicon-hand-left { &:before { content: \"\\e128\"; } }\n.glyphicon-hand-up { &:before { content: \"\\e129\"; } }\n.glyphicon-hand-down { &:before { content: \"\\e130\"; } }\n.glyphicon-circle-arrow-right { &:before { content: \"\\e131\"; } }\n.glyphicon-circle-arrow-left { &:before { content: \"\\e132\"; } }\n.glyphicon-circle-arrow-up { &:before { content: \"\\e133\"; } }\n.glyphicon-circle-arrow-down { &:before { content: \"\\e134\"; } }\n.glyphicon-globe { &:before { content: \"\\e135\"; } }\n.glyphicon-wrench { &:before { content: \"\\e136\"; } }\n.glyphicon-tasks { &:before { content: \"\\e137\"; } }\n.glyphicon-filter { &:before { content: \"\\e138\"; } }\n.glyphicon-briefcase { &:before { content: \"\\e139\"; } }\n.glyphicon-fullscreen { &:before { content: \"\\e140\"; } }\n.glyphicon-dashboard { &:before { content: \"\\e141\"; } }\n.glyphicon-paperclip { &:before { content: \"\\e142\"; } }\n.glyphicon-heart-empty { &:before { content: \"\\e143\"; } }\n.glyphicon-link { &:before { content: \"\\e144\"; } }\n.glyphicon-phone { &:before { content: \"\\e145\"; } }\n.glyphicon-pushpin { &:before { content: \"\\e146\"; } }\n.glyphicon-usd { &:before { content: \"\\e148\"; } }\n.glyphicon-gbp { &:before { content: \"\\e149\"; } }\n.glyphicon-sort { &:before { content: \"\\e150\"; } }\n.glyphicon-sort-by-alphabet { &:before { content: \"\\e151\"; } }\n.glyphicon-sort-by-alphabet-alt { &:before { content: \"\\e152\"; } }\n.glyphicon-sort-by-order { &:before { content: \"\\e153\"; } }\n.glyphicon-sort-by-order-alt { &:before { content: \"\\e154\"; } }\n.glyphicon-sort-by-attributes { &:before { content: \"\\e155\"; } }\n.glyphicon-sort-by-attributes-alt { &:before { content: \"\\e156\"; } }\n.glyphicon-unchecked { &:before { content: \"\\e157\"; } }\n.glyphicon-expand { &:before { content: \"\\e158\"; } }\n.glyphicon-collapse-down { &:before { content: \"\\e159\"; } }\n.glyphicon-collapse-up { &:before { content: \"\\e160\"; } }\n.glyphicon-log-in { &:before { content: \"\\e161\"; } }\n.glyphicon-flash { &:before { content: \"\\e162\"; } }\n.glyphicon-log-out { &:before { content: \"\\e163\"; } }\n.glyphicon-new-window { &:before { content: \"\\e164\"; } }\n.glyphicon-record { &:before { content: \"\\e165\"; } }\n.glyphicon-save { &:before { content: \"\\e166\"; } }\n.glyphicon-open { &:before { content: \"\\e167\"; } }\n.glyphicon-saved { &:before { content: \"\\e168\"; } }\n.glyphicon-import { &:before { content: \"\\e169\"; } }\n.glyphicon-export { &:before { content: \"\\e170\"; } }\n.glyphicon-send { &:before { content: \"\\e171\"; } }\n.glyphicon-floppy-disk { &:before { content: \"\\e172\"; } }\n.glyphicon-floppy-saved { &:before { content: \"\\e173\"; } }\n.glyphicon-floppy-remove { &:before { content: \"\\e174\"; } }\n.glyphicon-floppy-save { &:before { content: \"\\e175\"; } }\n.glyphicon-floppy-open { &:before { content: \"\\e176\"; } }\n.glyphicon-credit-card { &:before { content: \"\\e177\"; } }\n.glyphicon-transfer { &:before { content: \"\\e178\"; } }\n.glyphicon-cutlery { &:before { content: \"\\e179\"; } }\n.glyphicon-header { &:before { content: \"\\e180\"; } }\n.glyphicon-compressed { &:before { content: \"\\e181\"; } }\n.glyphicon-earphone { &:before { content: \"\\e182\"; } }\n.glyphicon-phone-alt { &:before { content: \"\\e183\"; } }\n.glyphicon-tower { &:before { content: \"\\e184\"; } }\n.glyphicon-stats { &:before { content: \"\\e185\"; } }\n.glyphicon-sd-video { &:before { content: \"\\e186\"; } }\n.glyphicon-hd-video { &:before { content: \"\\e187\"; } }\n.glyphicon-subtitles { &:before { content: \"\\e188\"; } }\n.glyphicon-sound-stereo { &:before { content: \"\\e189\"; } }\n.glyphicon-sound-dolby { &:before { content: \"\\e190\"; } }\n.glyphicon-sound-5-1 { &:before { content: \"\\e191\"; } }\n.glyphicon-sound-6-1 { &:before { content: \"\\e192\"; } }\n.glyphicon-sound-7-1 { &:before { content: \"\\e193\"; } }\n.glyphicon-copyright-mark { &:before { content: \"\\e194\"; } }\n.glyphicon-registration-mark { &:before { content: \"\\e195\"; } }\n.glyphicon-cloud-download { &:before { content: \"\\e197\"; } }\n.glyphicon-cloud-upload { &:before { content: \"\\e198\"; } }\n.glyphicon-tree-conifer { &:before { content: \"\\e199\"; } }\n.glyphicon-tree-deciduous { &:before { content: \"\\e200\"; } }\n.glyphicon-cd { &:before { content: \"\\e201\"; } }\n.glyphicon-save-file { &:before { content: \"\\e202\"; } }\n.glyphicon-open-file { &:before { content: \"\\e203\"; } }\n.glyphicon-level-up { &:before { content: \"\\e204\"; } }\n.glyphicon-copy { &:before { content: \"\\e205\"; } }\n.glyphicon-paste { &:before { content: \"\\e206\"; } }\n// The following 2 Glyphicons are omitted for the time being because\n// they currently use Unicode codepoints that are outside the\n// Basic Multilingual Plane (BMP). Older buggy versions of WebKit can't handle\n// non-BMP codepoints in CSS string escapes, and thus can't display these two icons.\n// Notably, the bug affects some older versions of the Android Browser.\n// More info: https://github.com/twbs/bootstrap/issues/10106\n// .glyphicon-door { &:before { content: \"\\1f6aa\"; } }\n// .glyphicon-key { &:before { content: \"\\1f511\"; } }\n.glyphicon-alert { &:before { content: \"\\e209\"; } }\n.glyphicon-equalizer { &:before { content: \"\\e210\"; } }\n.glyphicon-king { &:before { content: \"\\e211\"; } }\n.glyphicon-queen { &:before { content: \"\\e212\"; } }\n.glyphicon-pawn { &:before { content: \"\\e213\"; } }\n.glyphicon-bishop { &:before { content: \"\\e214\"; } }\n.glyphicon-knight { &:before { content: \"\\e215\"; } }\n.glyphicon-baby-formula { &:before { content: \"\\e216\"; } }\n.glyphicon-tent { &:before { content: \"\\26fa\"; } }\n.glyphicon-blackboard { &:before { content: \"\\e218\"; } }\n.glyphicon-bed { &:before { content: \"\\e219\"; } }\n.glyphicon-apple { &:before { content: \"\\f8ff\"; } }\n.glyphicon-erase { &:before { content: \"\\e221\"; } }\n.glyphicon-hourglass { &:before { content: \"\\231b\"; } }\n.glyphicon-lamp { &:before { content: \"\\e223\"; } }\n.glyphicon-duplicate { &:before { content: \"\\e224\"; } }\n.glyphicon-piggy-bank { &:before { content: \"\\e225\"; } }\n.glyphicon-scissors { &:before { content: \"\\e226\"; } }\n.glyphicon-bitcoin { &:before { content: \"\\e227\"; } }\n.glyphicon-btc { &:before { content: \"\\e227\"; } }\n.glyphicon-xbt { &:before { content: \"\\e227\"; } }\n.glyphicon-yen { &:before { content: \"\\00a5\"; } }\n.glyphicon-jpy { &:before { content: \"\\00a5\"; } }\n.glyphicon-ruble { &:before { content: \"\\20bd\"; } }\n.glyphicon-rub { &:before { content: \"\\20bd\"; } }\n.glyphicon-scale { &:before { content: \"\\e230\"; } }\n.glyphicon-ice-lolly { &:before { content: \"\\e231\"; } }\n.glyphicon-ice-lolly-tasted { &:before { content: \"\\e232\"; } }\n.glyphicon-education { &:before { content: \"\\e233\"; } }\n.glyphicon-option-horizontal { &:before { content: \"\\e234\"; } }\n.glyphicon-option-vertical { &:before { content: \"\\e235\"; } }\n.glyphicon-menu-hamburger { &:before { content: \"\\e236\"; } }\n.glyphicon-modal-window { &:before { content: \"\\e237\"; } }\n.glyphicon-oil { &:before { content: \"\\e238\"; } }\n.glyphicon-grain { &:before { content: \"\\e239\"; } }\n.glyphicon-sunglasses { &:before { content: \"\\e240\"; } }\n.glyphicon-text-size { &:before { content: \"\\e241\"; } }\n.glyphicon-text-color { &:before { content: \"\\e242\"; } }\n.glyphicon-text-background { &:before { content: \"\\e243\"; } }\n.glyphicon-object-align-top { &:before { content: \"\\e244\"; } }\n.glyphicon-object-align-bottom { &:before { content: \"\\e245\"; } }\n.glyphicon-object-align-horizontal{ &:before { content: \"\\e246\"; } }\n.glyphicon-object-align-left { &:before { content: \"\\e247\"; } }\n.glyphicon-object-align-vertical { &:before { content: \"\\e248\"; } }\n.glyphicon-object-align-right { &:before { content: \"\\e249\"; } }\n.glyphicon-triangle-right { &:before { content: \"\\e250\"; } }\n.glyphicon-triangle-left { &:before { content: \"\\e251\"; } }\n.glyphicon-triangle-bottom { &:before { content: \"\\e252\"; } }\n.glyphicon-triangle-top { &:before { content: \"\\e253\"; } }\n.glyphicon-console { &:before { content: \"\\e254\"; } }\n.glyphicon-superscript { &:before { content: \"\\e255\"; } }\n.glyphicon-subscript { &:before { content: \"\\e256\"; } }\n.glyphicon-menu-left { &:before { content: \"\\e257\"; } }\n.glyphicon-menu-right { &:before { content: \"\\e258\"; } }\n.glyphicon-menu-down { &:before { content: \"\\e259\"; } }\n.glyphicon-menu-up { &:before { content: \"\\e260\"; } }\n","//\n// Scaffolding\n// --------------------------------------------------\n\n\n// Reset the box-sizing\n//\n// Heads up! This reset may cause conflicts with some third-party widgets.\n// For recommendations on resolving such conflicts, see\n// http://getbootstrap.com/getting-started/#third-box-sizing\n* {\n .box-sizing(border-box);\n}\n*:before,\n*:after {\n .box-sizing(border-box);\n}\n\n\n// Body reset\n\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0,0,0,0);\n}\n\nbody {\n font-family: @font-family-base;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @text-color;\n background-color: @body-bg;\n}\n\n// Reset fonts for relevant elements\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\n\n// Links\n\na {\n color: @link-color;\n text-decoration: none;\n\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: @link-hover-decoration;\n }\n\n &:focus {\n .tab-focus();\n }\n}\n\n\n// Figures\n//\n// We reset this here because previously Normalize had no `figure` margins. This\n// ensures we don't break anyone's use of the element.\n\nfigure {\n margin: 0;\n}\n\n\n// Images\n\nimg {\n vertical-align: middle;\n}\n\n// Responsive images (ensure images don't scale beyond their parents)\n.img-responsive {\n .img-responsive();\n}\n\n// Rounded corners\n.img-rounded {\n border-radius: @border-radius-large;\n}\n\n// Image thumbnails\n//\n// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.\n.img-thumbnail {\n padding: @thumbnail-padding;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(all .2s ease-in-out);\n\n // Keep them at most 100% wide\n .img-responsive(inline-block);\n}\n\n// Perfect circle\n.img-circle {\n border-radius: 50%; // set radius in percents\n}\n\n\n// Horizontal rules\n\nhr {\n margin-top: @line-height-computed;\n margin-bottom: @line-height-computed;\n border: 0;\n border-top: 1px solid @hr-border;\n}\n\n\n// Only display content to screen readers\n//\n// See: http://a11yproject.com/posts/how-to-hide-content/\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0,0,0,0);\n border: 0;\n}\n\n// Use in conjunction with .sr-only to only display content when it's focused.\n// Useful for \"Skip to main content\" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1\n// Credit: HTML5 Boilerplate\n\n.sr-only-focusable {\n &:active,\n &:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n }\n}\n\n\n// iOS \"clickable elements\" fix for role=\"button\"\n//\n// Fixes \"clickability\" issue (and more generally, the firing of events such as focus as well)\n// for traditionally non-focusable elements with role=\"button\"\n// see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile\n\n[role=\"button\"] {\n cursor: pointer;\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// WebKit-style focus\n\n.tab-focus() {\n // Default\n outline: thin dotted;\n // WebKit\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n.img-responsive(@display: block) {\n display: @display;\n max-width: 100%; // Part 1: Set a maximum relative to the parent\n height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size. Note that the\n// spelling of `min--moz-device-pixel-ratio` is intentional.\n.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {\n background-image: url(\"@{file-1x}\");\n\n @media\n only screen and (-webkit-min-device-pixel-ratio: 2),\n only screen and ( min--moz-device-pixel-ratio: 2),\n only screen and ( -o-min-device-pixel-ratio: 2/1),\n only screen and ( min-device-pixel-ratio: 2),\n only screen and ( min-resolution: 192dpi),\n only screen and ( min-resolution: 2dppx) {\n background-image: url(\"@{file-2x}\");\n background-size: @width-1x @height-1x;\n }\n}\n","//\n// Typography\n// --------------------------------------------------\n\n\n// Headings\n// -------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n font-family: @headings-font-family;\n font-weight: @headings-font-weight;\n line-height: @headings-line-height;\n color: @headings-color;\n\n small,\n .small {\n font-weight: normal;\n line-height: 1;\n color: @headings-small-color;\n }\n}\n\nh1, .h1,\nh2, .h2,\nh3, .h3 {\n margin-top: @line-height-computed;\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 65%;\n }\n}\nh4, .h4,\nh5, .h5,\nh6, .h6 {\n margin-top: (@line-height-computed / 2);\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 75%;\n }\n}\n\nh1, .h1 { font-size: @font-size-h1; }\nh2, .h2 { font-size: @font-size-h2; }\nh3, .h3 { font-size: @font-size-h3; }\nh4, .h4 { font-size: @font-size-h4; }\nh5, .h5 { font-size: @font-size-h5; }\nh6, .h6 { font-size: @font-size-h6; }\n\n\n// Body text\n// -------------------------\n\np {\n margin: 0 0 (@line-height-computed / 2);\n}\n\n.lead {\n margin-bottom: @line-height-computed;\n font-size: floor((@font-size-base * 1.15));\n font-weight: 300;\n line-height: 1.4;\n\n @media (min-width: @screen-sm-min) {\n font-size: (@font-size-base * 1.5);\n }\n}\n\n\n// Emphasis & misc\n// -------------------------\n\n// Ex: (12px small font / 14px base font) * 100% = about 85%\nsmall,\n.small {\n font-size: floor((100% * @font-size-small / @font-size-base));\n}\n\nmark,\n.mark {\n background-color: @state-warning-bg;\n padding: .2em;\n}\n\n// Alignment\n.text-left { text-align: left; }\n.text-right { text-align: right; }\n.text-center { text-align: center; }\n.text-justify { text-align: justify; }\n.text-nowrap { white-space: nowrap; }\n\n// Transformation\n.text-lowercase { text-transform: lowercase; }\n.text-uppercase { text-transform: uppercase; }\n.text-capitalize { text-transform: capitalize; }\n\n// Contextual colors\n.text-muted {\n color: @text-muted;\n}\n.text-primary {\n .text-emphasis-variant(@brand-primary);\n}\n.text-success {\n .text-emphasis-variant(@state-success-text);\n}\n.text-info {\n .text-emphasis-variant(@state-info-text);\n}\n.text-warning {\n .text-emphasis-variant(@state-warning-text);\n}\n.text-danger {\n .text-emphasis-variant(@state-danger-text);\n}\n\n// Contextual backgrounds\n// For now we'll leave these alongside the text classes until v4 when we can\n// safely shift things around (per SemVer rules).\n.bg-primary {\n // Given the contrast here, this is the only class to have its color inverted\n // automatically.\n color: #fff;\n .bg-variant(@brand-primary);\n}\n.bg-success {\n .bg-variant(@state-success-bg);\n}\n.bg-info {\n .bg-variant(@state-info-bg);\n}\n.bg-warning {\n .bg-variant(@state-warning-bg);\n}\n.bg-danger {\n .bg-variant(@state-danger-bg);\n}\n\n\n// Page header\n// -------------------------\n\n.page-header {\n padding-bottom: ((@line-height-computed / 2) - 1);\n margin: (@line-height-computed * 2) 0 @line-height-computed;\n border-bottom: 1px solid @page-header-border-color;\n}\n\n\n// Lists\n// -------------------------\n\n// Unordered and Ordered lists\nul,\nol {\n margin-top: 0;\n margin-bottom: (@line-height-computed / 2);\n ul,\n ol {\n margin-bottom: 0;\n }\n}\n\n// List options\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n .list-unstyled();\n margin-left: -5px;\n\n > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n }\n}\n\n// Description Lists\ndl {\n margin-top: 0; // Remove browser default\n margin-bottom: @line-height-computed;\n}\ndt,\ndd {\n line-height: @line-height-base;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0; // Undo browser default\n}\n\n// Horizontal description lists\n//\n// Defaults to being stacked without any of the below styles applied, until the\n// grid breakpoint is reached (default of ~768px).\n\n.dl-horizontal {\n dd {\n &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present\n }\n\n @media (min-width: @dl-horizontal-breakpoint) {\n dt {\n float: left;\n width: (@dl-horizontal-offset - 20);\n clear: left;\n text-align: right;\n .text-overflow();\n }\n dd {\n margin-left: @dl-horizontal-offset;\n }\n }\n}\n\n\n// Misc\n// -------------------------\n\n// Abbreviations and acronyms\nabbr[title],\n// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted @abbr-border-color;\n}\n.initialism {\n font-size: 90%;\n .text-uppercase();\n}\n\n// Blockquotes\nblockquote {\n padding: (@line-height-computed / 2) @line-height-computed;\n margin: 0 0 @line-height-computed;\n font-size: @blockquote-font-size;\n border-left: 5px solid @blockquote-border-color;\n\n p,\n ul,\n ol {\n &:last-child {\n margin-bottom: 0;\n }\n }\n\n // Note: Deprecated small and .small as of v3.1.0\n // Context: https://github.com/twbs/bootstrap/issues/11660\n footer,\n small,\n .small {\n display: block;\n font-size: 80%; // back to default font-size\n line-height: @line-height-base;\n color: @blockquote-small-color;\n\n &:before {\n content: '\\2014 \\00A0'; // em dash, nbsp\n }\n }\n}\n\n// Opposite alignment of blockquote\n//\n// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid @blockquote-border-color;\n border-left: 0;\n text-align: right;\n\n // Account for citation\n footer,\n small,\n .small {\n &:before { content: ''; }\n &:after {\n content: '\\00A0 \\2014'; // nbsp, em dash\n }\n }\n}\n\n// Addresses\naddress {\n margin-bottom: @line-height-computed;\n font-style: normal;\n line-height: @line-height-base;\n}\n","// Typography\n\n.text-emphasis-variant(@color) {\n color: @color;\n a&:hover,\n a&:focus {\n color: darken(@color, 10%);\n }\n}\n","// Contextual backgrounds\n\n.bg-variant(@color) {\n background-color: @color;\n a&:hover,\n a&:focus {\n background-color: darken(@color, 10%);\n }\n}\n","// Text overflow\n// Requires inline-block or block for proper styling\n\n.text-overflow() {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n","//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n font-family: @font-family-monospace;\n}\n\n// Inline code\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: @code-color;\n background-color: @code-bg;\n border-radius: @border-radius-base;\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: @kbd-color;\n background-color: @kbd-bg;\n border-radius: @border-radius-small;\n box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);\n\n kbd {\n padding: 0;\n font-size: 100%;\n font-weight: bold;\n box-shadow: none;\n }\n}\n\n// Blocks of code\npre {\n display: block;\n padding: ((@line-height-computed - 1) / 2);\n margin: 0 0 (@line-height-computed / 2);\n font-size: (@font-size-base - 1); // 14px to 13px\n line-height: @line-height-base;\n word-break: break-all;\n word-wrap: break-word;\n color: @pre-color;\n background-color: @pre-bg;\n border: 1px solid @pre-border-color;\n border-radius: @border-radius-base;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: @pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","//\n// Grid system\n// --------------------------------------------------\n\n\n// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n.container {\n .container-fixed();\n\n @media (min-width: @screen-sm-min) {\n width: @container-sm;\n }\n @media (min-width: @screen-md-min) {\n width: @container-md;\n }\n @media (min-width: @screen-lg-min) {\n width: @container-lg;\n }\n}\n\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but without any defined\n// width for fluid, full width layouts.\n\n.container-fluid {\n .container-fixed();\n}\n\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n.row {\n .make-row();\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n.make-grid-columns();\n\n\n// Extra small grid\n//\n// Columns, offsets, pushes, and pulls for extra small devices like\n// smartphones.\n\n.make-grid(xs);\n\n\n// Small grid\n//\n// Columns, offsets, pushes, and pulls for the small device range, from phones\n// to tablets.\n\n@media (min-width: @screen-sm-min) {\n .make-grid(sm);\n}\n\n\n// Medium grid\n//\n// Columns, offsets, pushes, and pulls for the desktop device range.\n\n@media (min-width: @screen-md-min) {\n .make-grid(md);\n}\n\n\n// Large grid\n//\n// Columns, offsets, pushes, and pulls for the large desktop device range.\n\n@media (min-width: @screen-lg-min) {\n .make-grid(lg);\n}\n","// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n// Centered container element\n.container-fixed(@gutter: @grid-gutter-width) {\n margin-right: auto;\n margin-left: auto;\n padding-left: floor((@gutter / 2));\n padding-right: ceil((@gutter / 2));\n &:extend(.clearfix all);\n}\n\n// Creates a wrapper for a series of columns\n.make-row(@gutter: @grid-gutter-width) {\n margin-left: ceil((@gutter / -2));\n margin-right: floor((@gutter / -2));\n &:extend(.clearfix all);\n}\n\n// Generate the extra small columns\n.make-xs-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n float: left;\n width: percentage((@columns / @grid-columns));\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n}\n.make-xs-column-offset(@columns) {\n margin-left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-push(@columns) {\n left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-pull(@columns) {\n right: percentage((@columns / @grid-columns));\n}\n\n// Generate the small columns\n.make-sm-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-sm-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-offset(@columns) {\n @media (min-width: @screen-sm-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-push(@columns) {\n @media (min-width: @screen-sm-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-pull(@columns) {\n @media (min-width: @screen-sm-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the medium columns\n.make-md-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-md-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-offset(@columns) {\n @media (min-width: @screen-md-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-push(@columns) {\n @media (min-width: @screen-md-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-pull(@columns) {\n @media (min-width: @screen-md-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the large columns\n.make-lg-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-lg-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-offset(@columns) {\n @media (min-width: @screen-lg-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-push(@columns) {\n @media (min-width: @screen-lg-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-pull(@columns) {\n @media (min-width: @screen-lg-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `@grid-columns`.\n\n.make-grid-columns() {\n // Common styles for all sizes of grid columns, widths 1-12\n .col(@index) { // initial\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general; \"=<\" isn't a typo\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n position: relative;\n // Prevent columns from collapsing when empty\n min-height: 1px;\n // Inner gutter via padding\n padding-left: ceil((@grid-gutter-width / 2));\n padding-right: floor((@grid-gutter-width / 2));\n }\n }\n .col(1); // kickstart it\n}\n\n.float-grid-columns(@class) {\n .col(@index) { // initial\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n float: left;\n }\n }\n .col(1); // kickstart it\n}\n\n.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {\n .col-@{class}-@{index} {\n width: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {\n .col-@{class}-push-@{index} {\n left: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {\n .col-@{class}-push-0 {\n left: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {\n .col-@{class}-pull-@{index} {\n right: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {\n .col-@{class}-pull-0 {\n right: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = offset) {\n .col-@{class}-offset-@{index} {\n margin-left: percentage((@index / @grid-columns));\n }\n}\n\n// Basic looping in LESS\n.loop-grid-columns(@index, @class, @type) when (@index >= 0) {\n .calc-grid-column(@index, @class, @type);\n // next iteration\n .loop-grid-columns((@index - 1), @class, @type);\n}\n\n// Create grid for specific class\n.make-grid(@class) {\n .float-grid-columns(@class);\n .loop-grid-columns(@grid-columns, @class, width);\n .loop-grid-columns(@grid-columns, @class, pull);\n .loop-grid-columns(@grid-columns, @class, push);\n .loop-grid-columns(@grid-columns, @class, offset);\n}\n","//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n background-color: @table-bg;\n}\ncaption {\n padding-top: @table-cell-padding;\n padding-bottom: @table-cell-padding;\n color: @text-muted;\n text-align: left;\n}\nth {\n text-align: left;\n}\n\n\n// Baseline styles\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: @line-height-computed;\n // Cells\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-cell-padding;\n line-height: @line-height-base;\n vertical-align: top;\n border-top: 1px solid @table-border-color;\n }\n }\n }\n // Bottom align for column headings\n > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid @table-border-color;\n }\n // Remove top border from thead by default\n > caption + thead,\n > colgroup + thead,\n > thead:first-child {\n > tr:first-child {\n > th,\n > td {\n border-top: 0;\n }\n }\n }\n // Account for multiple tbody instances\n > tbody + tbody {\n border-top: 2px solid @table-border-color;\n }\n\n // Nesting\n .table {\n background-color: @body-bg;\n }\n}\n\n\n// Condensed table w/ half padding\n\n.table-condensed {\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-condensed-cell-padding;\n }\n }\n }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n border: 1px solid @table-border-color;\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n border: 1px solid @table-border-color;\n }\n }\n }\n > thead > tr {\n > th,\n > td {\n border-bottom-width: 2px;\n }\n }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n > tbody > tr:nth-of-type(odd) {\n background-color: @table-bg-accent;\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n > tbody > tr:hover {\n background-color: @table-bg-hover;\n }\n}\n\n\n// Table cell sizing\n//\n// Reset default table behavior\n\ntable col[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-column;\n}\ntable {\n td,\n th {\n &[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-cell;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n// Generate the contextual variants\n.table-row-variant(active; @table-bg-active);\n.table-row-variant(success; @state-success-bg);\n.table-row-variant(info; @state-info-bg);\n.table-row-variant(warning; @state-warning-bg);\n.table-row-variant(danger; @state-danger-bg);\n\n\n// Responsive tables\n//\n// Wrap your tables in `.table-responsive` and we'll make them mobile friendly\n// by enabling horizontal scrolling. Only applies <768px. Everything above that\n// will display normally.\n\n.table-responsive {\n overflow-x: auto;\n min-height: 0.01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837)\n\n @media screen and (max-width: @screen-xs-max) {\n width: 100%;\n margin-bottom: (@line-height-computed * 0.75);\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid @table-border-color;\n\n // Tighten up spacing\n > .table {\n margin-bottom: 0;\n\n // Ensure the content doesn't wrap\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n white-space: nowrap;\n }\n }\n }\n }\n\n // Special overrides for the bordered tables\n > .table-bordered {\n border: 0;\n\n // Nuke the appropriate borders so that the parent can handle them\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th:first-child,\n > td:first-child {\n border-left: 0;\n }\n > th:last-child,\n > td:last-child {\n border-right: 0;\n }\n }\n }\n\n // Only nuke the last row's bottom-border in `tbody` and `tfoot` since\n // chances are there will be only one `tr` in a `thead` and that would\n // remove the border altogether.\n > tbody,\n > tfoot {\n > tr:last-child {\n > th,\n > td {\n border-bottom: 0;\n }\n }\n }\n\n }\n }\n}\n","// Tables\n\n.table-row-variant(@state; @background) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table > thead > tr,\n .table > tbody > tr,\n .table > tfoot > tr {\n > td.@{state},\n > th.@{state},\n &.@{state} > td,\n &.@{state} > th {\n background-color: @background;\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover > tbody > tr {\n > td.@{state}:hover,\n > th.@{state}:hover,\n &.@{state}:hover > td,\n &:hover > .@{state},\n &.@{state}:hover > th {\n background-color: darken(@background, 5%);\n }\n }\n}\n","//\n// Forms\n// --------------------------------------------------\n\n\n// Normalize non-controls\n//\n// Restyle and baseline non-control form elements.\n\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n // Chrome and Firefox set a `min-width: min-content;` on fieldsets,\n // so we reset that to ensure it behaves more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359.\n min-width: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: @line-height-computed;\n font-size: (@font-size-base * 1.5);\n line-height: inherit;\n color: @legend-color;\n border: 0;\n border-bottom: 1px solid @legend-border-color;\n}\n\nlabel {\n display: inline-block;\n max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141)\n margin-bottom: 5px;\n font-weight: bold;\n}\n\n\n// Normalize form controls\n//\n// While most of our form styles require extra classes, some basic normalization\n// is required to ensure optimum display with or without those classes to better\n// address browser inconsistencies.\n\n// Override content-box in Normalize (* isn't specific enough)\ninput[type=\"search\"] {\n .box-sizing(border-box);\n}\n\n// Position radios and checkboxes better\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9; // IE8-9\n line-height: normal;\n}\n\ninput[type=\"file\"] {\n display: block;\n}\n\n// Make range inputs behave like textual form controls\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\n\n// Make multiple select elements height not fixed\nselect[multiple],\nselect[size] {\n height: auto;\n}\n\n// Focus for file, radio, and checkbox\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n .tab-focus();\n}\n\n// Adjust output element\noutput {\n display: block;\n padding-top: (@padding-base-vertical + 1);\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n}\n\n\n// Common form controls\n//\n// Shared size and type resets for form controls. Apply `.form-control` to any\n// of the following form controls:\n//\n// select\n// textarea\n// input[type=\"text\"]\n// input[type=\"password\"]\n// input[type=\"datetime\"]\n// input[type=\"datetime-local\"]\n// input[type=\"date\"]\n// input[type=\"month\"]\n// input[type=\"time\"]\n// input[type=\"week\"]\n// input[type=\"number\"]\n// input[type=\"email\"]\n// input[type=\"url\"]\n// input[type=\"search\"]\n// input[type=\"tel\"]\n// input[type=\"color\"]\n\n.form-control {\n display: block;\n width: 100%;\n height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n background-color: @input-bg;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid @input-border;\n border-radius: @input-border-radius; // Note: This has no effect on s in CSS.\n .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));\n .transition(~\"border-color ease-in-out .15s, box-shadow ease-in-out .15s\");\n\n // Customize the `:focus` state to imitate native WebKit styles.\n .form-control-focus();\n\n // Placeholder\n .placeholder();\n\n // Unstyle the caret on ``\n// element gets special love because it's special, and that's a fact!\n.input-size(@input-height; @padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n height: @input-height;\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n border-radius: @border-radius;\n\n select& {\n height: @input-height;\n line-height: @input-height;\n }\n\n textarea&,\n select[multiple]& {\n height: auto;\n }\n}\n","//\n// Buttons\n// --------------------------------------------------\n\n\n// Base styles\n// --------------------------------------------------\n\n.btn {\n display: inline-block;\n margin-bottom: 0; // For input.btn\n font-weight: @btn-font-weight;\n text-align: center;\n vertical-align: middle;\n touch-action: manipulation;\n cursor: pointer;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid transparent;\n white-space: nowrap;\n .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @btn-border-radius-base);\n .user-select(none);\n\n &,\n &:active,\n &.active {\n &:focus,\n &.focus {\n .tab-focus();\n }\n }\n\n &:hover,\n &:focus,\n &.focus {\n color: @btn-default-color;\n text-decoration: none;\n }\n\n &:active,\n &.active {\n outline: 0;\n background-image: none;\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n cursor: @cursor-disabled;\n .opacity(.65);\n .box-shadow(none);\n }\n\n a& {\n &.disabled,\n fieldset[disabled] & {\n pointer-events: none; // Future-proof disabling of clicks on `` elements\n }\n }\n}\n\n\n// Alternate buttons\n// --------------------------------------------------\n\n.btn-default {\n .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border);\n}\n.btn-primary {\n .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border);\n}\n// Success appears as green\n.btn-success {\n .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border);\n}\n// Info appears as blue-green\n.btn-info {\n .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border);\n}\n// Warning appears as orange\n.btn-warning {\n .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border);\n}\n// Danger and error appear as red\n.btn-danger {\n .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border);\n}\n\n\n// Link buttons\n// -------------------------\n\n// Make a button look and behave like a link\n.btn-link {\n color: @link-color;\n font-weight: normal;\n border-radius: 0;\n\n &,\n &:active,\n &.active,\n &[disabled],\n fieldset[disabled] & {\n background-color: transparent;\n .box-shadow(none);\n }\n &,\n &:hover,\n &:focus,\n &:active {\n border-color: transparent;\n }\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: @link-hover-decoration;\n background-color: transparent;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @btn-link-disabled-color;\n text-decoration: none;\n }\n }\n}\n\n\n// Button Sizes\n// --------------------------------------------------\n\n.btn-lg {\n // line-height: ensure even-numbered height of button next to large input\n .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @btn-border-radius-large);\n}\n.btn-sm {\n // line-height: ensure proper height of button next to small input\n .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small);\n}\n.btn-xs {\n .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small);\n}\n\n\n// Block button\n// --------------------------------------------------\n\n.btn-block {\n display: block;\n width: 100%;\n}\n\n// Vertically space out multiple block buttons\n.btn-block + .btn-block {\n margin-top: 5px;\n}\n\n// Specificity overrides\ninput[type=\"submit\"],\ninput[type=\"reset\"],\ninput[type=\"button\"] {\n &.btn-block {\n width: 100%;\n }\n}\n","// Button variants\n//\n// Easily pump out default styles, as well as :hover, :focus, :active,\n// and disabled options for all buttons\n\n.button-variant(@color; @background; @border) {\n color: @color;\n background-color: @background;\n border-color: @border;\n\n &:focus,\n &.focus {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 25%);\n }\n &:hover {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 12%);\n }\n &:active,\n &.active,\n .open > .dropdown-toggle& {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 12%);\n\n &:hover,\n &:focus,\n &.focus {\n color: @color;\n background-color: darken(@background, 17%);\n border-color: darken(@border, 25%);\n }\n }\n &:active,\n &.active,\n .open > .dropdown-toggle& {\n background-image: none;\n }\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus,\n &.focus {\n background-color: @background;\n border-color: @border;\n }\n }\n\n .badge {\n color: @background;\n background-color: @color;\n }\n}\n\n// Button sizes\n.button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n border-radius: @border-radius;\n}\n","// Opacity\n\n.opacity(@opacity) {\n opacity: @opacity;\n // IE8 filter\n @opacity-ie: (@opacity * 100);\n filter: ~\"alpha(opacity=@{opacity-ie})\";\n}\n","//\n// Component animations\n// --------------------------------------------------\n\n// Heads up!\n//\n// We don't use the `.opacity()` mixin here since it causes a bug with text\n// fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552.\n\n.fade {\n opacity: 0;\n .transition(opacity .15s linear);\n &.in {\n opacity: 1;\n }\n}\n\n.collapse {\n display: none;\n\n &.in { display: block; }\n tr&.in { display: table-row; }\n tbody&.in { display: table-row-group; }\n}\n\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n .transition-property(~\"height, visibility\");\n .transition-duration(.35s);\n .transition-timing-function(ease);\n}\n","//\n// Dropdown menus\n// --------------------------------------------------\n\n\n// Dropdown arrow/caret\n.caret {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 2px;\n vertical-align: middle;\n border-top: @caret-width-base dashed;\n border-top: @caret-width-base solid ~\"\\9\"; // IE8\n border-right: @caret-width-base solid transparent;\n border-left: @caret-width-base solid transparent;\n}\n\n// The dropdown wrapper (div)\n.dropup,\n.dropdown {\n position: relative;\n}\n\n// Prevent the focus on the dropdown toggle when closing dropdowns\n.dropdown-toggle:focus {\n outline: 0;\n}\n\n// The dropdown menu (ul)\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: @zindex-dropdown;\n display: none; // none by default, but block on \"open\" of the menu\n float: left;\n min-width: 160px;\n padding: 5px 0;\n margin: 2px 0 0; // override default ul\n list-style: none;\n font-size: @font-size-base;\n text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)\n background-color: @dropdown-bg;\n border: 1px solid @dropdown-fallback-border; // IE8 fallback\n border: 1px solid @dropdown-border;\n border-radius: @border-radius-base;\n .box-shadow(0 6px 12px rgba(0,0,0,.175));\n background-clip: padding-box;\n\n // Aligns the dropdown menu to right\n //\n // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]`\n &.pull-right {\n right: 0;\n left: auto;\n }\n\n // Dividers (basically an hr) within the dropdown\n .divider {\n .nav-divider(@dropdown-divider-bg);\n }\n\n // Links within the dropdown menu\n > li > a {\n display: block;\n padding: 3px 20px;\n clear: both;\n font-weight: normal;\n line-height: @line-height-base;\n color: @dropdown-link-color;\n white-space: nowrap; // prevent links from randomly breaking onto new lines\n }\n}\n\n// Hover/Focus state\n.dropdown-menu > li > a {\n &:hover,\n &:focus {\n text-decoration: none;\n color: @dropdown-link-hover-color;\n background-color: @dropdown-link-hover-bg;\n }\n}\n\n// Active state\n.dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: @dropdown-link-active-color;\n text-decoration: none;\n outline: 0;\n background-color: @dropdown-link-active-bg;\n }\n}\n\n// Disabled state\n//\n// Gray out text and ensure the hover/focus state remains gray\n\n.dropdown-menu > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @dropdown-link-disabled-color;\n }\n\n // Nuke hover/focus effects\n &:hover,\n &:focus {\n text-decoration: none;\n background-color: transparent;\n background-image: none; // Remove CSS gradient\n .reset-filter();\n cursor: @cursor-disabled;\n }\n}\n\n// Open state for the dropdown\n.open {\n // Show the menu\n > .dropdown-menu {\n display: block;\n }\n\n // Remove the outline when :focus is triggered\n > a {\n outline: 0;\n }\n}\n\n// Menu positioning\n//\n// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown\n// menu with the parent.\n.dropdown-menu-right {\n left: auto; // Reset the default from `.dropdown-menu`\n right: 0;\n}\n// With v3, we enabled auto-flipping if you have a dropdown within a right\n// aligned nav component. To enable the undoing of that, we provide an override\n// to restore the default dropdown menu alignment.\n//\n// This is only for left-aligning a dropdown menu within a `.navbar-right` or\n// `.pull-right` nav component.\n.dropdown-menu-left {\n left: 0;\n right: auto;\n}\n\n// Dropdown section headers\n.dropdown-header {\n display: block;\n padding: 3px 20px;\n font-size: @font-size-small;\n line-height: @line-height-base;\n color: @dropdown-header-color;\n white-space: nowrap; // as with > li > a\n}\n\n// Backdrop to catch body clicks on mobile, etc.\n.dropdown-backdrop {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n top: 0;\n z-index: (@zindex-dropdown - 10);\n}\n\n// Right aligned dropdowns\n.pull-right > .dropdown-menu {\n right: 0;\n left: auto;\n}\n\n// Allow for dropdowns to go bottom up (aka, dropup-menu)\n//\n// Just add .dropup after the standard .dropdown class and you're set, bro.\n// TODO: abstract this so that the navbar fixed styles are not placed here?\n\n.dropup,\n.navbar-fixed-bottom .dropdown {\n // Reverse the caret\n .caret {\n border-top: 0;\n border-bottom: @caret-width-base dashed;\n border-bottom: @caret-width-base solid ~\"\\9\"; // IE8\n content: \"\";\n }\n // Different positioning for bottom up menu\n .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-bottom: 2px;\n }\n}\n\n\n// Component alignment\n//\n// Reiterate per navbar.less and the modified component alignment there.\n\n@media (min-width: @grid-float-breakpoint) {\n .navbar-right {\n .dropdown-menu {\n .dropdown-menu-right();\n }\n // Necessary for overrides of the default right aligned menu.\n // Will remove come v4 in all likelihood.\n .dropdown-menu-left {\n .dropdown-menu-left();\n }\n }\n}\n","// Horizontal dividers\n//\n// Dividers (basically an hr) within dropdowns and nav lists\n\n.nav-divider(@color: #e5e5e5) {\n height: 1px;\n margin: ((@line-height-computed / 2) - 1) 0;\n overflow: hidden;\n background-color: @color;\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n","//\n// Button groups\n// --------------------------------------------------\n\n// Make the div behave like a button\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-block;\n vertical-align: middle; // match .btn alignment given font-size hack above\n > .btn {\n position: relative;\n float: left;\n // Bring the \"active\" button to the front\n &:hover,\n &:focus,\n &:active,\n &.active {\n z-index: 2;\n }\n }\n}\n\n// Prevent double borders when buttons are next to each other\n.btn-group {\n .btn + .btn,\n .btn + .btn-group,\n .btn-group + .btn,\n .btn-group + .btn-group {\n margin-left: -1px;\n }\n}\n\n// Optional: Group multiple button groups together for a toolbar\n.btn-toolbar {\n margin-left: -5px; // Offset the first child's margin\n &:extend(.clearfix all);\n\n .btn,\n .btn-group,\n .input-group {\n float: left;\n }\n > .btn,\n > .btn-group,\n > .input-group {\n margin-left: 5px;\n }\n}\n\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n border-radius: 0;\n}\n\n// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match\n.btn-group > .btn:first-child {\n margin-left: 0;\n &:not(:last-child):not(.dropdown-toggle) {\n .border-right-radius(0);\n }\n}\n// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n .border-left-radius(0);\n}\n\n// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group)\n.btn-group > .btn-group {\n float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) {\n > .btn:last-child,\n > .dropdown-toggle {\n .border-right-radius(0);\n }\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n .border-left-radius(0);\n}\n\n// On active and open, don't show outline\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n outline: 0;\n}\n\n\n// Sizing\n//\n// Remix the default button sizing classes into new ones for easier manipulation.\n\n.btn-group-xs > .btn { &:extend(.btn-xs); }\n.btn-group-sm > .btn { &:extend(.btn-sm); }\n.btn-group-lg > .btn { &:extend(.btn-lg); }\n\n\n// Split button dropdowns\n// ----------------------\n\n// Give the line between buttons some depth\n.btn-group > .btn + .dropdown-toggle {\n padding-left: 8px;\n padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n// The clickable button for toggling the menu\n// Remove the gradient and set the same inset shadow as the :active state\n.btn-group.open .dropdown-toggle {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n\n // Show no shadow for `.btn-link` since it has no other button styles.\n &.btn-link {\n .box-shadow(none);\n }\n}\n\n\n// Reposition the caret\n.btn .caret {\n margin-left: 0;\n}\n// Carets in other button sizes\n.btn-lg .caret {\n border-width: @caret-width-large @caret-width-large 0;\n border-bottom-width: 0;\n}\n// Upside down carets for .dropup\n.dropup .btn-lg .caret {\n border-width: 0 @caret-width-large @caret-width-large;\n}\n\n\n// Vertical button groups\n// ----------------------\n\n.btn-group-vertical {\n > .btn,\n > .btn-group,\n > .btn-group > .btn {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n }\n\n // Clear floats so dropdown menus can be properly placed\n > .btn-group {\n &:extend(.clearfix all);\n > .btn {\n float: none;\n }\n }\n\n > .btn + .btn,\n > .btn + .btn-group,\n > .btn-group + .btn,\n > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n }\n}\n\n.btn-group-vertical > .btn {\n &:not(:first-child):not(:last-child) {\n border-radius: 0;\n }\n &:first-child:not(:last-child) {\n .border-top-radius(@btn-border-radius-base);\n .border-bottom-radius(0);\n }\n &:last-child:not(:first-child) {\n .border-top-radius(0);\n .border-bottom-radius(@btn-border-radius-base);\n }\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) {\n > .btn:last-child,\n > .dropdown-toggle {\n .border-bottom-radius(0);\n }\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n .border-top-radius(0);\n}\n\n\n// Justified button groups\n// ----------------------\n\n.btn-group-justified {\n display: table;\n width: 100%;\n table-layout: fixed;\n border-collapse: separate;\n > .btn,\n > .btn-group {\n float: none;\n display: table-cell;\n width: 1%;\n }\n > .btn-group .btn {\n width: 100%;\n }\n\n > .btn-group .dropdown-menu {\n left: auto;\n }\n}\n\n\n// Checkbox and radio options\n//\n// In order to support the browser's form validation feedback, powered by the\n// `required` attribute, we have to \"hide\" the inputs via `clip`. We cannot use\n// `display: none;` or `visibility: hidden;` as that also hides the popover.\n// Simply visually hiding the inputs via `opacity` would leave them clickable in\n// certain cases which is prevented by using `clip` and `pointer-events`.\n// This way, we ensure a DOM element is visible to position the popover from.\n//\n// See https://github.com/twbs/bootstrap/pull/12794 and\n// https://github.com/twbs/bootstrap/pull/14559 for more information.\n\n[data-toggle=\"buttons\"] {\n > .btn,\n > .btn-group > .btn {\n input[type=\"radio\"],\n input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0,0,0,0);\n pointer-events: none;\n }\n }\n}\n","// Single side border-radius\n\n.border-top-radius(@radius) {\n border-top-right-radius: @radius;\n border-top-left-radius: @radius;\n}\n.border-right-radius(@radius) {\n border-bottom-right-radius: @radius;\n border-top-right-radius: @radius;\n}\n.border-bottom-radius(@radius) {\n border-bottom-right-radius: @radius;\n border-bottom-left-radius: @radius;\n}\n.border-left-radius(@radius) {\n border-bottom-left-radius: @radius;\n border-top-left-radius: @radius;\n}\n","//\n// Input groups\n// --------------------------------------------------\n\n// Base styles\n// -------------------------\n.input-group {\n position: relative; // For dropdowns\n display: table;\n border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table\n\n // Undo padding and float of grid classes\n &[class*=\"col-\"] {\n float: none;\n padding-left: 0;\n padding-right: 0;\n }\n\n .form-control {\n // Ensure that the input is always above the *appended* addon button for\n // proper border colors.\n position: relative;\n z-index: 2;\n\n // IE9 fubars the placeholder attribute in text inputs and the arrows on\n // select elements in input groups. To fix it, we float the input. Details:\n // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855\n float: left;\n\n width: 100%;\n margin-bottom: 0;\n \n &:focus {\n z-index: 3;\n }\n }\n}\n\n// Sizing options\n//\n// Remix the default form control sizing classes into new ones for easier\n// manipulation.\n\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n .input-lg();\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n .input-sm();\n}\n\n\n// Display as table-cell\n// -------------------------\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n display: table-cell;\n\n &:not(:first-child):not(:last-child) {\n border-radius: 0;\n }\n}\n// Addon and addon wrapper for buttons\n.input-group-addon,\n.input-group-btn {\n width: 1%;\n white-space: nowrap;\n vertical-align: middle; // Match the inputs\n}\n\n// Text input groups\n// -------------------------\n.input-group-addon {\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n font-weight: normal;\n line-height: 1;\n color: @input-color;\n text-align: center;\n background-color: @input-group-addon-bg;\n border: 1px solid @input-group-addon-border-color;\n border-radius: @input-border-radius;\n\n // Sizing\n &.input-sm {\n padding: @padding-small-vertical @padding-small-horizontal;\n font-size: @font-size-small;\n border-radius: @input-border-radius-small;\n }\n &.input-lg {\n padding: @padding-large-vertical @padding-large-horizontal;\n font-size: @font-size-large;\n border-radius: @input-border-radius-large;\n }\n\n // Nuke default margins from checkboxes and radios to vertically center within.\n input[type=\"radio\"],\n input[type=\"checkbox\"] {\n margin-top: 0;\n }\n}\n\n// Reset rounded corners\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n .border-right-radius(0);\n}\n.input-group-addon:first-child {\n border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n .border-left-radius(0);\n}\n.input-group-addon:last-child {\n border-left: 0;\n}\n\n// Button input groups\n// -------------------------\n.input-group-btn {\n position: relative;\n // Jankily prevent input button groups from wrapping with `white-space` and\n // `font-size` in combination with `inline-block` on buttons.\n font-size: 0;\n white-space: nowrap;\n\n // Negative margin for spacing, position for bringing hovered/focused/actived\n // element above the siblings.\n > .btn {\n position: relative;\n + .btn {\n margin-left: -1px;\n }\n // Bring the \"active\" button to the front\n &:hover,\n &:focus,\n &:active {\n z-index: 2;\n }\n }\n\n // Negative margin to only have a 1px border between the two\n &:first-child {\n > .btn,\n > .btn-group {\n margin-right: -1px;\n }\n }\n &:last-child {\n > .btn,\n > .btn-group {\n z-index: 2;\n margin-left: -1px;\n }\n }\n}\n","//\n// Navs\n// --------------------------------------------------\n\n\n// Base class\n// --------------------------------------------------\n\n.nav {\n margin-bottom: 0;\n padding-left: 0; // Override default ul/ol\n list-style: none;\n &:extend(.clearfix all);\n\n > li {\n position: relative;\n display: block;\n\n > a {\n position: relative;\n display: block;\n padding: @nav-link-padding;\n &:hover,\n &:focus {\n text-decoration: none;\n background-color: @nav-link-hover-bg;\n }\n }\n\n // Disabled state sets text to gray and nukes hover/tab effects\n &.disabled > a {\n color: @nav-disabled-link-color;\n\n &:hover,\n &:focus {\n color: @nav-disabled-link-hover-color;\n text-decoration: none;\n background-color: transparent;\n cursor: @cursor-disabled;\n }\n }\n }\n\n // Open dropdowns\n .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @nav-link-hover-bg;\n border-color: @link-color;\n }\n }\n\n // Nav dividers (deprecated with v3.0.1)\n //\n // This should have been removed in v3 with the dropping of `.nav-list`, but\n // we missed it. We don't currently support this anywhere, but in the interest\n // of maintaining backward compatibility in case you use it, it's deprecated.\n .nav-divider {\n .nav-divider();\n }\n\n // Prevent IE8 from misplacing imgs\n //\n // See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989\n > li > a > img {\n max-width: none;\n }\n}\n\n\n// Tabs\n// -------------------------\n\n// Give the tabs something to sit on\n.nav-tabs {\n border-bottom: 1px solid @nav-tabs-border-color;\n > li {\n float: left;\n // Make the list-items overlay the bottom border\n margin-bottom: -1px;\n\n // Actual tabs (as links)\n > a {\n margin-right: 2px;\n line-height: @line-height-base;\n border: 1px solid transparent;\n border-radius: @border-radius-base @border-radius-base 0 0;\n &:hover {\n border-color: @nav-tabs-link-hover-border-color @nav-tabs-link-hover-border-color @nav-tabs-border-color;\n }\n }\n\n // Active state, and its :hover to override normal :hover\n &.active > a {\n &,\n &:hover,\n &:focus {\n color: @nav-tabs-active-link-hover-color;\n background-color: @nav-tabs-active-link-hover-bg;\n border: 1px solid @nav-tabs-active-link-hover-border-color;\n border-bottom-color: transparent;\n cursor: default;\n }\n }\n }\n // pulling this in mainly for less shorthand\n &.nav-justified {\n .nav-justified();\n .nav-tabs-justified();\n }\n}\n\n\n// Pills\n// -------------------------\n.nav-pills {\n > li {\n float: left;\n\n // Links rendered as pills\n > a {\n border-radius: @nav-pills-border-radius;\n }\n + li {\n margin-left: 2px;\n }\n\n // Active state\n &.active > a {\n &,\n &:hover,\n &:focus {\n color: @nav-pills-active-link-hover-color;\n background-color: @nav-pills-active-link-hover-bg;\n }\n }\n }\n}\n\n\n// Stacked pills\n.nav-stacked {\n > li {\n float: none;\n + li {\n margin-top: 2px;\n margin-left: 0; // no need for this gap between nav items\n }\n }\n}\n\n\n// Nav variations\n// --------------------------------------------------\n\n// Justified nav links\n// -------------------------\n\n.nav-justified {\n width: 100%;\n\n > li {\n float: none;\n > a {\n text-align: center;\n margin-bottom: 5px;\n }\n }\n\n > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n }\n\n @media (min-width: @screen-sm-min) {\n > li {\n display: table-cell;\n width: 1%;\n > a {\n margin-bottom: 0;\n }\n }\n }\n}\n\n// Move borders to anchors instead of bottom of list\n//\n// Mixin for adding on top the shared `.nav-justified` styles for our tabs\n.nav-tabs-justified {\n border-bottom: 0;\n\n > li > a {\n // Override margin from .nav-tabs\n margin-right: 0;\n border-radius: @border-radius-base;\n }\n\n > .active > a,\n > .active > a:hover,\n > .active > a:focus {\n border: 1px solid @nav-tabs-justified-link-border-color;\n }\n\n @media (min-width: @screen-sm-min) {\n > li > a {\n border-bottom: 1px solid @nav-tabs-justified-link-border-color;\n border-radius: @border-radius-base @border-radius-base 0 0;\n }\n > .active > a,\n > .active > a:hover,\n > .active > a:focus {\n border-bottom-color: @nav-tabs-justified-active-link-border-color;\n }\n }\n}\n\n\n// Tabbable tabs\n// -------------------------\n\n// Hide tabbable panes to start, show them when `.active`\n.tab-content {\n > .tab-pane {\n display: none;\n }\n > .active {\n display: block;\n }\n}\n\n\n// Dropdowns\n// -------------------------\n\n// Specific dropdowns\n.nav-tabs .dropdown-menu {\n // make dropdown border overlap tab border\n margin-top: -1px;\n // Remove the top rounded corners here since there is a hard edge above the menu\n .border-top-radius(0);\n}\n","//\n// Navbars\n// --------------------------------------------------\n\n\n// Wrapper and base class\n//\n// Provide a static navbar from which we expand to create full-width, fixed, and\n// other navbar variations.\n\n.navbar {\n position: relative;\n min-height: @navbar-height; // Ensure a navbar always shows (e.g., without a .navbar-brand in collapsed mode)\n margin-bottom: @navbar-margin-bottom;\n border: 1px solid transparent;\n\n // Prevent floats from breaking the navbar\n &:extend(.clearfix all);\n\n @media (min-width: @grid-float-breakpoint) {\n border-radius: @navbar-border-radius;\n }\n}\n\n\n// Navbar heading\n//\n// Groups `.navbar-brand` and `.navbar-toggle` into a single component for easy\n// styling of responsive aspects.\n\n.navbar-header {\n &:extend(.clearfix all);\n\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n }\n}\n\n\n// Navbar collapse (body)\n//\n// Group your navbar content into this for easy collapsing and expanding across\n// various device sizes. By default, this content is collapsed when <768px, but\n// will expand past that for a horizontal display.\n//\n// To start (on mobile devices) the navbar links, forms, and buttons are stacked\n// vertically and include a `max-height` to overflow in case you have too much\n// content for the user's viewport.\n\n.navbar-collapse {\n overflow-x: visible;\n padding-right: @navbar-padding-horizontal;\n padding-left: @navbar-padding-horizontal;\n border-top: 1px solid transparent;\n box-shadow: inset 0 1px 0 rgba(255,255,255,.1);\n &:extend(.clearfix all);\n -webkit-overflow-scrolling: touch;\n\n &.in {\n overflow-y: auto;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n width: auto;\n border-top: 0;\n box-shadow: none;\n\n &.collapse {\n display: block !important;\n height: auto !important;\n padding-bottom: 0; // Override default setting\n overflow: visible !important;\n }\n\n &.in {\n overflow-y: visible;\n }\n\n // Undo the collapse side padding for navbars with containers to ensure\n // alignment of right-aligned contents.\n .navbar-fixed-top &,\n .navbar-static-top &,\n .navbar-fixed-bottom & {\n padding-left: 0;\n padding-right: 0;\n }\n }\n}\n\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n .navbar-collapse {\n max-height: @navbar-collapse-max-height;\n\n @media (max-device-width: @screen-xs-min) and (orientation: landscape) {\n max-height: 200px;\n }\n }\n}\n\n\n// Both navbar header and collapse\n//\n// When a container is present, change the behavior of the header and collapse.\n\n.container,\n.container-fluid {\n > .navbar-header,\n > .navbar-collapse {\n margin-right: -@navbar-padding-horizontal;\n margin-left: -@navbar-padding-horizontal;\n\n @media (min-width: @grid-float-breakpoint) {\n margin-right: 0;\n margin-left: 0;\n }\n }\n}\n\n\n//\n// Navbar alignment options\n//\n// Display the navbar across the entirety of the page or fixed it to the top or\n// bottom of the page.\n\n// Static top (unfixed, but 100% wide) navbar\n.navbar-static-top {\n z-index: @zindex-navbar;\n border-width: 0 0 1px;\n\n @media (min-width: @grid-float-breakpoint) {\n border-radius: 0;\n }\n}\n\n// Fix the top/bottom navbars when screen real estate supports it\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n position: fixed;\n right: 0;\n left: 0;\n z-index: @zindex-navbar-fixed;\n\n // Undo the rounded corners\n @media (min-width: @grid-float-breakpoint) {\n border-radius: 0;\n }\n}\n.navbar-fixed-top {\n top: 0;\n border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n bottom: 0;\n margin-bottom: 0; // override .navbar defaults\n border-width: 1px 0 0;\n}\n\n\n// Brand/project name\n\n.navbar-brand {\n float: left;\n padding: @navbar-padding-vertical @navbar-padding-horizontal;\n font-size: @font-size-large;\n line-height: @line-height-computed;\n height: @navbar-height;\n\n &:hover,\n &:focus {\n text-decoration: none;\n }\n\n > img {\n display: block;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n .navbar > .container &,\n .navbar > .container-fluid & {\n margin-left: -@navbar-padding-horizontal;\n }\n }\n}\n\n\n// Navbar toggle\n//\n// Custom button for toggling the `.navbar-collapse`, powered by the collapse\n// JavaScript plugin.\n\n.navbar-toggle {\n position: relative;\n float: right;\n margin-right: @navbar-padding-horizontal;\n padding: 9px 10px;\n .navbar-vertical-align(34px);\n background-color: transparent;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid transparent;\n border-radius: @border-radius-base;\n\n // We remove the `outline` here, but later compensate by attaching `:hover`\n // styles to `:focus`.\n &:focus {\n outline: 0;\n }\n\n // Bars\n .icon-bar {\n display: block;\n width: 22px;\n height: 2px;\n border-radius: 1px;\n }\n .icon-bar + .icon-bar {\n margin-top: 4px;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n display: none;\n }\n}\n\n\n// Navbar nav links\n//\n// Builds on top of the `.nav` components with its own modifier class to make\n// the nav the full height of the horizontal nav (above 768px).\n\n.navbar-nav {\n margin: (@navbar-padding-vertical / 2) -@navbar-padding-horizontal;\n\n > li > a {\n padding-top: 10px;\n padding-bottom: 10px;\n line-height: @line-height-computed;\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display when collapsed\n .open .dropdown-menu {\n position: static;\n float: none;\n width: auto;\n margin-top: 0;\n background-color: transparent;\n border: 0;\n box-shadow: none;\n > li > a,\n .dropdown-header {\n padding: 5px 15px 5px 25px;\n }\n > li > a {\n line-height: @line-height-computed;\n &:hover,\n &:focus {\n background-image: none;\n }\n }\n }\n }\n\n // Uncollapse the nav\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n margin: 0;\n\n > li {\n float: left;\n > a {\n padding-top: @navbar-padding-vertical;\n padding-bottom: @navbar-padding-vertical;\n }\n }\n }\n}\n\n\n// Navbar form\n//\n// Extension of the `.form-inline` with some extra flavor for optimum display in\n// our navbars.\n\n.navbar-form {\n margin-left: -@navbar-padding-horizontal;\n margin-right: -@navbar-padding-horizontal;\n padding: 10px @navbar-padding-horizontal;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n @shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n\n // Mixin behavior for optimum display\n .form-inline();\n\n .form-group {\n @media (max-width: @grid-float-breakpoint-max) {\n margin-bottom: 5px;\n\n &:last-child {\n margin-bottom: 0;\n }\n }\n }\n\n // Vertically center in expanded, horizontal navbar\n .navbar-vertical-align(@input-height-base);\n\n // Undo 100% width for pull classes\n @media (min-width: @grid-float-breakpoint) {\n width: auto;\n border: 0;\n margin-left: 0;\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n .box-shadow(none);\n }\n}\n\n\n// Dropdown menus\n\n// Menu position and menu carets\n.navbar-nav > li > .dropdown-menu {\n margin-top: 0;\n .border-top-radius(0);\n}\n// Menu position and menu caret support for dropups via extra dropup class\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n margin-bottom: 0;\n .border-top-radius(@navbar-border-radius);\n .border-bottom-radius(0);\n}\n\n\n// Buttons in navbars\n//\n// Vertically center a button within a navbar (when *not* in a form).\n\n.navbar-btn {\n .navbar-vertical-align(@input-height-base);\n\n &.btn-sm {\n .navbar-vertical-align(@input-height-small);\n }\n &.btn-xs {\n .navbar-vertical-align(22);\n }\n}\n\n\n// Text in navbars\n//\n// Add a class to make any element properly align itself vertically within the navbars.\n\n.navbar-text {\n .navbar-vertical-align(@line-height-computed);\n\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n margin-left: @navbar-padding-horizontal;\n margin-right: @navbar-padding-horizontal;\n }\n}\n\n\n// Component alignment\n//\n// Repurpose the pull utilities as their own navbar utilities to avoid specificity\n// issues with parents and chaining. Only do this when the navbar is uncollapsed\n// though so that navbar contents properly stack and align in mobile.\n//\n// Declared after the navbar components to ensure more specificity on the margins.\n\n@media (min-width: @grid-float-breakpoint) {\n .navbar-left { .pull-left(); }\n .navbar-right {\n .pull-right();\n margin-right: -@navbar-padding-horizontal;\n\n ~ .navbar-right {\n margin-right: 0;\n }\n }\n}\n\n\n// Alternate navbars\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n background-color: @navbar-default-bg;\n border-color: @navbar-default-border;\n\n .navbar-brand {\n color: @navbar-default-brand-color;\n &:hover,\n &:focus {\n color: @navbar-default-brand-hover-color;\n background-color: @navbar-default-brand-hover-bg;\n }\n }\n\n .navbar-text {\n color: @navbar-default-color;\n }\n\n .navbar-nav {\n > li > a {\n color: @navbar-default-link-color;\n\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n background-color: @navbar-default-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-active-color;\n background-color: @navbar-default-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n background-color: @navbar-default-link-disabled-bg;\n }\n }\n }\n\n .navbar-toggle {\n border-color: @navbar-default-toggle-border-color;\n &:hover,\n &:focus {\n background-color: @navbar-default-toggle-hover-bg;\n }\n .icon-bar {\n background-color: @navbar-default-toggle-icon-bar-bg;\n }\n }\n\n .navbar-collapse,\n .navbar-form {\n border-color: @navbar-default-border;\n }\n\n // Dropdown menu items\n .navbar-nav {\n // Remove background color from open dropdown\n > .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @navbar-default-link-active-bg;\n color: @navbar-default-link-active-color;\n }\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display when collapsed\n .open .dropdown-menu {\n > li > a {\n color: @navbar-default-link-color;\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n background-color: @navbar-default-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-active-color;\n background-color: @navbar-default-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n background-color: @navbar-default-link-disabled-bg;\n }\n }\n }\n }\n }\n\n\n // Links in navbars\n //\n // Add a class to ensure links outside the navbar nav are colored correctly.\n\n .navbar-link {\n color: @navbar-default-link-color;\n &:hover {\n color: @navbar-default-link-hover-color;\n }\n }\n\n .btn-link {\n color: @navbar-default-link-color;\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n }\n }\n }\n}\n\n// Inverse navbar\n\n.navbar-inverse {\n background-color: @navbar-inverse-bg;\n border-color: @navbar-inverse-border;\n\n .navbar-brand {\n color: @navbar-inverse-brand-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-brand-hover-color;\n background-color: @navbar-inverse-brand-hover-bg;\n }\n }\n\n .navbar-text {\n color: @navbar-inverse-color;\n }\n\n .navbar-nav {\n > li > a {\n color: @navbar-inverse-link-color;\n\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n background-color: @navbar-inverse-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-active-color;\n background-color: @navbar-inverse-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n background-color: @navbar-inverse-link-disabled-bg;\n }\n }\n }\n\n // Darken the responsive nav toggle\n .navbar-toggle {\n border-color: @navbar-inverse-toggle-border-color;\n &:hover,\n &:focus {\n background-color: @navbar-inverse-toggle-hover-bg;\n }\n .icon-bar {\n background-color: @navbar-inverse-toggle-icon-bar-bg;\n }\n }\n\n .navbar-collapse,\n .navbar-form {\n border-color: darken(@navbar-inverse-bg, 7%);\n }\n\n // Dropdowns\n .navbar-nav {\n > .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @navbar-inverse-link-active-bg;\n color: @navbar-inverse-link-active-color;\n }\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display\n .open .dropdown-menu {\n > .dropdown-header {\n border-color: @navbar-inverse-border;\n }\n .divider {\n background-color: @navbar-inverse-border;\n }\n > li > a {\n color: @navbar-inverse-link-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n background-color: @navbar-inverse-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-active-color;\n background-color: @navbar-inverse-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n background-color: @navbar-inverse-link-disabled-bg;\n }\n }\n }\n }\n }\n\n .navbar-link {\n color: @navbar-inverse-link-color;\n &:hover {\n color: @navbar-inverse-link-hover-color;\n }\n }\n\n .btn-link {\n color: @navbar-inverse-link-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n }\n }\n }\n}\n","// Navbar vertical align\n//\n// Vertically center elements in the navbar.\n// Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin.\n\n.navbar-vertical-align(@element-height) {\n margin-top: ((@navbar-height - @element-height) / 2);\n margin-bottom: ((@navbar-height - @element-height) / 2);\n}\n","//\n// Utility classes\n// --------------------------------------------------\n\n\n// Floats\n// -------------------------\n\n.clearfix {\n .clearfix();\n}\n.center-block {\n .center-block();\n}\n.pull-right {\n float: right !important;\n}\n.pull-left {\n float: left !important;\n}\n\n\n// Toggling content\n// -------------------------\n\n// Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1\n.hide {\n display: none !important;\n}\n.show {\n display: block !important;\n}\n.invisible {\n visibility: hidden;\n}\n.text-hide {\n .text-hide();\n}\n\n\n// Hide from screenreaders and browsers\n//\n// Credit: HTML5 Boilerplate\n\n.hidden {\n display: none !important;\n}\n\n\n// For Affix plugin\n// -------------------------\n\n.affix {\n position: fixed;\n}\n","//\n// Breadcrumbs\n// --------------------------------------------------\n\n\n.breadcrumb {\n padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal;\n margin-bottom: @line-height-computed;\n list-style: none;\n background-color: @breadcrumb-bg;\n border-radius: @border-radius-base;\n\n > li {\n display: inline-block;\n\n + li:before {\n content: \"@{breadcrumb-separator}\\00a0\"; // Unicode space added since inline-block means non-collapsing white-space\n padding: 0 5px;\n color: @breadcrumb-color;\n }\n }\n\n > .active {\n color: @breadcrumb-active-color;\n }\n}\n","//\n// Pagination (multiple pages)\n// --------------------------------------------------\n.pagination {\n display: inline-block;\n padding-left: 0;\n margin: @line-height-computed 0;\n border-radius: @border-radius-base;\n\n > li {\n display: inline; // Remove list-style and block-level defaults\n > a,\n > span {\n position: relative;\n float: left; // Collapse white-space\n padding: @padding-base-vertical @padding-base-horizontal;\n line-height: @line-height-base;\n text-decoration: none;\n color: @pagination-color;\n background-color: @pagination-bg;\n border: 1px solid @pagination-border;\n margin-left: -1px;\n }\n &:first-child {\n > a,\n > span {\n margin-left: 0;\n .border-left-radius(@border-radius-base);\n }\n }\n &:last-child {\n > a,\n > span {\n .border-right-radius(@border-radius-base);\n }\n }\n }\n\n > li > a,\n > li > span {\n &:hover,\n &:focus {\n z-index: 2;\n color: @pagination-hover-color;\n background-color: @pagination-hover-bg;\n border-color: @pagination-hover-border;\n }\n }\n\n > .active > a,\n > .active > span {\n &,\n &:hover,\n &:focus {\n z-index: 3;\n color: @pagination-active-color;\n background-color: @pagination-active-bg;\n border-color: @pagination-active-border;\n cursor: default;\n }\n }\n\n > .disabled {\n > span,\n > span:hover,\n > span:focus,\n > a,\n > a:hover,\n > a:focus {\n color: @pagination-disabled-color;\n background-color: @pagination-disabled-bg;\n border-color: @pagination-disabled-border;\n cursor: @cursor-disabled;\n }\n }\n}\n\n// Sizing\n// --------------------------------------------------\n\n// Large\n.pagination-lg {\n .pagination-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);\n}\n\n// Small\n.pagination-sm {\n .pagination-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small);\n}\n","// Pagination\n\n.pagination-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n > li {\n > a,\n > span {\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n }\n &:first-child {\n > a,\n > span {\n .border-left-radius(@border-radius);\n }\n }\n &:last-child {\n > a,\n > span {\n .border-right-radius(@border-radius);\n }\n }\n }\n}\n","//\n// Pager pagination\n// --------------------------------------------------\n\n\n.pager {\n padding-left: 0;\n margin: @line-height-computed 0;\n list-style: none;\n text-align: center;\n &:extend(.clearfix all);\n li {\n display: inline;\n > a,\n > span {\n display: inline-block;\n padding: 5px 14px;\n background-color: @pager-bg;\n border: 1px solid @pager-border;\n border-radius: @pager-border-radius;\n }\n\n > a:hover,\n > a:focus {\n text-decoration: none;\n background-color: @pager-hover-bg;\n }\n }\n\n .next {\n > a,\n > span {\n float: right;\n }\n }\n\n .previous {\n > a,\n > span {\n float: left;\n }\n }\n\n .disabled {\n > a,\n > a:hover,\n > a:focus,\n > span {\n color: @pager-disabled-color;\n background-color: @pager-bg;\n cursor: @cursor-disabled;\n }\n }\n}\n","//\n// Labels\n// --------------------------------------------------\n\n.label {\n display: inline;\n padding: .2em .6em .3em;\n font-size: 75%;\n font-weight: bold;\n line-height: 1;\n color: @label-color;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: .25em;\n\n // Add hover effects, but only for links\n a& {\n &:hover,\n &:focus {\n color: @label-link-hover-color;\n text-decoration: none;\n cursor: pointer;\n }\n }\n\n // Empty labels collapse automatically (not available in IE8)\n &:empty {\n display: none;\n }\n\n // Quick fix for labels in buttons\n .btn & {\n position: relative;\n top: -1px;\n }\n}\n\n// Colors\n// Contextual variations (linked labels get darker on :hover)\n\n.label-default {\n .label-variant(@label-default-bg);\n}\n\n.label-primary {\n .label-variant(@label-primary-bg);\n}\n\n.label-success {\n .label-variant(@label-success-bg);\n}\n\n.label-info {\n .label-variant(@label-info-bg);\n}\n\n.label-warning {\n .label-variant(@label-warning-bg);\n}\n\n.label-danger {\n .label-variant(@label-danger-bg);\n}\n","// Labels\n\n.label-variant(@color) {\n background-color: @color;\n\n &[href] {\n &:hover,\n &:focus {\n background-color: darken(@color, 10%);\n }\n }\n}\n","//\n// Badges\n// --------------------------------------------------\n\n\n// Base class\n.badge {\n display: inline-block;\n min-width: 10px;\n padding: 3px 7px;\n font-size: @font-size-small;\n font-weight: @badge-font-weight;\n color: @badge-color;\n line-height: @badge-line-height;\n vertical-align: middle;\n white-space: nowrap;\n text-align: center;\n background-color: @badge-bg;\n border-radius: @badge-border-radius;\n\n // Empty badges collapse automatically (not available in IE8)\n &:empty {\n display: none;\n }\n\n // Quick fix for badges in buttons\n .btn & {\n position: relative;\n top: -1px;\n }\n\n .btn-xs &,\n .btn-group-xs > .btn & {\n top: 0;\n padding: 1px 5px;\n }\n\n // Hover state, but only for links\n a& {\n &:hover,\n &:focus {\n color: @badge-link-hover-color;\n text-decoration: none;\n cursor: pointer;\n }\n }\n\n // Account for badges in navs\n .list-group-item.active > &,\n .nav-pills > .active > a > & {\n color: @badge-active-color;\n background-color: @badge-active-bg;\n }\n\n .list-group-item > & {\n float: right;\n }\n\n .list-group-item > & + & {\n margin-right: 5px;\n }\n\n .nav-pills > li > a > & {\n margin-left: 3px;\n }\n}\n","//\n// Jumbotron\n// --------------------------------------------------\n\n\n.jumbotron {\n padding-top: @jumbotron-padding;\n padding-bottom: @jumbotron-padding;\n margin-bottom: @jumbotron-padding;\n color: @jumbotron-color;\n background-color: @jumbotron-bg;\n\n h1,\n .h1 {\n color: @jumbotron-heading-color;\n }\n\n p {\n margin-bottom: (@jumbotron-padding / 2);\n font-size: @jumbotron-font-size;\n font-weight: 200;\n }\n\n > hr {\n border-top-color: darken(@jumbotron-bg, 10%);\n }\n\n .container &,\n .container-fluid & {\n border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container\n padding-left: (@grid-gutter-width / 2);\n padding-right: (@grid-gutter-width / 2);\n }\n\n .container {\n max-width: 100%;\n }\n\n @media screen and (min-width: @screen-sm-min) {\n padding-top: (@jumbotron-padding * 1.6);\n padding-bottom: (@jumbotron-padding * 1.6);\n\n .container &,\n .container-fluid & {\n padding-left: (@jumbotron-padding * 2);\n padding-right: (@jumbotron-padding * 2);\n }\n\n h1,\n .h1 {\n font-size: @jumbotron-heading-font-size;\n }\n }\n}\n","//\n// Thumbnails\n// --------------------------------------------------\n\n\n// Mixin and adjust the regular image class\n.thumbnail {\n display: block;\n padding: @thumbnail-padding;\n margin-bottom: @line-height-computed;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(border .2s ease-in-out);\n\n > img,\n a > img {\n &:extend(.img-responsive);\n margin-left: auto;\n margin-right: auto;\n }\n\n // Add a hover state for linked versions only\n a&:hover,\n a&:focus,\n a&.active {\n border-color: @link-color;\n }\n\n // Image captions\n .caption {\n padding: @thumbnail-caption-padding;\n color: @thumbnail-caption-color;\n }\n}\n","//\n// Alerts\n// --------------------------------------------------\n\n\n// Base styles\n// -------------------------\n\n.alert {\n padding: @alert-padding;\n margin-bottom: @line-height-computed;\n border: 1px solid transparent;\n border-radius: @alert-border-radius;\n\n // Headings for larger alerts\n h4 {\n margin-top: 0;\n // Specified for the h4 to prevent conflicts of changing @headings-color\n color: inherit;\n }\n\n // Provide class for links that match alerts\n .alert-link {\n font-weight: @alert-link-font-weight;\n }\n\n // Improve alignment and spacing of inner content\n > p,\n > ul {\n margin-bottom: 0;\n }\n\n > p + p {\n margin-top: 5px;\n }\n}\n\n// Dismissible alerts\n//\n// Expand the right padding and account for the close button's positioning.\n\n.alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0.\n.alert-dismissible {\n padding-right: (@alert-padding + 20);\n\n // Adjust close link position\n .close {\n position: relative;\n top: -2px;\n right: -21px;\n color: inherit;\n }\n}\n\n// Alternate styles\n//\n// Generate contextual modifier classes for colorizing the alert.\n\n.alert-success {\n .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text);\n}\n\n.alert-info {\n .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text);\n}\n\n.alert-warning {\n .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text);\n}\n\n.alert-danger {\n .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text);\n}\n","// Alerts\n\n.alert-variant(@background; @border; @text-color) {\n background-color: @background;\n border-color: @border;\n color: @text-color;\n\n hr {\n border-top-color: darken(@border, 5%);\n }\n .alert-link {\n color: darken(@text-color, 10%);\n }\n}\n","//\n// Progress bars\n// --------------------------------------------------\n\n\n// Bar animations\n// -------------------------\n\n// WebKit\n@-webkit-keyframes progress-bar-stripes {\n from { background-position: 40px 0; }\n to { background-position: 0 0; }\n}\n\n// Spec and IE10+\n@keyframes progress-bar-stripes {\n from { background-position: 40px 0; }\n to { background-position: 0 0; }\n}\n\n\n// Bar itself\n// -------------------------\n\n// Outer container\n.progress {\n overflow: hidden;\n height: @line-height-computed;\n margin-bottom: @line-height-computed;\n background-color: @progress-bg;\n border-radius: @progress-border-radius;\n .box-shadow(inset 0 1px 2px rgba(0,0,0,.1));\n}\n\n// Bar of progress\n.progress-bar {\n float: left;\n width: 0%;\n height: 100%;\n font-size: @font-size-small;\n line-height: @line-height-computed;\n color: @progress-bar-color;\n text-align: center;\n background-color: @progress-bar-bg;\n .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));\n .transition(width .6s ease);\n}\n\n// Striped bars\n//\n// `.progress-striped .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar-striped` class, which you just add to an existing\n// `.progress-bar`.\n.progress-striped .progress-bar,\n.progress-bar-striped {\n #gradient > .striped();\n background-size: 40px 40px;\n}\n\n// Call animation for the active one\n//\n// `.progress.active .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar.active` approach.\n.progress.active .progress-bar,\n.progress-bar.active {\n .animation(progress-bar-stripes 2s linear infinite);\n}\n\n\n// Variations\n// -------------------------\n\n.progress-bar-success {\n .progress-bar-variant(@progress-bar-success-bg);\n}\n\n.progress-bar-info {\n .progress-bar-variant(@progress-bar-info-bg);\n}\n\n.progress-bar-warning {\n .progress-bar-variant(@progress-bar-warning-bg);\n}\n\n.progress-bar-danger {\n .progress-bar-variant(@progress-bar-danger-bg);\n}\n","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Progress bars\n\n.progress-bar-variant(@color) {\n background-color: @color;\n\n // Deprecated parent class requirement as of v3.2.0\n .progress-striped & {\n #gradient > .striped();\n }\n}\n",".media {\n // Proper spacing between instances of .media\n margin-top: 15px;\n\n &:first-child {\n margin-top: 0;\n }\n}\n\n.media,\n.media-body {\n zoom: 1;\n overflow: hidden;\n}\n\n.media-body {\n width: 10000px;\n}\n\n.media-object {\n display: block;\n\n // Fix collapse in webkit from max-width: 100% and display: table-cell.\n &.img-thumbnail {\n max-width: none;\n }\n}\n\n.media-right,\n.media > .pull-right {\n padding-left: 10px;\n}\n\n.media-left,\n.media > .pull-left {\n padding-right: 10px;\n}\n\n.media-left,\n.media-right,\n.media-body {\n display: table-cell;\n vertical-align: top;\n}\n\n.media-middle {\n vertical-align: middle;\n}\n\n.media-bottom {\n vertical-align: bottom;\n}\n\n// Reset margins on headings for tighter default spacing\n.media-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n\n// Media list variation\n//\n// Undo default ul/ol styles\n.media-list {\n padding-left: 0;\n list-style: none;\n}\n","//\n// List groups\n// --------------------------------------------------\n\n\n// Base class\n//\n// Easily usable on