commit 4225cc4dde6f1eaea9ebf5695f645b4d633d0cd5 Author: Michael Zhang Date: Tue Feb 20 22:37:10 2018 -0600 Initial. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bfb7922 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.data +.env +.vagrant +.vscode +.digitalocean-token +.idea + +__pycache__ +*.pyc +ubuntu-xenial-16.04-cloudimg-console.log diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 0000000..47092ad --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,13 @@ +#!/usr/bin/ruby + +Vagrant.configure("2") do |config| + config.vm.box = "ubuntu/xenial64" + config.vm.network "forwarded_port", guest: 80, host: 7000 + config.vm.network "forwarded_port", guest: 8000, host: 8000 + + config.vm.synced_folder "../problems", "/problems" + config.vm.provider "virtualbox" do |vb| + end + + # config.vm.provision "shell", path: "provision.sh" +end diff --git a/config.json b/config.json new file mode 100644 index 0000000..48f8868 --- /dev/null +++ b/config.json @@ -0,0 +1,7 @@ +{ + "resource_prefix": "staging_", + "region": "nyc3", + "load_balancer": { + "name": "load_balancer" + } +} \ No newline at end of file diff --git a/db/Vagrantfile b/db/Vagrantfile new file mode 100644 index 0000000..e69de29 diff --git a/db/conf.d/easyctf.cnf b/db/conf.d/easyctf.cnf new file mode 100755 index 0000000..dbec0f9 --- /dev/null +++ b/db/conf.d/easyctf.cnf @@ -0,0 +1,15 @@ +[client] +default-character-set=utf8mb4 + +[mysql] +default-character-set=utf8mb4 + +[mysqld] +innodb_buffer_pool_size=20M +init_connect='SET collation_connection = utf8_unicode_ci' +init_connect='SET NAMES utf8mb4' +character-set-server=utf8mb4 +collation-server=utf8_unicode_ci +skip-character-set-client-handshake +max_allowed_packet=512M +wait_timeout=31536000 diff --git a/deploy/.gitignore b/deploy/.gitignore new file mode 100644 index 0000000..1d3ed4c --- /dev/null +++ b/deploy/.gitignore @@ -0,0 +1 @@ +config.yml diff --git a/deploy/config.yml.exmaple b/deploy/config.yml.exmaple new file mode 100644 index 0000000..a772fca --- /dev/null +++ b/deploy/config.yml.exmaple @@ -0,0 +1 @@ +private_key: "~/.ssh/id_rsa" diff --git a/deploy/deploy.py b/deploy/deploy.py new file mode 100644 index 0000000..bfcc1a0 --- /dev/null +++ b/deploy/deploy.py @@ -0,0 +1,54 @@ +import paramiko +import yaml +import os +import sys + + +def read_config(): + with open(os.path.join(os.path.dirname(__file__), "config.yml")) as f: + data = yaml.load(f) + return data + + +def get_client(): + client = paramiko.SSHClient() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + return client + + +def update_web_server(client, host, pkey): + client.connect(host, username="root", pkey=pkey) + (sin, sout, serr) = client.exec_command("/bin/bash -c 'cd /root/easyctf-platform && git reset --hard && git pull origin master && systemctl restart easyctf'") + print(sout.read(), serr.read()) + client.close() + + +def reimport_problems(client, host, pkey): + client.connect(host, username="root", pkey=pkey) + (sin, sout, serr) = client.exec_command("/bin/bash -c 'cd /root/problems && git reset --hard && git pull origin master && cd /root/easyctf-platform/server && dotenv /var/easyctf/env python3 manage.py import /root/problems'") + print(sout.read(), serr.read()) + client.close() + + +def update_judge(client, host, pkey): + client.connect(host, username="root", pkey=pkey) + (sin, sout, serr) = client.exec_command("/bin/bash -c 'cd /root/easyctf-platform && git reset --hard && git pull origin master && systemctl restart judge'") + print(sout.read(), serr.read()) + client.close() + + +if __name__ == "__main__": + service = None + if len(sys.argv) > 1: + service = sys.argv[1] + config = read_config() + key_path = os.path.expanduser(config.get("private_key")) + pkey = paramiko.RSAKey.from_private_key_file(key_path) + client = get_client() + if not service or service == "web": + for host in config.get("web"): + update_web_server(client, host, pkey) + reimport_problems(client, host, pkey) + if not service or service == "judge": + for host in config.get("judge"): + update_judge(client, host, pkey) diff --git a/filestore/.gitignore b/filestore/.gitignore new file mode 100755 index 0000000..2eea525 --- /dev/null +++ b/filestore/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/filestore/app.py b/filestore/app.py new file mode 100755 index 0000000..ed7a857 --- /dev/null +++ b/filestore/app.py @@ -0,0 +1,53 @@ +import hashlib +import json +import logging +import os + +from flask import Flask, abort, request, send_file + +app = Flask(__name__) +app.config["UPLOAD_FOLDER"] = os.getenv("UPLOAD_FOLDER", "/usr/share/nginx/html") + +if not os.path.exists(app.config["UPLOAD_FOLDER"]): + os.makedirs(app.config["UPLOAD_FOLDER"]) + + +@app.route("/") +def index(): + return "You shouldn't be here." + + +@app.route("/save", methods=["POST"]) +def save(): + if "file" not in request.files: + return "no file uploaded", 400 + file = request.files["file"] + if file.filename == "": + return "no filename found", 400 + filename = hashlib.sha256(file.read()).hexdigest() + file.seek(0) + if "filename" in request.form: + name, ext = json.loads(request.form["filename"]) + filename = "%s.%s.%s" % (name, filename, ext) + else: + if "prefix" in request.form: + filename = "%s%s" % (request.form["prefix"], filename) + if "suffix" in request.form: + filename = "%s%s" % (filename, request.form["suffix"]) + file.save(os.path.join(app.config["UPLOAD_FOLDER"], filename)) + return filename + + +# This route should be used for debugging filestore locally. +@app.route("/static/") +def serve(path): + path = os.path.join(app.config["UPLOAD_FOLDER"], path) + if not os.path.exists(path): + return abort(404) + return send_file(path) + + +if __name__ == "__main__": + logging.warning("Uploading to {}".format(app.config["UPLOAD_FOLDER"])) + port = int(os.getenv("FILESTORE_PORT", "8001")) + app.run(use_debugger=True, use_reloader=True, port=port, host="0.0.0.0") diff --git a/filestore/cloud-provision.sh b/filestore/cloud-provision.sh new file mode 100755 index 0000000..f1b4937 --- /dev/null +++ b/filestore/cloud-provision.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# run this to set up the server +# only do this the first time +set -e +set -o xtrace + +PROJECT_DIRECTORY="/var/filestore/src" +PYTHON=python3 + +echo "installing system dependencies..." +if [ ! -f $HOME/.installdep.filestore.apt ]; then + apt-get update && apt-get install -y \ + git \ + nginx \ + python3 \ + python3-dev \ + python3-nose \ + python3-pip \ + realpath \ + systemd + touch $HOME/.installdep.filestore.apt +fi + +mkdir -p /var/filestore +mkdir -p /var/log/filestore + +if [ ! -d $PROJECT_DIRECTORY ]; then + b=`realpath $(basename $0)` + c=`dirname $b` + # cp -r $d $PROJECT_DIRECTORY + ln -s $c $PROJECT_DIRECTORY +else + (cd $PROJECT_DIRECTORY; git pull origin master || true) +fi + +mkdir -p /usr/share/nginx/html/static +touch /usr/share/nginx/html/static/index.html +echo "" > /usr/share/nginx/html/static/index.html +rm -rf /etc/nginx/conf.d/* /etc/nginx/sites-enabled/* +cp $PROJECT_DIRECTORY/default.conf /etc/nginx/sites-enabled/filestore + +service nginx reload +service nginx restart + +echo "installing python dependencies..." +if [ ! -f $HOME/.installdep.filestore.pip ]; then + $PYTHON -m pip install -U pip + $PYTHON -m pip install gunicorn + $PYTHON -m pip install -r $PROJECT_DIRECTORY/requirements.txt + touch $HOME/.installdep.filestore.pip +fi + +# dirty hack +KILL=/bin/kill +eval "echo \"$(< $PROJECT_DIRECTORY/systemd/filestore.service)\"" > /etc/systemd/system/filestore.service + +echo "Filestore has been deployed!" +echo "Modify the env file at /var/filestore/env." +echo "Then run" +echo +echo "systemctl --system daemon-reload && systemctl restart filestore" +echo "gucci gang" + +cp env.example /var/filestore/env +systemctl --system daemon-reload && systemctl restart filestore diff --git a/filestore/default.conf b/filestore/default.conf new file mode 100755 index 0000000..1002186 --- /dev/null +++ b/filestore/default.conf @@ -0,0 +1,9 @@ +server { + listen 80; + server_name localhost; + + location / { + root /usr/share/nginx/html; + index index.html index.html; + } +} diff --git a/filestore/entrypoint.sh b/filestore/entrypoint.sh new file mode 100755 index 0000000..e345473 --- /dev/null +++ b/filestore/entrypoint.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -e +cd /var/filestore/src + +PYTHON=/usr/bin/python3 + +echo "determining bind location..." +BIND_PORT=${FILESTORE_PORT:-8000} +PRIVATE_BIND_ADDR_=$(curl -w "\n" http://169.254.169.254/metadata/v1/interfaces/private/0/ipv4/address --connect-timeout 2 || printf "0.0.0.0") +PRIVATE_BIND_ADDR=$(echo $BIND_ADDR_ | xargs) +PUBLIC_BIND_ADDR_=$(curl -w "\n" http://169.254.169.254/metadata/v1/interfaces/public/0/ipv4/address --connect-timeout 2 || printf "0.0.0.0") +PUBLIC_BIND_ADDR=$(echo $BIND_ADDR_ | xargs) + +WORKERS=${WORKERS:-4} +ENVIRONMENT=${ENVIRONMENT:-production} +service nginx start +if [ "$ENVIRONMENT" == "development" ]; then + $PYTHON app.py +else + exec gunicorn --bind="$PRIVATE_BIND_ADDR:$BIND_PORT" --bind="$PUBLIC_BIND_ADDR:$BIND_PORT" -w $WORKERS app:app +fi diff --git a/filestore/env.example b/filestore/env.example new file mode 100644 index 0000000..d3552ba --- /dev/null +++ b/filestore/env.example @@ -0,0 +1,2 @@ +UPLOAD_FOLDER=/usr/share/nginx/html +FILESTORE_PORT=8001 diff --git a/filestore/requirements.txt b/filestore/requirements.txt new file mode 100755 index 0000000..e4a286c --- /dev/null +++ b/filestore/requirements.txt @@ -0,0 +1,2 @@ +flask +gunicorn diff --git a/filestore/systemd/filestore.service b/filestore/systemd/filestore.service new file mode 100644 index 0000000..e56c3ca --- /dev/null +++ b/filestore/systemd/filestore.service @@ -0,0 +1,16 @@ +[Unit] +Description=easyctf static file server +After=network.target + +[Service] +EnvironmentFile=/var/filestore/env +PIDFile=/run/filestore/pid +User=root +WorkingDirectory=$PROJECT_DIRECTORY +ExecStart=$PROJECT_DIRECTORY/entrypoint.sh +ExecReload=$KILL -s HUP \$MAINPID +ExecStop=$KILL -s TERM \$MAINPID +PrivateTmp=true + +[Install] +WantedBy=multi-user.target diff --git a/judge/.gitignore b/judge/.gitignore new file mode 100644 index 0000000..3ebd176 --- /dev/null +++ b/judge/.gitignore @@ -0,0 +1,10 @@ +build +# ideally these should all be in a subfolder +generator.py +grader.py +program.py +case_number +input +report +error +.idea diff --git a/judge/api.py b/judge/api.py new file mode 100644 index 0000000..6e3613b --- /dev/null +++ b/judge/api.py @@ -0,0 +1,50 @@ +import logging + +import requests + +from languages import languages, Python3 +from models import Job, Problem + + +class API(object): + def __init__(self, key, base_url): + self.key = key + self.base_url = base_url + + def api_call(self, url, method="GET", data=None, headers=None): + if headers is None: + headers = dict() + headers.update({"API-Key": self.key}) + r = requests.request(method, url, data=data, headers=headers) + return r + + def claim(self): + r = self.api_call(self.base_url + "/jobs") + print("text:", repr(r.text)) + if not r.text: + return None + required_fields = ["id", "language", "source", "pid", "test_cases", "time_limit", "memory_limit", "generator_code", "grader_code", "source_verifier_code"] + # create job object + obj = r.json() + if not all(field in obj for field in required_fields): + return None + problem = Problem(obj["pid"], obj["test_cases"], obj["time_limit"], obj["memory_limit"], + obj["generator_code"], Python3, + obj["grader_code"], Python3, + obj["source_verifier_code"], Python3) + language = languages.get(obj["language"]) + if not language: + return None # TODO: should definitely not do this + return Job(obj["id"], problem, obj["source"], language) + + def submit(self, result): + verdict = result.verdict + data = dict( + id=result.job.id, + verdict=result.verdict.value if verdict else "JE", + last_ran_case=result.last_ran_case, + execution_time=result.execution_time, + execution_memory=result.execution_memory + ) + r = self.api_call(self.base_url + "/jobs", method="POST", data=data) + return r.status_code // 100 == 2 diff --git a/judge/cloud-provision.sh b/judge/cloud-provision.sh new file mode 100755 index 0000000..37bf9e1 --- /dev/null +++ b/judge/cloud-provision.sh @@ -0,0 +1,55 @@ +#!/bin/bash +declare API_KEY=$1 +declare JUDGE_URL=$2 + +if [ ! $API_KEY ]; then + echo "please provide a key." + exit 1 +fi + +PROJECT_DIRECTORY="/var/judge/src" +PYTHON=$(which python3) +mkdir -p /var/judge +mkdir -p /var/log/judge + +echo "installing system dependencies..." +if [ ! -f $HOME/.installdep.judge.apt ]; then + apt-get update && apt-get install -y software-properties-common && \ + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 5BB92C09DB82666C && \ + add-apt-repository -y ppa:fkrull/deadsnakes && \ + add-apt-repository -y ppa:openjdk-r/ppa && \ + apt-get install -y \ + build-essential \ + openjdk-7-jdk \ + pkg-config \ + python2.7 \ + python3.5 \ + python3 \ + python3-pip + touch $HOME/.installdep.judge.apt +fi + +if [ ! -d $PROJECT_DIRECTORY ]; then + b=`realpath $(basename $0)` + c=`dirname $b` + d=`dirname $c` + ln -s $c $PROJECT_DIRECTORY +else + (cd $PROJECT_DIRECTORY; git pull origin master || true) +fi + +echo "installing python dependencies..." +if [ ! -f $HOME/.installdep.judge.pip ]; then + $PYTHON -m pip install -U pip + $PYTHON -m pip install requests + touch $HOME/.installdep.judge.pip +fi + +# dirty hack +echo "writing systemd entry..." +PYTHON=$(which python3) +eval "echo \"$(< $PROJECT_DIRECTORY/systemd/judge.service)\"" > /etc/systemd/system/judge.service + +systemctl daemon-reload +systemctl enable judge +systemctl start judge diff --git a/judge/config.py b/judge/config.py new file mode 100644 index 0000000..b7c893f --- /dev/null +++ b/judge/config.py @@ -0,0 +1,13 @@ +import os +import pathlib +from typing import Dict + + +APP_ROOT = pathlib.Path(os.path.dirname(os.path.abspath(__file__))) +CONFINE_PATH = APP_ROOT / 'confine' + +COMPILATION_TIME_LIMIT = 10 +GRADER_TIME_LIMIT = 10 + +PARTIAL_JOB_SUBMIT_TIME_THRESHOLD = 2 # Seconds +PARTIAL_JOB_SUBMIT_CASES_THRESHOLD = 10 diff --git a/judge/confine b/judge/confine new file mode 100755 index 0000000..6a24feb Binary files /dev/null and b/judge/confine differ diff --git a/judge/executor.py b/judge/executor.py new file mode 100644 index 0000000..7d424ac --- /dev/null +++ b/judge/executor.py @@ -0,0 +1,300 @@ +import json +import logging +import os +import shutil +import subprocess +import tempfile +import time +from functools import wraps +from typing import Iterator + +import config +from languages import Language +from models import ExecutionResult, Job, JobVerdict, Problem + +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) +logging.info('Starting up') + +verdict_map = { + 'InternalError': JobVerdict.judge_error, + 'RuntimeError': JobVerdict.runtime_error, + 'TimeLimitExceeded': JobVerdict.time_limit_exceeded, + 'MemoryLimitExceeded': JobVerdict.memory_limit_exceeded, + 'IllegalSyscall': JobVerdict.illegal_syscall, + 'IllegalOpen': JobVerdict.illegal_syscall, + 'IllegalWrite': JobVerdict.illegal_syscall, +} + + +class ExecutionReport: + def __init__(self, execution_ok: bool, execution_error_code: JobVerdict, exitcode: int, realtime: float, + cputime: float, + memory: int): + self.execution_ok = execution_ok + self.execution_error_code = execution_error_code + self.exitcode = exitcode + self.realtime = realtime + self.cputime = cputime + self.memory = memory + + @classmethod + def error_report(cls): + return cls( + execution_ok=False, + execution_error_code=JobVerdict.judge_error, + exitcode=-1, + realtime=0, + cputime=0, + memory=0, + ) + + @classmethod + def from_json(cls, json_string: str): + try: + obj = json.loads(json_string) + return cls( + execution_ok=obj['execution_ok'], + execution_error_code=verdict_map[obj['execution_error_code']['code']] if obj['execution_error_code'] else None, + exitcode=obj['exitcode'], + realtime=obj['realtime'], + cputime=obj['cputime'], + memory=obj['memory'], + ) + except (json.JSONDecodeError, KeyError): + logger.error('Failed to load execution report from json!') + return cls.error_report() + + +class ExecutionProfile: + def __init__(self, confine_path: str, problem: Problem, language: Language, workdir: str, + input_file='input', output_file='output', error_file='error', report_file='report'): + self.confine_path = confine_path + self.language = language + self.workdir = workdir + self.time_limit = problem.time_limit + self.memory_limit = problem.memory_limit + self.input_file = os.path.join(workdir, input_file) + self.output_file = os.path.join(workdir, output_file) + self.error_file = os.path.join(workdir, error_file) + self.report_file = os.path.join(workdir, report_file) + + def as_json(self, executable_name: str): + return json.dumps({ + 'cputime_limit': self.time_limit, + 'realtime_limit': self.time_limit * 1000, + 'allowed_files': self.language.get_allowed_files(self.workdir, executable_name), + 'allowed_prefixes': self.language.get_allowed_file_prefixes(self.workdir, executable_name), + 'stdin_file': self.input_file, + 'stdout_file': self.output_file, + 'stderr_file': self.error_file, + 'json_report_file': self.report_file, + }) + + def execute(self, executable_name: str) -> ExecutionReport: + return Executor(self).execute(executable_name) + + +class Executor: + def __init__(self, profile: ExecutionProfile): + self.profile = profile + + def execute(self, executable_name: str) -> ExecutionReport: + command = self.profile.language.get_command(self.profile.workdir, executable_name) + + config_file_path = os.path.join(self.profile.workdir, 'confine.json') + with open(config_file_path, 'w') as config_file: + config_file.write(self.profile.as_json(executable_name)) + + try: + subprocess.check_call([self.profile.confine_path, '-c', config_file_path, '--', *command], + timeout=self.profile.time_limit * 2) + except (subprocess.CalledProcessError, subprocess.TimeoutExpired): + return ExecutionReport.error_report() + + with open(os.path.join(self.profile.workdir, self.profile.report_file)) as report_file: + execution_report = ExecutionReport.from_json(report_file.read()) + return execution_report + + +def use_tempdir(func): + @wraps(func) + def wrapper(*args, **kwargs): + before_dir = os.getcwd() + tempdir = tempfile.mkdtemp(prefix='jury-') + os.chdir(tempdir) + + result = func(*args, tempdir=tempdir, **kwargs) + + os.chdir(before_dir) + # shutil.rmtree(tempdir) + return result + + return wrapper + + +# @use_tempdir +def run_job(job: Job, tempdir: str) -> Iterator[ExecutionResult]: + result = ExecutionResult( + job=job, + verdict=JobVerdict.judge_error, + last_ran_case=0, + execution_time=0, + execution_memory=0, + ) + + if job.problem.source_verifier_code and job.problem.source_verifier_language: + source_verifier_executable = job.problem.source_verifier_language.compile(job.problem.source_verifier_code, tempdir, + 'source_verifier') + if not source_verifier_executable: + logger.error('Source verifier failed to compile for problem %d' % job.problem.id) + result.verdict = JobVerdict.judge_error + yield result + return + + with open(os.path.join(tempdir, 'source'), 'wb') as source_file: + source_file.write(job.code.encode('utf-8')) + + execution_profile = ExecutionProfile( + confine_path=str(config.CONFINE_PATH), + problem=job.problem, + language=job.problem.source_verifier_language, + workdir=tempdir, + input_file='source', + output_file='source_verifier_result', + ) + + execution_result = execution_profile.execute(source_verifier_executable) + + if not execution_result.execution_ok: + result.verdict = JobVerdict.judge_error + yield result + return + + with open(os.path.join(tempdir, 'source_verifier_result')) as source_verifier_result_file: + if source_verifier_result_file.read().strip() != 'OK': + result.verdict = JobVerdict.invalid_source + yield result + return + + program_executable = job.language.compile(job.code, tempdir, 'program') + if not program_executable: + result.verdict = JobVerdict.compilation_error + yield result + return + + generator_executable = job.problem.generator_language.compile(job.problem.generator_code, tempdir, 'generator') + if not generator_executable: + logger.error('Generator failed to compile for problem %d' % job.problem.id) + result.verdict = JobVerdict.judge_error + yield result + return + + grader_executable = job.problem.grader_language.compile(job.problem.grader_code, tempdir, 'grader') + if not grader_executable: + logger.error('Grader failed to compile for problem %d' % job.problem.id) + result.verdict = JobVerdict.judge_error + yield result + return + + result.verdict = None + last_submitted_time = time.time() + last_submitted_case = 0 + for case_number in range(1, job.problem.test_cases + 1): + result.last_ran_case = case_number + case_result = run_test_case(job, case_number, tempdir, program_executable, generator_executable, grader_executable) + if case_result.verdict != JobVerdict.accepted: + result = case_result + break + result.execution_time = max(result.execution_time, case_result.execution_time) + result.execution_memory = max(result.execution_memory, case_result.execution_memory) + + # Yield result if over threshold and is not last case + # If verdict calculation takes time, result should be changed to yield even if is last case. + if (time.time() - last_submitted_time > config.PARTIAL_JOB_SUBMIT_TIME_THRESHOLD or + case_number - last_submitted_case > config.PARTIAL_JOB_SUBMIT_CASES_THRESHOLD) and \ + case_number != job.problem.test_cases + 1: + yield result + + # We want to let the programs run for `threshold` time before another potential pause + last_submitted_time = time.time() + last_submitted_case = case_number + + if not result.verdict: + result.verdict = JobVerdict.accepted + + yield result + + +def run_test_case(job: Job, case_number: int, workdir: str, program_executable: str, generator_executable: str, + grader_executable: str) -> ExecutionResult: + result = ExecutionResult( + job=job, + verdict=JobVerdict.judge_error, + last_ran_case=case_number, + execution_time=0, + execution_memory=0, + ) + + with open(os.path.join(workdir, 'case_number'), 'wb') as case_number_file: + case_number_file.write(str(case_number).encode('utf-8')) + + generator_execution_profile = ExecutionProfile( + confine_path=str(config.CONFINE_PATH), + problem=job.problem, + language=job.problem.generator_language, + workdir=workdir, + input_file='case_number', + output_file='input', + ) + generator_result = generator_execution_profile.execute(generator_executable) + + if not generator_result.execution_ok: + logger.error('Generator failed for test case %d of problem %d with error %s' % + (case_number, job.problem.id, generator_result.execution_error_code)) + return result + + program_execution_profile = ExecutionProfile( + confine_path=str(config.CONFINE_PATH), + problem=job.problem, + language=job.language, + workdir=workdir, + input_file='input', + output_file='program_output', + error_file='program_error', + ) + execution_result = program_execution_profile.execute(program_executable) + + result.execution_time = execution_result.realtime + result.execution_memory = execution_result.memory + if not execution_result.execution_ok: + result.verdict = execution_result.execution_error_code + return result + + grader_execution_profile = ExecutionProfile( + confine_path=str(config.CONFINE_PATH), + problem=job.problem, + language=job.problem.grader_language, + workdir=workdir, + input_file='input', + output_file='grader_output', + error_file='grader_error', + ) + grader_result = grader_execution_profile.execute(grader_executable) + + if not grader_result.execution_ok: + logger.error('Grader failed for test case %d of problem %d with error %s' % + (case_number, job.problem.id, grader_result.execution_error_code)) + result.verdict = JobVerdict.judge_error + return result + + with open(os.path.join(workdir, 'program_output'), 'rb') as program_output, \ + open(os.path.join(workdir, 'grader_output'), 'rb') as grader_output: + if program_output.read().strip() == grader_output.read().strip(): + final_verdict = JobVerdict.accepted + else: + final_verdict = JobVerdict.wrong_answer + + result.verdict = final_verdict + + return result diff --git a/judge/judge.py b/judge/judge.py new file mode 100644 index 0000000..bfd41d4 --- /dev/null +++ b/judge/judge.py @@ -0,0 +1,76 @@ +import logging +import os +import shutil +import signal +import sys +import time +import tempfile +import traceback + +import executor +from api import API +from models import Job + +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) +logging.info('Starting up') + + +api = None +judge_url = None +current_job = None # type: Job + + +def loop(): + global current_job + job = api.claim() + current_job = job + if not job: + logger.debug('No jobs available.') + return False + logger.info('Got job %d.', job.id) + + tempdir = tempfile.mkdtemp(prefix='jury-') + try: + for execution_result in executor.run_job(job, tempdir): + # execution_result is partial here + + logger.info('Job %d partially judged; case: %d, time: %.2f, memory: %d', + job.id, execution_result.last_ran_case, execution_result.execution_time, + execution_result.execution_memory) + + if execution_result.verdict: + # This should be the last value returned by run_job + logger.info('Job %d finished with verdict %s.' % (job.id, execution_result.verdict.value)) + + if api.submit(execution_result): + logger.info('Job %d successfully partially submitted.' % job.id) + else: + logger.info('Job %d failed to partially submit.' % job.id) + except: + traceback.print_exc(file=sys.stderr) + shutil.rmtree(tempdir, ignore_errors=True) + finally: + shutil.rmtree(tempdir, ignore_errors=True) + + return True + + +if __name__ == '__main__': + api_key = os.getenv("API_KEY") + if not api_key: + print("no api key", file=sys.stderr) + sys.exit(1) + judge_url = os.getenv("JUDGE_URL") + if not judge_url: + print("no judge url", file=sys.stderr) + sys.exit(1) + api = API(api_key, judge_url) + while True: + try: + if not loop(): + time.sleep(3) + except KeyboardInterrupt: + sys.exit(0) + except: + traceback.print_exc(file=sys.stderr) diff --git a/judge/languages.py b/judge/languages.py new file mode 100644 index 0000000..3f98a61 --- /dev/null +++ b/judge/languages.py @@ -0,0 +1,157 @@ +import logging +import subprocess +import os +from abc import ABCMeta +from typing import List, Dict + +import config +from models import JobVerdict + +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) +logging.info('Starting up') + + +class Language(metaclass=ABCMeta): + @classmethod + def compile(cls, source_code: str, workdir: str, executable_name: str, time_limit: float = config.COMPILATION_TIME_LIMIT) -> str: + raise NotImplementedError() + + @classmethod + def get_command(cls, workdir: str, executable_name: str) -> List[str]: + raise NotImplementedError() + + @classmethod + def get_allowed_files(cls, workdir: str, executable_name: str): + raise NotImplementedError() + + @classmethod + def get_allowed_file_prefixes(cls, workdir: str, executable_name: str): + raise NotImplementedError() + + +class CXX(Language): + @classmethod + def compile(cls, source_code: str, workdir: str, executable_name: str, time_limit: float = config.COMPILATION_TIME_LIMIT) -> str: + source_file_path = os.path.join(workdir, 'source.cpp') + with open(source_file_path, 'wb') as source_file: + source_file.write(source_code.encode('utf-8')) + + executable_file_path = os.path.join(workdir, executable_name) + try: + subprocess.check_call(['g++', '--std=c++1y', '-o', executable_file_path, source_file_path], + timeout=config.COMPILATION_TIME_LIMIT) + except (subprocess.CalledProcessError, subprocess.TimeoutExpired): + return None + + return executable_name + + @classmethod + def get_command(cls, workdir: str, executable_name: str) -> List[str]: + return [os.path.join(workdir, executable_name)] + + @classmethod + def get_allowed_files(cls, workdir: str, executable_name: str): + return [] + + @classmethod + def get_allowed_file_prefixes(cls, workdir: str, executable_name: str): + return [] + + +class Python(Language): + language_name = 'python' + interpreter_name = 'python' + + @classmethod + def compile(cls, source_code: str, workdir: str, executable_name: str, time_limit: float = config.COMPILATION_TIME_LIMIT) -> str: + executable_name += '.py' + executable_path = os.path.join(workdir, executable_name) + with open(executable_path, 'wb') as executable_file: + executable_file.write(source_code.encode('utf-8')) + + """try: + subprocess.check_call([cls.interpreter_name, '-m', 'py_compile', executable_name], + timeout=config.COMPILATION_TIME_LIMIT) + except (subprocess.CalledProcessError, subprocess.TimeoutExpired): + return None""" + + return executable_name + + @classmethod + def get_command(cls, workdir: str, executable_name: str) -> List[str]: + return [os.path.join('/usr/bin', cls.interpreter_name), '-s', '-S', os.path.join(workdir, executable_name)] + + @classmethod + def get_allowed_files(cls, workdir: str, executable_name: str): + return [ + '/etc/nsswitch.conf', + '/etc/passwd', + '/dev/urandom', # TODO: come up with random policy + '/tmp', + '/bin/Modules/Setup', + workdir, + os.path.join(workdir, executable_name), + ] + + @classmethod + def get_allowed_file_prefixes(cls, workdir: str, executable_name: str): + return [] + + +class Java(Language): + @classmethod + def compile(cls, source_code: str, workdir: str, executable_name: str, time_limit: float = config.COMPILATION_TIME_LIMIT) -> str: + source_file_path = os.path.join(workdir, 'Main.java') + with open(source_file_path, 'wb') as source_file: + source_file.write(source_code.encode('utf-8')) + + executable_file_path = os.path.join(workdir, 'Main') + try: + subprocess.check_call(['javac', '-d', workdir, source_file_path], + timeout=config.COMPILATION_TIME_LIMIT) + except (subprocess.CalledProcessError, subprocess.TimeoutExpired): + return None + + return 'Main' + + @classmethod + def get_command(cls, workdir: str, executable_name: str) -> List[str]: + return ['/usr/bin/java', '-XX:-UsePerfData', '-XX:+DisableAttachMechanism', '-Xmx256m', '-Xrs', '-cp', + workdir, executable_name] + + @classmethod + def get_allowed_files(cls, workdir: str, executable_name: str): + return [ + '/etc/nsswitch.conf', + '/etc/passwd', + '/tmp', + workdir, + os.path.join(workdir, executable_name + '.class'), + ] + + @classmethod + def get_allowed_file_prefixes(cls, workdir: str, executable_name: str): + return [ + '/etc/java-7-openjdk/', + '/tmp/.java_pid', + '/tmp/', + ] + + +class Python2(Python): + language_name = 'python2' + interpreter_name = 'python2.7' + + +class Python3(Python): + language_name = 'python3' + interpreter_name = 'python3.5' + + +languages = { + 'cxx': CXX, + 'python2': Python2, + 'python3': Python3, + 'java': Java, +} # type: Dict[str, Language] diff --git a/judge/models.py b/judge/models.py new file mode 100644 index 0000000..76a102d --- /dev/null +++ b/judge/models.py @@ -0,0 +1,47 @@ +import enum + + +class Problem: + def __init__(self, id: int, test_cases: int, time_limit: float, memory_limit: int, + generator_code: str, generator_language, grader_code: str, grader_language, + source_verifier_code: str=None, source_verifier_language=None): + self.id = id + self.test_cases = test_cases + self.time_limit = time_limit + self.memory_limit = memory_limit + self.generator_code = generator_code + self.generator_language = generator_language + self.grader_code = grader_code + self.grader_language = grader_language + self.source_verifier_code = source_verifier_code + self.source_verifier_language = source_verifier_language + + +class Job: + def __init__(self, id: int, problem: Problem, code: str, language): + self.id = id + self.problem = problem + self.code = code + self.language = language + + +class JobVerdict(enum.Enum): + accepted = 'AC' + ran = 'RAN' + invalid_source = 'IS' + wrong_answer = 'WA' + time_limit_exceeded = 'TLE' + memory_limit_exceeded = 'MLE' + runtime_error = 'RTE' + illegal_syscall = 'ISC' + compilation_error = 'CE' + judge_error = 'JE' + + +class ExecutionResult: + def __init__(self, job: Job, verdict: JobVerdict, last_ran_case: int, execution_time: float, execution_memory: int): + self.job = job + self.verdict = verdict + self.last_ran_case = last_ran_case + self.execution_time = execution_time + self.execution_memory = execution_memory diff --git a/judge/nsjail b/judge/nsjail new file mode 100755 index 0000000..99635b1 Binary files /dev/null and b/judge/nsjail differ diff --git a/judge/output b/judge/output new file mode 100644 index 0000000..e69de29 diff --git a/judge/systemd/judge.service b/judge/systemd/judge.service new file mode 100644 index 0000000..eb1171a --- /dev/null +++ b/judge/systemd/judge.service @@ -0,0 +1,12 @@ +[Unit] +Description=indepedent judging unit + +[Service] +Restart=always +Environment=\"API_KEY=$API_KEY\" +Environment=\"JUDGE_URL=$JUDGE_URL\" +ExecStart=$PYTHON $PROJECT_DIRECTORY/judge.py +ExecStop=: + +[Install] +WantedBy=default.target diff --git a/nginx/easyctf.conf b/nginx/easyctf.conf new file mode 100755 index 0000000..82f757a --- /dev/null +++ b/nginx/easyctf.conf @@ -0,0 +1,25 @@ +server { + listen 80; + server_name localhost localhost.easyctf.com; + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log error; + + underscores_in_headers on; + + location /static { + proxy_set_header HOST $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://filestore/; + } + + location / { + proxy_set_header HOST $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://easyctf:5000/; + } +} diff --git a/nginx/easyctf.vagrant.conf b/nginx/easyctf.vagrant.conf new file mode 100755 index 0000000..c8f93ec --- /dev/null +++ b/nginx/easyctf.vagrant.conf @@ -0,0 +1,22 @@ +server { + listen 80; + server_name localhost localhost.easyctf.com; + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log error; + + underscores_in_headers on; + + location /static { + root /var/opt/filestore; + autoindex off; + } + + location / { + proxy_set_header HOST $http_host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://localhost:5000/; + } +} diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100755 index 0000000..83acb75 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,32 @@ +# user nginx; +worker_processes 1; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile off; + #tcp_nopush on; + + keepalive_timeout 65; + + gzip on; + gzip_proxied any; + gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; + + include /etc/nginx/conf.d/*.conf; + include /etc/nginx/sites-enabled/easyctf.conf; +} diff --git a/provision.sh b/provision.sh new file mode 100755 index 0000000..70699bc --- /dev/null +++ b/provision.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +apt-get update && apt-get install -y \ + build-essential \ + git \ + libffi-dev \ + libjpeg-dev \ + libmysqlclient-dev \ + libpng-dev \ + libssl-dev \ + mysql-client \ + mysql-server \ + nginx \ + openssh-client \ + pkg-config \ + python2.7 \ + python3 \ + python3-dev \ + python3-nose \ + python3-pip \ + realpath \ + redis-server \ + systemd \ + +(cd server; ./cloud-provision.sh) + +(cd filestore; ./cloud-provision.sh) diff --git a/server/.gitignore b/server/.gitignore new file mode 100755 index 0000000..3348b98 --- /dev/null +++ b/server/.gitignore @@ -0,0 +1,4 @@ +__pycache__ +*.pyc +easyctf.db +secret.sh \ No newline at end of file diff --git a/server/app.py b/server/app.py new file mode 100755 index 0000000..f39bf04 --- /dev/null +++ b/server/app.py @@ -0,0 +1,3 @@ +from easyctf import create_app + +app = create_app() diff --git a/server/cloud-provision.sh b/server/cloud-provision.sh new file mode 100755 index 0000000..b288367 --- /dev/null +++ b/server/cloud-provision.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# run this to set up the server +# only do this the first time +set -e +# set -o xtrace + +REPOSITORY="git@github.com:iptq/easyctf-platform.git" +PROJECT_DIRECTORY="/var/easyctf/src" +PYTHON=python3 + +echo "installing system dependencies..." +if [ ! -f $HOME/.installdep.server.apt ]; then + apt-get update && apt-get install -y \ + git \ + libffi-dev \ + libjpeg-dev \ + libmysqlclient-dev \ + libpng-dev \ + libssl-dev \ + mysql-client \ + openssh-client \ + python3 \ + python3-dev \ + python3-nose \ + python3-pip \ + realpath \ + systemd + touch $HOME/.installdep.server.apt +fi + +mkdir -p /var/easyctf +mkdir -p /var/log/easyctf + +if [ ! -d $PROJECT_DIRECTORY ]; then + # why the fuck shoul i clone if i already hav this file LMAO + b=`realpath $(basename $0)` + c=`dirname $b` + d=`dirname $c` + # cp -r $d $PROJECT_DIRECTORY + ln -s $c $PROJECT_DIRECTORY +else + (cd $PROJECT_DIRECTORY; git pull origin master || true) +fi + +echo "installing python dependencies..." +if [ ! -f $HOME/.installdep.server.pip ]; then + $PYTHON -m pip install -U pip + $PYTHON -m pip install gunicorn + $PYTHON -m pip install -r $PROJECT_DIRECTORY/requirements.txt + touch $HOME/.installdep.server.pip +fi + +# dirty hack +KILL=/bin/kill +eval "echo \"$(< $PROJECT_DIRECTORY/systemd/easyctf.service)\"" > /etc/systemd/system/easyctf.service + +echo "EasyCTF has been deployed!" +echo "Modify the env file at /var/easyctf/env." +echo "Then run" +echo +echo "systemctl --system daemon-reload && systemctl restart easyctf" +echo "gucci gang" + +cp env.example /var/easyctf/env +systemctl --system daemon-reload && systemctl restart easyctf diff --git a/server/easyctf/__init__.py b/server/easyctf/__init__.py new file mode 100755 index 0000000..ed9998e --- /dev/null +++ b/server/easyctf/__init__.py @@ -0,0 +1,86 @@ +from datetime import datetime +import time +import logging +import socket + +from flask import Flask, request +from flask_login import current_user + + +def create_app(config=None): + app = Flask(__name__, static_folder="assets", static_path="/assets") + hostname = socket.gethostname() + + if not config: + from easyctf.config import Config + config = Config() + app.config.from_object(config) + + from easyctf.objects import cache, db, login_manager, sentry + import easyctf.models + cache.init_app(app) + db.init_app(app) + login_manager.init_app(app) + if app.config.get("ENVIRONMENT") != "development": + sentry.init_app(app, logging=True, level=logging.WARNING) + + from easyctf.utils import filestore, to_place_str, to_timestamp + app.jinja_env.globals.update(filestore=filestore) + app.jinja_env.filters["to_timestamp"] = to_timestamp + app.jinja_env.filters["to_place_str"] = to_place_str + + from easyctf.models import Config + + def get_competition_running(): + configs = Config.get_many(["start_time", "end_time"]) + if "start_time" not in configs or "end_time" not in configs: + return None, None, False + start_time_str = configs["start_time"] + end_time_str = configs["end_time"] + + start_time = datetime.fromtimestamp(float(start_time_str)) + end_time = datetime.fromtimestamp(float(end_time_str)) + now = datetime.utcnow() + + competition_running = start_time < now and now < end_time + return start_time, end_time, competition_running + + @app.after_request + def easter_egg_link(response): + if not request.cookies.get("easter_egg_enabled"): + response.set_cookie("easter_egg_enabled", "0") + return response + + # TODO: actually finish this + @app.context_processor + def inject_config(): + competition_start, competition_end, competition_running = get_competition_running() + easter_egg_enabled = False + if competition_running and current_user.is_authenticated: + try: + easter_egg_enabled = int(request.cookies.get("easter_egg_enabled")) == 1 + except: + pass + config = dict( + admin_email="", + hostname=hostname, + competition_running=competition_running, + competition_start=competition_start, + competition_end=competition_end, + ctf_name=Config.get("ctf_name", "OpenCTF"), + easter_egg_enabled=easter_egg_enabled, + environment=app.config.get("ENVIRONMENT", "production") + ) + return config + + from easyctf.views import admin, base, classroom, chals, game, judge, teams, users + app.register_blueprint(admin.blueprint, url_prefix="/admin") + app.register_blueprint(base.blueprint) + app.register_blueprint(classroom.blueprint, url_prefix="/classroom") + app.register_blueprint(chals.blueprint, url_prefix="/chals") + app.register_blueprint(game.blueprint, url_prefix="/game") + app.register_blueprint(judge.blueprint, url_prefix="/judge") + app.register_blueprint(teams.blueprint, url_prefix="/teams") + app.register_blueprint(users.blueprint, url_prefix="/users") + + return app diff --git a/server/easyctf/assets/css/bootstrap.min.css b/server/easyctf/assets/css/bootstrap.min.css new file mode 100755 index 0000000..a9558eb --- /dev/null +++ b/server/easyctf/assets/css/bootstrap.min.css @@ -0,0 +1,5 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} \ No newline at end of file diff --git a/server/easyctf/assets/css/font-awesome.css b/server/easyctf/assets/css/font-awesome.css new file mode 100755 index 0000000..ee906a8 --- /dev/null +++ b/server/easyctf/assets/css/font-awesome.css @@ -0,0 +1,2337 @@ +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */ +/* FONT PATH + * -------------------------- */ +@font-face { + font-family: 'FontAwesome'; + src: url('../fonts/fontawesome-webfont.eot?v=4.7.0'); + src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg'); + font-weight: normal; + font-style: normal; +} +.fa { + display: inline-block; + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +/* makes the font 33% larger relative to the icon container */ +.fa-lg { + font-size: 1.33333333em; + line-height: 0.75em; + vertical-align: -15%; +} +.fa-2x { + font-size: 2em; +} +.fa-3x { + font-size: 3em; +} +.fa-4x { + font-size: 4em; +} +.fa-5x { + font-size: 5em; +} +.fa-fw { + width: 1.28571429em; + text-align: center; +} +.fa-ul { + padding-left: 0; + margin-left: 2.14285714em; + list-style-type: none; +} +.fa-ul > li { + position: relative; +} +.fa-li { + position: absolute; + left: -2.14285714em; + width: 2.14285714em; + top: 0.14285714em; + text-align: center; +} +.fa-li.fa-lg { + left: -1.85714286em; +} +.fa-border { + padding: .2em .25em .15em; + border: solid 0.08em #eeeeee; + border-radius: .1em; +} +.fa-pull-left { + float: left; +} +.fa-pull-right { + float: right; +} +.fa.fa-pull-left { + margin-right: .3em; +} +.fa.fa-pull-right { + margin-left: .3em; +} +/* Deprecated as of 4.4.0 */ +.pull-right { + float: right; +} +.pull-left { + float: left; +} +.fa.pull-left { + margin-right: .3em; +} +.fa.pull-right { + margin-left: .3em; +} +.fa-spin { + -webkit-animation: fa-spin 2s infinite linear; + animation: fa-spin 2s infinite linear; +} +.fa-pulse { + -webkit-animation: fa-spin 1s infinite steps(8); + animation: fa-spin 1s infinite steps(8); +} +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +.fa-rotate-90 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)"; + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} +.fa-rotate-180 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)"; + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} +.fa-rotate-270 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)"; + -webkit-transform: rotate(270deg); + -ms-transform: rotate(270deg); + transform: rotate(270deg); +} +.fa-flip-horizontal { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)"; + -webkit-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + transform: scale(-1, 1); +} +.fa-flip-vertical { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"; + -webkit-transform: scale(1, -1); + -ms-transform: scale(1, -1); + transform: scale(1, -1); +} +:root .fa-rotate-90, +:root .fa-rotate-180, +:root .fa-rotate-270, +:root .fa-flip-horizontal, +:root .fa-flip-vertical { + filter: none; +} +.fa-stack { + position: relative; + display: inline-block; + width: 2em; + height: 2em; + line-height: 2em; + vertical-align: middle; +} +.fa-stack-1x, +.fa-stack-2x { + position: absolute; + left: 0; + width: 100%; + text-align: center; +} +.fa-stack-1x { + line-height: inherit; +} +.fa-stack-2x { + font-size: 2em; +} +.fa-inverse { + color: #ffffff; +} +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen + readers do not read off random characters that represent icons */ +.fa-glass:before { + content: "\f000"; +} +.fa-music:before { + content: "\f001"; +} +.fa-search:before { + content: "\f002"; +} +.fa-envelope-o:before { + content: "\f003"; +} +.fa-heart:before { + content: "\f004"; +} +.fa-star:before { + content: "\f005"; +} +.fa-star-o:before { + content: "\f006"; +} +.fa-user:before { + content: "\f007"; +} +.fa-film:before { + content: "\f008"; +} +.fa-th-large:before { + content: "\f009"; +} +.fa-th:before { + content: "\f00a"; +} +.fa-th-list:before { + content: "\f00b"; +} +.fa-check:before { + content: "\f00c"; +} +.fa-remove:before, +.fa-close:before, +.fa-times:before { + content: "\f00d"; +} +.fa-search-plus:before { + content: "\f00e"; +} +.fa-search-minus:before { + content: "\f010"; +} +.fa-power-off:before { + content: "\f011"; +} +.fa-signal:before { + content: "\f012"; +} +.fa-gear:before, +.fa-cog:before { + content: "\f013"; +} +.fa-trash-o:before { + content: "\f014"; +} +.fa-home:before { + content: "\f015"; +} +.fa-file-o:before { + content: "\f016"; +} +.fa-clock-o:before { + content: "\f017"; +} +.fa-road:before { + content: "\f018"; +} +.fa-download:before { + content: "\f019"; +} +.fa-arrow-circle-o-down:before { + content: "\f01a"; +} +.fa-arrow-circle-o-up:before { + content: "\f01b"; +} +.fa-inbox:before { + content: "\f01c"; +} +.fa-play-circle-o:before { + content: "\f01d"; +} +.fa-rotate-right:before, +.fa-repeat:before { + content: "\f01e"; +} +.fa-refresh:before { + content: "\f021"; +} +.fa-list-alt:before { + content: "\f022"; +} +.fa-lock:before { + content: "\f023"; +} +.fa-flag:before { + content: "\f024"; +} +.fa-headphones:before { + content: "\f025"; +} +.fa-volume-off:before { + content: "\f026"; +} +.fa-volume-down:before { + content: "\f027"; +} +.fa-volume-up:before { + content: "\f028"; +} +.fa-qrcode:before { + content: "\f029"; +} +.fa-barcode:before { + content: "\f02a"; +} +.fa-tag:before { + content: "\f02b"; +} +.fa-tags:before { + content: "\f02c"; +} +.fa-book:before { + content: "\f02d"; +} +.fa-bookmark:before { + content: "\f02e"; +} +.fa-print:before { + content: "\f02f"; +} +.fa-camera:before { + content: "\f030"; +} +.fa-font:before { + content: "\f031"; +} +.fa-bold:before { + content: "\f032"; +} +.fa-italic:before { + content: "\f033"; +} +.fa-text-height:before { + content: "\f034"; +} +.fa-text-width:before { + content: "\f035"; +} +.fa-align-left:before { + content: "\f036"; +} +.fa-align-center:before { + content: "\f037"; +} +.fa-align-right:before { + content: "\f038"; +} +.fa-align-justify:before { + content: "\f039"; +} +.fa-list:before { + content: "\f03a"; +} +.fa-dedent:before, +.fa-outdent:before { + content: "\f03b"; +} +.fa-indent:before { + content: "\f03c"; +} +.fa-video-camera:before { + content: "\f03d"; +} +.fa-photo:before, +.fa-image:before, +.fa-picture-o:before { + content: "\f03e"; +} +.fa-pencil:before { + content: "\f040"; +} +.fa-map-marker:before { + content: "\f041"; +} +.fa-adjust:before { + content: "\f042"; +} +.fa-tint:before { + content: "\f043"; +} +.fa-edit:before, +.fa-pencil-square-o:before { + content: "\f044"; +} +.fa-share-square-o:before { + content: "\f045"; +} +.fa-check-square-o:before { + content: "\f046"; +} +.fa-arrows:before { + content: "\f047"; +} +.fa-step-backward:before { + content: "\f048"; +} +.fa-fast-backward:before { + content: "\f049"; +} +.fa-backward:before { + content: "\f04a"; +} +.fa-play:before { + content: "\f04b"; +} +.fa-pause:before { + content: "\f04c"; +} +.fa-stop:before { + content: "\f04d"; +} +.fa-forward:before { + content: "\f04e"; +} +.fa-fast-forward:before { + content: "\f050"; +} +.fa-step-forward:before { + content: "\f051"; +} +.fa-eject:before { + content: "\f052"; +} +.fa-chevron-left:before { + content: "\f053"; +} +.fa-chevron-right:before { + content: "\f054"; +} +.fa-plus-circle:before { + content: "\f055"; +} +.fa-minus-circle:before { + content: "\f056"; +} +.fa-times-circle:before { + content: "\f057"; +} +.fa-check-circle:before { + content: "\f058"; +} +.fa-question-circle:before { + content: "\f059"; +} +.fa-info-circle:before { + content: "\f05a"; +} +.fa-crosshairs:before { + content: "\f05b"; +} +.fa-times-circle-o:before { + content: "\f05c"; +} +.fa-check-circle-o:before { + content: "\f05d"; +} +.fa-ban:before { + content: "\f05e"; +} +.fa-arrow-left:before { + content: "\f060"; +} +.fa-arrow-right:before { + content: "\f061"; +} +.fa-arrow-up:before { + content: "\f062"; +} +.fa-arrow-down:before { + content: "\f063"; +} +.fa-mail-forward:before, +.fa-share:before { + content: "\f064"; +} +.fa-expand:before { + content: "\f065"; +} +.fa-compress:before { + content: "\f066"; +} +.fa-plus:before { + content: "\f067"; +} +.fa-minus:before { + content: "\f068"; +} +.fa-asterisk:before { + content: "\f069"; +} +.fa-exclamation-circle:before { + content: "\f06a"; +} +.fa-gift:before { + content: "\f06b"; +} +.fa-leaf:before { + content: "\f06c"; +} +.fa-fire:before { + content: "\f06d"; +} +.fa-eye:before { + content: "\f06e"; +} +.fa-eye-slash:before { + content: "\f070"; +} +.fa-warning:before, +.fa-exclamation-triangle:before { + content: "\f071"; +} +.fa-plane:before { + content: "\f072"; +} +.fa-calendar:before { + content: "\f073"; +} +.fa-random:before { + content: "\f074"; +} +.fa-comment:before { + content: "\f075"; +} +.fa-magnet:before { + content: "\f076"; +} +.fa-chevron-up:before { + content: "\f077"; +} +.fa-chevron-down:before { + content: "\f078"; +} +.fa-retweet:before { + content: "\f079"; +} +.fa-shopping-cart:before { + content: "\f07a"; +} +.fa-folder:before { + content: "\f07b"; +} +.fa-folder-open:before { + content: "\f07c"; +} +.fa-arrows-v:before { + content: "\f07d"; +} +.fa-arrows-h:before { + content: "\f07e"; +} +.fa-bar-chart-o:before, +.fa-bar-chart:before { + content: "\f080"; +} +.fa-twitter-square:before { + content: "\f081"; +} +.fa-facebook-square:before { + content: "\f082"; +} +.fa-camera-retro:before { + content: "\f083"; +} +.fa-key:before { + content: "\f084"; +} +.fa-gears:before, +.fa-cogs:before { + content: "\f085"; +} +.fa-comments:before { + content: "\f086"; +} +.fa-thumbs-o-up:before { + content: "\f087"; +} +.fa-thumbs-o-down:before { + content: "\f088"; +} +.fa-star-half:before { + content: "\f089"; +} +.fa-heart-o:before { + content: "\f08a"; +} +.fa-sign-out:before { + content: "\f08b"; +} +.fa-linkedin-square:before { + content: "\f08c"; +} +.fa-thumb-tack:before { + content: "\f08d"; +} +.fa-external-link:before { + content: "\f08e"; +} +.fa-sign-in:before { + content: "\f090"; +} +.fa-trophy:before { + content: "\f091"; +} +.fa-github-square:before { + content: "\f092"; +} +.fa-upload:before { + content: "\f093"; +} +.fa-lemon-o:before { + content: "\f094"; +} +.fa-phone:before { + content: "\f095"; +} +.fa-square-o:before { + content: "\f096"; +} +.fa-bookmark-o:before { + content: "\f097"; +} +.fa-phone-square:before { + content: "\f098"; +} +.fa-twitter:before { + content: "\f099"; +} +.fa-facebook-f:before, +.fa-facebook:before { + content: "\f09a"; +} +.fa-github:before { + content: "\f09b"; +} +.fa-unlock:before { + content: "\f09c"; +} +.fa-credit-card:before { + content: "\f09d"; +} +.fa-feed:before, +.fa-rss:before { + content: "\f09e"; +} +.fa-hdd-o:before { + content: "\f0a0"; +} +.fa-bullhorn:before { + content: "\f0a1"; +} +.fa-bell:before { + content: "\f0f3"; +} +.fa-certificate:before { + content: "\f0a3"; +} +.fa-hand-o-right:before { + content: "\f0a4"; +} +.fa-hand-o-left:before { + content: "\f0a5"; +} +.fa-hand-o-up:before { + content: "\f0a6"; +} +.fa-hand-o-down:before { + content: "\f0a7"; +} +.fa-arrow-circle-left:before { + content: "\f0a8"; +} +.fa-arrow-circle-right:before { + content: "\f0a9"; +} +.fa-arrow-circle-up:before { + content: "\f0aa"; +} +.fa-arrow-circle-down:before { + content: "\f0ab"; +} +.fa-globe:before { + content: "\f0ac"; +} +.fa-wrench:before { + content: "\f0ad"; +} +.fa-tasks:before { + content: "\f0ae"; +} +.fa-filter:before { + content: "\f0b0"; +} +.fa-briefcase:before { + content: "\f0b1"; +} +.fa-arrows-alt:before { + content: "\f0b2"; +} +.fa-group:before, +.fa-users:before { + content: "\f0c0"; +} +.fa-chain:before, +.fa-link:before { + content: "\f0c1"; +} +.fa-cloud:before { + content: "\f0c2"; +} +.fa-flask:before { + content: "\f0c3"; +} +.fa-cut:before, +.fa-scissors:before { + content: "\f0c4"; +} +.fa-copy:before, +.fa-files-o:before { + content: "\f0c5"; +} +.fa-paperclip:before { + content: "\f0c6"; +} +.fa-save:before, +.fa-floppy-o:before { + content: "\f0c7"; +} +.fa-square:before { + content: "\f0c8"; +} +.fa-navicon:before, +.fa-reorder:before, +.fa-bars:before { + content: "\f0c9"; +} +.fa-list-ul:before { + content: "\f0ca"; +} +.fa-list-ol:before { + content: "\f0cb"; +} +.fa-strikethrough:before { + content: "\f0cc"; +} +.fa-underline:before { + content: "\f0cd"; +} +.fa-table:before { + content: "\f0ce"; +} +.fa-magic:before { + content: "\f0d0"; +} +.fa-truck:before { + content: "\f0d1"; +} +.fa-pinterest:before { + content: "\f0d2"; +} +.fa-pinterest-square:before { + content: "\f0d3"; +} +.fa-google-plus-square:before { + content: "\f0d4"; +} +.fa-google-plus:before { + content: "\f0d5"; +} +.fa-money:before { + content: "\f0d6"; +} +.fa-caret-down:before { + content: "\f0d7"; +} +.fa-caret-up:before { + content: "\f0d8"; +} +.fa-caret-left:before { + content: "\f0d9"; +} +.fa-caret-right:before { + content: "\f0da"; +} +.fa-columns:before { + content: "\f0db"; +} +.fa-unsorted:before, +.fa-sort:before { + content: "\f0dc"; +} +.fa-sort-down:before, +.fa-sort-desc:before { + content: "\f0dd"; +} +.fa-sort-up:before, +.fa-sort-asc:before { + content: "\f0de"; +} +.fa-envelope:before { + content: "\f0e0"; +} +.fa-linkedin:before { + content: "\f0e1"; +} +.fa-rotate-left:before, +.fa-undo:before { + content: "\f0e2"; +} +.fa-legal:before, +.fa-gavel:before { + content: "\f0e3"; +} +.fa-dashboard:before, +.fa-tachometer:before { + content: "\f0e4"; +} +.fa-comment-o:before { + content: "\f0e5"; +} +.fa-comments-o:before { + content: "\f0e6"; +} +.fa-flash:before, +.fa-bolt:before { + content: "\f0e7"; +} +.fa-sitemap:before { + content: "\f0e8"; +} +.fa-umbrella:before { + content: "\f0e9"; +} +.fa-paste:before, +.fa-clipboard:before { + content: "\f0ea"; +} +.fa-lightbulb-o:before { + content: "\f0eb"; +} +.fa-exchange:before { + content: "\f0ec"; +} +.fa-cloud-download:before { + content: "\f0ed"; +} +.fa-cloud-upload:before { + content: "\f0ee"; +} +.fa-user-md:before { + content: "\f0f0"; +} +.fa-stethoscope:before { + content: "\f0f1"; +} +.fa-suitcase:before { + content: "\f0f2"; +} +.fa-bell-o:before { + content: "\f0a2"; +} +.fa-coffee:before { + content: "\f0f4"; +} +.fa-cutlery:before { + content: "\f0f5"; +} +.fa-file-text-o:before { + content: "\f0f6"; +} +.fa-building-o:before { + content: "\f0f7"; +} +.fa-hospital-o:before { + content: "\f0f8"; +} +.fa-ambulance:before { + content: "\f0f9"; +} +.fa-medkit:before { + content: "\f0fa"; +} +.fa-fighter-jet:before { + content: "\f0fb"; +} +.fa-beer:before { + content: "\f0fc"; +} +.fa-h-square:before { + content: "\f0fd"; +} +.fa-plus-square:before { + content: "\f0fe"; +} +.fa-angle-double-left:before { + content: "\f100"; +} +.fa-angle-double-right:before { + content: "\f101"; +} +.fa-angle-double-up:before { + content: "\f102"; +} +.fa-angle-double-down:before { + content: "\f103"; +} +.fa-angle-left:before { + content: "\f104"; +} +.fa-angle-right:before { + content: "\f105"; +} +.fa-angle-up:before { + content: "\f106"; +} +.fa-angle-down:before { + content: "\f107"; +} +.fa-desktop:before { + content: "\f108"; +} +.fa-laptop:before { + content: "\f109"; +} +.fa-tablet:before { + content: "\f10a"; +} +.fa-mobile-phone:before, +.fa-mobile:before { + content: "\f10b"; +} +.fa-circle-o:before { + content: "\f10c"; +} +.fa-quote-left:before { + content: "\f10d"; +} +.fa-quote-right:before { + content: "\f10e"; +} +.fa-spinner:before { + content: "\f110"; +} +.fa-circle:before { + content: "\f111"; +} +.fa-mail-reply:before, +.fa-reply:before { + content: "\f112"; +} +.fa-github-alt:before { + content: "\f113"; +} +.fa-folder-o:before { + content: "\f114"; +} +.fa-folder-open-o:before { + content: "\f115"; +} +.fa-smile-o:before { + content: "\f118"; +} +.fa-frown-o:before { + content: "\f119"; +} +.fa-meh-o:before { + content: "\f11a"; +} +.fa-gamepad:before { + content: "\f11b"; +} +.fa-keyboard-o:before { + content: "\f11c"; +} +.fa-flag-o:before { + content: "\f11d"; +} +.fa-flag-checkered:before { + content: "\f11e"; +} +.fa-terminal:before { + content: "\f120"; +} +.fa-code:before { + content: "\f121"; +} +.fa-mail-reply-all:before, +.fa-reply-all:before { + content: "\f122"; +} +.fa-star-half-empty:before, +.fa-star-half-full:before, +.fa-star-half-o:before { + content: "\f123"; +} +.fa-location-arrow:before { + content: "\f124"; +} +.fa-crop:before { + content: "\f125"; +} +.fa-code-fork:before { + content: "\f126"; +} +.fa-unlink:before, +.fa-chain-broken:before { + content: "\f127"; +} +.fa-question:before { + content: "\f128"; +} +.fa-info:before { + content: "\f129"; +} +.fa-exclamation:before { + content: "\f12a"; +} +.fa-superscript:before { + content: "\f12b"; +} +.fa-subscript:before { + content: "\f12c"; +} +.fa-eraser:before { + content: "\f12d"; +} +.fa-puzzle-piece:before { + content: "\f12e"; +} +.fa-microphone:before { + content: "\f130"; +} +.fa-microphone-slash:before { + content: "\f131"; +} +.fa-shield:before { + content: "\f132"; +} +.fa-calendar-o:before { + content: "\f133"; +} +.fa-fire-extinguisher:before { + content: "\f134"; +} +.fa-rocket:before { + content: "\f135"; +} +.fa-maxcdn:before { + content: "\f136"; +} +.fa-chevron-circle-left:before { + content: "\f137"; +} +.fa-chevron-circle-right:before { + content: "\f138"; +} +.fa-chevron-circle-up:before { + content: "\f139"; +} +.fa-chevron-circle-down:before { + content: "\f13a"; +} +.fa-html5:before { + content: "\f13b"; +} +.fa-css3:before { + content: "\f13c"; +} +.fa-anchor:before { + content: "\f13d"; +} +.fa-unlock-alt:before { + content: "\f13e"; +} +.fa-bullseye:before { + content: "\f140"; +} +.fa-ellipsis-h:before { + content: "\f141"; +} +.fa-ellipsis-v:before { + content: "\f142"; +} +.fa-rss-square:before { + content: "\f143"; +} +.fa-play-circle:before { + content: "\f144"; +} +.fa-ticket:before { + content: "\f145"; +} +.fa-minus-square:before { + content: "\f146"; +} +.fa-minus-square-o:before { + content: "\f147"; +} +.fa-level-up:before { + content: "\f148"; +} +.fa-level-down:before { + content: "\f149"; +} +.fa-check-square:before { + content: "\f14a"; +} +.fa-pencil-square:before { + content: "\f14b"; +} +.fa-external-link-square:before { + content: "\f14c"; +} +.fa-share-square:before { + content: "\f14d"; +} +.fa-compass:before { + content: "\f14e"; +} +.fa-toggle-down:before, +.fa-caret-square-o-down:before { + content: "\f150"; +} +.fa-toggle-up:before, +.fa-caret-square-o-up:before { + content: "\f151"; +} +.fa-toggle-right:before, +.fa-caret-square-o-right:before { + content: "\f152"; +} +.fa-euro:before, +.fa-eur:before { + content: "\f153"; +} +.fa-gbp:before { + content: "\f154"; +} +.fa-dollar:before, +.fa-usd:before { + content: "\f155"; +} +.fa-rupee:before, +.fa-inr:before { + content: "\f156"; +} +.fa-cny:before, +.fa-rmb:before, +.fa-yen:before, +.fa-jpy:before { + content: "\f157"; +} +.fa-ruble:before, +.fa-rouble:before, +.fa-rub:before { + content: "\f158"; +} +.fa-won:before, +.fa-krw:before { + content: "\f159"; +} +.fa-bitcoin:before, +.fa-btc:before { + content: "\f15a"; +} +.fa-file:before { + content: "\f15b"; +} +.fa-file-text:before { + content: "\f15c"; +} +.fa-sort-alpha-asc:before { + content: "\f15d"; +} +.fa-sort-alpha-desc:before { + content: "\f15e"; +} +.fa-sort-amount-asc:before { + content: "\f160"; +} +.fa-sort-amount-desc:before { + content: "\f161"; +} +.fa-sort-numeric-asc:before { + content: "\f162"; +} +.fa-sort-numeric-desc:before { + content: "\f163"; +} +.fa-thumbs-up:before { + content: "\f164"; +} +.fa-thumbs-down:before { + content: "\f165"; +} +.fa-youtube-square:before { + content: "\f166"; +} +.fa-youtube:before { + content: "\f167"; +} +.fa-xing:before { + content: "\f168"; +} +.fa-xing-square:before { + content: "\f169"; +} +.fa-youtube-play:before { + content: "\f16a"; +} +.fa-dropbox:before { + content: "\f16b"; +} +.fa-stack-overflow:before { + content: "\f16c"; +} +.fa-instagram:before { + content: "\f16d"; +} +.fa-flickr:before { + content: "\f16e"; +} +.fa-adn:before { + content: "\f170"; +} +.fa-bitbucket:before { + content: "\f171"; +} +.fa-bitbucket-square:before { + content: "\f172"; +} +.fa-tumblr:before { + content: "\f173"; +} +.fa-tumblr-square:before { + content: "\f174"; +} +.fa-long-arrow-down:before { + content: "\f175"; +} +.fa-long-arrow-up:before { + content: "\f176"; +} +.fa-long-arrow-left:before { + content: "\f177"; +} +.fa-long-arrow-right:before { + content: "\f178"; +} +.fa-apple:before { + content: "\f179"; +} +.fa-windows:before { + content: "\f17a"; +} +.fa-android:before { + content: "\f17b"; +} +.fa-linux:before { + content: "\f17c"; +} +.fa-dribbble:before { + content: "\f17d"; +} +.fa-skype:before { + content: "\f17e"; +} +.fa-foursquare:before { + content: "\f180"; +} +.fa-trello:before { + content: "\f181"; +} +.fa-female:before { + content: "\f182"; +} +.fa-male:before { + content: "\f183"; +} +.fa-gittip:before, +.fa-gratipay:before { + content: "\f184"; +} +.fa-sun-o:before { + content: "\f185"; +} +.fa-moon-o:before { + content: "\f186"; +} +.fa-archive:before { + content: "\f187"; +} +.fa-bug:before { + content: "\f188"; +} +.fa-vk:before { + content: "\f189"; +} +.fa-weibo:before { + content: "\f18a"; +} +.fa-renren:before { + content: "\f18b"; +} +.fa-pagelines:before { + content: "\f18c"; +} +.fa-stack-exchange:before { + content: "\f18d"; +} +.fa-arrow-circle-o-right:before { + content: "\f18e"; +} +.fa-arrow-circle-o-left:before { + content: "\f190"; +} +.fa-toggle-left:before, +.fa-caret-square-o-left:before { + content: "\f191"; +} +.fa-dot-circle-o:before { + content: "\f192"; +} +.fa-wheelchair:before { + content: "\f193"; +} +.fa-vimeo-square:before { + content: "\f194"; +} +.fa-turkish-lira:before, +.fa-try:before { + content: "\f195"; +} +.fa-plus-square-o:before { + content: "\f196"; +} +.fa-space-shuttle:before { + content: "\f197"; +} +.fa-slack:before { + content: "\f198"; +} +.fa-envelope-square:before { + content: "\f199"; +} +.fa-wordpress:before { + content: "\f19a"; +} +.fa-openid:before { + content: "\f19b"; +} +.fa-institution:before, +.fa-bank:before, +.fa-university:before { + content: "\f19c"; +} +.fa-mortar-board:before, +.fa-graduation-cap:before { + content: "\f19d"; +} +.fa-yahoo:before { + content: "\f19e"; +} +.fa-google:before { + content: "\f1a0"; +} +.fa-reddit:before { + content: "\f1a1"; +} +.fa-reddit-square:before { + content: "\f1a2"; +} +.fa-stumbleupon-circle:before { + content: "\f1a3"; +} +.fa-stumbleupon:before { + content: "\f1a4"; +} +.fa-delicious:before { + content: "\f1a5"; +} +.fa-digg:before { + content: "\f1a6"; +} +.fa-pied-piper-pp:before { + content: "\f1a7"; +} +.fa-pied-piper-alt:before { + content: "\f1a8"; +} +.fa-drupal:before { + content: "\f1a9"; +} +.fa-joomla:before { + content: "\f1aa"; +} +.fa-language:before { + content: "\f1ab"; +} +.fa-fax:before { + content: "\f1ac"; +} +.fa-building:before { + content: "\f1ad"; +} +.fa-child:before { + content: "\f1ae"; +} +.fa-paw:before { + content: "\f1b0"; +} +.fa-spoon:before { + content: "\f1b1"; +} +.fa-cube:before { + content: "\f1b2"; +} +.fa-cubes:before { + content: "\f1b3"; +} +.fa-behance:before { + content: "\f1b4"; +} +.fa-behance-square:before { + content: "\f1b5"; +} +.fa-steam:before { + content: "\f1b6"; +} +.fa-steam-square:before { + content: "\f1b7"; +} +.fa-recycle:before { + content: "\f1b8"; +} +.fa-automobile:before, +.fa-car:before { + content: "\f1b9"; +} +.fa-cab:before, +.fa-taxi:before { + content: "\f1ba"; +} +.fa-tree:before { + content: "\f1bb"; +} +.fa-spotify:before { + content: "\f1bc"; +} +.fa-deviantart:before { + content: "\f1bd"; +} +.fa-soundcloud:before { + content: "\f1be"; +} +.fa-database:before { + content: "\f1c0"; +} +.fa-file-pdf-o:before { + content: "\f1c1"; +} +.fa-file-word-o:before { + content: "\f1c2"; +} +.fa-file-excel-o:before { + content: "\f1c3"; +} +.fa-file-powerpoint-o:before { + content: "\f1c4"; +} +.fa-file-photo-o:before, +.fa-file-picture-o:before, +.fa-file-image-o:before { + content: "\f1c5"; +} +.fa-file-zip-o:before, +.fa-file-archive-o:before { + content: "\f1c6"; +} +.fa-file-sound-o:before, +.fa-file-audio-o:before { + content: "\f1c7"; +} +.fa-file-movie-o:before, +.fa-file-video-o:before { + content: "\f1c8"; +} +.fa-file-code-o:before { + content: "\f1c9"; +} +.fa-vine:before { + content: "\f1ca"; +} +.fa-codepen:before { + content: "\f1cb"; +} +.fa-jsfiddle:before { + content: "\f1cc"; +} +.fa-life-bouy:before, +.fa-life-buoy:before, +.fa-life-saver:before, +.fa-support:before, +.fa-life-ring:before { + content: "\f1cd"; +} +.fa-circle-o-notch:before { + content: "\f1ce"; +} +.fa-ra:before, +.fa-resistance:before, +.fa-rebel:before { + content: "\f1d0"; +} +.fa-ge:before, +.fa-empire:before { + content: "\f1d1"; +} +.fa-git-square:before { + content: "\f1d2"; +} +.fa-git:before { + content: "\f1d3"; +} +.fa-y-combinator-square:before, +.fa-yc-square:before, +.fa-hacker-news:before { + content: "\f1d4"; +} +.fa-tencent-weibo:before { + content: "\f1d5"; +} +.fa-qq:before { + content: "\f1d6"; +} +.fa-wechat:before, +.fa-weixin:before { + content: "\f1d7"; +} +.fa-send:before, +.fa-paper-plane:before { + content: "\f1d8"; +} +.fa-send-o:before, +.fa-paper-plane-o:before { + content: "\f1d9"; +} +.fa-history:before { + content: "\f1da"; +} +.fa-circle-thin:before { + content: "\f1db"; +} +.fa-header:before { + content: "\f1dc"; +} +.fa-paragraph:before { + content: "\f1dd"; +} +.fa-sliders:before { + content: "\f1de"; +} +.fa-share-alt:before { + content: "\f1e0"; +} +.fa-share-alt-square:before { + content: "\f1e1"; +} +.fa-bomb:before { + content: "\f1e2"; +} +.fa-soccer-ball-o:before, +.fa-futbol-o:before { + content: "\f1e3"; +} +.fa-tty:before { + content: "\f1e4"; +} +.fa-binoculars:before { + content: "\f1e5"; +} +.fa-plug:before { + content: "\f1e6"; +} +.fa-slideshare:before { + content: "\f1e7"; +} +.fa-twitch:before { + content: "\f1e8"; +} +.fa-yelp:before { + content: "\f1e9"; +} +.fa-newspaper-o:before { + content: "\f1ea"; +} +.fa-wifi:before { + content: "\f1eb"; +} +.fa-calculator:before { + content: "\f1ec"; +} +.fa-paypal:before { + content: "\f1ed"; +} +.fa-google-wallet:before { + content: "\f1ee"; +} +.fa-cc-visa:before { + content: "\f1f0"; +} +.fa-cc-mastercard:before { + content: "\f1f1"; +} +.fa-cc-discover:before { + content: "\f1f2"; +} +.fa-cc-amex:before { + content: "\f1f3"; +} +.fa-cc-paypal:before { + content: "\f1f4"; +} +.fa-cc-stripe:before { + content: "\f1f5"; +} +.fa-bell-slash:before { + content: "\f1f6"; +} +.fa-bell-slash-o:before { + content: "\f1f7"; +} +.fa-trash:before { + content: "\f1f8"; +} +.fa-copyright:before { + content: "\f1f9"; +} +.fa-at:before { + content: "\f1fa"; +} +.fa-eyedropper:before { + content: "\f1fb"; +} +.fa-paint-brush:before { + content: "\f1fc"; +} +.fa-birthday-cake:before { + content: "\f1fd"; +} +.fa-area-chart:before { + content: "\f1fe"; +} +.fa-pie-chart:before { + content: "\f200"; +} +.fa-line-chart:before { + content: "\f201"; +} +.fa-lastfm:before { + content: "\f202"; +} +.fa-lastfm-square:before { + content: "\f203"; +} +.fa-toggle-off:before { + content: "\f204"; +} +.fa-toggle-on:before { + content: "\f205"; +} +.fa-bicycle:before { + content: "\f206"; +} +.fa-bus:before { + content: "\f207"; +} +.fa-ioxhost:before { + content: "\f208"; +} +.fa-angellist:before { + content: "\f209"; +} +.fa-cc:before { + content: "\f20a"; +} +.fa-shekel:before, +.fa-sheqel:before, +.fa-ils:before { + content: "\f20b"; +} +.fa-meanpath:before { + content: "\f20c"; +} +.fa-buysellads:before { + content: "\f20d"; +} +.fa-connectdevelop:before { + content: "\f20e"; +} +.fa-dashcube:before { + content: "\f210"; +} +.fa-forumbee:before { + content: "\f211"; +} +.fa-leanpub:before { + content: "\f212"; +} +.fa-sellsy:before { + content: "\f213"; +} +.fa-shirtsinbulk:before { + content: "\f214"; +} +.fa-simplybuilt:before { + content: "\f215"; +} +.fa-skyatlas:before { + content: "\f216"; +} +.fa-cart-plus:before { + content: "\f217"; +} +.fa-cart-arrow-down:before { + content: "\f218"; +} +.fa-diamond:before { + content: "\f219"; +} +.fa-ship:before { + content: "\f21a"; +} +.fa-user-secret:before { + content: "\f21b"; +} +.fa-motorcycle:before { + content: "\f21c"; +} +.fa-street-view:before { + content: "\f21d"; +} +.fa-heartbeat:before { + content: "\f21e"; +} +.fa-venus:before { + content: "\f221"; +} +.fa-mars:before { + content: "\f222"; +} +.fa-mercury:before { + content: "\f223"; +} +.fa-intersex:before, +.fa-transgender:before { + content: "\f224"; +} +.fa-transgender-alt:before { + content: "\f225"; +} +.fa-venus-double:before { + content: "\f226"; +} +.fa-mars-double:before { + content: "\f227"; +} +.fa-venus-mars:before { + content: "\f228"; +} +.fa-mars-stroke:before { + content: "\f229"; +} +.fa-mars-stroke-v:before { + content: "\f22a"; +} +.fa-mars-stroke-h:before { + content: "\f22b"; +} +.fa-neuter:before { + content: "\f22c"; +} +.fa-genderless:before { + content: "\f22d"; +} +.fa-facebook-official:before { + content: "\f230"; +} +.fa-pinterest-p:before { + content: "\f231"; +} +.fa-whatsapp:before { + content: "\f232"; +} +.fa-server:before { + content: "\f233"; +} +.fa-user-plus:before { + content: "\f234"; +} +.fa-user-times:before { + content: "\f235"; +} +.fa-hotel:before, +.fa-bed:before { + content: "\f236"; +} +.fa-viacoin:before { + content: "\f237"; +} +.fa-train:before { + content: "\f238"; +} +.fa-subway:before { + content: "\f239"; +} +.fa-medium:before { + content: "\f23a"; +} +.fa-yc:before, +.fa-y-combinator:before { + content: "\f23b"; +} +.fa-optin-monster:before { + content: "\f23c"; +} +.fa-opencart:before { + content: "\f23d"; +} +.fa-expeditedssl:before { + content: "\f23e"; +} +.fa-battery-4:before, +.fa-battery:before, +.fa-battery-full:before { + content: "\f240"; +} +.fa-battery-3:before, +.fa-battery-three-quarters:before { + content: "\f241"; +} +.fa-battery-2:before, +.fa-battery-half:before { + content: "\f242"; +} +.fa-battery-1:before, +.fa-battery-quarter:before { + content: "\f243"; +} +.fa-battery-0:before, +.fa-battery-empty:before { + content: "\f244"; +} +.fa-mouse-pointer:before { + content: "\f245"; +} +.fa-i-cursor:before { + content: "\f246"; +} +.fa-object-group:before { + content: "\f247"; +} +.fa-object-ungroup:before { + content: "\f248"; +} +.fa-sticky-note:before { + content: "\f249"; +} +.fa-sticky-note-o:before { + content: "\f24a"; +} +.fa-cc-jcb:before { + content: "\f24b"; +} +.fa-cc-diners-club:before { + content: "\f24c"; +} +.fa-clone:before { + content: "\f24d"; +} +.fa-balance-scale:before { + content: "\f24e"; +} +.fa-hourglass-o:before { + content: "\f250"; +} +.fa-hourglass-1:before, +.fa-hourglass-start:before { + content: "\f251"; +} +.fa-hourglass-2:before, +.fa-hourglass-half:before { + content: "\f252"; +} +.fa-hourglass-3:before, +.fa-hourglass-end:before { + content: "\f253"; +} +.fa-hourglass:before { + content: "\f254"; +} +.fa-hand-grab-o:before, +.fa-hand-rock-o:before { + content: "\f255"; +} +.fa-hand-stop-o:before, +.fa-hand-paper-o:before { + content: "\f256"; +} +.fa-hand-scissors-o:before { + content: "\f257"; +} +.fa-hand-lizard-o:before { + content: "\f258"; +} +.fa-hand-spock-o:before { + content: "\f259"; +} +.fa-hand-pointer-o:before { + content: "\f25a"; +} +.fa-hand-peace-o:before { + content: "\f25b"; +} +.fa-trademark:before { + content: "\f25c"; +} +.fa-registered:before { + content: "\f25d"; +} +.fa-creative-commons:before { + content: "\f25e"; +} +.fa-gg:before { + content: "\f260"; +} +.fa-gg-circle:before { + content: "\f261"; +} +.fa-tripadvisor:before { + content: "\f262"; +} +.fa-odnoklassniki:before { + content: "\f263"; +} +.fa-odnoklassniki-square:before { + content: "\f264"; +} +.fa-get-pocket:before { + content: "\f265"; +} +.fa-wikipedia-w:before { + content: "\f266"; +} +.fa-safari:before { + content: "\f267"; +} +.fa-chrome:before { + content: "\f268"; +} +.fa-firefox:before { + content: "\f269"; +} +.fa-opera:before { + content: "\f26a"; +} +.fa-internet-explorer:before { + content: "\f26b"; +} +.fa-tv:before, +.fa-television:before { + content: "\f26c"; +} +.fa-contao:before { + content: "\f26d"; +} +.fa-500px:before { + content: "\f26e"; +} +.fa-amazon:before { + content: "\f270"; +} +.fa-calendar-plus-o:before { + content: "\f271"; +} +.fa-calendar-minus-o:before { + content: "\f272"; +} +.fa-calendar-times-o:before { + content: "\f273"; +} +.fa-calendar-check-o:before { + content: "\f274"; +} +.fa-industry:before { + content: "\f275"; +} +.fa-map-pin:before { + content: "\f276"; +} +.fa-map-signs:before { + content: "\f277"; +} +.fa-map-o:before { + content: "\f278"; +} +.fa-map:before { + content: "\f279"; +} +.fa-commenting:before { + content: "\f27a"; +} +.fa-commenting-o:before { + content: "\f27b"; +} +.fa-houzz:before { + content: "\f27c"; +} +.fa-vimeo:before { + content: "\f27d"; +} +.fa-black-tie:before { + content: "\f27e"; +} +.fa-fonticons:before { + content: "\f280"; +} +.fa-reddit-alien:before { + content: "\f281"; +} +.fa-edge:before { + content: "\f282"; +} +.fa-credit-card-alt:before { + content: "\f283"; +} +.fa-codiepie:before { + content: "\f284"; +} +.fa-modx:before { + content: "\f285"; +} +.fa-fort-awesome:before { + content: "\f286"; +} +.fa-usb:before { + content: "\f287"; +} +.fa-product-hunt:before { + content: "\f288"; +} +.fa-mixcloud:before { + content: "\f289"; +} +.fa-scribd:before { + content: "\f28a"; +} +.fa-pause-circle:before { + content: "\f28b"; +} +.fa-pause-circle-o:before { + content: "\f28c"; +} +.fa-stop-circle:before { + content: "\f28d"; +} +.fa-stop-circle-o:before { + content: "\f28e"; +} +.fa-shopping-bag:before { + content: "\f290"; +} +.fa-shopping-basket:before { + content: "\f291"; +} +.fa-hashtag:before { + content: "\f292"; +} +.fa-bluetooth:before { + content: "\f293"; +} +.fa-bluetooth-b:before { + content: "\f294"; +} +.fa-percent:before { + content: "\f295"; +} +.fa-gitlab:before { + content: "\f296"; +} +.fa-wpbeginner:before { + content: "\f297"; +} +.fa-wpforms:before { + content: "\f298"; +} +.fa-envira:before { + content: "\f299"; +} +.fa-universal-access:before { + content: "\f29a"; +} +.fa-wheelchair-alt:before { + content: "\f29b"; +} +.fa-question-circle-o:before { + content: "\f29c"; +} +.fa-blind:before { + content: "\f29d"; +} +.fa-audio-description:before { + content: "\f29e"; +} +.fa-volume-control-phone:before { + content: "\f2a0"; +} +.fa-braille:before { + content: "\f2a1"; +} +.fa-assistive-listening-systems:before { + content: "\f2a2"; +} +.fa-asl-interpreting:before, +.fa-american-sign-language-interpreting:before { + content: "\f2a3"; +} +.fa-deafness:before, +.fa-hard-of-hearing:before, +.fa-deaf:before { + content: "\f2a4"; +} +.fa-glide:before { + content: "\f2a5"; +} +.fa-glide-g:before { + content: "\f2a6"; +} +.fa-signing:before, +.fa-sign-language:before { + content: "\f2a7"; +} +.fa-low-vision:before { + content: "\f2a8"; +} +.fa-viadeo:before { + content: "\f2a9"; +} +.fa-viadeo-square:before { + content: "\f2aa"; +} +.fa-snapchat:before { + content: "\f2ab"; +} +.fa-snapchat-ghost:before { + content: "\f2ac"; +} +.fa-snapchat-square:before { + content: "\f2ad"; +} +.fa-pied-piper:before { + content: "\f2ae"; +} +.fa-first-order:before { + content: "\f2b0"; +} +.fa-yoast:before { + content: "\f2b1"; +} +.fa-themeisle:before { + content: "\f2b2"; +} +.fa-google-plus-circle:before, +.fa-google-plus-official:before { + content: "\f2b3"; +} +.fa-fa:before, +.fa-font-awesome:before { + content: "\f2b4"; +} +.fa-handshake-o:before { + content: "\f2b5"; +} +.fa-envelope-open:before { + content: "\f2b6"; +} +.fa-envelope-open-o:before { + content: "\f2b7"; +} +.fa-linode:before { + content: "\f2b8"; +} +.fa-address-book:before { + content: "\f2b9"; +} +.fa-address-book-o:before { + content: "\f2ba"; +} +.fa-vcard:before, +.fa-address-card:before { + content: "\f2bb"; +} +.fa-vcard-o:before, +.fa-address-card-o:before { + content: "\f2bc"; +} +.fa-user-circle:before { + content: "\f2bd"; +} +.fa-user-circle-o:before { + content: "\f2be"; +} +.fa-user-o:before { + content: "\f2c0"; +} +.fa-id-badge:before { + content: "\f2c1"; +} +.fa-drivers-license:before, +.fa-id-card:before { + content: "\f2c2"; +} +.fa-drivers-license-o:before, +.fa-id-card-o:before { + content: "\f2c3"; +} +.fa-quora:before { + content: "\f2c4"; +} +.fa-free-code-camp:before { + content: "\f2c5"; +} +.fa-telegram:before { + content: "\f2c6"; +} +.fa-thermometer-4:before, +.fa-thermometer:before, +.fa-thermometer-full:before { + content: "\f2c7"; +} +.fa-thermometer-3:before, +.fa-thermometer-three-quarters:before { + content: "\f2c8"; +} +.fa-thermometer-2:before, +.fa-thermometer-half:before { + content: "\f2c9"; +} +.fa-thermometer-1:before, +.fa-thermometer-quarter:before { + content: "\f2ca"; +} +.fa-thermometer-0:before, +.fa-thermometer-empty:before { + content: "\f2cb"; +} +.fa-shower:before { + content: "\f2cc"; +} +.fa-bathtub:before, +.fa-s15:before, +.fa-bath:before { + content: "\f2cd"; +} +.fa-podcast:before { + content: "\f2ce"; +} +.fa-window-maximize:before { + content: "\f2d0"; +} +.fa-window-minimize:before { + content: "\f2d1"; +} +.fa-window-restore:before { + content: "\f2d2"; +} +.fa-times-rectangle:before, +.fa-window-close:before { + content: "\f2d3"; +} +.fa-times-rectangle-o:before, +.fa-window-close-o:before { + content: "\f2d4"; +} +.fa-bandcamp:before { + content: "\f2d5"; +} +.fa-grav:before { + content: "\f2d6"; +} +.fa-etsy:before { + content: "\f2d7"; +} +.fa-imdb:before { + content: "\f2d8"; +} +.fa-ravelry:before { + content: "\f2d9"; +} +.fa-eercast:before { + content: "\f2da"; +} +.fa-microchip:before { + content: "\f2db"; +} +.fa-snowflake-o:before { + content: "\f2dc"; +} +.fa-superpowers:before { + content: "\f2dd"; +} +.fa-wpexplorer:before { + content: "\f2de"; +} +.fa-meetup:before { + content: "\f2e0"; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; +} diff --git a/server/easyctf/assets/css/font-awesome.min.css b/server/easyctf/assets/css/font-awesome.min.css new file mode 100755 index 0000000..540440c --- /dev/null +++ b/server/easyctf/assets/css/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} diff --git a/server/easyctf/assets/css/main.css b/server/easyctf/assets/css/main.css new file mode 100755 index 0000000..011d36f --- /dev/null +++ b/server/easyctf/assets/css/main.css @@ -0,0 +1,213 @@ +@font-face { + font-family: "Source Sans Pro"; + font-weight: 300; + src: url("../fonts/SourceSansPro-Light.ttf") format("truetype"); +} + +@font-face { + font-family: "Source Sans Pro"; + font-weight: 400; + src: url("../fonts/SourceSansPro-Regular.ttf") format("truetype"); +} + +html, body { + font-family: "Source Sans Pro"; + font-weight: 300 !important; +} + +h1, h2, h3, h4, h5, h6, .btn { + font-weight: 300 !important; +} + +#main-content { + min-height: 85vh; +} + +.navbar { + margin-bottom: 0; + border-radius: 0; + -moz-border-radius: 0; + -webkit-border-radius: 0; +} + +.navatar { + width: 20px; + height: 20px; + border-radius: 2px; + margin-right: 4px; +} + +.section { + padding: 30px 0 50px 0; +} + +.logo-table { + margin: 10px; +} + +.logo-table td { + padding: 10px; + vertical-align: middle; +} + +.logo-table a:hover { + text-decoration: none; +} + +.logo-table h2 { + margin: 0; +} + +.logo { + max-height: 100px; +} + +.badge a { + color: white; + text-decoration: none; +} + +.gradient { + color: #FFF; + background: #31abc6; + background: -moz-linear-gradient(45deg, #31abc6 0%, #5b69bf 100%); + background: -webkit-linear-gradient(45deg, #31abc6 0%,#5b69bf 100%); + background: linear-gradient(45deg, #31abc6 0%,#5b69bf 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#695bbf', endColorstr='#c631ab',GradientType=1 ); +} + +#masthead { + min-height:40vh; + position:relative; + margin: 0; + padding-top: 34px; + padding-bottom: 42px; + + color: #FFF; +} + +#title h1 { + color: rgba(255, 255, 255, 1); + font-size: 6.5em; +} + +#title h2 { + color: rgba(255, 255, 255, 0.8); + font-size: 2.0em; +} + +#title a { + color: rgba(255, 255, 255, 0.6); + font-size: 1.2em; + text-decoration: none; +} + +#links a { + margin: 0 12px 0 12px; + color: #555; + font-size: 0.75em; +} + +.site-footer { + /*margin-top: 90px;*/ + background-color: #eee; + color: #999; + padding: 3em 0 0; + font-size: 14.3px; + position: relative; +} + +.site-footer .footer-heading { + color: #555; +} + +.footer-copyright { + margin-top: 2em; + padding: 2em 2em; + border-top: 1px solid rgba(0, 0, 0, 0.1); + text-align: center; +} + +.site-footer h5, .site-footer .h5 { + font-size: 1.2em; +} + +.container.jumbotron { + background-color: transparent; +} + + +html, body, h1, h2, h3, h4, h5, h6 { + font-family: "Source Sans Pro", Arial, sans-serif; + font-weight: 300; +} + +b { + font-weight: 400; +} + +.large-text { + font-size: 1.6em; +} + +.tab-content { + padding: 15px; +} + +.tab-content > .tab-pane { + display: none; +} + +.tab-content > .active { + display: block; +} + +.navbar { + border-radius: 0; +} + +.site-footer { + background-color: #eee; + color: #999; + padding: 3em 0 0; + font-size: 14.3px; + position: relative; +} + +.site-footer .footer-heading { + color: #555; +} + +.footer-copyright { + margin-top: 2em; + padding: 2em 2em; + border-top: 1px solid rgba(0, 0, 0, 0.1); + text-align: center; +} + +h5, .h5 { + font-size: 1.2em; +} + +#links a { + margin: 0 12px 0 12px; + color: #555; + font-size: 0.75em; +} + +.selectize-input { + height: 34px; + padding: 6px 12px; + font-size: 14px; + -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; +} + +.selectize-control { + margin-top: 8px !important; +} + +.selectize-input .item { + padding: 1px 6px !important; +} \ No newline at end of file diff --git a/server/easyctf/assets/css/selectize.min.css b/server/easyctf/assets/css/selectize.min.css new file mode 100644 index 0000000..5a660ab --- /dev/null +++ b/server/easyctf/assets/css/selectize.min.css @@ -0,0 +1 @@ +.selectize-control.plugin-drag_drop.multi>.selectize-input>div.ui-sortable-placeholder{visibility:visible!important;background:#f2f2f2!important;background:rgba(0,0,0,.06)!important;border:0!important;-webkit-box-shadow:inset 0 0 12px 4px #fff;box-shadow:inset 0 0 12px 4px #fff}.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after{content:'!';visibility:hidden}.selectize-control.plugin-drag_drop .ui-sortable-helper{-webkit-box-shadow:0 2px 5px rgba(0,0,0,.2);box-shadow:0 2px 5px rgba(0,0,0,.2)}.selectize-dropdown-header{position:relative;border-bottom:1px solid #d0d0d0;background:#f8f8f8;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.selectize-dropdown-header-close{position:absolute;right:12px;top:50%;color:#333;opacity:.4;margin-top:-12px;line-height:20px;font-size:20px!important}.selectize-dropdown-header-close:hover{color:#000}.selectize-dropdown.plugin-optgroup_columns .optgroup{border-right:1px solid #f2f2f2;border-top:0 none;float:left;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.selectize-control.plugin-remove_button [data-value] .remove,.selectize-input{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;display:inline-block}.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child{border-right:0 none}.selectize-dropdown.plugin-optgroup_columns .optgroup:before{display:none}.selectize-dropdown.plugin-optgroup_columns .optgroup-header{border-top:0 none}.selectize-control.plugin-remove_button [data-value]{position:relative;padding-right:24px!important}.selectize-control.plugin-remove_button [data-value] .remove{z-index:1;position:absolute;top:0;right:0;bottom:0;width:17px;text-align:center;font-weight:700;font-size:12px;color:inherit;text-decoration:none;vertical-align:middle;padding:1px 0 0;border-left:1px solid transparent;-webkit-border-radius:0 2px 2px 0;-moz-border-radius:0 2px 2px 0;border-radius:0 2px 2px 0;box-sizing:border-box}.selectize-control.plugin-remove_button [data-value] .remove:hover{background:rgba(0,0,0,.05)}.selectize-control.plugin-remove_button [data-value].active .remove{border-left-color:transparent}.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover{background:0 0}.selectize-control.plugin-remove_button .disabled [data-value] .remove{border-left-color:rgba(77,77,77,0)}.selectize-control.plugin-remove_button .remove-single{position:absolute;right:28px;top:6px;font-size:23px}.selectize-control,.selectize-input{position:relative}.selectize-dropdown,.selectize-input,.selectize-input input{color:#333;font-family:inherit;font-size:inherit;line-height:20px;-webkit-font-smoothing:inherit}.selectize-control.single .selectize-input.input-active,.selectize-input{background:#fff;cursor:text;display:inline-block}.selectize-input{border:1px solid #ccc;padding:6px 12px;width:auto;overflow:hidden;z-index:1;box-sizing:border-box;-webkit-box-shadow:none;box-shadow:none;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.selectize-control.multi .selectize-input.has-items{padding:5px 12px 2px}.selectize-input.full{background-color:#fff}.selectize-input.disabled,.selectize-input.disabled *{cursor:default!important}.selectize-input>*{vertical-align:baseline;display:-moz-inline-stack;display:inline-block;zoom:1}.selectize-control.multi .selectize-input>div{cursor:pointer;margin:0 3px 3px 0;padding:1px 3px;background:#efefef;color:#333;border:0 solid transparent}.selectize-control.multi .selectize-input>div.active{background:#428bca;color:#fff;border:0 solid transparent}.selectize-control.multi .selectize-input.disabled>div,.selectize-control.multi .selectize-input.disabled>div.active{color:grey;background:#fff;border:0 solid rgba(77,77,77,0)}.selectize-input>input{display:inline-block!important;padding:0!important;min-height:0!important;max-height:none!important;max-width:100%!important;margin:0!important;text-indent:0!important;border:0!important;background:0 0!important;line-height:inherit!important;-webkit-user-select:auto!important;-webkit-box-shadow:none!important;box-shadow:none!important}.selectize-input>input::-ms-clear{display:none}.selectize-input>input:focus{outline:0!important}.selectize-input::after{content:' ';display:block;clear:left}.selectize-input.dropdown-active::before{content:' ';position:absolute;background:#fff;height:1px;bottom:0;left:0;right:0}.selectize-dropdown{position:absolute;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.selectize-dropdown [data-selectable]{cursor:pointer;overflow:hidden}.selectize-dropdown [data-selectable] .highlight{background:rgba(255,237,40,.4);-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.selectize-dropdown .optgroup-header,.selectize-dropdown [data-selectable]{padding:3px 12px}.selectize-dropdown .optgroup:first-child .optgroup-header{border-top:0 none}.selectize-dropdown .optgroup-header{color:#777;background:#fff;cursor:default;font-size:12px;line-height:1.42857143}.selectize-dropdown .active{background-color:#f5f5f5;color:#262626}.selectize-dropdown .active.create{color:#262626}.selectize-dropdown .create{color:rgba(51,51,51,.5)}.selectize-dropdown-content{overflow-y:auto;overflow-x:hidden;max-height:200px;-webkit-overflow-scrolling:touch}.selectize-control.single .selectize-input,.selectize-control.single .selectize-input input{cursor:pointer}.selectize-control.single .selectize-input.input-active,.selectize-control.single .selectize-input.input-active input{cursor:text}.selectize-control.single .selectize-input:after{content:' ';display:block;position:absolute;top:50%;right:17px;margin-top:-3px;width:0;height:0;border-style:solid;border-width:5px 5px 0;border-color:#333 transparent transparent}.selectize-control.single .selectize-input.dropdown-active:after{margin-top:-4px;border-width:0 5px 5px;border-color:transparent transparent #333}.selectize-control.rtl.single .selectize-input:after{left:17px;right:auto}.selectize-control.rtl .selectize-input>input{margin:0 4px 0 -2px!important}.selectize-control .selectize-input.disabled{opacity:.5;background-color:#fff}.selectize-dropdown,.selectize-dropdown.form-control{height:auto;padding:0;margin:2px 0 0;z-index:1000;background:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.selectize-dropdown .optgroup:first-child:before{display:none}.selectize-dropdown .optgroup:before{content:' ';display:block;height:1px;margin:9px -12px;overflow:hidden;background-color:#e5e5e5}.selectize-dropdown-content{padding:5px 0}.selectize-dropdown-header{padding:6px 12px}.selectize-input{min-height:34px}.selectize-input.dropdown-active{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.selectize-input.dropdown-active::before{display:none}.selectize-input.focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.has-error .selectize-input{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .selectize-input:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.selectize-control.multi .selectize-input.has-items{padding-left:9px;padding-right:9px}.selectize-control.multi .selectize-input>div{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.form-control.selectize-control{padding:0;height:auto;border:none;background:0 0;-webkit-box-shadow:none;box-shadow:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0} \ No newline at end of file diff --git a/server/easyctf/assets/fonts/FontAwesome.otf b/server/easyctf/assets/fonts/FontAwesome.otf new file mode 100755 index 0000000..401ec0f Binary files /dev/null and b/server/easyctf/assets/fonts/FontAwesome.otf differ diff --git a/server/easyctf/assets/fonts/OFL.txt b/server/easyctf/assets/fonts/OFL.txt new file mode 100755 index 0000000..72d81ab --- /dev/null +++ b/server/easyctf/assets/fonts/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2010, 2012, 2014 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name ‘Source’. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/server/easyctf/assets/fonts/SourceSansPro-Black.ttf b/server/easyctf/assets/fonts/SourceSansPro-Black.ttf new file mode 100755 index 0000000..7ea0260 Binary files /dev/null and b/server/easyctf/assets/fonts/SourceSansPro-Black.ttf differ diff --git a/server/easyctf/assets/fonts/SourceSansPro-BlackItalic.ttf b/server/easyctf/assets/fonts/SourceSansPro-BlackItalic.ttf new file mode 100755 index 0000000..e1a7482 Binary files /dev/null and b/server/easyctf/assets/fonts/SourceSansPro-BlackItalic.ttf differ diff --git a/server/easyctf/assets/fonts/SourceSansPro-Bold.ttf b/server/easyctf/assets/fonts/SourceSansPro-Bold.ttf new file mode 100755 index 0000000..f698646 Binary files /dev/null and b/server/easyctf/assets/fonts/SourceSansPro-Bold.ttf differ diff --git a/server/easyctf/assets/fonts/SourceSansPro-BoldItalic.ttf b/server/easyctf/assets/fonts/SourceSansPro-BoldItalic.ttf new file mode 100755 index 0000000..5c00b64 Binary files /dev/null and b/server/easyctf/assets/fonts/SourceSansPro-BoldItalic.ttf differ diff --git a/server/easyctf/assets/fonts/SourceSansPro-ExtraLight.ttf b/server/easyctf/assets/fonts/SourceSansPro-ExtraLight.ttf new file mode 100755 index 0000000..f1da6b2 Binary files /dev/null and b/server/easyctf/assets/fonts/SourceSansPro-ExtraLight.ttf differ diff --git a/server/easyctf/assets/fonts/SourceSansPro-ExtraLightItalic.ttf b/server/easyctf/assets/fonts/SourceSansPro-ExtraLightItalic.ttf new file mode 100755 index 0000000..15f7344 Binary files /dev/null and b/server/easyctf/assets/fonts/SourceSansPro-ExtraLightItalic.ttf differ diff --git a/server/easyctf/assets/fonts/SourceSansPro-Italic.ttf b/server/easyctf/assets/fonts/SourceSansPro-Italic.ttf new file mode 100755 index 0000000..82e8762 Binary files /dev/null and b/server/easyctf/assets/fonts/SourceSansPro-Italic.ttf differ diff --git a/server/easyctf/assets/fonts/SourceSansPro-Light.ttf b/server/easyctf/assets/fonts/SourceSansPro-Light.ttf new file mode 100755 index 0000000..ea1104b Binary files /dev/null and b/server/easyctf/assets/fonts/SourceSansPro-Light.ttf differ diff --git a/server/easyctf/assets/fonts/SourceSansPro-LightItalic.ttf b/server/easyctf/assets/fonts/SourceSansPro-LightItalic.ttf new file mode 100755 index 0000000..b78f1b0 Binary files /dev/null and b/server/easyctf/assets/fonts/SourceSansPro-LightItalic.ttf differ diff --git a/server/easyctf/assets/fonts/SourceSansPro-Regular.ttf b/server/easyctf/assets/fonts/SourceSansPro-Regular.ttf new file mode 100755 index 0000000..278ad8a Binary files /dev/null and b/server/easyctf/assets/fonts/SourceSansPro-Regular.ttf differ diff --git a/server/easyctf/assets/fonts/SourceSansPro-SemiBold.ttf b/server/easyctf/assets/fonts/SourceSansPro-SemiBold.ttf new file mode 100755 index 0000000..ac3e0d1 Binary files /dev/null and b/server/easyctf/assets/fonts/SourceSansPro-SemiBold.ttf differ diff --git a/server/easyctf/assets/fonts/SourceSansPro-SemiBoldItalic.ttf b/server/easyctf/assets/fonts/SourceSansPro-SemiBoldItalic.ttf new file mode 100755 index 0000000..b0737bb Binary files /dev/null and b/server/easyctf/assets/fonts/SourceSansPro-SemiBoldItalic.ttf differ diff --git a/server/easyctf/assets/fonts/fontawesome-webfont.eot b/server/easyctf/assets/fonts/fontawesome-webfont.eot new file mode 100755 index 0000000..e9f60ca Binary files /dev/null and b/server/easyctf/assets/fonts/fontawesome-webfont.eot differ diff --git a/server/easyctf/assets/fonts/fontawesome-webfont.svg b/server/easyctf/assets/fonts/fontawesome-webfont.svg new file mode 100755 index 0000000..855c845 --- /dev/null +++ b/server/easyctf/assets/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/server/easyctf/assets/fonts/fontawesome-webfont.ttf b/server/easyctf/assets/fonts/fontawesome-webfont.ttf new file mode 100755 index 0000000..35acda2 Binary files /dev/null and b/server/easyctf/assets/fonts/fontawesome-webfont.ttf differ diff --git a/server/easyctf/assets/fonts/fontawesome-webfont.woff b/server/easyctf/assets/fonts/fontawesome-webfont.woff new file mode 100755 index 0000000..400014a Binary files /dev/null and b/server/easyctf/assets/fonts/fontawesome-webfont.woff differ diff --git a/server/easyctf/assets/fonts/fontawesome-webfont.woff2 b/server/easyctf/assets/fonts/fontawesome-webfont.woff2 new file mode 100755 index 0000000..4d13fc6 Binary files /dev/null and b/server/easyctf/assets/fonts/fontawesome-webfont.woff2 differ diff --git a/server/easyctf/assets/game.json b/server/easyctf/assets/game.json new file mode 100644 index 0000000..7ba4644 --- /dev/null +++ b/server/easyctf/assets/game.json @@ -0,0 +1,4146 @@ +{ + "images": { + "game_interface_bg": "https://i.imgur.com/IHEUAT4.gif", + "A": "https://imgur.com/Zxl68XK.png", + "B": "https://i.imgur.com/v0oCH21.png", + "C": "https://i.imgur.com/bUv0V7J.png", + "D": "https://i.imgur.com/d7t07Rx.png", + "E": "https://i.imgur.com/TPlcxap.png", + "bg": "https://i.imgur.com/IHEUAT4.gif", + "blue": "https://i.imgur.com/6PJUIW3.png" + }, + "category_icons": { + "crypto": "", + "web": "", + "forensics": "", + "binary": "", + "reversing": "", + "misc": "" + }, + "scenes": { + "B0": { + "background_image": "blue", + "choreography": [ + { + "action": "dialogue", + "speaker": null, + "text": "WELCOME TO A NEW REPUBLIC." + }, + { + "action": "dialogue", + "speaker": null, + "text": "DO YOUR DUTY WELL." + }, + { + "action": "dialogue", + "speaker": null, + "text": " " + } + ], + "on": { + "return": true + } + }, + "1": { + "background_image": "bg", + "choreography": [ + { + "action": "show_character", + "sprite": "E", + "position": 1 + }, + { + "action": "dialogue", + "speaker": "Minako", + "text": "H-hi! Could I ask you for a favor?" + }, + { + "action": "dialogue", + "speaker": "You", + "text": "Sure." + }, + { + "action": "show_problem", + "problem_id": 91 + }, + { + "action": "dialogue", + "speaker": "Minako", + "text": "Could you look something up for me?" + }, + { + "action": "dialogue", + "speaker": "You", + "text": "Sure, that’s what I’m here for." + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "speaker": null, + "text": " " + } + ], + "on": { + "after_scenes": [ + "B0" + ] + } + }, + "2": { + "background_image": "bg", + "choreography": [ + { + "action": "show_character", + "sprite": "B", + "position": 1 + }, + { + "action": "dialogue", + "speaker": "Director", + "text": "Greetings, servant of the Republic." + }, + { + "action": "dialogue", + "speaker": "You", + "text": "..." + }, + { + "action": "dialogue", + "speaker": "You", + "text": "Glory to the Republic." + }, + { + "action": "dialogue", + "speaker": "Director", + "text": "..." + }, + { + "action": "dialogue", + "speaker": "Director", + "text": "You may recognize me as the director of the Department of Security." + }, + { + "action": "show_problem", + "problem_id": 66 + }, + { + "action": "dialogue", + "speaker": "Director", + "text": "I expect my request to be given first priority." + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "speaker": null, + "text": " " + } + ], + "on": { + "after_scenes": [ + "1" + ] + } + }, + "3": { + "choreography": [ + { + "action": "show_character", + "sprite": "D", + "position": 1 + }, + { + "action": "dialogue", + "text": "Hey.", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "\u2026 Yes?", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "You work here?", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "I sure hope so, or I\u2019ll find myself arrested soon.", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Ha-ha.", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "...", + "speaker": "Julia" + }, + { + "action": "show_problem", + "problem_id": 65 + }, + { + "action": "dialogue", + "text": "I\u2019m from the Department of Prosperity. I need an agricultural report, or something?", + "speaker": "Julia" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "speaker": null, + "text": " " + } + ], + "on": { + "after_scenes": [ + "2" + ] + } + }, + "4": { + "on": { + "after_scenes": [ + "3" + ] + }, + "choreography": [ + { + "action": "show_character", + "sprite": "C", + "position": 1 + }, + { + "action": "dialogue", + "text": "heww0! c0uld u pw3ase help m3?? :3", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "Um\u2026 why do you talk like that?", + "speaker": "You" + }, + { + "action": "show_problem", + "problem_id": 89 + }, + { + "action": "dialogue", + "text": "dw0nt be rude >:3 im not fwom aw0und h3r3, u kn0.", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "\u2026 OK.", + "speaker": "You" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "5": { + "on": { + "after_scenes": [ + "4" + ] + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "A" + }, + { + "action": "dialogue", + "text": "We come seeking the truth.", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "...", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "Yes\u2026 you\u2019re in the Department of Truth. Can I help you?", + "speaker": "You" + }, + { + "action": "show_problem", + "problem_id": 92 + }, + { + "action": "dialogue", + "text": "\u2026 ", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "\u2026", + "speaker": "You" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "6": { + "on": { + "problems_solved": { + "problems": [ + 89, + 91 + ], + "thresh": 1 + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "C" + }, + { + "action": "dialogue", + "text": "h1!!!11 w4nt 2 kno my st4r sign?? :0", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "Sorry, your what? ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "3: my astrowogicalw s1gn!! l1ke t3h constewwations!! ", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "Oh. I don\u2019t really believe in that stuff.", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "I mean, you can\u2019t even see them anymore anyway because of the screens. So what\u2019s the point?", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "... But sure, what is it?", + "speaker": "You" + }, + { + "action": "show_problem", + "problem_id": 103 + }, + { + "action": "dialogue", + "text": " 1t\u2019s cancer!!11 :3", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "\u2026 OK.", + "speaker": "You" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "7": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 66, + 89 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "B" + }, + { + "action": "dialogue", + "text": "These are sensitive times that we live in, Investigator, with the war, you know. ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "They are, Director. ", + "speaker": "You" + }, + { + "action": "show_problem", + "problem_id": 87 + }, + { + "action": "dialogue", + "text": "Remember in these times that the Minister sees all. ", + "speaker": "Director" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "8": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 92, + 66 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "A" + }, + { + "action": "show_problem", + "problem_id": 81 + }, + { + "action": "dialogue", + "text": "Tell me, when did the war start? ", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "\u2026 A long time ago\u2026 I think...", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "...", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "I can find you the date. ", + "speaker": "You" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "9": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 92, + 65 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "D" + }, + { + "action": "dialogue", + "text": "You like your job?", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "Yes, I do. Maybe not all aspects of it, but yes. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Sucks, I don\u2019t like mine. I\u2019m basically a glorified messenger. ", + "speaker": "Julia" + }, + { + "action": "show_problem", + "problem_id": 75 + }, + { + "action": "dialogue", + "text": "I guess it\u2019s more fun being a glorified record-keeper, Investigator. ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "...", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "You\u2019d think living under an oppressive regime would at least be interesting.", + "speaker": "Julia" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "10": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 65, + 91 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "E" + }, + { + "action": "dialogue", + "text": "H-hi again!", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "I\u2019ve seen you around before, haven\u2019t I?", + "speaker": "You" + }, + { + "action": "show_problem", + "problem_id": 82 + }, + { + "action": "dialogue", + "text": "U-um, I asked you for something else, the other day\u2026", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "No, before that maybe?", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Um, I also work here, I suppose\u2026", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "I\u2019m cleaning staff. Like, um, cleaning the building.", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "\u2026", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "\u2026 Ah, I guess I didn\u2019t notice.", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Oh, that\u2019s okay.", + "speaker": "Minako" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "11": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 75, + 81 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "D" + }, + { + "action": "show_problem", + "problem_id": 78 + }, + { + "action": "dialogue", + "text": "I\u2019m back, I need a study on some new strain of grain. ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "Sure. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "...", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "You ever read what\u2019s in the stuff you hand out? ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "Don\u2019t read too closely. ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "I\u2019ll keep that in mind.", + "speaker": "You" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "12": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 103, + 75 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "C" + }, + { + "action": "dialogue", + "text": "0w0 ", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "What?", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Sorry\u2026 It\u2019s a bit hard to understand your accent. Exposure, I guess -- this isn\u2019t the hottest travel destination. ", + "speaker": "You" + }, + { + "action": "show_problem", + "problem_id": 69 + }, + { + "action": "dialogue", + "text": "0w0 ", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "Where did you say you were from, again?", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "UwU", + "speaker": "x_SLAYER_X" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "13": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 103, + 82 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "E" + }, + { + "action": "show_problem", + "problem_id": 99 + }, + { + "action": "dialogue", + "text": "Can you get me a copy of the June 15 and July 15 official Economic Reports? From 2142? ", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "\u2026", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Odd, but okay. This is for work?", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "O-odd? I don\u2019t think so.", + "speaker": "Minako" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "14": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 82, + 87 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "B" + }, + { + "action": "dialogue", + "text": "I require last month\u2019s Social Report -- ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "The Report? Surely you know --", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Don\u2019t interrupt me. You tread a fine line, Investigator.", + "speaker": "Director" + }, + { + "action": "show_problem", + "problem_id": 100 + }, + { + "action": "dialogue", + "text": "The one we don\u2019t publish. You know where to find it, I presume.", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "Of course. ", + "speaker": "You" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "15": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 87, + 81 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "A" + }, + { + "action": "dialogue", + "text": "How far do your records stretch?", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "Do you mean, chronologically? Spatially? Geographically? ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "\u2026", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "Whatever you need to know, I know. ", + "speaker": "You" + }, + { + "action": "show_problem", + "problem_id": 88 + }, + { + "action": "dialogue", + "text": " Do you know anything the Minister does not?", + "speaker": "???" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "16": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 99, + 100 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "B" + }, + { + "action": "dialogue", + "text": "Are you a native of the capital, Investigator?", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "No. I come from a town in the East. ", + "speaker": "You" + }, + { + "action": "show_problem", + "problem_id": 62 + }, + { + "action": "dialogue", + "text": "Was the capital what you imagined when you came here?", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "No, it isn\u2019t. But I didn\u2019t come here for the sightseeing. I came here for the work. ", + "speaker": "You" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "17": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 100, + 69 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "C" + }, + { + "action": "show_problem", + "problem_id": 85 + }, + { + "action": "dialogue", + "text": "wat r th3 b3st pwaces to v1s1t in t3h repubwic??", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "Would you like our official Travel Guide? ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "n0 wh0 cares waht t3h m1n1ster think5!! w4t do u th1nk??", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "\u2026 You shouldn\u2019t let people hear you say that, you know.", + "speaker": "You" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "18": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 69, + 78 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "D" + }, + { + "action": "dialogue", + "text": "Hi. ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "Didn\u2019t notice your bracelet before. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Yeah, silver. Nice, isn\u2019t it.", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "Is that regulation? ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "What, are you gonna report me for a bracelet? ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "I\u2019m not", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "\u2026", + "speaker": "Julia" + }, + { + "action": "show_problem", + "problem_id": 90 + }, + { + "action": "dialogue", + "text": "Nice to have something of physical value...", + "speaker": "Julia" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "19": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 78, + 88 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "A" + }, + { + "action": "show_problem", + "problem_id": 63 + }, + { + "action": "dialogue", + "text": "To whom does the Republic belong?", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "\u2026 The Minister, of course.", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "I thought this was the Department of Truth.", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "It is.", + "speaker": "You" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "20": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 88, + 99 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "E" + }, + { + "action": "dialogue", + "text": "H-haha, I know this is weird, but...", + "speaker": "Minako" + }, + { + "action": "show_problem", + "problem_id": 94 + }, + { + "action": "dialogue", + "text": "Could I have the original broadcast on January 28, 2066 of the Minister\u2019s speech?", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "\u2026", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "G-glory to the Minister! Glory to the Republic!", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "You don\u2019t have to say that, you know. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "W-well, you know with the war and all\u2026 ", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "You never know\u2026", + "speaker": "Minako" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "21": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 85, + 63 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "A" + }, + { + "action": "dialogue", + "text": "Do you love the Republic? ", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "\u2026 ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "I do. ", + "speaker": "You" + }, + { + "action": "show_problem", + "problem_id": 57 + }, + { + "action": "dialogue", + "text": "So does the Order. ", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "We are more numerous than you think.", + "speaker": "???" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "22": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 63, + 90 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "D" + }, + { + "action": "dialogue", + "text": "Hear the news lately?", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "What news? ", + "speaker": "You" + }, + { + "action": "show_problem", + "problem_id": 61 + }, + { + "action": "dialogue", + "text": "Fire three buildings down from Department of Liberty. ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "How do you know this before I do? ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "\u2026", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "Better that you pretend you never heard this, actually. ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "Keep your head down.", + "speaker": "Julia" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "23": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 90, + 94 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "E" + }, + { + "action": "dialogue", + "text": "I-I know your name.", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "Um, yes. It\u2019s on my desk. ", + "speaker": "You" + }, + { + "action": "show_problem", + "problem_id": 74 + }, + { + "action": "dialogue", + "text": "Um, would you like to know mine?", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "Your name is Minako. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "H-how do you know that? There should be no way! Um, I mean, it\u2019s fine, like the Minister always says, a clean mind -- ", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "It\u2019s on your name tag.", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "\u2026", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "Oh. What a relief\u2026", + "speaker": "Minako" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "24": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 94, + 62 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "B" + }, + { + "action": "dialogue", + "text": "I hope for the fortune of knowing that everyone in the Department of Truth is loyal to the Republic. ", + "speaker": "Director" + }, + { + "action": "show_problem", + "problem_id": 64 + }, + { + "action": "dialogue", + "text": "Remember to report all suspicion of treason to the Department of Security. ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "Glory to the Republic. ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "...", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Glory to the Republic.", + "speaker": "You" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "25": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 62, + 85 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "C" + }, + { + "action": "dialogue", + "text": "w0w u ppl R so p00r!! :0", + "speaker": "x_SLAYER_X" + }, + { + "action": "show_problem", + "problem_id": 70 + }, + { + "action": "dialogue", + "text": "t3h bu1ld1ngs r all so cramped 4nd ugwy!!!11 ", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "all th3s3 ppl in t3h str33t :\u20159", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "...", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Would you mind tipping then? ", + "speaker": "You" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "26": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 57 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "A" + }, + { + "action": "dialogue", + "text": "Great change is upon us. ", + "speaker": "???" + }, + { + "action": "show_problem", + "problem_id": 67 + }, + { + "action": "dialogue", + "text": "Will you aid us, or be swept away by the current?", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "... ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "The Order must be informed. ", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "I understand. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Where is the Minister\u2019s arsenal?", + "speaker": "???" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "27": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 64 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "B" + }, + { + "action": "dialogue", + "text": "You have served your nation well in the years you have been here. ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "Am I right to believe you trustworthy?", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "\u2026 ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "You are. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "...", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "What I ask of you must be conducted with the utmost secrecy. ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "This is a great honor. ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "And what do you ask of me? ", + "speaker": "You" + }, + { + "action": "show_problem", + "problem_id": 58 + }, + { + "action": "dialogue", + "text": "In times of tension, minds are easily swayed. ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "I have reason to suspect\u2026 ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "I would like a file on the Directors of Truth and Justice. ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "There are several members on the Minister\u2019s Committee of Public Safety, as well\u2026 ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "Glory to the Republic.", + "speaker": "Director" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "28": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 70 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "C" + }, + { + "action": "dialogue", + "text": "rawr xd :33", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "hiii!!!1", + "speaker": "x_SLAYER_X" + }, + { + "action": "show_problem", + "problem_id": 60 + }, + { + "action": "dialogue", + "text": "u lik livin her3 ?? ", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "I do feel some patriotism\u2026 ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "But at the same time, living here\u2026", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "I know this is not what the Republic is supposed to be. ", + "speaker": "You" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "29": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 61 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "D" + }, + { + "action": "dialogue", + "text": "You might hear of a lot more stuff like that fire lately. ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "Why should I? ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Harmless accident. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "I\u2019m pretty sure fires are harmful. ", + "speaker": "Julia" + }, + { + "action": "show_problem", + "problem_id": 71 + }, + { + "action": "dialogue", + "text": "\u2026 I wouldn\u2019t call it an accident, either. ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "I know some people who are pretty angry. ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "Stupid and angry.", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "That\u2019s a bit dangerous to say. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "\u2026 ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "Good. It\u2019s good to think that way.", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "\u2026 But I can\u2019t tell if you really buy into this Ministry stuff. ", + "speaker": "Julia" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "30": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 74 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "E" + }, + { + "action": "show_problem", + "problem_id": 80 + }, + { + "action": "dialogue", + "text": "H-hi, can I see the state happiness poll -- ", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "For 10 years ago, during the famine? Or maybe 14 years ago, when the plague --", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "I -- yes -- please don\u2019t report me!", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "It\u2019s not illegal to look at reports. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "\u2026 Even if you want some things the Republic published that it probably wants you to forget.", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "I-I\u2019m not doing this for work. It\u2019s a -- personal project?", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "I guessed as much. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Eep!", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "\u2026 I just told you, it\u2019s not illegal to look. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Thank you. ", + "speaker": "Minako" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "31": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 67 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "A" + }, + { + "action": "dialogue", + "text": "Freedom awaits the new Republic. ", + "speaker": "???" + }, + { + "action": "show_problem", + "problem_id": 79 + }, + { + "action": "dialogue", + "text": "The wall that surrounds the capital\u2026 ", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "Where is it be penetrable?", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "The doors, maybe? I came through the checkpoint when I applied to move here. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "That\u2019s not what I mean.", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "It\u2019s best if the guards don\u2019t know.", + "speaker": "???" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "32": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 58 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "B" + }, + { + "action": "dialogue", + "text": "Have you heard of the Order of [REDACTED], Investigator? ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "\u2026 ", + "speaker": "You" + }, + { + "action": "show_problem", + "problem_id": 86 + }, + { + "action": "dialogue", + "text": "Anything that you find on the Order and the Minister\u2019s government -- deliver it to me. ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "Do not mention this name to anyone else.", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "It is a seditious evil.", + "speaker": "Director" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "33": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 60 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "C" + }, + { + "action": "show_problem", + "problem_id": 95 + }, + { + "action": "dialogue", + "text": "u w4nt 2 vwisit m3 wen i go h0m3?? ", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "w3 hav trees!!11", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "That\u2019s kind of hard. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Especially since our nations are at war... ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "They\u2019ll think I\u2019m a spy. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "i v0uch fow u!!", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "im pw3tty imp0wrtant u kno 3:<", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "ju5t n33d s0m d0cwuments\u2026. ", + "speaker": "x_SLAYER_X" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "34": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 71 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "D" + }, + { + "action": "dialogue", + "text": "Yo. ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "Yo. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Ugh, it\u2019s weird coming from you. You don\u2019t talk like that. ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "\u2026 Isn\u2019t that pretty old slang? ", + "speaker": "You" + }, + { + "action": "show_problem", + "problem_id": 59 + }, + { + "action": "dialogue", + "text": "Most people have forgotten stuff like that. That\u2019s what makes it cool. ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "Better to be forgotten than banned, I suppose.", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Yeah, isn\u2019t that lit? Isn\u2019t it gucci? ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "\u2026 I can tell I\u2019m losing you.", + "speaker": "Julia" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "35": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 80 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "E" + }, + { + "action": "dialogue", + "text": "I can trust you, right?", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "Y-you\u2019ve read all these reports?", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "Maybe \u201cskimmed.\u201d", + "speaker": "You" + }, + { + "action": "show_problem", + "problem_id": 77 + }, + { + "action": "dialogue", + "text": "Well, then you know that we\u2019ve been lied to. ", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "D-discrepancies between Economic Reports in the same year, between the history of the war and the Minister\u2019s speech\u2026 There was a plague and e-everyone was cheerful! Is the war e-even --", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "I know that. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "There\u2019s a lot more than you think like that. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "So you know? And do nothing?", + "speaker": "Minako" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "36": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 79 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "A" + }, + { + "action": "dialogue", + "text": "What do you think of the war? ", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "It\u2019s harder to buy oranges. The chocolate gets bitterer every year. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "It was easier to notice when I lived outside the capital. Sometimes it doesn\u2019t seem real, except for those little things.", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "...", + "speaker": "???" + }, + { + "action": "show_problem", + "problem_id": 93 + }, + { + "action": "dialogue", + "text": "Sometimes blood needs to be shed. ", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "Sometimes a war needs to begin. ", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "\u2026 ", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "Tell me of the water supply to the capital. ", + "speaker": "???" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "37": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 86 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "B" + }, + { + "action": "dialogue", + "text": "You used to live in the East?", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "That\u2019s right. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Did you live close to the Tyrian border?", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "Not really. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "That\u2019s lucky. We find those near the border to be most susceptible to unclean thought. ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "Doubly dangerous in the war, you know. ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "Do you believe our borders to be impenetrable?", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "Well, I\u2019m not the Director of Security.", + "speaker": "You" + }, + { + "action": "show_problem", + "problem_id": 76 + }, + { + "action": "dialogue", + "text": "Foreign agents\u2026 ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "Conduct an investigation for me. ", + "speaker": "Director" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "38": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 95 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "C" + }, + { + "action": "dialogue", + "text": "c0m h0m3 w m33!!", + "speaker": "x_SLAYER_X" + }, + { + "action": "show_problem", + "problem_id": 102 + }, + { + "action": "dialogue", + "text": "t1s pwace sux u cant d0 4nyt1ng 3\u2019:", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "\u2026 ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Does everyone talk like you where you\u2019re from?", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "n0p :3", + "speaker": "x_SLAYER_X" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "39": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 59 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "D" + }, + { + "action": "dialogue", + "text": "Hear the news lately?", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "What news? ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Fire three buildings down from Department of Liberty. ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "How do you know this before I do? ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "\u2026", + "speaker": "Julia" + }, + { + "action": "show_problem", + "problem_id": 68 + }, + { + "action": "dialogue", + "text": "Better that you pretend you never heard this, actually. ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "Keep your head down. ", + "speaker": "Julia" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "40": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 77 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "E" + }, + { + "action": "dialogue", + "text": "I-I have to to get this out. People can know the truth!", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "The Minister writes the truth. ", + "speaker": "You" + }, + { + "action": "show_problem", + "problem_id": 83 + }, + { + "action": "dialogue", + "text": "I know you don\u2019t really believe that. ", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "We can do the right thing. Help me publish this.", + "speaker": "Minako" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "41": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 9300 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "A" + }, + { + "action": "dialogue", + "text": "The dawn approaches. ", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "This is your chance to free us all from evil. ", + "speaker": "???" + }, + { + "action": "show_problem", + "problem_id": 9700 + }, + { + "action": "dialogue", + "text": "Get us control of the Minister\u2019s Peacekeeping drones. ", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "The Order thanks you. ", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "Haste. ", + "speaker": "???" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "42": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 7600 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "B" + }, + { + "action": "dialogue", + "text": "You would be willing to do anything for your country, would you not?", + "speaker": "Director" + }, + { + "action": "show_problem", + "problem_id": 9800 + }, + { + "action": "dialogue", + "text": "These are orders from the Minister himself. ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "Hide this device inside the office of the Director of Truth. ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "This is the sequence to broadcast. ", + "speaker": "Director" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "43": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 10200 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "C" + }, + { + "action": "dialogue", + "text": " im g0in home th1s wednesdayw !!", + "speaker": "x_SLAYER_X" + }, + { + "action": "show_problem", + "problem_id": 9900 + }, + { + "action": "dialogue", + "text": "f0rge ur p3rmit :3", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "b th3r3 or i wleave w1th0ut u!!! 3:<", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "c u on t3h oht3r s1de :3", + "speaker": "x_SLAYER_X" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "44": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 6800 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "D" + }, + { + "action": "dialogue", + "text": "Trust me, please. ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "I can\u2019t tell you exactly, but very soon, the Department of Truth will become very unsafe. ", + "speaker": "Julia" + }, + { + "action": "show_problem", + "problem_id": 10000 + }, + { + "action": "dialogue", + "text": "You need to find a way to get out of work. Just for a few days. ", + "speaker": "Julia" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "45": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + 8300 + ] + } + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "E" + }, + { + "action": "dialogue", + "text": "I\u2019ve compiled all the evidence here. ", + "speaker": "Minako" + }, + { + "action": "show_problem", + "problem_id": 10100 + }, + { + "action": "dialogue", + "text": "Please, get it out. ", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "P-please hurry. We can\u2019t be caught with this stuff on us...", + "speaker": "Minako" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": " ", + "speaker": null + } + ] + }, + "P01": { + "on": { + "problem": 91 + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "E" + }, + { + "action": "dialogue", + "text": "...", + "speaker": "Minako" + } + ] + }, + "P02": { + "on": { + "problem": 66 + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "B" + }, + { + "action": "dialogue", + "text": "...", + "speaker": "Director" + } + ] + }, + "P03": { + "on": { + "problem": 65 + }, + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "D" + }, + { + "action": "dialogue", + "text": "...", + "speaker": "Julia" + } + ] + }, + "P04": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "C" + }, + { + "action": "dialogue", + "speaker": "x_SLAYER_X", + "text": "..." + } + ], + "on": { + "problem": 89 + } + }, + "P05": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "A" + }, + { + "action": "dialogue", + "speaker": "???", + "text": "..." + } + ], + "on": { + "problem": 92 + } + }, + "P06": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "C" + }, + { + "action": "dialogue", + "speaker": "x_SLAYER_X", + "text": "..." + } + ], + "on": { + "problem": 103 + } + }, + "P07": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "B" + }, + { + "action": "dialogue", + "speaker": "Director", + "text": "..." + } + ], + "on": { + "problem": 87 + } + }, + "P08": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "A" + }, + { + "action": "dialogue", + "speaker": "???", + "text": "..." + } + ], + "on": { + "problem": 81 + } + }, + "P09": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "D" + }, + { + "action": "dialogue", + "speaker": "Julia", + "text": "..." + } + ], + "on": { + "problem": 75 + } + }, + "P10": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "E" + }, + { + "action": "dialogue", + "speaker": "Minako", + "text": "..." + } + ], + "on": { + "problem": 82 + } + }, + "P11": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "D" + }, + { + "action": "dialogue", + "speaker": "Julia", + "text": "..." + } + ], + "on": { + "problem": 78 + } + }, + "P12": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "C" + }, + { + "action": "dialogue", + "speaker": "x_SLAYER_X", + "text": "..." + } + ], + "on": { + "problem": 69 + } + }, + "P13": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "E" + }, + { + "action": "dialogue", + "speaker": "Minako", + "text": "..." + } + ], + "on": { + "problem": 99 + } + }, + "P14": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "B" + }, + { + "action": "dialogue", + "speaker": "Director", + "text": "..." + } + ], + "on": { + "problem": 100 + } + }, + "P15": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "A" + }, + { + "action": "dialogue", + "speaker": "???", + "text": "..." + } + ], + "on": { + "problem": 88 + } + }, + "P16": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "B" + }, + { + "action": "dialogue", + "speaker": "Director", + "text": "..." + } + ], + "on": { + "problem": 62 + } + }, + "P17": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "C" + }, + { + "action": "dialogue", + "speaker": "x_SLAYER_X", + "text": "..." + } + ], + "on": { + "problem": 85 + } + }, + "P18": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "D" + }, + { + "action": "dialogue", + "speaker": "Julia", + "text": "..." + } + ], + "on": { + "problem": 90 + } + }, + "P19": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "A" + }, + { + "action": "dialogue", + "speaker": "???", + "text": "..." + } + ], + "on": { + "problem": 63 + } + }, + "P20": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "E" + }, + { + "action": "dialogue", + "speaker": "Minako", + "text": "..." + } + ], + "on": { + "problem": 94 + } + }, + "P21": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "A" + }, + { + "action": "dialogue", + "speaker": "???", + "text": "..." + } + ], + "on": { + "problem": 57 + } + }, + "P22": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "D" + }, + { + "action": "dialogue", + "speaker": "Julia", + "text": "..." + } + ], + "on": { + "problem": 61 + } + }, + "P23": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "E" + }, + { + "action": "dialogue", + "speaker": "Minako", + "text": "..." + } + ], + "on": { + "problem": 74 + } + }, + "P24": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "B" + }, + { + "action": "dialogue", + "speaker": "Director", + "text": "..." + } + ], + "on": { + "problem": 64 + } + }, + "P25": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "C" + }, + { + "action": "dialogue", + "speaker": "x_SLAYER_X", + "text": "..." + } + ], + "on": { + "problem": 70 + } + }, + "P26": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "A" + }, + { + "action": "dialogue", + "speaker": "???", + "text": "..." + } + ], + "on": { + "problem": 67 + } + }, + "P27": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "B" + }, + { + "action": "dialogue", + "speaker": "Director", + "text": "..." + } + ], + "on": { + "problem": 58 + } + }, + "P28": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "C" + }, + { + "action": "dialogue", + "speaker": "x_SLAYER_X", + "text": "..." + } + ], + "on": { + "problem": 60 + } + }, + "P29": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "D" + }, + { + "action": "dialogue", + "speaker": "Julia", + "text": "..." + } + ], + "on": { + "problem": 71 + } + }, + "P30": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "E" + }, + { + "action": "dialogue", + "speaker": "Minako", + "text": "..." + } + ], + "on": { + "problem": 80 + } + }, + "P31": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "A" + }, + { + "action": "dialogue", + "speaker": "???", + "text": "..." + } + ], + "on": { + "problem": 79 + } + }, + "P32": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "B" + }, + { + "action": "dialogue", + "speaker": "Director", + "text": "..." + } + ], + "on": { + "problem": 86 + } + }, + "P33": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "C" + }, + { + "action": "dialogue", + "speaker": "x_SLAYER_X", + "text": "..." + } + ], + "on": { + "problem": 95 + } + }, + "P34": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "D" + }, + { + "action": "dialogue", + "speaker": "Julia", + "text": "..." + } + ], + "on": { + "problem": 59 + } + }, + "P35": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "E" + }, + { + "action": "dialogue", + "speaker": "Minako", + "text": "..." + } + ], + "on": { + "problem": 77 + } + }, + "P36": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "A" + }, + { + "action": "dialogue", + "speaker": "???", + "text": "..." + } + ], + "on": { + "problem": 93 + } + }, + "P37": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "B" + }, + { + "action": "dialogue", + "speaker": "Director", + "text": "..." + } + ], + "on": { + "problem": 76 + } + }, + "P38": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "C" + }, + { + "action": "dialogue", + "speaker": "x_SLAYER_X", + "text": "..." + } + ], + "on": { + "problem": 102 + } + }, + "P39": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "D" + }, + { + "action": "dialogue", + "speaker": "Julia", + "text": "..." + } + ], + "on": { + "problem": 68 + } + }, + "P40": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "E" + }, + { + "action": "dialogue", + "speaker": "Minako", + "text": "..." + } + ], + "on": { + "problem": 83 + } + }, + "P41": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "A" + }, + { + "action": "dialogue", + "speaker": "???", + "text": "..." + } + ], + "on": { + "problem": 9700 + } + }, + "P42": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "B" + }, + { + "action": "dialogue", + "speaker": "Director", + "text": "..." + } + ], + "on": { + "problem": 9800 + } + }, + "P43": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "C" + }, + { + "action": "dialogue", + "speaker": "x_SLAYER_X", + "text": "..." + } + ], + "on": { + "problem": 9900 + } + }, + "P44": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "D" + }, + { + "action": "dialogue", + "speaker": "Julia", + "text": "..." + } + ], + "on": { + "problem": 10000 + } + }, + "P45": { + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "E" + }, + { + "action": "dialogue", + "speaker": "Minako", + "text": "..." + } + ], + "on": { + "problem": 10100 + } + }, + "E1a": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + ] + } + }, + "background_image": "blue", + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "A" + }, + { + "action": "dialogue", + "text": "Hello. ", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "Welcome to a new order. ", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "We thank you for your role. ", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "You have been spared from the purges. ", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "We welcome you to our ranks.", + "speaker": "???" + }, + { + "action": "dialogue", + "text": "Thank you. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Glory to the Order.", + "speaker": "You" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": "ROUTE END", + "speaker": null + } + ] + }, + "E1b": { + "on": { + "after_scenes": [ + "E1a" + ] + }, + "background_image": "bg", + "choreography": [ + { + "action": "dialogue", + "text": "- END ROUTE 1 -", + "speaker": null + }, + { + "action": "dialogue", + "text": "(CONTINUE)", + "speaker": null + } + ] + }, + "E2a": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + ] + } + }, + "background_image": "blue", + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "B" + }, + { + "action": "dialogue", + "text": "Well done, Investigator. ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "You have proven yourself a valuable servant to the state, and you have preserved the security of the Republic. ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "We have uncovered the plot of the Order and the Director of Truth has been rehabilitated.", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "Your salary has been upgraded to level 4 as a reward for your service. ", + "speaker": "Director" + }, + { + "action": "dialogue", + "text": "Thank you, Director.", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Glory to the Republic.", + "speaker": "Director" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": "ROUTE END", + "speaker": null + } + ] + }, + "E2b": { + "on": { + "after_scenes": [ + "E2a" + ] + }, + "background_image": "bg", + "choreography": [ + { + "action": "dialogue", + "text": "- END ROUTE 2 -", + "speaker": null + }, + { + "action": "dialogue", + "text": "(CONTINUE)", + "speaker": null + } + ] + }, + "E3b": { + "on": { + "after_scenes": [ + "E3a" + ] + }, + "background_image": "bg", + "choreography": [ + { + "action": "dialogue", + "text": "- END ROUTE 3 -", + "speaker": null + }, + { + "action": "dialogue", + "text": "(CONTINUE)", + "speaker": null + } + ] + }, + "E3a": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + ] + } + }, + "background_image": "blue", + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "C" + }, + { + "action": "dialogue", + "text": "hii!!!!111", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "u g0t thru :00", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "Thank you. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "\u2026 Poor everyone still in there. ", + "speaker": "You" + }, + { + "action": "dialogue", + "text": ":3", + "speaker": "x_SLAYER_X" + }, + { + "action": "dialogue", + "text": "c0m with m3 !! :3", + "speaker": "x_SLAYER_X" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": "ROUTE END", + "speaker": null + } + ] + }, + "E4b": { + "on": { + "after_scenes": [ + "E4a" + ] + }, + "background_image": "bg", + "choreography": [ + { + "action": "dialogue", + "text": "- END ROUTE 4 -", + "speaker": null + }, + { + "action": "dialogue", + "text": "(CONTINUE)", + "speaker": null + } + ] + }, + "E4a": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + ] + } + }, + "background_image": "blue", + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "D" + }, + { + "action": "dialogue", + "text": "Well, you\u2019re alive. ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "Did they mean to kill people?", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "It\u2019s horrible. I can\u2019t tell", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "How long before the smoke clears, do you think?", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "They\u2019re going to be caught. The gloves, the cans --", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "Yeah, I know. Stupid and angry, like I said. ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "I wish they hadn\u2019t. ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "At least you\u2019re alive. ", + "speaker": "Julia" + }, + { + "action": "dialogue", + "text": "At least I\u2019m alive.", + "speaker": "You" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": "ROUTE END", + "speaker": null + } + ] + }, + "E5a": { + "on": { + "problems_solved": { + "thresh": 1, + "problems": [ + ] + } + }, + "background_image": "blue", + "choreography": [ + { + "action": "show_character", + "position": 1, + "sprite": "E" + }, + { + "action": "dialogue", + "text": "H-hello. ", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "Did you hear what happened the other day?", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "Another riot in front of the Department of Security.", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "I know\u2026 this is because of what we did.", + "speaker": "Minako" + }, + { + "action": "dialogue", + "text": "Yes, I heard.", + "speaker": "You" + }, + { + "action": "dialogue", + "text": "I\u2019m glad.", + "speaker": "Minako" + }, + { + "action": "hide_character", + "position": 1 + }, + { + "action": "dialogue", + "text": "ROUTE END", + "speaker": null + } + ] + }, + "E5b": { + "on": { + "after_scenes": [ + "E5a" + ] + }, + "background_image": "bg", + "choreography": [ + { + "action": "dialogue", + "text": "- END ROUTE 5 -", + "speaker": null + }, + { + "action": "dialogue", + "text": "(CONTINUE)", + "speaker": null + } + ] + } + }, + "problem_triggers": { + "91": { + "after_scenes": [ + "1" + ] + }, + "66": { + "after_scenes": [ + "2" + ] + }, + "65": { + "after_scenes": [ + "3" + ] + }, + "89": { + "after_scenes": [ + "4" + ] + }, + "92": { + "after_scenes": [ + "5" + ] + }, + "103": { + "after_scenes": [ + "6" + ] + }, + "87": { + "after_scenes": [ + "7" + ] + }, + "81": { + "after_scenes": [ + "8" + ] + }, + "75": { + "after_scenes": [ + "9" + ] + }, + "82": { + "after_scenes": [ + "10" + ] + }, + "78": { + "after_scenes": [ + "11" + ] + }, + "69": { + "after_scenes": [ + "12" + ] + }, + "99": { + "after_scenes": [ + "13" + ] + }, + "100": { + "after_scenes": [ + "14" + ] + }, + "88": { + "after_scenes": [ + "15" + ] + }, + "62": { + "after_scenes": [ + "16" + ] + }, + "85": { + "after_scenes": [ + "17" + ] + }, + "90": { + "after_scenes": [ + "18" + ] + }, + "63": { + "after_scenes": [ + "19" + ] + }, + "94": { + "after_scenes": [ + "20" + ] + }, + "57": { + "after_scenes": [ + "21" + ] + }, + "61": { + "after_scenes": [ + "22" + ] + }, + "74": { + "after_scenes": [ + "23" + ] + }, + "64": { + "after_scenes": [ + "24" + ] + }, + "70": { + "after_scenes": [ + "25" + ] + }, + "67": { + "after_scenes": [ + "26" + ] + }, + "58": { + "after_scenes": [ + "27" + ] + }, + "60": { + "after_scenes": [ + "28" + ] + }, + "71": { + "after_scenes": [ + "29" + ] + }, + "80": { + "after_scenes": [ + "30" + ] + }, + "79": { + "after_scenes": [ + "31" + ] + }, + "86": { + "after_scenes": [ + "32" + ] + }, + "95": { + "after_scenes": [ + "33" + ] + }, + "59": { + "after_scenes": [ + "34" + ] + }, + "77": { + "after_scenes": [ + "35" + ] + }, + "93": { + "after_scenes": [ + "36" + ] + }, + "76": { + "after_scenes": [ + "37" + ] + }, + "102": { + "after_scenes": [ + "38" + ] + }, + "68": { + "after_scenes": [ + "39" + ] + }, + "83": { + "after_scenes": [ + "40" + ] + } + }, + "routes": { + "1": { + "trigger_spec": { + }, + "release_spec": { + "route": 1, + "after_scenes": [ + "1" + ] + } + } + } +} \ No newline at end of file diff --git a/server/easyctf/assets/images/digitalocean.png b/server/easyctf/assets/images/digitalocean.png new file mode 100644 index 0000000..aeee746 Binary files /dev/null and b/server/easyctf/assets/images/digitalocean.png differ diff --git a/server/easyctf/assets/images/game/090939.png b/server/easyctf/assets/images/game/090939.png new file mode 100644 index 0000000..f2d32f4 Binary files /dev/null and b/server/easyctf/assets/images/game/090939.png differ diff --git a/server/easyctf/assets/images/game/background.png b/server/easyctf/assets/images/game/background.png new file mode 100644 index 0000000..1ad83ec Binary files /dev/null and b/server/easyctf/assets/images/game/background.png differ diff --git a/server/easyctf/assets/images/game/background_rain.gif b/server/easyctf/assets/images/game/background_rain.gif new file mode 100644 index 0000000..cb1f225 Binary files /dev/null and b/server/easyctf/assets/images/game/background_rain.gif differ diff --git a/server/easyctf/assets/images/game/char_A_scale.png b/server/easyctf/assets/images/game/char_A_scale.png new file mode 100644 index 0000000..9ca950a Binary files /dev/null and b/server/easyctf/assets/images/game/char_A_scale.png differ diff --git a/server/easyctf/assets/images/game/char_B_scale.png b/server/easyctf/assets/images/game/char_B_scale.png new file mode 100644 index 0000000..e44c3b0 Binary files /dev/null and b/server/easyctf/assets/images/game/char_B_scale.png differ diff --git a/server/easyctf/assets/images/game/char_C_scale.png b/server/easyctf/assets/images/game/char_C_scale.png new file mode 100644 index 0000000..52acada Binary files /dev/null and b/server/easyctf/assets/images/game/char_C_scale.png differ diff --git a/server/easyctf/assets/images/game/char_D_scale.png b/server/easyctf/assets/images/game/char_D_scale.png new file mode 100644 index 0000000..a22b865 Binary files /dev/null and b/server/easyctf/assets/images/game/char_D_scale.png differ diff --git a/server/easyctf/assets/images/game/char_E_scale.png b/server/easyctf/assets/images/game/char_E_scale.png new file mode 100644 index 0000000..c582f23 Binary files /dev/null and b/server/easyctf/assets/images/game/char_E_scale.png differ diff --git a/server/easyctf/assets/js/bootstrap.min.js b/server/easyctf/assets/js/bootstrap.min.js new file mode 100755 index 0000000..9bcd2fc --- /dev/null +++ b/server/easyctf/assets/js/bootstrap.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under the MIT license + */ +if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.7",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a("#"===f?[]:f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.7",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c).prop(c,!0)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c).prop(c,!1))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target).closest(".btn");b.call(d,"toggle"),a(c.target).is('input[type="radio"], input[type="checkbox"]')||(c.preventDefault(),d.is("input,button")?d.trigger("focus"):d.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(a>this.$items.length-1||a<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.7",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.7",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);if(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),!c.isInStateTrue())return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null,a.$element=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.7",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.7",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.7",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return e=a-d&&"bottom"},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file diff --git a/server/easyctf/assets/js/bootstrap3-typeahead.min.js b/server/easyctf/assets/js/bootstrap3-typeahead.min.js new file mode 100755 index 0000000..93d4c92 --- /dev/null +++ b/server/easyctf/assets/js/bootstrap3-typeahead.min.js @@ -0,0 +1 @@ +(function(root,factory){"use strict";if(typeof module!=="undefined"&&module.exports){module.exports=factory(require("jquery"))}else if(typeof define==="function"&&define.amd){define(["jquery"],function($){return factory($)})}else{factory(root.jQuery)}})(this,function($){"use strict";var Typeahead=function(element,options){this.$element=$(element);this.options=$.extend({},$.fn.typeahead.defaults,options);this.matcher=this.options.matcher||this.matcher;this.sorter=this.options.sorter||this.sorter;this.select=this.options.select||this.select;this.autoSelect=typeof this.options.autoSelect=="boolean"?this.options.autoSelect:true;this.highlighter=this.options.highlighter||this.highlighter;this.render=this.options.render||this.render;this.updater=this.options.updater||this.updater;this.displayText=this.options.displayText||this.displayText;this.source=this.options.source;this.delay=this.options.delay;this.$menu=$(this.options.menu);this.$appendTo=this.options.appendTo?$(this.options.appendTo):null;this.fitToElement=typeof this.options.fitToElement=="boolean"?this.options.fitToElement:false;this.shown=false;this.listen();this.showHintOnFocus=typeof this.options.showHintOnFocus=="boolean"||this.options.showHintOnFocus==="all"?this.options.showHintOnFocus:false;this.afterSelect=this.options.afterSelect;this.addItem=false;this.value=this.$element.val()||this.$element.text()};Typeahead.prototype={constructor:Typeahead,select:function(){var val=this.$menu.find(".active").data("value");this.$element.data("active",val);if(this.autoSelect||val){var newVal=this.updater(val);if(!newVal){newVal=""}this.$element.val(this.displayText(newVal)||newVal).text(this.displayText(newVal)||newVal).change();this.afterSelect(newVal)}return this.hide()},updater:function(item){return item},setSource:function(source){this.source=source},show:function(){var pos=$.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});var scrollHeight=typeof this.options.scrollHeight=="function"?this.options.scrollHeight.call():this.options.scrollHeight;var element;if(this.shown){element=this.$menu}else if(this.$appendTo){element=this.$menu.appendTo(this.$appendTo);this.hasSameParent=this.$appendTo.is(this.$element.parent())}else{element=this.$menu.insertAfter(this.$element);this.hasSameParent=true}if(!this.hasSameParent){element.css("position","fixed");var offset=this.$element.offset();pos.top=offset.top;pos.left=offset.left}var dropup=$(element).parent().hasClass("dropup");var newTop=dropup?"auto":pos.top+pos.height+scrollHeight;var right=$(element).hasClass("dropdown-menu-right");var newLeft=right?"auto":pos.left;element.css({top:newTop,left:newLeft}).show();if(this.options.fitToElement===true){element.css("width",this.$element.outerWidth()+"px")}this.shown=true;return this},hide:function(){this.$menu.hide();this.shown=false;return this},lookup:function(query){var items;if(typeof query!="undefined"&&query!==null){this.query=query}else{this.query=this.$element.val()||this.$element.text()||""}if(this.query.length0){this.$element.data("active",items[0])}else{this.$element.data("active",null)}if(this.options.addItem){items.push(this.options.addItem)}if(this.options.items=="all"){return this.render(items).show()}else{return this.render(items.slice(0,this.options.items)).show()}},matcher:function(item){var it=this.displayText(item);return~it.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(items){var beginswith=[];var caseSensitive=[];var caseInsensitive=[];var item;while(item=items.shift()){var it=this.displayText(item);if(!it.toLowerCase().indexOf(this.query.toLowerCase()))beginswith.push(item);else if(~it.indexOf(this.query))caseSensitive.push(item);else caseInsensitive.push(item)}return beginswith.concat(caseSensitive,caseInsensitive)},highlighter:function(item){var html=$("
");var query=this.query;var i=item.toLowerCase().indexOf(query.toLowerCase());var len=query.length;var leftPart;var middlePart;var rightPart;var strong;if(len===0){return html.text(item).html()}while(i>-1){leftPart=item.substr(0,i);middlePart=item.substr(i,len);rightPart=item.substr(i+len);strong=$("").text(middlePart);html.append(document.createTextNode(leftPart)).append(strong);item=rightPart;i=item.toLowerCase().indexOf(query.toLowerCase())}return html.append(document.createTextNode(item)).html()},render:function(items){var that=this;var self=this;var activeFound=false;var data=[];var _category=that.options.separator;$.each(items,function(key,value){if(key>0&&value[_category]!==items[key-1][_category]){data.push({__type:"divider"})}if(value[_category]&&(key===0||value[_category]!==items[key-1][_category])){data.push({__type:"category",name:value[_category]})}data.push(value)});items=$(data).map(function(i,item){if((item.__type||false)=="category"){return $(that.options.headerHtml).text(item.name)[0]}if((item.__type||false)=="divider"){return $(that.options.headerDivider)[0]}var text=self.displayText(item);i=$(that.options.item).data("value",item);i.find("a").html(that.highlighter(text,item));if(text==self.$element.val()){i.addClass("active");self.$element.data("active",item);activeFound=true}return i[0]});if(this.autoSelect&&!activeFound){items.filter(":not(.dropdown-header)").first().addClass("active");this.$element.data("active",items.first().data("value"))}this.$menu.html(items);return this},displayText:function(item){return typeof item!=="undefined"&&typeof item.name!="undefined"&&item.name||item},next:function(event){var active=this.$menu.find(".active").removeClass("active");var next=active.next();if(!next.length){next=$(this.$menu.find("li")[0])}next.addClass("active")},prev:function(event){var active=this.$menu.find(".active").removeClass("active");var prev=active.prev();if(!prev.length){prev=this.$menu.find("li").last()}prev.addClass("active")},listen:function(){this.$element.on("focus",$.proxy(this.focus,this)).on("blur",$.proxy(this.blur,this)).on("keypress",$.proxy(this.keypress,this)).on("input",$.proxy(this.input,this)).on("keyup",$.proxy(this.keyup,this));if(this.eventSupported("keydown")){this.$element.on("keydown",$.proxy(this.keydown,this))}this.$menu.on("click",$.proxy(this.click,this)).on("mouseenter","li",$.proxy(this.mouseenter,this)).on("mouseleave","li",$.proxy(this.mouseleave,this)).on("mousedown",$.proxy(this.mousedown,this))},destroy:function(){this.$element.data("typeahead",null);this.$element.data("active",null);this.$element.off("focus").off("blur").off("keypress").off("input").off("keyup");if(this.eventSupported("keydown")){this.$element.off("keydown")}this.$menu.remove();this.destroyed=true},eventSupported:function(eventName){var isSupported=eventName in this.$element;if(!isSupported){this.$element.setAttribute(eventName,"return;");isSupported=typeof this.$element[eventName]==="function"}return isSupported},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:if(e.shiftKey)return;e.preventDefault();this.prev();break;case 40:if(e.shiftKey)return;e.preventDefault();this.next();break}},keydown:function(e){this.suppressKeyPressRepeat=~$.inArray(e.keyCode,[40,38,9,13,27]);if(!this.shown&&e.keyCode==40){this.lookup()}else{this.move(e)}},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},input:function(e){var currentValue=this.$element.val()||this.$element.text();if(this.value!==currentValue){this.value=currentValue;this.lookup()}},keyup:function(e){if(this.destroyed){return}switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break}},focus:function(e){if(!this.focused){this.focused=true;if(this.options.showHintOnFocus&&this.skipShowHintOnFocus!==true){if(this.options.showHintOnFocus==="all"){this.lookup("")}else{this.lookup()}}}if(this.skipShowHintOnFocus){this.skipShowHintOnFocus=false}},blur:function(e){if(!this.mousedover&&!this.mouseddown&&this.shown){this.hide();this.focused=false}else if(this.mouseddown){this.skipShowHintOnFocus=true;this.$element.focus();this.mouseddown=false}},click:function(e){e.preventDefault();this.skipShowHintOnFocus=true;this.select();this.$element.focus();this.hide()},mouseenter:function(e){this.mousedover=true;this.$menu.find(".active").removeClass("active");$(e.currentTarget).addClass("active")},mouseleave:function(e){this.mousedover=false;if(!this.focused&&this.shown)this.hide()},mousedown:function(e){this.mouseddown=true;this.$menu.one("mouseup",function(e){this.mouseddown=false}.bind(this))}};var old=$.fn.typeahead;$.fn.typeahead=function(option){var arg=arguments;if(typeof option=="string"&&option=="getActive"){return this.data("active")}return this.each(function(){var $this=$(this);var data=$this.data("typeahead");var options=typeof option=="object"&&option;if(!data)$this.data("typeahead",data=new Typeahead(this,options));if(typeof option=="string"&&data[option]){if(arg.length>1){data[option].apply(data,Array.prototype.slice.call(arg,1))}else{data[option]()}}})};$.fn.typeahead.defaults={source:[],items:8,menu:'',item:'
  • ',minLength:1,scrollHeight:0,autoSelect:true,afterSelect:$.noop,addItem:false,delay:0,separator:"category",headerHtml:'',headerDivider:''};$.fn.typeahead.Constructor=Typeahead;$.fn.typeahead.noConflict=function(){$.fn.typeahead=old;return this};$(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(e){var $this=$(this);if($this.data("typeahead"))return;$this.typeahead($this.data())})}); diff --git a/server/easyctf/assets/js/jquery-2.1.4.min.js b/server/easyctf/assets/js/jquery-2.1.4.min.js new file mode 100755 index 0000000..49990d6 --- /dev/null +++ b/server/easyctf/assets/js/jquery-2.1.4.min.js @@ -0,0 +1,4 @@ +/*! jQuery v2.1.4 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)+1>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b="length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+K.uid++}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){ +return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthx",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,ba=/<([\w:]+)/,ca=/<|&#?\w+;/,da=/<(?:script|style|link)/i,ea=/checked\s*(?:[^=]|=\s*.checked.)/i,fa=/^$|\/(?:java|ecma)script/i,ga=/^true\/(.*)/,ha=/^\s*\s*$/g,ia={option:[1,""],thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};ia.optgroup=ia.option,ia.tbody=ia.tfoot=ia.colgroup=ia.caption=ia.thead,ia.th=ia.td;function ja(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function ka(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function la(a){var b=ga.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function ma(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function na(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function oa(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pa(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=oa(h),f=oa(a),d=0,e=f.length;e>d;d++)pa(f[d],g[d]);if(b)if(c)for(f=f||oa(a),g=g||oa(h),d=0,e=f.length;e>d;d++)na(f[d],g[d]);else na(a,h);return g=oa(h,"script"),g.length>0&&ma(g,!i&&oa(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(ca.test(e)){f=f||k.appendChild(b.createElement("div")),g=(ba.exec(e)||["",""])[1].toLowerCase(),h=ia[g]||ia._default,f.innerHTML=h[1]+e.replace(aa,"<$1>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=oa(k.appendChild(e),"script"),i&&ma(f),c)){j=0;while(e=f[j++])fa.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(oa(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&ma(oa(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(oa(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!da.test(a)&&!ia[(ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(aa,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(oa(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(oa(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&ea.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(oa(c,"script"),ka),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,oa(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,la),j=0;g>j;j++)h=f[j],fa.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(ha,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qa,ra={};function sa(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function ta(a){var b=l,c=ra[a];return c||(c=sa(a,b),"none"!==c&&c||(qa=(qa||n(" + + + + +{% endblock %} \ No newline at end of file diff --git a/server/easyctf/templates/chals/list.html b/server/easyctf/templates/chals/list.html new file mode 100755 index 0000000..129163f --- /dev/null +++ b/server/easyctf/templates/chals/list.html @@ -0,0 +1,150 @@ +{% from "templates.html" import render_field, render_generic_field %} +{% extends "layout.html" %} +{% block title %}Problems{% endblock %} + +{% block content %} + + + + +
    +
    +
    +

    Points: {{ current_user.team.points() }}

    +
    +

    Problems

    +
    +
    +
    +
    + +

    +
    +
    +
    + +{% endblock %} \ No newline at end of file diff --git a/server/easyctf/templates/chals/programming.html b/server/easyctf/templates/chals/programming.html new file mode 100644 index 0000000..db914b6 --- /dev/null +++ b/server/easyctf/templates/chals/programming.html @@ -0,0 +1,138 @@ +{% extends "layout.html" %} +{% block title %}Programming{% endblock %} + +{% block content %} + +
    +
    +

    Programming Editor

    +
    +
    +
    +
    +
    + If you are using Java, name your class Main. +
    + +
    +
    +
    + +
    +
    +
    +

    + Programming Editor - {{ problem.title }} +

    +
    + +
    +
    +

    Problem Statement

    + {{ problem.render_description(tid=current_user.tid) | safe }} +
    +
    +
    {% if programming_submit_form.code.data %}{{ programming_submit_form.code.data }}{% endif %}
    + +
    +
    +
    +
    +
    +
    +
    + +{% endblock %} diff --git a/server/easyctf/templates/chals/shell.html b/server/easyctf/templates/chals/shell.html new file mode 100644 index 0000000..61bf296 --- /dev/null +++ b/server/easyctf/templates/chals/shell.html @@ -0,0 +1,50 @@ +{% from "templates.html" import render_field, render_generic_field %} +{% extends "layout.html" %} +{% block title %}Shell{% endblock %} + +{% block content %} +
    +
    +

    Shell

    +
    +
    +
    +
    +

    The shell server allows you to connect to a live Linux server and use it to help you + as you solve challenges. You'll need to log in with a different set of credentials, + which you can view by clicking the button below. Note that (1) in order to paste into + the terminal emulator below, you must right click and select "paste from browser", and + (2) the password will not be displayed as you type it in.

    +

    Our server supports mosh login!

    +
    +
    +

    EasyCTF Shell Server

    +
    +
    + + + + + +
    + Reveal Credentials + + Full Screen +
    +
    + +
    + Abuse is not tolerated and will lead to immediate disqualification and removal from the competition. +
    +
    +
    +
    + +{% endblock %} \ No newline at end of file diff --git a/server/easyctf/templates/chals/solves.html b/server/easyctf/templates/chals/solves.html new file mode 100644 index 0000000..2f6c093 --- /dev/null +++ b/server/easyctf/templates/chals/solves.html @@ -0,0 +1,33 @@ +{% from "templates.html" import render_field, render_generic_field %} +{% extends "layout.html" %} +{% block title %}Solves{% endblock %} + +{% block content %} +
    +
    +

    Recent Solves on {{ problem.title }}

    +
    +
    +
    +
    + + + + + + + + + {% for solve in problem.solves|sort(attribute="date", reverse=True) %} + + + + + {% endfor %} + +
    Team NameDate
    {{ solve.team.teamname }} + +
    +
    +
    +{% endblock %} diff --git a/server/easyctf/templates/chals/status.html b/server/easyctf/templates/chals/status.html new file mode 100644 index 0000000..b32494c --- /dev/null +++ b/server/easyctf/templates/chals/status.html @@ -0,0 +1,51 @@ +{% extends "layout.html" %} +{% block title %}Submissions{% endblock %} + +{% block content %} +
    +
    + + + +
    +
    + + + + + + + + + + + + + + + + {% for job in jobs %} + + + + + + + + + + + + {% endfor %} + +
    Submission IDSubmittedJudgedUserProblemLanguageVerdictTimeMemory
    {{ job.id }}{{ job.user.username}}{{ job.problem.title }}{{ job.language }}{{ job.verdict }}{{ job.execution_time }}{{ job.execution_memory }}
    +
    +
    +
    +
    +{% endblock %} \ No newline at end of file diff --git a/server/easyctf/templates/chals/submission.html b/server/easyctf/templates/chals/submission.html new file mode 100644 index 0000000..814b963 --- /dev/null +++ b/server/easyctf/templates/chals/submission.html @@ -0,0 +1,61 @@ +{% extends "layout.html" %} +{% block title %}Submission #{{ job.id }} on {{ problem.title }}{% endblock %} + +{% block content %} + +
    +
    + + + +
    +
    + +

    + Submitted by {{ user.username }} + . +

    + +
    {{ job.contents }}
    + +

    + Status: {{ job.status }}
    + {% if job.status == 2 %} + Verdict: {{ job.verdict }}
    + Time: {{ job.execution_time }}
    + Memory: {{ job.execution_memory }}
    + {% endif %} +

    +

    Verdict Abbreviation

    +
      +
    • AC = Accepted
    • +
    • IS = Invalid Source
    • +
    • WA = Wrong Answer
    • +
    • TLE = Time Limit Exceeded
    • +
    • RTE = RunTime Error
    • +
    • ISC = Illegal SysCall
    • +
    • CE = Compilation Error
    • +
    • JE = Judge Error (you should probably report this)
    • +
    +
    +
    +
    +
    + +{% endblock %} diff --git a/server/easyctf/templates/classroom/index.html b/server/easyctf/templates/classroom/index.html new file mode 100644 index 0000000..3c73fd9 --- /dev/null +++ b/server/easyctf/templates/classroom/index.html @@ -0,0 +1,88 @@ +{% extends "layout.html" %} +{% block title %}Classrooms{% endblock %} + +{% block content %} +
    +
    + {% if current_user.level == 3 %} +
    + New Class +
    + {% endif %} +

    Classrooms

    +
    +
    +
    +
    + {% if current_user.level == 3 %} +

    These are the classrooms that you manage. You can create a new one by clicking + here.

    + + + + + + + + + {% for class in classes %} + + + + + {% endfor %} + +
    NameMembers
    + {{ class.name }} + {{ class.size }}
    + {% else %} +
    +
    +

    These are the classrooms that you are a part of.

    + + + + + + + + + {% for class in classes %} + + + + + {% endfor %} + +
    NameMembers
    + {{ class.name }} + {{ class.size }}
    +
    +
    +

    The teacher of these classrooms have invited you to join their classroom.

    + + + + + + + + + + {% for class in invites %} + + + + + + {% endfor %} + +
    NameMembers
    {{ class.name }}{{ class.size }} + Join » +
    +
    +
    + {% endif %} +
    +
    +{% endblock %} \ No newline at end of file diff --git a/server/easyctf/templates/classroom/new.html b/server/easyctf/templates/classroom/new.html new file mode 100644 index 0000000..d88ef2b --- /dev/null +++ b/server/easyctf/templates/classroom/new.html @@ -0,0 +1,31 @@ +{% from "templates.html" import render_field, %} +{% extends "layout.html" %} +{% block title %}New Classroom{% endblock %} + +{% block content %} +
    +
    +
    +
    +

    + « Back +

    +
    +
    +

    New Classroom

    +
    +
    +
    + {{ new_classroom_form.csrf_token }} +
    + {{ render_field(new_classroom_form.name) }} +
    + {{ new_classroom_form.submit(class_="btn btn-primary") }} +
    +
    +
    +
    +
    +
    +
    +{% endblock %} \ No newline at end of file diff --git a/server/easyctf/templates/classroom/view.html b/server/easyctf/templates/classroom/view.html new file mode 100644 index 0000000..5001a5d --- /dev/null +++ b/server/easyctf/templates/classroom/view.html @@ -0,0 +1,184 @@ +{% from "templates.html" import render_field %} +{% extends "layout.html" %} +{% block title %}Classrooms{% endblock %} + +{% block content %} +
    +
    +

    {{ classroom.name }}

    +
    +
    +
    +
    +
    +
    + {% if classroom.size %} + {% cache 120, "classroom", "%d" % classroom.id %} +
    +
    +

    Class Leaderboard

    +
    + + + + + + + + {% if current_user.uid == classroom.owner %} + + {% endif %} + + + + {% set rank = 1 %} + {% for team in classroom.scoreboard %} + + + + + + {% if current_user.uid == classroom.owner %} + + {% endif %} + + {% set rank = rank + 1 %} + {% endfor %} + +
    RankTeam NameSchoolPoints
    {{ rank }} + {{ team.teamname }} + {{ team.school }}{{ team.points() }} + +
    +
    +
    +
    +

    Individual Leaderboard

    +
    + + + + + + + {% if current_user.uid == classroom.owner %} + + {% endif %} + + + + {% set rank = 1 %} + {% for user in users %} + + + + + + {% endfor %} + +
    RankNamePoints
    {{ rank }} + {{ user.name }} + {{ user.points() }}
    +
    + {% endcache %} + {% else %} +
    +
    +

    Welcome to your new classroom!

    +
    +
    + Use the panel to the right to start adding teams to your classroom! +
    +
    + {% endif %} + {% if current_user.uid == classroom.owner and classroom.invites %} +
    +
    +

    Invited Teams

    +
    +
    + These are teams you've invited but haven't yet accepted the invitation to join your class. +
    + + + + + + + + + {% for team in classroom.invites %} + + + + + {% endfor %} + +
    Team NameSize
    + {{ team.teamname }} + {{ team.size }}
    +
    + {% endif %} +
    +
    + {% if current_user.uid == classroom.owner %} +
    +
    +

    Classroom Management

    +
    +
    +

    Type in the name of a team to invite them to join your class + {{ classroom.name }}!

    +
    +
    + {{ add_team_form.csrf_token }} +
    +
    + {% if add_team_form.name.errors %} +

    + {% for error in add_team_form.name.errors %} + {{ error }} + {% endfor %} +

    + {% endif %} +
    + {{ add_team_form.name(class_="form-control", autocomplete="off") }} + + {{ add_team_form.submit(class_="btn btn-success") }} + +
    +
    +
    +
    +
    +
    + +
    +
    + {% else %} + {% endif %} +
    +
    +
    +
    +{% endblock %} \ No newline at end of file diff --git a/server/easyctf/templates/footer.html b/server/easyctf/templates/footer.html new file mode 100755 index 0000000..230280b --- /dev/null +++ b/server/easyctf/templates/footer.html @@ -0,0 +1,34 @@ +
    +
    +
    +
    + +

    EasyCTF is a national, online, student-run high school hacking competition that opens the door to computer science and cybersecurity for students all over the world.

    +
      +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    +
    +
    + +
    diff --git a/server/easyctf/templates/game/game.html b/server/easyctf/templates/game/game.html new file mode 100644 index 0000000..43cb496 --- /dev/null +++ b/server/easyctf/templates/game/game.html @@ -0,0 +1,435 @@ +{% extends "layout.html" %} +{% block title %}Game{% endblock %} + +{% block content %} + + +
    +
    +
    +

    Points: {{ current_user.team.points() }}

    +
    +

    Game

    +
    +
    +
    +
    +
    +
    +
    + +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +   +
    +
    + +
    +
    +
    + +
    +
    + +
    +
    +
    + {{ problem_submit_form.csrf_token }} + + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    + ▶▶ +
    +
    + +
    +
    + +
    +
    +
    + +
    + + Loading... + +
    +
    +
    +
    + + + +{% endblock %} \ No newline at end of file diff --git a/server/easyctf/templates/layout.html b/server/easyctf/templates/layout.html new file mode 100755 index 0000000..f49b65c --- /dev/null +++ b/server/easyctf/templates/layout.html @@ -0,0 +1,69 @@ +{% from "templates.html" import flashes %} + + + + + + + + {% block title %}{% endblock %} - EasyCTF IV, High School CTF Competition + + + + + + + + {% if keywords %} + {% endif %} + + + + + + + + + + + + + + {% if current_user.admin %} +
    +
    + competition status: + {{ "not " if not competition_running }}running. + (window: to ) +
    +
    + {% endif %} + + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} +
    + {% for category, message in messages %} +
    +
    {{ message }}
    +
    + {% endfor %} +
    + {% endif %} + {% endwith %} +
    + {% block content %}{% endblock %} +
    + + + + + diff --git a/server/easyctf/templates/navbar.html b/server/easyctf/templates/navbar.html new file mode 100755 index 0000000..5fa4524 --- /dev/null +++ b/server/easyctf/templates/navbar.html @@ -0,0 +1,70 @@ +
    + + +
    diff --git a/server/easyctf/templates/teams/create.html b/server/easyctf/templates/teams/create.html new file mode 100755 index 0000000..fb7035e --- /dev/null +++ b/server/easyctf/templates/teams/create.html @@ -0,0 +1,81 @@ +{% extends "layout.html" %} +{% from "templates.html" import render_field, render_generic_field %} +{% block title %}Create or Join Team{% endblock %} + +{% block content %} +
    +
    +
    +
    +
    +
    +

    Create Team

    +
    +
    + {% if current_user.level == 3 %} +
    + Hey there! Even though you're a teacher, we're going to ask that you create an account simply because of how the system works. In terms of scoring, your team will be considered an observer team. +
    + {% endif %} +
    + {{ create_team_form.csrf_token }} +
    + {{ render_field(create_team_form.teamname) }} + {{ render_field(create_team_form.school) }} +
    + {{ create_team_form.submit(class_="btn btn-primary") }} +
    +
    +
    +
    +
    +
    +
    +

    Join Team

    +
    + {% if current_user.level == 3 %} +
    + A word of warning: if you join a team with your students, the team will be marked as an observer team and will be disqualified from winning prizes. If you want to keep track of your students' progress, please use our classrooms feature. +
    + {% endif %} + {% set incoming_invitations = current_user.incoming_invitations %} + {% if incoming_invitations %} +
    + Here are your current invitations. Click "Accept" to join the team. +
    +
    + {% for team in incoming_invitations %} + + {% endfor %} +
    + {% else %} +
    + To join a team, you must have an invitation from that team. Contact your team to send you an + invitation, or find their team profile page and request to join their team. +
    + {% endif %} +
    + {% set outgoing_invitations = current_user.outgoing_invitations %} + {% if outgoing_invitations %} +
    +
    +

    Sent Requests

    +
    +
    + {% for req in outgoing_invitations %} + + {% endfor %} +
    +
    + {% endif %} +
    +
    +
    +
    +{% endblock %} \ No newline at end of file diff --git a/server/easyctf/templates/teams/profile.html b/server/easyctf/templates/teams/profile.html new file mode 100755 index 0000000..5d1a972 --- /dev/null +++ b/server/easyctf/templates/teams/profile.html @@ -0,0 +1,207 @@ +{% from "templates.html" import render_field %} +{% extends "layout.html" %} +{% block title %}{{ team.teamname }}{% endblock %} + +{% block content %} + {% if team.banned %} +
    +
    + Your team has been disqualified for breaking CTF rules. You can continue to solve challenges and score points, but your public profile has been disabled and your team removed from the scoreboard listing. If you believe this is a mistake, please contact one of the organizers, but remember that organizers' decisions are final. +
    +
    + {% endif %} +
    +
    + + + + + +
    +

    {{ team.teamname }}

    +

    {{ team.school }} +

    +

    +
    + {% if current_user.tid == team.tid %} +
    + I'm in the team! +
    + {% endif %} + {% if team.observer %} +
    + Observer +
    + {% endif %} +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Team Members

    +
    +
    + {% for member in team.members %} +
    + + + + + +
    + + + + +

    {{ member.name }}

    + {% if member.uid == team.owner %} +
    Captain
    + {% endif %} +

    + @{{ member.username }} +

    +
    +
    + {% endfor %} +
    +
    +
    +
    + +
    + +
    + +
    + {% set solves = team.solves | sort(attribute="date", reverse=True) %} + + + + + + + + + + + {% for solve in solves %} + + + {% set problem = solve.problem %} + + + + + + + + {% endfor %} +
    ProblemCategoryValueSolverTime
    {{ problem.title }}{{ problem.category }}{{ problem.value }}{{ solve.user.username }} + +
    +
    + {% if current_user.uid == team.owner %} +
    + {% if manage_team_form %} +
    +
    +

    Manage Team

    +
    +
    +
    + {{ manage_team_form.csrf_token }} +
    +
    + {{ render_field(manage_team_form.teamname) }} + {{ render_field(manage_team_form.school) }} + {{ manage_team_form.submit(class_="btn btn-primary") }} +
    +
    +
    +
    +
    + {% endif %} + {% if disband_team_form %} +
    +
    +

    Disband Team

    +
    +
    +

    Your team will still be visible as a "ghost" team. However, no one will be able to join it anymore.

    +
    + {{ disband_team_form.csrf_token }} +
    +
    + {% if disband_team_form.teamname.errors %} +

    + {% for error in disband_team_form.teamname.errors %} + {{ error }} + {% endfor %} +

    + {% endif %} +
    + {{ disband_team_form.teamname(class_="form-control", placeholder="Confirm Team Name") }} + + {{ disband_team_form.submit(class_="btn btn-danger") }} + +
    +
    +
    +
    +
    +
    + {% endif %} +
    + {% endif %} +
    +
    +
    +
    +
    + + + +{% endblock %} diff --git a/server/easyctf/templates/teams/settings.html b/server/easyctf/templates/teams/settings.html new file mode 100755 index 0000000..fa346b6 --- /dev/null +++ b/server/easyctf/templates/teams/settings.html @@ -0,0 +1,125 @@ +{% from "templates.html" import render_field, render_generic_field %} +{% extends "layout.html" %} +{% block title %}Team Settings{% endblock %} + +{% block content %} +
    +
    +
    +
    + +

    +
    +
    +

    {{ team.teamname }} Settings

    +
    + +
    +
    +
    +
    +
    +
    +

    Team Profile

    +
    +
    +
    +
    + +
    +
    +
    + {{ profile_edit_form.csrf_token }} +
    + {{ render_field(profile_edit_form.teamname) }} + {{ render_field(profile_edit_form.school) }} + {{ render_generic_field(profile_edit_form.avatar) }} + {{ render_generic_field(profile_edit_form.remove_avatar) }} +
    + {{ profile_edit_form.submit(class_="btn btn-primary") }} +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Current Members

    +
    +
    +
    + {% for user in team.members %} +
    + {% if user.uid != current_user.uid %} +
    + Evict +
    + {% endif %} + +   + {{ user.username }} +
    + {% endfor %} +
    +
    +
    +
    +
    +
    +
    +

    Outgoing Invitations

    +
    +
    + {% set outgoing_invitations = team.outgoing_invitations %} + {% if outgoing_invitations %} +
    + {% for user in outgoing_invitations %} +
    +
    + Withdraw +
    + +   + {{ user.username }} +
    + {% endfor %} +
    + {% endif %} +
    + {{ add_member_form.csrf_token }} +
    +
    + {% if add_member_form.username.errors %} +

    + {% for error in add_member_form.username.errors %} + {{ error }} + {% endfor %} +

    + {% endif %} +
    + {{ add_member_form.username(class_="form-control") }} + + {{ add_member_form.submit(class_="btn btn-success") }} + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +{% endblock %} \ No newline at end of file diff --git a/server/easyctf/templates/templates.html b/server/easyctf/templates/templates.html new file mode 100755 index 0000000..1eeaf0c --- /dev/null +++ b/server/easyctf/templates/templates.html @@ -0,0 +1,60 @@ +{% macro render_field(field) %} +
    + {{ field.label(class_="control-label") }} + {{ field(class_="form-control", autocomplete="off", **kwargs.get("options", {})) }} + {% if field.errors %} +

    + {% for error in field.errors %} + {{ error }}
    + {% endfor %} +

    + {% endif %} +
    +{% endmacro %} + +{% macro render_generic_field(field) %} +
    + {{ field.label(class_="control-label") }} + {{ field(autocomplete="off") }} + {% if field.errors %} +

    + {% for error in field.errors %} + {{ error }}
    + {% endfor %} +

    + {% endif %} +
    +{% endmacro %} + +{% macro render_editor(field, language) %} +
    + {{ field.label(class_="control-label") }} + {{ field(style="display:none;") }} +
    + {% if field.errors %} +

    + {% for error in field.errors %} + {{ error }}
    + {% endfor %} +

    + {% endif %} +
    + +{% endmacro %} \ No newline at end of file diff --git a/server/easyctf/templates/users/forgot.html b/server/easyctf/templates/users/forgot.html new file mode 100644 index 0000000..a177a06 --- /dev/null +++ b/server/easyctf/templates/users/forgot.html @@ -0,0 +1,28 @@ +{% from "templates.html" import render_field, render_generic_field %} +{% extends "layout.html" %} +{% block title %}Forgot Password{% endblock %} + +{% block content %} +
    +
    +
    +
    +
    +
    +

    Forgot Password

    +
    +
    +
    + {{ forgot_form.csrf_token }} +
    + {{ render_field(forgot_form.email) }} +
    + {{ forgot_form.submit(class_="btn btn-primary") }} +
    +
    +
    +
    +
    +
    +
    +{% endblock %} \ No newline at end of file diff --git a/server/easyctf/templates/users/login.html b/server/easyctf/templates/users/login.html new file mode 100755 index 0000000..54824bf --- /dev/null +++ b/server/easyctf/templates/users/login.html @@ -0,0 +1,68 @@ +{% from "templates.html" import render_field, render_generic_field %} +{% extends "layout.html" %} +{% block title %}Login{% endblock %} + +{% block content %} +
    +
    +

    Login

    +
    +
    +
    +
    +
    +
    +
    +
    +

    Login

    +
    +
    +
    +
    + {{ login_form.csrf_token }} +
    + {{ render_field(login_form.username) }} + {{ render_field(login_form.password) }} + +
    + {{ login_form.remember(autocomplete="off") }} + {{ login_form.remember.label(class_="control-label") }} +
    +
    + {{ login_form.submit(class_="btn btn-primary") }} + + Forgot Password +
    +
    +
    +
    +
    +
    +
    +
    + +{% endblock %} diff --git a/server/easyctf/templates/users/profile.html b/server/easyctf/templates/users/profile.html new file mode 100755 index 0000000..5936832 --- /dev/null +++ b/server/easyctf/templates/users/profile.html @@ -0,0 +1,113 @@ +{% from "templates.html" import render_field %} +{% extends "layout.html" %} +{% block title %}{{ user.name }} (@{{ user.username }}){% endblock %} + +{% block content %} +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    {{ user.name }}

    + @{{ user.username }} + {% if user.easyctf %} + EasyCTF Organizer + {% endif %} +
    +
    + + {{ user.type }} +
    + {% if user == current_user and user.email %} + + {% endif %} +
    + + Joined + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    Team Information

    +
    + {% if not user.team %} +
    + {% if current_user == user and user.pending_invitations %} +
    You have pending invitations! Click here to view them »
    + {% endif %} + {{ user.name }} is not a part of a team. +
    + {% else %} + + + + + + + + + + {# + + + #} +
    Team Name + {{ user.team.teamname }} +
    School{{ user.team.school }}
    Place{{ user.team.place()[1] }}
    + {% endif %} +
    +
    +
    +
    + +
    +
    +
    +
    +
    + +{% endblock %} \ No newline at end of file diff --git a/server/easyctf/templates/users/register.html b/server/easyctf/templates/users/register.html new file mode 100755 index 0000000..d40a81e --- /dev/null +++ b/server/easyctf/templates/users/register.html @@ -0,0 +1,46 @@ +{% from "templates.html" import render_field, render_generic_field %} +{% extends "layout.html" %} +{% block title %}Register{% endblock %} + +{% block content %} +
    +
    +

    Register

    +
    +
    +
    +
    +
    +
    +
    +
    +

    Register

    +
    +
    +
    +
    + {{ register_form.csrf_token }} +
    + {{ render_field(register_form.name) }} + {{ render_field(register_form.email) }} + {{ render_field(register_form.username) }} +
    +
    + {{ render_field(register_form.password) }} +
    +
    + {{ render_field(register_form.confirm_password) }} +
    +
    + {{ render_generic_field(register_form.level) }} +
    + {{ register_form.submit(class_="btn btn-primary") }} +
    +
    +
    +
    +
    +
    +
    +
    +{% endblock %} \ No newline at end of file diff --git a/server/easyctf/templates/users/reset.html b/server/easyctf/templates/users/reset.html new file mode 100644 index 0000000..2f8a655 --- /dev/null +++ b/server/easyctf/templates/users/reset.html @@ -0,0 +1,29 @@ +{% from "templates.html" import render_field, render_generic_field %} +{% extends "layout.html" %} +{% block title %}Reset Password{% endblock %} + +{% block content %} +
    +
    +
    +
    +
    +
    +

    Reset Password

    +
    +
    +
    + {{ reset_form.csrf_token }} +
    + {{ render_field(reset_form.password) }} + {{ render_field(reset_form.confirm_password) }} +
    + {{ reset_form.submit(class_="btn btn-primary") }} +
    +
    +
    +
    +
    +
    +
    +{% endblock %} \ No newline at end of file diff --git a/server/easyctf/templates/users/settings.html b/server/easyctf/templates/users/settings.html new file mode 100755 index 0000000..b1e08b1 --- /dev/null +++ b/server/easyctf/templates/users/settings.html @@ -0,0 +1,116 @@ +{% from "templates.html" import render_field, render_generic_field %} +{% extends "layout.html" %} +{% block title %}Settings{% endblock %} + +{% block content %} +
    +
    +
    +
    + +

    +
    +
    +

    Account Settings

    +
    + +
    +
    +
    +
    +
    +
    +

    User Profile

    +
    +
    +
    +
    + +
    +
    +
    + {{ profile_edit_form.csrf_token }} +
    + {{ render_field(profile_edit_form.name) }} + {{ render_generic_field(profile_edit_form.avatar) }} + {{ render_generic_field(profile_edit_form.remove_avatar) }} +
    + {{ profile_edit_form.submit(class_="btn btn-primary") }} +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Change Login Info

    +
    +
    +
    + {{ change_login_form.csrf_token }} +
    +

    Enter your password to make any changes to your account.

    + {{ render_field(change_login_form.old_password) }} + {{ render_field(change_login_form.email) }} +

    + {% if current_user.email_verified %} + Your email has been verified. + {% else %} + Your email has not yet been verified. + Verify your email » + {% endif %} +

    +
    +

    Leave this next part blank if you don't want to change your password.

    + {{ render_field(change_login_form.password) }} + {{ render_field(change_login_form.confirm_password) }} +
    + {{ change_login_form.submit(class_="btn btn-primary") }} +
    +
    +
    +
    +
    +

    Two-Factor Authentication

    +
    +
    + {% if current_user.otp_confirmed %} +

    Two-factor authentication is enabled on your account.

    + Disable + {% else %} +

    Two-factor authentication isn't enabled on your account.

    + Setup two-factor authentication now » + {% endif %} +
    +
    +
    +
    +
    +
    +
    + +{% endblock %} diff --git a/server/easyctf/templates/users/two_factor/setup.html b/server/easyctf/templates/users/two_factor/setup.html new file mode 100644 index 0000000..21f415c --- /dev/null +++ b/server/easyctf/templates/users/two_factor/setup.html @@ -0,0 +1,35 @@ +{% from "templates.html" import render_field, render_generic_field, flashes %} +{% extends "layout.html" %} +{% block title %}Setup Two-Factor Authentication{% endblock %} + +{% block content %} +
    +
    +
    +
    +
    +
    +

    Two-Factor Authentication

    +
    +
    +

    Two-factor authentication protects your account by requiring you to verify your identity using a means other than your password. We'll use the app Authy to moderate this process. To begin, scan the following QR code with your app, and then enter the 6-digit code shown on your screen.

    +
    + {{ two_factor_form.csrf_token }} +
    +
    +
    + +
    +
    + {{ render_field(two_factor_form.code) }} + {{ render_field(two_factor_form.password) }} +
    + {{ two_factor_form.submit(class_="btn btn-primary") }} +
    +
    +
    +
    +
    +
    +
    +{% endblock %} diff --git a/server/easyctf/utils.py b/server/easyctf/utils.py new file mode 100755 index 0000000..fba5d4d --- /dev/null +++ b/server/easyctf/utils.py @@ -0,0 +1,151 @@ +import hashlib +import re +import time +from io import BytesIO +from string import hexdigits +from urllib.parse import urljoin, urlparse + +import requests +from flask import current_app, redirect, request, url_for +from PIL import Image, ImageDraw, ImageOps + +from easyctf.objects import random + +VALID_USERNAME = re.compile(r"^[A-Za-z_][A-Za-z\d_]*$") +VALID_PROBLEM_NAME = re.compile(r"^[a-z_][a-z\-\d_]*$") + + +def generate_string(length=32, alpha=hexdigits): + characters = [random.choice(alpha) for x in range(length)] + return "".join(characters) + + +def generate_short_string(): + return generate_string(length=16) + + +def send_mail(recipient, subject, body): + data = { + "from": current_app.config["ADMIN_EMAIL"], + "subject": subject, + "html": body + } + data["bcc" if type(recipient) == list else "to"] = recipient + auth = ("api", current_app.config["MAILGUN_API_KEY"]) + url = "{}/messages".format(current_app.config["MAILGUN_URL"]) + return requests.post(url, auth=auth, data=data) + + +def filestore(name): + prefix = current_app.config.get("FILESTORE_STATIC", "/static") + return prefix + "/" + name + + +def save_file(file, **params): + url = current_app.config.get( + "FILESTORE_SAVE_ENDPOINT", "http://filestore:5001/save") + return requests.post(url, data=params, files=dict(file=file)) + + +def to_timestamp(date): + if date is None: + return "" + return int(time.mktime(date.timetuple())) + + +def to_place_str(n): + k = n % 10 + return "%d%s" % (n, "tsnrhtdd"[(n / 10 % 10 != 1) * (k < 4) * k::4]) + + +def is_safe_url(target): + ref_url = urlparse(request.host_url) + test_url = urlparse(urljoin(request.host_url, target)) + return test_url.scheme in ("http", "https") and \ + ref_url.netloc == test_url.netloc + + +def get_redirect_target(): + for target in request.values.get("next"), request.referrer: + if not target: + continue + if is_safe_url(target): + return target + + +def redirect_back(endpoint, **values): + target = request.form["next"] + if not target or not is_safe_url(target): + target = url_for(endpoint, **values) + return redirect(target) + + +def sanitize_avatar(f): + try: + im = Image.open(f) + im2 = ImageOps.fit(im, (512, 512), Image.ANTIALIAS) + + buf = BytesIO() + im2.save(buf, format="png") + buf.seek(0) + return buf + except: + return None + + +def generate_identicon(seed): + seed = seed.strip().lower().encode("utf-8") + h = hashlib.sha1(seed).hexdigest() + size = 256 + margin = 0.08 + base_margin = int(size * margin) + cell = int((size - base_margin * 2.0) / 5) + margin = int((size - cell * 5.0) / 2) + image = Image.new("RGB", (size, size)) + draw = ImageDraw.Draw(image) + + def hsl2rgb(h, s, b): + h *= 6 + s1 = [] + s *= b if b < 0.5 else 1 - b + b += s + s1.append(b) + s1.append(b - h % 1 * s * 2) + s *= 2 + b -= s + s1.append(b) + s1.append(b) + s1.append(b + h % 1 * s) + s1.append(b + s) + + return [ + s1[~~h % 6], s1[(h | 16) % 6], s1[(h | 8) % 6] + ] + + rgb = hsl2rgb(int(h[-7:], 16) & 0xfffffff, 0.5, 0.7) + bg = (255, 255, 255) + fg = (int(rgb[0] * 255), int(rgb[1] * 255), int(rgb[2] * 255)) + draw.rectangle([(0, 0), (size, size)], fill=bg) + + for i in range(15): + c = bg if int(h[i], 16) % 2 == 1 else fg + if i < 5: + draw.rectangle([(2 * cell + margin, i * cell + margin), + (3 * cell + margin, (i + 1) * cell + margin)], + fill=c) + elif i < 10: + draw.rectangle([(1 * cell + margin, (i - 5) * cell + margin), + (2 * cell + margin, (i - 4) * cell + margin)], + fill=c) + draw.rectangle([(3 * cell + margin, (i - 5) * cell + margin), + (4 * cell + margin, (i - 4) * cell + margin)], + fill=c) + elif i < 15: + draw.rectangle( + [(0 * cell + margin, (i - 10) * cell + margin), + (1 * cell + margin, (i - 9) * cell + margin)], fill=c) + draw.rectangle( + [(4 * cell + margin, (i - 10) * cell + margin), + (5 * cell + margin, (i - 9) * cell + margin)], fill=c) + + return image diff --git a/server/easyctf/views/__init__.py b/server/easyctf/views/__init__.py new file mode 100755 index 0000000..792d600 --- /dev/null +++ b/server/easyctf/views/__init__.py @@ -0,0 +1 @@ +# diff --git a/server/easyctf/views/admin.py b/server/easyctf/views/admin.py new file mode 100755 index 0000000..dcf2127 --- /dev/null +++ b/server/easyctf/views/admin.py @@ -0,0 +1,108 @@ +from flask import (Blueprint, abort, flash, redirect, render_template, request, + url_for) +from wtforms_components import read_only + +from easyctf.decorators import admin_required +from easyctf.forms.admin import ProblemForm, SettingsForm +from easyctf.models import AutogenFile, Config, Problem, JudgeKey +from easyctf.objects import db + +blueprint = Blueprint("admin", __name__, template_folder="templates") + +DEFAULT_GRADER = """def grade(random, submission): + if "correct_flag" in submission: + return True, "Nice!" + return False, "Nope." +""" + + +@blueprint.route("/problems//delete", methods=["POST"]) +@admin_required +def delete_problem(pid): + problem = Problem.get_by_id(pid) + if problem is None: + abort(404) + db.session.delete(problem) + db.session.commit() + flash("Problem {} has been deleted!".format(repr(problem.name)), "info") + return redirect(url_for("admin.problems")) + + +@blueprint.route("/problems", methods=["GET", "POST"]) +@blueprint.route("/problems/", methods=["GET", "POST"]) +@admin_required +def problems(pid=None): + problem = None + problem_form = ProblemForm() + if problem_form.validate_on_submit(): + new_problem = False + if pid is None: + p = Problem.query.filter_by(name=problem_form.name.data).first() + if p: + flash("Please choose a unique name for this problem.", "warning") + return redirect(url_for("admin.problems")) + new_problem = True + problem = Problem() + else: + problem = Problem.get_by_id(pid) + if problem is None: + abort(404) + problem_form.populate_obj(problem) + db.session.add(problem) + db.session.flush() + autogen_files = AutogenFile.query.filter_by(pid=pid) + if autogen_files.count(): + autogen_files.delete() + db.session.commit() + if new_problem: + flash("Problem {} has been created!".format(repr(problem.name)), "info") + return redirect(url_for("admin.problems", pid=problem.pid)) + problems = Problem.query.order_by(Problem.value).all() + if pid is not None: + problem = Problem.get_by_id(pid) + if not problem: + return abort(404) + if request.method != "POST": + problem_form = ProblemForm(obj=problem) + # if problem.programming: + # judge_problem = judge_api.problems_get(pid) + # if judge_problem.status_code == 404: + # abort(500) + # problem_form.grader.data = judge_problem.data['grader_code'] + # problem_form.generator.data = judge_problem.data['generator_code'] + else: + problem_form.grader.data = DEFAULT_GRADER + return render_template("admin/problems.html", current_problem=problem, problems=problems, problem_form=problem_form) + + +@blueprint.route("/settings/judge/key") +@admin_required +def judge_key(): + key = JudgeKey() + db.session.add(key) + db.session.commit() + flash("Key created: {}. This won't be shown again.".format(key.key), "success") + return redirect(url_for("admin.settings")) + + +@blueprint.route("/settings", methods=["GET", "POST"]) +@admin_required +def settings(): + settings_form = SettingsForm() + if settings_form.validate_on_submit(): + configs = dict() + for field in settings_form: + if field.short_name in ["csrf_token", "submit"]: + continue + configs.update(**{field.short_name: field.data}) + Config.set_many(configs) + flash("CTF settings updated!", "success") + return redirect(url_for("admin.settings")) + else: + configs = Config.get_many([field.short_name for field in settings_form]) + for field in settings_form: + if field.short_name == "csrf_token": + continue + if field.short_name in configs: + field.data = configs.get(field.short_name, "") + return render_template("admin/settings.html", settings_form=settings_form) diff --git a/server/easyctf/views/base.py b/server/easyctf/views/base.py new file mode 100755 index 0000000..b7f2dd0 --- /dev/null +++ b/server/easyctf/views/base.py @@ -0,0 +1,94 @@ +from flask import Blueprint, abort, redirect, render_template, request, url_for, flash +from flask_login import current_user, login_required +from sqlalchemy import func + +from easyctf.models import Egg, WrongEgg, Team, User, EggSolve +from easyctf.objects import cache, db + +blueprint = Blueprint("base", __name__, template_folder="templates") + + +@blueprint.route("/") +def index(): + return render_template("base/index.html") + + +@blueprint.route("/about") +def about(): + return render_template("base/about.html") + + +@blueprint.route("/rules") +def rules(): + return render_template("base/rules.html") + + +@blueprint.route("/prizes") +def prizes(): + return render_template("base/prizes.html") + + +@blueprint.route("/sponsors") +def sponsors(): + return render_template("base/sponsors.html") + + +@blueprint.route("/team") +# @cache.cached(timeout=0) +def team(): + easyctf_team = db.session.query(User).filter(User.easyctf == True).all() + return render_template("base/team.html", easyctf_team=easyctf_team) + + +@blueprint.route("/updates") +def updates(): + return render_template("base/updates.html") + + +@blueprint.route("/scoreboard") +@login_required +def scoreboard(): + scoreboard = Team.scoreboard() + return render_template("base/scoreboard.html", scoreboard=scoreboard) + + +@blueprint.route("/shibboleet", methods=["GET", "POST"]) +def easter(): + if not (current_user.is_authenticated and (current_user.admin or current_user.team)): + return abort(404) + eggs = [] + if request.method == "POST": + if current_user.admin and request.form.get("submit"): + newegg_str = request.form.get("egg") + if newegg_str: + newegg = Egg(flag=newegg_str) + db.session.add(newegg) + db.session.commit() + flash("New egg has been added!", "success") + else: + cand = request.form.get("egg") + egg = Egg.query.filter_by(flag=cand).first() + if egg: + solve = EggSolve.query.filter_by(eid=egg.eid, tid=current_user.tid).first() + if solve: + flash("You already got this one", "info") + else: + solve = EggSolve(eid=egg.eid, tid=current_user.tid, uid=current_user.uid) + db.session.add(solve) + db.session.commit() + flash("Congrats!", "success") + else: + submission = WrongEgg.query.filter_by(tid=current_user.tid, submission=cand).first() + if submission: + flash("You've already tried that egg", "info") + else: + submission = WrongEgg(tid=current_user.tid, uid=current_user.uid, submission=cand) + db.session.add(submission) + db.session.commit() + flash("Nope, sorry", "danger") + return redirect(url_for("base.easter")) + if current_user.admin: + eggs = Egg.query.all() + else: + eggs = EggSolve.query.filter_by(tid=current_user.tid).all() + return render_template("base/easter.html", eggs=eggs) diff --git a/server/easyctf/views/chals.py b/server/easyctf/views/chals.py new file mode 100755 index 0000000..41afa5f --- /dev/null +++ b/server/easyctf/views/chals.py @@ -0,0 +1,161 @@ +import json +import os + +from flask import Blueprint, abort, current_app, flash, redirect, render_template, url_for +from flask_login import current_user, login_required + +from easyctf.decorators import block_before_competition, team_required, no_cache +from easyctf.forms.chals import ProblemSubmitForm, ProgrammingSubmitForm +from easyctf.models import AutogenFile, Job, Problem, Solve, User, WrongFlag +from easyctf.objects import cache, db, sentry + +blueprint = Blueprint("chals", __name__, template_folder="templates") + + +@blueprint.route("/list", methods=["GET", "POST"]) +@login_required +@team_required +@block_before_competition +def list(): + problem_submit_form = ProblemSubmitForm() + if problem_submit_form.validate_on_submit(): + problem = Problem.get_by_id(int(problem_submit_form.pid.data)) + if problem is None or not current_user.team.has_unlocked(problem): + flash("Problem not found.", "info") + return redirect(url_for("chals.list")) + + result, message = problem.try_submit(problem_submit_form.flag.data) + + if result == "success": + flash(message, "success") + elif result == "error": + flash(message, "info") + if result == "failure": + flash(message, "danger") + return redirect(url_for("chals.list")) + categories = Problem.categories() + if current_user.admin: + problems = Problem.query.filter(Problem.value > 0).order_by(Problem.value).all() + else: + problems = current_user.team.get_unlocked_problems() + return render_template("chals/list.html", categories=categories, problems=problems, problem_submit_form=problem_submit_form) + + +@blueprint.route("/solves/") +@login_required +@team_required +@block_before_competition +def solves(pid): + problem = Problem.query.filter_by(pid=pid).first() + if problem is None: + abort(404) + return render_template("chals/solves.html", problem=problem) + + +@blueprint.route("/shell") +@block_before_competition +@team_required +@login_required +def shell(): + return render_template("chals/shell.html") + + +@blueprint.route("/shell/credentials") +@block_before_competition +@team_required +@login_required +def shell_credentials(): + team = current_user.team + credentials = team.credentials() + if not credentials: + return abort(500) + username, password = credentials + return json.dumps(dict(username=username, password=password)) + + +@blueprint.route("/programming/") +@blueprint.route("/programming/", methods=["GET", "POST"]) +@login_required +@team_required +@block_before_competition +def programming(pid=None): + problems = current_user.team.get_unlocked_problems(programming=True) + if pid is None: + if len(problems) == 0: + return redirect(url_for("chals.list")) + return redirect(url_for("chals.programming", pid=problems[0].pid)) + problem = Problem.get_by_id(pid) + if not problem: + return abort(404) + if not current_user.team.has_unlocked(problem) or not problem.programming: + return redirect(url_for("chals.list")) + + programming_submit_form = ProgrammingSubmitForm() + if programming_submit_form.validate_on_submit(): + if not problem.programming: + return redirect(url_for("chals.list")) + job = Job(uid=current_user.uid, tid=current_user.tid, pid=pid, + language=programming_submit_form.language.data, contents=programming_submit_form.code.data) + db.session.add(job) + db.session.commit() + flash("Code was sent! Refresh the page for updates.", "success") + return redirect(url_for("chals.submission", id=job.id)) + + return render_template("chals/programming.html", problem=problem, + problems=problems, programming_submit_form=programming_submit_form) + + +@blueprint.route("/programming/status") +@login_required +@team_required +@block_before_competition +def status(): + jobs = Job.query.filter_by(tid=current_user.tid).order_by(Job.submitted.desc()).all() + return render_template("chals/status.html", jobs=jobs) + + +@blueprint.route("/programming/submission/") +@login_required +@team_required +@block_before_competition +def submission(id): + job = Job.query.filter_by(id=id).first() + if not job: + return abort(404) + if not current_user.admin and job.tid != current_user.tid: + return abort(403) + return render_template("chals/submission.html", problem=job.problem, job=job, user=job.user) + + +@blueprint.route("/autogen//") +@login_required +@team_required +@block_before_competition +@no_cache +def autogen(pid, filename): + problem = Problem.query.filter_by(pid=pid).first() + if not problem or not problem.autogen: + return abort(404) + + tid = current_user.tid + # If autogen file exists in db, redirect to filestore + autogen_file = AutogenFile.query.filter_by(pid=pid, tid=tid, filename=filename).first() + if autogen_file: + return redirect("{}/{}".format(current_app.config["FILESTORE_STATIC"], autogen_file.url)) + + current_path = os.getcwd() + if problem.path: + os.chdir(problem.path) + autogen = problem.get_autogen(tid) + grader = problem.get_grader() + generated_problem = grader.generate(autogen) + if "files" in generated_problem: + data = generated_problem["files"].get(filename) + if data is None: + return abort(404) + autogen_file = AutogenFile(pid=pid, tid=tid, filename=filename, data=data) + db.session.add(autogen_file) + db.session.commit() + return redirect("{}/{}".format(current_app.config["FILESTORE_STATIC"], autogen_file.url)) + os.chdir(current_path) + return abort(404) diff --git a/server/easyctf/views/classroom.py b/server/easyctf/views/classroom.py new file mode 100644 index 0000000..5f5f2bc --- /dev/null +++ b/server/easyctf/views/classroom.py @@ -0,0 +1,120 @@ +from flask import Blueprint, abort, flash, redirect, render_template, url_for +from flask_login import current_user, login_required +from sqlalchemy import func + +from easyctf.decorators import teacher_required, team_required +from easyctf.forms.classroom import AddTeamForm, NewClassroomForm +from easyctf.models import (Classroom, Team, classroom_invitation, + team_classroom) +from easyctf.objects import db + +blueprint = Blueprint("classroom", __name__) + + +@blueprint.route("/") +@team_required +@login_required +def index(): + invites = None + if current_user.level == 3: + classes = Classroom.query.filter_by(owner=current_user.uid).all() + elif not current_user.tid: + flash("You must be a part of a team to join classes.", "info") + return redirect(url_for("teams.create")) + else: + classes = current_user.team.classrooms + invites = current_user.team.classroom_invites + return render_template("classroom/index.html", classes=classes, + invites=invites) + + +@blueprint.route("/new", methods=["GET", "POST"]) +@login_required +@teacher_required +def new(): + new_classroom_form = NewClassroomForm() + if new_classroom_form.validate_on_submit(): + classroom = Classroom(name=new_classroom_form.name.data, + owner=current_user.uid) + db.session.add(classroom) + db.session.commit() + flash("Created classroom.", "success") + return redirect(url_for("classroom.view", id=classroom.id)) + return render_template("classroom/new.html", + new_classroom_form=new_classroom_form) + + +@blueprint.route("/delete/") +@login_required +@teacher_required +def delete(id): + classroom = Classroom.query.filter_by(id=id).first() + if not classroom: + abort(404) + if current_user.uid != classroom.owner: + abort(403) + db.session.delete(classroom) + db.session.commit() + return redirect(url_for("classroom.index")) + + +@blueprint.route("/accept/") +@login_required +def accept(id): + invitation = db.session.query(classroom_invitation).filter_by( + team_id=current_user.tid, classroom_id=id) + if not invitation: + abort(404) + classroom = Classroom.query.filter_by(id=id).first() + if not classroom: + abort(404) + classroom.teams.append(current_user.team) + if current_user.team in classroom.invites: + classroom.invites.remove(current_user.team) + db.session.commit() + flash("Joined classroom.", "success") + return redirect(url_for("classroom.view", id=id)) + + +@blueprint.route("/remove//") +@login_required +@teacher_required +def remove(cid, tid): + classroom = Classroom.query.filter_by(id=cid).first() + if not classroom: + abort(404) + if current_user.uid != classroom.owner: + abort(403) + team = Team.query.filter_by(tid=tid).first() + if not team: + abort(404) + if team not in classroom: + abort(403) + classroom.teams.remove(team) + db.session.commit() + return redirect(url_for("classroom.view", id=cid)) + + +@blueprint.route("/", methods=["GET", "POST"]) +@login_required +def view(id): + classroom = Classroom.query.filter_by(id=id).first() + if not classroom: + return redirect("classroom.index") + if not (current_user.uid == classroom.owner or db.session.query( + team_classroom).filter_by(team_id=current_user.tid, + classroom_id=classroom.id).count()): + abort(403) + add_team_form = AddTeamForm(prefix="addteam") + if add_team_form.validate_on_submit(): + if current_user.uid != classroom.owner: + abort(403) + team = Team.query.filter(func.lower( + Team.teamname) == add_team_form.name.data.lower()).first() + classroom.invites.append(team) + flash("Team invited.", "success") + db.session.commit() + return redirect(url_for("classroom.view", id=id)) + users = [user for _team in classroom.teams for user in _team.members] + return render_template("classroom/view.html", classroom=classroom, + users=users, add_team_form=add_team_form) diff --git a/server/easyctf/views/game.py b/server/easyctf/views/game.py new file mode 100644 index 0000000..0f98aa6 --- /dev/null +++ b/server/easyctf/views/game.py @@ -0,0 +1,98 @@ +import json +import os +from functools import wraps + +from flask import Blueprint, abort, current_app, flash, make_response, render_template, request, url_for +from flask_login import current_user, login_required + +from easyctf.decorators import block_before_competition, team_required +from easyctf.forms.chals import ProblemSubmitForm, ProgrammingSubmitForm +from easyctf.forms.game import GameStateUpdateForm +from easyctf.models import AutogenFile, GameState, Job, Problem, Solve, User, WrongFlag +from easyctf.objects import cache, db, sentry + +blueprint = Blueprint("game", __name__, template_folder="templates") + + +def api_view(f): + @wraps(f) + def wrapper(*args, **kwargs): + status, result = f(*args, **kwargs) + return make_response(json.dumps(result or dict()), status, {"Content-Type": "application/json; charset=utf-8"}) + return wrapper + + +@blueprint.route("/", methods=["GET"]) +@login_required +@team_required +@block_before_competition +def game(): + problem_submit_form = ProblemSubmitForm() + + return render_template("game/game.html", problem_submit_form=problem_submit_form) + + +@blueprint.route("/problems", methods=["GET"]) +@login_required +@team_required +@block_before_competition +@api_view +def problems(): + if current_user.admin: + problems = Problem.query.order_by(Problem.value).all() + else: + problems = current_user.team.get_unlocked_problems() + formatted_problems = {problem.pid: problem.api_summary() for problem in problems} + return 200, formatted_problems + + +@blueprint.route("/submit", methods=["POST"]) +@login_required +@team_required +@block_before_competition +@api_view +def submit(): + problem_submit_form = ProblemSubmitForm(**request.get_json()) + if problem_submit_form.validate(): + problem = Problem.query.get_or_404(int(problem_submit_form.pid.data)) + result, message = problem.try_submit(problem_submit_form.flag.data) + + return 200, { + "result": result, + "message": message, + } + return 400, None # TODO: actually return validation error + + +@blueprint.route("/state", methods=["GET", "POST"]) +@login_required +@team_required +@block_before_competition +def game_state_get(): + game_state = GameState.query.filter_by(uid=current_user.uid).first() + if game_state is None: + game_state = GameState(uid=current_user.uid) + # TODO: proper upserting + db.session.add(game_state) + db.session.commit() + return make_response(game_state.state, 200, {"Content-Type": "application/json; charset=utf-8"}) + + +@blueprint.route("/state/update", methods=["POST"]) +@login_required +@team_required +@block_before_competition +@api_view +def game_state_update(): + game_state_update_form = GameStateUpdateForm(**request.get_json()) + if game_state_update_form.validate(): + state = game_state_update_form.state.data + game_state = GameState.query.filter_by(uid=current_user.uid).first() + if game_state is None: + game_state = GameState(uid=current_user.uid) + # TODO: proper upserting + db.session.add(game_state) + game_state.state = state + db.session.commit() + return 200, None + return 400, None # TODO: actually return validation error diff --git a/server/easyctf/views/judge.py b/server/easyctf/views/judge.py new file mode 100644 index 0000000..190c690 --- /dev/null +++ b/server/easyctf/views/judge.py @@ -0,0 +1,88 @@ +import enum +import json +import traceback +from datetime import datetime, timedelta +from functools import wraps + +from flask import Blueprint, abort, make_response, request + +from sqlalchemy import and_, or_ +from easyctf.models import Job, JudgeKey, Solve, Problem +from easyctf.objects import db + +blueprint = Blueprint("judge", __name__) + + +def api_view(f): + @wraps(f) + def wrapper(*args, **kwargs): + api_key = request.headers.get("API-Key") + if not api_key: + return abort(403) + key = JudgeKey.query.filter_by(key=api_key).first() + if not key: + return abort(403) + status, result = f(*args, **kwargs) + return make_response(json.dumps(result or dict()), status, {"Content-Type": "application/json; charset=utf-8"}) + return wrapper + + +@blueprint.route("/jobs", methods=["GET", "POST"]) +@api_view +def jobs(): + if request.method == "GET": + # implement language preference later + available = Job.query.filter(or_(Job.status == 0, and_(Job.status == 1, Job.claimed < datetime.utcnow() - timedelta(minutes=5)))).order_by(Job.submitted).first() + if not available: + return 204, [] + # assign job to current judge + info = dict( + # job info + id=available.id, + language=available.language, + source=available.contents, + # problem info + pid=available.problem.pid, + test_cases=available.problem.test_cases, + time_limit=available.problem.time_limit, + memory_limit=available.problem.memory_limit, + generator_code=available.problem.generator, + grader_code=available.problem.grader, + source_verifier_code=available.problem.source_verifier or "", + ) + available.status = 1 + available.claimed = datetime.utcnow() + db.session.add(available) + db.session.commit() + return 202, info + elif request.method == "POST": + # expect + id_raw = request.form.get("id") + print("id = ", id_raw) + if not (id_raw and id_raw.isdigit()): + return 400, None + id = int(id_raw) + job = Job.query.filter_by(id=id).first() + if not job: + return 404, None + try: + job.status = 2 + job.verdict = request.form.get("verdict") + job.execution_time = float(request.form.get("execution_time")) + job.execution_memory = int(request.form.get("execution_memory")) + job.completed = datetime.utcnow() + db.session.add(job) + if job.verdict == "AC": + solve = Solve.query.filter_by(pid=job.pid, tid=job.tid).first() + if not solve: + solve = Solve(pid=job.pid, uid=job.uid, tid=job.tid, _date=job.completed) + db.session.add(solve) + db.session.commit() + return 202, None + except Exception: + # failed + job.status = 3 + job.feedback = traceback.format_exc() + db.session.add(job) + db.session.commit() + return 400, None diff --git a/server/easyctf/views/teams.py b/server/easyctf/views/teams.py new file mode 100755 index 0000000..dc302d9 --- /dev/null +++ b/server/easyctf/views/teams.py @@ -0,0 +1,143 @@ +import logging +from io import BytesIO + +from flask import Blueprint, abort, flash, redirect, render_template, url_for +from flask_login import current_user, login_required + +from easyctf.decorators import is_team_captain, email_verification_required +from easyctf.forms.teams import AddMemberForm, CreateTeamForm, ProfileEditForm +from easyctf.models import Config, Team, User +from easyctf.objects import db +from easyctf.utils import sanitize_avatar, save_file +from easyctf.constants import USER_TEACHER + +blueprint = Blueprint("teams", __name__, template_folder="templates") + + +@blueprint.route("/accept/") +@is_team_captain +@login_required +def accept(id): + return "" + + +@blueprint.route("/cancel/") +@is_team_captain +@login_required +def cancel(id): + current_team = current_user.team + target_user = User.get_by_id(id) + try: + assert target_user != None, "User not found." + assert target_user in current_team.outgoing_invitations, "No invitation for this user found." + current_team.outgoing_invitations.remove(target_user) + db.session.add(current_team) + db.session.commit() + flash("Invitation to %s successfully withdrawn." % target_user.username, "success") + except AssertionError as e: + flash(str(e), "danger") + return redirect(url_for("teams.settings")) + + +@blueprint.route("/create", methods=["GET", "POST"]) +@email_verification_required +@login_required +def create(): + if current_user.tid: + return redirect(url_for("teams.profile")) + create_team_form = CreateTeamForm(prefix="create") + if create_team_form.validate_on_submit(): + new_team = create_team(create_team_form) + logging.info("Created team '%s' (id=%s)!" % (new_team.teamname, new_team.tid)) + return redirect(url_for("teams.profile")) + return render_template("teams/create.html", create_team_form=create_team_form) + + +@blueprint.route("/evict/") +@is_team_captain +@login_required +def evict(id): + current_team = current_user.team + target_user = User.get_by_id(id) + try: + assert target_user != None, "User not found." + assert target_user in current_team.members, "This user isn't in your team!" + assert target_user.uid != current_team.owner, "You can't evict the captain!" + current_team.members.remove(target_user) + db.session.add(target_user) + db.session.commit() + flash("Removed %s from the team." % target_user.username, "success") + except AssertionError as e: + flash(str(e), "danger") + return redirect(url_for("teams.settings")) + + +@blueprint.route("/profile", methods=["GET", "POST"]) +@blueprint.route("/profile/", methods=["GET", "POST"]) +def profile(tid=None): + if tid is None and current_user.is_authenticated: + if current_user.tid is None: + return redirect(url_for("teams.create")) + else: + return redirect(url_for("teams.profile", tid=current_user.tid)) + team = Team.get_by_id(tid) + if team is None: + abort(404) + if not current_user.is_authenticated: + flash("Please login to view team profiles!", "warning") + return redirect(url_for("users.login")) + if not current_user.admin and (team.banned and current_user.tid != team.tid): + abort(404) + return render_template("teams/profile.html", team=team) + + +@blueprint.route("/settings", methods=["GET", "POST"]) +@is_team_captain +@login_required +def settings(): + current_team = current_user.team + add_member_form = AddMemberForm(prefix="add-member") + profile_edit_form = ProfileEditForm(prefix="profile-edit") + if add_member_form.submit.data and add_member_form.validate_on_submit(): + target_user = add_member_form.get_user() + current_team.outgoing_invitations.append(target_user) + db.session.add(current_team) + db.session.commit() + flash("Invitation to %s sent!" % target_user.username, "info") + return redirect(url_for("teams.settings")) + elif profile_edit_form.submit.data and profile_edit_form.validate_on_submit(): + for field in profile_edit_form: + if field.short_name == "avatar": + if hasattr(field.data, "read") and len(field.data.read()) > 0: + field.data.seek(0) + f = BytesIO(field.data.read()) + new_avatar = sanitize_avatar(f) + if new_avatar: + response = save_file(new_avatar, prefix="team_avatar", suffix=".png") + if response.status_code == 200: + current_team._avatar = response.text + continue + if hasattr(current_team, field.short_name): + setattr(current_team, field.short_name, field.data) + if profile_edit_form.remove_avatar.data: + current_team._avatar = None + db.session.add(current_team) + db.session.commit() + flash("Profile updated.", "success") + return redirect(url_for("teams.settings")) + else: + for field in profile_edit_form: + if hasattr(current_team, field.short_name): + field.data = getattr(current_team, field.short_name, "") + return render_template("teams/settings.html", team=current_team, profile_edit_form=profile_edit_form, add_member_form=add_member_form) + + +def create_team(form): + new_team = Team(owner=current_user.uid) + db.session.add(new_team) + db.session.commit() + current_user.tid = new_team.tid + form.populate_obj(current_user.team) + db.session.add(current_user) + db.session.commit() + return new_team diff --git a/server/easyctf/views/users.py b/server/easyctf/views/users.py new file mode 100755 index 0000000..6788eec --- /dev/null +++ b/server/easyctf/views/users.py @@ -0,0 +1,287 @@ +import json +from datetime import datetime, timedelta +from io import BytesIO +from string import Template + +import pyqrcode +from flask import (Blueprint, abort, flash, redirect, render_template, request, + url_for) +from flask_login import current_user, login_required, login_user, logout_user +from sqlalchemy import func + +from easyctf.constants import (FORGOT_EMAIL_TEMPLATE, + REGISTRATION_EMAIL_TEMPLATE, USER_LEVELS) +from easyctf.forms.users import (ChangeLoginForm, LoginForm, + PasswordForgotForm, PasswordResetForm, + ProfileEditForm, RegisterForm, + TwoFactorAuthSetupForm) +from easyctf.models import Config, PasswordResetToken, Team, User +from easyctf.objects import db, sentry +from easyctf.utils import (generate_string, get_redirect_target, redirect_back, + sanitize_avatar, save_file, send_mail) + +blueprint = Blueprint("users", __name__, template_folder="templates") + + +@blueprint.route("/accept/") +@login_required +def accept(id): + target_team = Team.get_by_id(id) + max_size = Config.get_team_size() + try: + assert not current_user.tid, "You're already in a team!" + assert current_user in target_team.outgoing_invitations, "There is no invitation for you!" + if not target_team.admin: + assert target_team.size < max_size, "This team has already reached the maximum member limit!" + target_team.outgoing_invitations.remove(current_user) + target_team.members.append(current_user) + db.session.add(current_user) + db.session.add(target_team) + db.session.commit() + flash("Successfully joined team %s!" % target_team.teamname, "success") + return redirect(url_for("teams.profile")) + except AssertionError as e: + flash(str(e), "danger") + return redirect(url_for("teams.create")) + + +@blueprint.route("/password/forgot", methods=["GET", "POST"]) +def forgot(): + forgot_form = PasswordForgotForm() + if forgot_form.validate_on_submit(): + if forgot_form.user is not None: + token = PasswordResetToken(active=True, uid=forgot_form.user.uid, email=forgot_form.email.data, expire=datetime.utcnow() + timedelta(days=1)) + db.session.add(token) + db.session.commit() + url = url_for("users.reset", code=token.token, _external=True) + # TODO: stick this into the template + send_mail(forgot_form.email.data, "%s Password Reset" % Config.get("ctf_name"), "Click here to reset your password: %s" % url) + flash("Sent! Check your email.", "success") + return redirect(url_for("users.forgot")) + return render_template("users/forgot.html", forgot_form=forgot_form) + + +@blueprint.route("/password/reset/", methods=["GET", "POST"]) +def reset(code): + token = PasswordResetToken.query.filter_by(token=code, active=True).first() + if (not token) or token.expired or token.email != token.user.email: + return redirect(url_for("base.index")) + reset_form = PasswordResetForm() + if reset_form.validate_on_submit(): + user = User.get_by_id(token.uid) + user.password = reset_form.password.data + token.active = False + db.session.add(user) + db.session.add(token) + db.session.commit() + flash("Password has been reset! Try logging in now.", "success") + return redirect(url_for("users.login", next=url_for("users.profile"))) + return render_template("users/reset.html", reset_form=reset_form) + + +@blueprint.route("/login", methods=["GET", "POST"]) +def login(): + if current_user.is_authenticated: + return redirect(url_for("users.profile", uid=current_user.uid)) + login_form = LoginForm(prefix="login") + next = get_redirect_target() + if login_form.validate_on_submit(): + target_user = login_form.get_user() + if target_user.otp_confirmed and not target_user.verify_totp(login_form.code.data): + flash("Invalid code.", "danger") + return render_template("users/login.html", login_form=login_form, next=next) + + login_user(target_user, remember=login_form.remember.data) + flash("Successfully logged in as %s!" % target_user.username, "success") + if sentry.client: + sentry.client.capture_breadcrumb(message="login", category="user:login", level="info", data=dict(uid=target_user.uid, username=target_user.username), timestamp=datetime.now()) + return redirect_back("users.profile") + return render_template("users/login.html", login_form=login_form, next=next) + + +@blueprint.route("/logout") +def logout(): + logout_user() + return redirect(url_for("base.index")) + + +@blueprint.route("/profile") +@blueprint.route("/profile/") +def profile(uid=None): + if uid is None and current_user.is_authenticated: + return redirect(url_for("users.profile", uid=current_user.uid)) + user = User.get_by_id(uid) + if user is None: + abort(404) + if not current_user.is_authenticated: + flash("Please login to view user profiles!", "warning") + return redirect(url_for("users.login")) + user.type = USER_LEVELS[user.level] + return render_template("users/profile.html", user=user) + + +@blueprint.route("/register", methods=["GET", "POST"]) +def register(): + if current_user.is_authenticated: + return redirect(url_for("users.profile", uid=current_user.uid)) + register_form = RegisterForm(prefix="register") + if register_form.validate_on_submit(): + new_user = register_user(register_form.name.data, + register_form.email.data, + register_form.username.data, + register_form.password.data, + int(register_form.level.data), admin=False) + login_user(new_user) + return redirect(url_for("users.profile")) + return render_template("users/register.html", register_form=register_form) + + +@blueprint.route("/settings", methods=["GET", "POST"]) +@login_required +def settings(): + change_login_form = ChangeLoginForm(prefix="change-password") + profile_edit_form = ProfileEditForm(prefix="profile-edit") + if change_login_form.validate_on_submit() and change_login_form.submit.data: + current_email = current_user.email + if change_login_form.old_password.data: + if change_login_form.email.data != current_email: + # flash("changed email") + current_user.email = change_login_form.email.data + current_user.email_verified = False + current_user.email_token = generate_string() + if change_login_form.password.data: + # flash("changed password") + current_user.password = change_login_form.password.data + db.session.add(current_user) + db.session.commit() + flash("Login info updated.", "success") + return redirect(url_for("users.settings")) + elif profile_edit_form.validate_on_submit() and profile_edit_form.submit.data: + for field in profile_edit_form: + if field.short_name == "avatar": + if hasattr(field.data, "read") and len(field.data.read()) > 0: + field.data.seek(0) + f = BytesIO(field.data.read()) + new_avatar = sanitize_avatar(f) + if new_avatar: + response = save_file(new_avatar, prefix="user_avatar", suffix=".png") + if response.status_code == 200: + current_user._avatar = response.text + continue + if hasattr(current_user, field.short_name): + setattr(current_user, field.short_name, field.data) + if profile_edit_form.remove_avatar.data: + current_user._avatar = None + db.session.add(current_user) + db.session.commit() + flash("Profile updated.", "success") + return redirect(url_for("users.settings")) + else: + change_login_form.email.data = current_user.email + for field in profile_edit_form: + if hasattr(current_user, field.short_name): + field.data = getattr(current_user, field.short_name, "") + return render_template("users/settings.html", change_login_form=change_login_form, profile_edit_form=profile_edit_form) + + +@blueprint.route("/two_factor/required") +def two_factor_required(): + user = User.query.filter( + func.lower(User.username) == request.args.get("username", "").lower() + ).first() + if not user: + return json.dumps(False) + return json.dumps(user.otp_confirmed) + + +@blueprint.route("/two_factor/setup", methods=["GET", "POST"]) +@login_required +def two_factor_setup(): + two_factor_form = TwoFactorAuthSetupForm() + if two_factor_form.validate_on_submit(): + current_user.otp_confirmed = True + db.session.add(current_user) + db.session.commit() + flash("Two-factor authentication setup is complete.", "success") + return redirect(url_for("users.settings")) + return render_template("users/two_factor/setup.html", two_factor_form=two_factor_form) + + +@blueprint.route("/two_factor/qr") +@login_required +def two_factor_qr(): + url = pyqrcode.create(current_user.get_totp_uri()) + stream = BytesIO() + url.svg(stream, scale=6) + return stream.getvalue(), 200, { + "Content-Type": "image/svg+xml", + "Cache-Control": "no-cache, no-store, must-revalidate", + "Pragma": "no-cache", + "Expires": 0, + "Secret": current_user.otp_secret + } + + +@blueprint.route("/two_factor/disable") +@login_required +def two_factor_disable(): + current_user.otp_confirmed = False + db.session.add(current_user) + db.session.commit() + flash("Two-factor authentication disabled.", "success") + return redirect(url_for("users.settings")) + + +@blueprint.route("/verify/") +@login_required +def verify(code): + if current_user.email_verified: + flash("You've already verified your email.", "info") + elif current_user.email_token == code: + current_user.email_verified = True + db.session.add(current_user) + db.session.commit() + flash("Email verified!", "success") + else: + flash("Incorrect code.", "danger") + return redirect(url_for("users.settings")) + + +@blueprint.route("/verify") +@login_required +def verify_email(): + if current_user.email_verified: + return "" + code = generate_string() + current_user.email_token = code + db.session.add(current_user) + db.session.commit() + try: + link = url_for("users.verify", code=code, _external=True) + response = send_verification_email(current_user.username, current_user.email, link) + if response.status_code // 100 != 2: + return "failed" + return "success" + except Exception as e: + return str(e) + + +def send_verification_email(username, email, verification_link): + subject = "[ACTION REQUIRED] EasyCTF Email Verification" + body = Template(REGISTRATION_EMAIL_TEMPLATE).substitute({ + "link": verification_link, + "username": username + }) + return send_mail(email, subject, body) + + +def register_user(name, email, username, password, level, admin=False, **kwargs): + new_user = User(name=name, username=username, password=password, email=email, level=level, admin=admin) + for key, value in list(kwargs.items()): + setattr(new_user, key, value) + code = generate_string() + new_user.email_token = code + send_verification_email(username, email, url_for("users.verify", code=code, _external=True)) + db.session.add(new_user) + db.session.commit() + return new_user diff --git a/server/entrypoint.sh b/server/entrypoint.sh new file mode 100755 index 0000000..786e3e9 --- /dev/null +++ b/server/entrypoint.sh @@ -0,0 +1,22 @@ +#!/bin/bash +set -e +cd /var/easyctf/src +PYTHON=python3 + +echo "determining bind location..." +BIND_PORT=8000 +BIND_ADDR_=$(curl -w "\n" http://169.254.169.254/metadata/v1/interfaces/private/0/ipv4/address --connect-timeout 2 || printf "0.0.0.0") +BIND_ADDR=$(echo $BIND_ADDR_ | xargs) + +echo "starting EasyCTF..." +COMMAND=${1:-runserver} +ENVIRONMENT=${ENVIRONMENT:-production} +WORKERS=${WORKERS:-4} +$PYTHON manage.py db upgrade +if [ "$COMMAND" == "runserver" ]; then + if [ "$ENVIRONMENT" == "development" ]; then + $PYTHON manage.py runserver + else + exec gunicorn --bind="$BIND_ADDR:$BIND_PORT" -w $WORKERS 'easyctf:create_app()' + fi +fi diff --git a/server/env.example b/server/env.example new file mode 100644 index 0000000..5f3daaf --- /dev/null +++ b/server/env.example @@ -0,0 +1,10 @@ +DATABASE_URL=mysql://username:password@127.0.0.1/easyctf +SECRET_KEY=key +FILESTORE_SAVE_ENDPOINT=http://example.com/save +FILESTORE_STATIC=http://example.com +CACHE_REDIS_HOST=127.0.0.1 +ENVIRONMENT=development +ADMIN_EMAIL=admin@example.com +MAILGUN_URL=https://api.mailgun.net/v3/example.com +MAILGUN_API_KEY=key +SENTRY_DSN= \ No newline at end of file diff --git a/server/forgot.mail b/server/forgot.mail new file mode 100755 index 0000000..e69de29 diff --git a/server/import_problems.sh b/server/import_problems.sh new file mode 100755 index 0000000..24af843 --- /dev/null +++ b/server/import_problems.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +export $(cat /var/easyctf/env | xargs) +python3 manage.py import ~/problems diff --git a/server/manage.py b/server/manage.py new file mode 100755 index 0000000..fa16d83 --- /dev/null +++ b/server/manage.py @@ -0,0 +1,28 @@ +from flask_migrate import Migrate, MigrateCommand +from flask_script import Command, Manager, Server + +from easyctf import create_app +from easyctf.models import Problem +from easyctf.objects import db + +app = create_app() +migrate = Migrate(app, db) + +manager = Manager(app) +manager.add_command("db", MigrateCommand) + +ServerCommand = Server(host="0.0.0.0", port=8000, use_debugger=True, use_reloader=True) +manager.add_command("runserver", ServerCommand) + + +class ImportCommand(Command): + "Import CTF challenges from local repository." + + def __init__(self): + Command.__init__(self, func=Problem.import_repository) + + +manager.add_command("import", ImportCommand) + +if __name__ == "__main__": + manager.run() diff --git a/server/migrations/README b/server/migrations/README new file mode 100755 index 0000000..98e4f9c --- /dev/null +++ b/server/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/server/migrations/alembic.ini b/server/migrations/alembic.ini new file mode 100644 index 0000000..f8ed480 --- /dev/null +++ b/server/migrations/alembic.ini @@ -0,0 +1,45 @@ +# A generic, single database configuration. + +[alembic] +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/server/migrations/env.py b/server/migrations/env.py new file mode 100755 index 0000000..23663ff --- /dev/null +++ b/server/migrations/env.py @@ -0,0 +1,87 @@ +from __future__ import with_statement +from alembic import context +from sqlalchemy import engine_from_config, pool +from logging.config import fileConfig +import logging + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +fileConfig(config.config_file_name) +logger = logging.getLogger('alembic.env') + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +from flask import current_app +config.set_main_option('sqlalchemy.url', + current_app.config.get('SQLALCHEMY_DATABASE_URI')) +target_metadata = current_app.extensions['migrate'].db.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure(url=url) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + # this callback is used to prevent an auto-migration from being generated + # when there are no changes to the schema + # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html + def process_revision_directives(context, revision, directives): + if getattr(config.cmd_opts, 'autogenerate', False): + script = directives[0] + if script.upgrade_ops.is_empty(): + directives[:] = [] + logger.info('No changes in schema detected.') + + engine = engine_from_config(config.get_section(config.config_ini_section), + prefix='sqlalchemy.', + poolclass=pool.NullPool) + + connection = engine.connect() + context.configure(connection=connection, + target_metadata=target_metadata, + process_revision_directives=process_revision_directives, + **current_app.extensions['migrate'].configure_args) + + try: + with context.begin_transaction(): + context.run_migrations() + finally: + connection.close() + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/server/migrations/script.py.mako b/server/migrations/script.py.mako new file mode 100755 index 0000000..2c01563 --- /dev/null +++ b/server/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/server/migrations/versions/59f8fa2f0c98_.py b/server/migrations/versions/59f8fa2f0c98_.py new file mode 100644 index 0000000..2d30eb4 --- /dev/null +++ b/server/migrations/versions/59f8fa2f0c98_.py @@ -0,0 +1,327 @@ +"""Initial. + +Revision ID: 59f8fa2f0c98 +Revises: +Create Date: 2018-02-21 04:34:27.788175 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '59f8fa2f0c98' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('classrooms', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.Unicode(length=64), nullable=False), + sa.Column('owner', sa.Integer(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('config', + sa.Column('cid', sa.Integer(), nullable=False), + sa.Column('key', sa.Unicode(length=32), nullable=True), + sa.Column('value', sa.Text(), nullable=True), + sa.PrimaryKeyConstraint('cid') + ) + op.create_index(op.f('ix_config_key'), 'config', ['key'], unique=False) + op.create_table('eggs', + sa.Column('eid', sa.Integer(), nullable=False), + sa.Column('flag', sa.Unicode(length=64), nullable=False), + sa.PrimaryKeyConstraint('eid') + ) + op.create_index(op.f('ix_eggs_flag'), 'eggs', ['flag'], unique=True) + op.create_table('judge_api_keys', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('key', sa.String(length=64), nullable=True), + sa.Column('ip', sa.Integer(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_judge_api_keys_key'), 'judge_api_keys', ['key'], unique=False) + op.create_table('problems', + sa.Column('pid', sa.Integer(), nullable=False), + sa.Column('author', sa.Unicode(length=32), nullable=True), + sa.Column('name', sa.String(length=32), nullable=True), + sa.Column('title', sa.Unicode(length=64), nullable=True), + sa.Column('description', sa.Text(), nullable=True), + sa.Column('hint', sa.Text(), nullable=True), + sa.Column('category', sa.Unicode(length=64), nullable=True), + sa.Column('value', sa.Integer(), nullable=True), + sa.Column('grader', sa.UnicodeText(), nullable=True), + sa.Column('autogen', sa.Boolean(), nullable=True), + sa.Column('programming', sa.Boolean(), nullable=True), + sa.Column('threshold', sa.Integer(), nullable=True), + sa.Column('weightmap', sa.PickleType(), nullable=True), + sa.Column('test_cases', sa.Integer(), nullable=True), + sa.Column('time_limit', sa.Integer(), nullable=True), + sa.Column('memory_limit', sa.Integer(), nullable=True), + sa.Column('generator', sa.Text(), nullable=True), + sa.Column('source_verifier', sa.Text(), nullable=True), + sa.Column('path', sa.String(length=128), nullable=True), + sa.PrimaryKeyConstraint('pid'), + sa.UniqueConstraint('name') + ) + op.create_index(op.f('ix_problems_pid'), 'problems', ['pid'], unique=False) + op.create_table('teams', + sa.Column('tid', sa.Integer(), nullable=False), + sa.Column('teamname', sa.Unicode(length=32), nullable=True), + sa.Column('school', sa.Unicode(length=64), nullable=True), + sa.Column('owner', sa.Integer(), nullable=True), + sa.Column('admin', sa.Boolean(), nullable=True), + sa.Column('shell_user', sa.String(length=16), nullable=True), + sa.Column('shell_pass', sa.String(length=32), nullable=True), + sa.Column('banned', sa.Boolean(), nullable=True), + sa.Column('avatar', sa.String(length=128), nullable=True), + sa.PrimaryKeyConstraint('tid'), + sa.UniqueConstraint('shell_user'), + sa.UniqueConstraint('teamname') + ) + op.create_index(op.f('ix_teams_tid'), 'teams', ['tid'], unique=False) + op.create_table('autogen_files', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pid', sa.Integer(), nullable=True), + sa.Column('tid', sa.Integer(), nullable=True), + sa.Column('filename', sa.Unicode(length=64), nullable=True), + sa.Column('url', sa.String(length=128), nullable=True), + sa.ForeignKeyConstraint(['pid'], ['problems.pid'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_autogen_files_filename'), 'autogen_files', ['filename'], unique=False) + op.create_index(op.f('ix_autogen_files_id'), 'autogen_files', ['id'], unique=False) + op.create_index(op.f('ix_autogen_files_pid'), 'autogen_files', ['pid'], unique=False) + op.create_index(op.f('ix_autogen_files_tid'), 'autogen_files', ['tid'], unique=False) + op.create_table('classroom_invitation', + sa.Column('team_id', sa.Integer(), nullable=False), + sa.Column('classroom_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['classroom_id'], ['classrooms.id'], ), + sa.ForeignKeyConstraint(['team_id'], ['teams.tid'], ), + sa.PrimaryKeyConstraint('team_id', 'classroom_id') + ) + op.create_table('files', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pid', sa.Integer(), nullable=True), + sa.Column('filename', sa.Unicode(length=64), nullable=True), + sa.Column('url', sa.String(length=128), nullable=True), + sa.ForeignKeyConstraint(['pid'], ['problems.pid'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_files_id'), 'files', ['id'], unique=False) + op.create_index(op.f('ix_files_pid'), 'files', ['pid'], unique=False) + op.create_table('team_classroom', + sa.Column('team_id', sa.Integer(), nullable=False), + sa.Column('classroom_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['classroom_id'], ['classrooms.id'], ), + sa.ForeignKeyConstraint(['team_id'], ['teams.tid'], ), + sa.PrimaryKeyConstraint('team_id', 'classroom_id') + ) + op.create_table('users', + sa.Column('uid', sa.Integer(), nullable=False), + sa.Column('tid', sa.Integer(), nullable=True), + sa.Column('name', sa.Unicode(length=32), nullable=True), + sa.Column('easyctf', sa.Boolean(), nullable=True), + sa.Column('username', sa.String(length=16), nullable=True), + sa.Column('email', sa.String(length=128), nullable=True), + sa.Column('password', sa.String(length=128), nullable=True), + sa.Column('admin', sa.Boolean(), nullable=True), + sa.Column('level', sa.Integer(), nullable=True), + sa.Column('register_time', sa.DateTime(), nullable=True), + sa.Column('reset_token', sa.String(length=32), nullable=True), + sa.Column('otp_secret', sa.String(length=16), nullable=True), + sa.Column('otp_confirmed', sa.Boolean(), nullable=True), + sa.Column('email_token', sa.String(length=32), nullable=True), + sa.Column('email_verified', sa.Boolean(), nullable=True), + sa.Column('avatar', sa.String(length=128), nullable=True), + sa.ForeignKeyConstraint(['tid'], ['teams.tid'], ), + sa.PrimaryKeyConstraint('uid'), + sa.UniqueConstraint('email') + ) + op.create_index(op.f('ix_users_easyctf'), 'users', ['easyctf'], unique=False) + op.create_index(op.f('ix_users_uid'), 'users', ['uid'], unique=False) + op.create_index(op.f('ix_users_username'), 'users', ['username'], unique=True) + op.create_table('egg_solves', + sa.Column('sid', sa.Integer(), nullable=False), + sa.Column('eid', sa.Integer(), nullable=True), + sa.Column('tid', sa.Integer(), nullable=True), + sa.Column('uid', sa.Integer(), nullable=True), + sa.Column('date', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['eid'], ['eggs.eid'], ), + sa.ForeignKeyConstraint(['tid'], ['teams.tid'], ), + sa.ForeignKeyConstraint(['uid'], ['users.uid'], ), + sa.PrimaryKeyConstraint('sid') + ) + op.create_index(op.f('ix_egg_solves_eid'), 'egg_solves', ['eid'], unique=False) + op.create_index(op.f('ix_egg_solves_tid'), 'egg_solves', ['tid'], unique=False) + op.create_index(op.f('ix_egg_solves_uid'), 'egg_solves', ['uid'], unique=False) + op.create_table('game_states', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('uid', sa.Integer(), nullable=True), + sa.Column('last_updated', sa.DateTime(), server_default=sa.text('now()'), nullable=True), + sa.Column('state', sa.UnicodeText(), nullable=False), + sa.ForeignKeyConstraint(['uid'], ['users.uid'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('last_updated'), + sa.UniqueConstraint('uid') + ) + op.create_table('jobs', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pid', sa.Integer(), nullable=True), + sa.Column('tid', sa.Integer(), nullable=True), + sa.Column('uid', sa.Integer(), nullable=True), + sa.Column('submitted', sa.DateTime(), nullable=True), + sa.Column('claimed', sa.DateTime(), nullable=True), + sa.Column('completed', sa.DateTime(), nullable=True), + sa.Column('execution_time', sa.Float(), nullable=True), + sa.Column('execution_memory', sa.Float(), nullable=True), + sa.Column('language', sa.String(length=16), nullable=False), + sa.Column('contents', sa.Text(), nullable=False), + sa.Column('feedback', sa.Text(), nullable=True), + sa.Column('status', sa.Integer(), nullable=False), + sa.Column('verdict', sa.String(length=8), nullable=True), + sa.Column('last_ran_case', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['pid'], ['problems.pid'], ), + sa.ForeignKeyConstraint(['tid'], ['teams.tid'], ), + sa.ForeignKeyConstraint(['uid'], ['users.uid'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_jobs_pid'), 'jobs', ['pid'], unique=False) + op.create_index(op.f('ix_jobs_status'), 'jobs', ['status'], unique=False) + op.create_index(op.f('ix_jobs_tid'), 'jobs', ['tid'], unique=False) + op.create_index(op.f('ix_jobs_uid'), 'jobs', ['uid'], unique=False) + op.create_table('password_reset_tokens', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('uid', sa.Integer(), nullable=True), + sa.Column('active', sa.Boolean(), nullable=True), + sa.Column('token', sa.String(length=32), nullable=True), + sa.Column('email', sa.Unicode(length=128), nullable=True), + sa.Column('expire', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['uid'], ['users.uid'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_password_reset_tokens_uid'), 'password_reset_tokens', ['uid'], unique=False) + op.create_table('player_team_invitation', + sa.Column('user_id', sa.Integer(), nullable=True), + sa.Column('team_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['team_id'], ['teams.tid'], ), + sa.ForeignKeyConstraint(['user_id'], ['users.uid'], ) + ) + op.create_table('solves', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pid', sa.Integer(), nullable=True), + sa.Column('tid', sa.Integer(), nullable=True), + sa.Column('uid', sa.Integer(), nullable=True), + sa.Column('date', sa.DateTime(), nullable=True), + sa.Column('flag', sa.Unicode(length=256), nullable=True), + sa.ForeignKeyConstraint(['pid'], ['problems.pid'], ), + sa.ForeignKeyConstraint(['tid'], ['teams.tid'], ), + sa.ForeignKeyConstraint(['uid'], ['users.uid'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('pid', 'tid') + ) + op.create_index(op.f('ix_solves_id'), 'solves', ['id'], unique=False) + op.create_index(op.f('ix_solves_pid'), 'solves', ['pid'], unique=False) + op.create_index(op.f('ix_solves_tid'), 'solves', ['tid'], unique=False) + op.create_index(op.f('ix_solves_uid'), 'solves', ['uid'], unique=False) + op.create_table('team_player_invitation', + sa.Column('team_id', sa.Integer(), nullable=True), + sa.Column('user_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['team_id'], ['teams.tid'], ), + sa.ForeignKeyConstraint(['user_id'], ['users.uid'], ) + ) + op.create_table('wrong_egg', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('eid', sa.Integer(), nullable=True), + sa.Column('tid', sa.Integer(), nullable=True), + sa.Column('uid', sa.Integer(), nullable=True), + sa.Column('date', sa.DateTime(), nullable=True), + sa.Column('submission', sa.Unicode(length=64), nullable=True), + sa.ForeignKeyConstraint(['eid'], ['eggs.eid'], ), + sa.ForeignKeyConstraint(['tid'], ['teams.tid'], ), + sa.ForeignKeyConstraint(['uid'], ['users.uid'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_wrong_egg_eid'), 'wrong_egg', ['eid'], unique=False) + op.create_index(op.f('ix_wrong_egg_tid'), 'wrong_egg', ['tid'], unique=False) + op.create_index(op.f('ix_wrong_egg_uid'), 'wrong_egg', ['uid'], unique=False) + op.create_table('wrong_flags', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pid', sa.Integer(), nullable=True), + sa.Column('tid', sa.Integer(), nullable=True), + sa.Column('uid', sa.Integer(), nullable=True), + sa.Column('date', sa.DateTime(), nullable=True), + sa.Column('flag', sa.Unicode(length=256), nullable=True), + sa.ForeignKeyConstraint(['pid'], ['problems.pid'], ), + sa.ForeignKeyConstraint(['tid'], ['teams.tid'], ), + sa.ForeignKeyConstraint(['uid'], ['users.uid'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_wrong_flags_flag'), 'wrong_flags', ['flag'], unique=False) + op.create_index(op.f('ix_wrong_flags_id'), 'wrong_flags', ['id'], unique=False) + op.create_index(op.f('ix_wrong_flags_pid'), 'wrong_flags', ['pid'], unique=False) + op.create_index(op.f('ix_wrong_flags_tid'), 'wrong_flags', ['tid'], unique=False) + op.create_index(op.f('ix_wrong_flags_uid'), 'wrong_flags', ['uid'], unique=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f('ix_wrong_flags_uid'), table_name='wrong_flags') + op.drop_index(op.f('ix_wrong_flags_tid'), table_name='wrong_flags') + op.drop_index(op.f('ix_wrong_flags_pid'), table_name='wrong_flags') + op.drop_index(op.f('ix_wrong_flags_id'), table_name='wrong_flags') + op.drop_index(op.f('ix_wrong_flags_flag'), table_name='wrong_flags') + op.drop_table('wrong_flags') + op.drop_index(op.f('ix_wrong_egg_uid'), table_name='wrong_egg') + op.drop_index(op.f('ix_wrong_egg_tid'), table_name='wrong_egg') + op.drop_index(op.f('ix_wrong_egg_eid'), table_name='wrong_egg') + op.drop_table('wrong_egg') + op.drop_table('team_player_invitation') + op.drop_index(op.f('ix_solves_uid'), table_name='solves') + op.drop_index(op.f('ix_solves_tid'), table_name='solves') + op.drop_index(op.f('ix_solves_pid'), table_name='solves') + op.drop_index(op.f('ix_solves_id'), table_name='solves') + op.drop_table('solves') + op.drop_table('player_team_invitation') + op.drop_index(op.f('ix_password_reset_tokens_uid'), table_name='password_reset_tokens') + op.drop_table('password_reset_tokens') + op.drop_index(op.f('ix_jobs_uid'), table_name='jobs') + op.drop_index(op.f('ix_jobs_tid'), table_name='jobs') + op.drop_index(op.f('ix_jobs_status'), table_name='jobs') + op.drop_index(op.f('ix_jobs_pid'), table_name='jobs') + op.drop_table('jobs') + op.drop_table('game_states') + op.drop_index(op.f('ix_egg_solves_uid'), table_name='egg_solves') + op.drop_index(op.f('ix_egg_solves_tid'), table_name='egg_solves') + op.drop_index(op.f('ix_egg_solves_eid'), table_name='egg_solves') + op.drop_table('egg_solves') + op.drop_index(op.f('ix_users_username'), table_name='users') + op.drop_index(op.f('ix_users_uid'), table_name='users') + op.drop_index(op.f('ix_users_easyctf'), table_name='users') + op.drop_table('users') + op.drop_table('team_classroom') + op.drop_index(op.f('ix_files_pid'), table_name='files') + op.drop_index(op.f('ix_files_id'), table_name='files') + op.drop_table('files') + op.drop_table('classroom_invitation') + op.drop_index(op.f('ix_autogen_files_tid'), table_name='autogen_files') + op.drop_index(op.f('ix_autogen_files_pid'), table_name='autogen_files') + op.drop_index(op.f('ix_autogen_files_id'), table_name='autogen_files') + op.drop_index(op.f('ix_autogen_files_filename'), table_name='autogen_files') + op.drop_table('autogen_files') + op.drop_index(op.f('ix_teams_tid'), table_name='teams') + op.drop_table('teams') + op.drop_index(op.f('ix_problems_pid'), table_name='problems') + op.drop_table('problems') + op.drop_index(op.f('ix_judge_api_keys_key'), table_name='judge_api_keys') + op.drop_table('judge_api_keys') + op.drop_index(op.f('ix_eggs_flag'), table_name='eggs') + op.drop_table('eggs') + op.drop_index(op.f('ix_config_key'), table_name='config') + op.drop_table('config') + op.drop_table('classrooms') + # ### end Alembic commands ### diff --git a/server/registration.mail b/server/registration.mail new file mode 100755 index 0000000..4946f9c --- /dev/null +++ b/server/registration.mail @@ -0,0 +1,12 @@ +

    Hey ${username}!

    + +

    You recently signed up for EasyCTF IV with this email. To confirm your +email, please click on the following link (or copy-paste it into your browser +your email client didn't render a link).

    + +${link} + +

    We hope you have fun!

    + +

    __
    +The EasyCTF Team

    \ No newline at end of file diff --git a/server/requirements.txt b/server/requirements.txt new file mode 100755 index 0000000..a64cd87 --- /dev/null +++ b/server/requirements.txt @@ -0,0 +1,33 @@ +bcrypt +celery +coverage +cryptography==2.1.4 +flask +flask-breadcrumbs +flask-caching +flask-celery-helper +flask-csp +flask-login +flask-migrate +flask-script +flask-sqlalchemy +flask-wtf +gitdb +gitpython +markdown2 +mysqlclient +onetimepass +paramiko +passlib +pathlib +pillow +pycryptodome +pyqrcode +pytest +pyyaml +rauth +raven[flask] +redis +requests +sqlalchemy==1.1.0b3 +wtforms_components diff --git a/server/systemd/easyctf.service b/server/systemd/easyctf.service new file mode 100644 index 0000000..82821cd --- /dev/null +++ b/server/systemd/easyctf.service @@ -0,0 +1,16 @@ +[Unit] +Description=easyctf web server +After=network.target + +[Service] +EnvironmentFile=/var/easyctf/env +PIDFile=/run/easyctf/pid +User=root +WorkingDirectory=$PROJECT_DIRECTORY +ExecStart=$PROJECT_DIRECTORY/entrypoint.sh +ExecReload=$KILL -s HUP \$MAINPID +ExecStop=$KILL -s TERM \$MAINPID +PrivateTmp=true + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/shell/.gitignore b/shell/.gitignore new file mode 100644 index 0000000..2ac0426 --- /dev/null +++ b/shell/.gitignore @@ -0,0 +1,2 @@ +.env +.vagrant \ No newline at end of file diff --git a/shell/Vagrantfile b/shell/Vagrantfile new file mode 100644 index 0000000..9756cd2 --- /dev/null +++ b/shell/Vagrantfile @@ -0,0 +1,72 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# All Vagrant configuration is done below. The "2" in Vagrant.configure +# configures the configuration version (we support older styles for +# backwards compatibility). Please don't change it unless you know what +# you're doing. +Vagrant.configure(2) do |config| + # The most common configuration options are documented and commented below. + # For a complete reference, please see the online documentation at + # https://docs.vagrantup.com. + + # Every Vagrant development environment requires a box. You can search for + # boxes at https://atlas.hashicorp.com/search. + config.vm.box = "ubuntu/trusty64" + + # Disable automatic box update checking. If you disable this, then + # boxes will only be checked for updates when the user runs + # `vagrant box outdated`. This is not recommended. + # config.vm.box_check_update = false + + # Create a forwarded port mapping which allows access to a specific port + # within the machine from a port on the host machine. In the example below, + # accessing "localhost:8080" will access port 80 on the guest machine. + # config.vm.network "forwarded_port", guest: 80, host: 8080 + + # Create a private network, which allows host-only access to the machine + # using a specific IP. + # config.vm.network "private_network", ip: "192.168.33.10" + + # Create a public network, which generally matched to bridged network. + # Bridged networks make the machine appear as another physical device on + # your network. + # config.vm.network "public_network" + + # Share an additional folder to the guest VM. The first argument is + # the path on the host to the actual folder. The second argument is + # the path on the guest to mount the folder. And the optional third + # argument is a set of non-required options. + # config.vm.synced_folder "../data", "/vagrant_data" + + # Provider-specific configuration so you can fine-tune various + # backing providers for Vagrant. These expose provider-specific options. + # Example for VirtualBox: + # + # config.vm.provider "virtualbox" do |vb| + # # Display the VirtualBox GUI when booting the machine + # vb.gui = true + # + # # Customize the amount of memory on the VM: + # vb.memory = "1024" + # end + # + # View the documentation for the provider you are using for more + # information on available options. + + # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies + # such as FTP and Heroku are also available. See the documentation at + # https://docs.vagrantup.com/v2/push/atlas.html for more information. + # config.push.define "atlas" do |push| + # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME" + # end + + # Enable provisioning with a shell script. Additional provisioners such as + # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the + # documentation for more information about their specific syntax and use. + # config.vm.provision "shell", inline: <<-SHELL + # sudo apt-get update + # sudo apt-get install -y apache2 + # SHELL + config.vm.provision "shell", path: "setup.sh" +end diff --git a/shell/include/bin/addctfuser b/shell/include/bin/addctfuser new file mode 100755 index 0000000..2ce0ef8 --- /dev/null +++ b/shell/include/bin/addctfuser @@ -0,0 +1,25 @@ +#!/bin/bash + +function randomuser() { cat /dev/urandom | tr -dc '0-9' | fold -w 5 | head -n 1; } +function randomsalt() { cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1; } +function randompass() { cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1; } + +#======================================== +echo "Creating CTF user." + +USERFOUND=0 +NEWUSER="" +while [ $USERFOUND -lt 1 ]; do + NEWUSER="user$(randomuser)" + getent passwd $NEWUSER >/dev/null 2>&1 || USERFOUND=1 +done +salt=$(randomsalt) +RAWPASS=$(randompass) +NEWPASS=$(openssl passwd -1 -salt $salt $RAWPASS) + +sudo useradd --gid ctfuser \ + --password $NEWPASS \ + --create-home \ + --no-user-group \ + --shell /bin/bash $NEWUSER +echo "$NEWUSER:$RAWPASS" diff --git a/shell/include/etc/adduser.conf b/shell/include/etc/adduser.conf new file mode 100644 index 0000000..3d70759 --- /dev/null +++ b/shell/include/etc/adduser.conf @@ -0,0 +1,88 @@ +# /etc/adduser.conf: `adduser' configuration. +# See adduser(8) and adduser.conf(5) for full documentation. + +# The DSHELL variable specifies the default login shell on your +# system. +DSHELL=/bin/bash + +# The DHOME variable specifies the directory containing users' home +# directories. +DHOME=/home + +# If GROUPHOMES is "yes", then the home directories will be created as +# /home/groupname/user. +GROUPHOMES=yes + +# If LETTERHOMES is "yes", then the created home directories will have +# an extra directory - the first letter of the user name. For example: +# /home/u/user. +LETTERHOMES=no + +# The SKEL variable specifies the directory containing "skeletal" user +# files; in other words, files such as a sample .profile that will be +# copied to the new user's home directory when it is created. +SKEL=/etc/skel + +# FIRST_SYSTEM_[GU]ID to LAST_SYSTEM_[GU]ID inclusive is the range for UIDs +# for dynamically allocated administrative and system accounts/groups. +# Please note that system software, such as the users allocated by the base-passwd +# package, may assume that UIDs less than 100 are unallocated. +FIRST_SYSTEM_UID=100 +LAST_SYSTEM_UID=999 + +FIRST_SYSTEM_GID=100 +LAST_SYSTEM_GID=999 + +# FIRST_[GU]ID to LAST_[GU]ID inclusive is the range of UIDs of dynamically +# allocated user accounts/groups. +FIRST_UID=1000 +LAST_UID=29999 + +FIRST_GID=1000 +LAST_GID=29999 + +# The USERGROUPS variable can be either "yes" or "no". If "yes" each +# created user will be given their own group to use as a default. If +# "no", each created user will be placed in the group whose gid is +# USERS_GID (see below). +USERGROUPS=no + +# If USERGROUPS is "no", then USERS_GID should be the GID of the group +# `users' (or the equivalent group) on your system. +USERS_GID=1337 + +# If DIR_MODE is set, directories will be created with the specified +# mode. Otherwise the default mode 0755 will be used. +DIR_MODE=0700 + +# If SETGID_HOME is "yes" home directories for users with their own +# group the setgid bit will be set. This was the default for +# versions << 3.13 of adduser. Because it has some bad side effects we +# no longer do this per default. If you want it nevertheless you can +# still set it here. +SETGID_HOME=no + +# If QUOTAUSER is set, a default quota will be set from that user with +# `edquota -p QUOTAUSER newuser' +QUOTAUSER="loluser" + +# If SKEL_IGNORE_REGEX is set, adduser will ignore files matching this +# regular expression when creating a new home directory +SKEL_IGNORE_REGEX="dpkg-(old|new|dist|save)" + +# Set this if you want the --add_extra_groups option to adduser to add +# new users to other groups. +# This is the list of groups that new non-system users will be added to +# Default: +#EXTRA_GROUPS="dialout cdrom floppy audio video plugdev users" + +# If ADD_EXTRA_GROUPS is set to something non-zero, the EXTRA_GROUPS +# option above will be default behavior for adding new, non-system users +#ADD_EXTRA_GROUPS=1 + + +# check user and group names also against this regular expression. +#NAME_REGEX="^[a-z][-a-z0-9_]*\$" + +# use extrausers by default +#USE_EXTRAUSERS=1 diff --git a/shell/include/etc/pam.d/login b/shell/include/etc/pam.d/login new file mode 100644 index 0000000..a08bd3a --- /dev/null +++ b/shell/include/etc/pam.d/login @@ -0,0 +1,110 @@ +# +# The PAM configuration file for the Shadow `login' service +# + +# Enforce a minimal delay in case of failure (in microseconds). +# (Replaces the `FAIL_DELAY' setting from login.defs) +# Note that other modules may require another minimal delay. (for example, +# to disable any delay, you should add the nodelay option to pam_unix) +auth optional pam_faildelay.so delay=3000000 + +# Outputs an issue file prior to each login prompt (Replaces the +# ISSUE_FILE option from login.defs). Uncomment for use +# auth required pam_issue.so issue=/etc/issue + +# Disallows root logins except on tty's listed in /etc/securetty +# (Replaces the `CONSOLE' setting from login.defs) +# +# With the default control of this module: +# [success=ok new_authtok_reqd=ok ignore=ignore user_unknown=bad default=die] +# root will not be prompted for a password on insecure lines. +# if an invalid username is entered, a password is prompted (but login +# will eventually be rejected) +# +# You can change it to a "requisite" module if you think root may mis-type +# her login and should not be prompted for a password in that case. But +# this will leave the system as vulnerable to user enumeration attacks. +# +# You can change it to a "required" module if you think it permits to +# guess valid user names of your system (invalid user names are considered +# as possibly being root on insecure lines), but root passwords may be +# communicated over insecure lines. +auth [success=ok new_authtok_reqd=ok ignore=ignore user_unknown=bad default=die] pam_securetty.so + +# Disallows other than root logins when /etc/nologin exists +# (Replaces the `NOLOGINS_FILE' option from login.defs) +auth requisite pam_nologin.so + +# SELinux needs to be the first session rule. This ensures that any +# lingering context has been cleared. Without out this it is possible +# that a module could execute code in the wrong domain. +# When the module is present, "required" would be sufficient (When SELinux +# is disabled, this returns success.) +session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close + +# This module parses environment configuration file(s) +# and also allows you to use an extended config +# file /etc/security/pam_env.conf. +# +# parsing /etc/environment needs "readenv=1" +session required pam_env.so readenv=1 +# locale variables are also kept into /etc/default/locale in etch +# reading this file *in addition to /etc/environment* does not hurt +session required pam_env.so readenv=1 envfile=/etc/default/locale + +# Standard Un*x authentication. +@include common-auth + +# This allows certain extra groups to be granted to a user +# based on things like time of day, tty, service, and user. +# Please edit /etc/security/group.conf to fit your needs +# (Replaces the `CONSOLE_GROUPS' option in login.defs) +auth optional pam_group.so + +# Uncomment and edit /etc/security/time.conf if you need to set +# time restrainst on logins. +# (Replaces the `PORTTIME_CHECKS_ENAB' option from login.defs +# as well as /etc/porttime) +# account requisite pam_time.so + +# Uncomment and edit /etc/security/access.conf if you need to +# set access limits. +# (Replaces /etc/login.access file) +# account required pam_access.so + +# Sets up user limits according to /etc/security/limits.conf +# (Replaces the use of /etc/limits in old login) +session required pam_limits.so + +# Prints the last login info upon succesful login +# (Replaces the `LASTLOG_ENAB' option from login.defs) +session optional pam_lastlog.so + +# Prints the message of the day upon succesful login. +# (Replaces the `MOTD_FILE' option in login.defs) +# This includes a dynamically generated part from /run/motd.dynamic +# and a static (admin-editable) part from /etc/motd. +# session optional pam_motd.so motd=/run/motd.dynamic noupdate +# session optional pam_motd.so + +# Prints the status of the user's mailbox upon succesful login +# (Replaces the `MAIL_CHECK_ENAB' option from login.defs). +# +# This also defines the MAIL environment variable +# However, userdel also needs MAIL_DIR and MAIL_FILE variables +# in /etc/login.defs to make sure that removing a user +# also removes the user's mail spool file. +# See comments in /etc/login.defs +session optional pam_mail.so standard + +# Standard Un*x account and session +@include common-account +@include common-session +@include common-password + +# SELinux needs to intervene at login time to ensure that the process +# starts in the proper default security context. Only sessions which are +# intended to run in the user's context should be run after this. +session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open +# When the module is present, "required" would be sufficient (When SELinux +# is disabled, this returns success.) diff --git a/shell/include/etc/security/limits.conf b/shell/include/etc/security/limits.conf new file mode 100644 index 0000000..a226624 --- /dev/null +++ b/shell/include/etc/security/limits.conf @@ -0,0 +1,60 @@ +# /etc/security/limits.conf +# +#Each line describes a limit for a user in the form: +# +# +# +#Where: +# can be: +# - a user name +# - a group name, with @group syntax +# - the wildcard *, for default entry +# - the wildcard %, can be also used with %group syntax, +# for maxlogin limit +# - NOTE: group and wildcard limits are not applied to root. +# To apply a limit to the root user, must be +# the literal username root. +# +# can have the two values: +# - "soft" for enforcing the soft limits +# - "hard" for enforcing hard limits +# +# can be one of the following: +# - core - limits the core file size (KB) +# - data - max data size (KB) +# - fsize - maximum filesize (KB) +# - memlock - max locked-in-memory address space (KB) +# - nofile - max number of open files +# - rss - max resident set size (KB) +# - stack - max stack size (KB) +# - cpu - max CPU time (MIN) +# - nproc - max number of processes +# - as - address space limit (KB) +# - maxlogins - max number of logins for this user +# - maxsyslogins - max number of logins on the system +# - priority - the priority to run user process with +# - locks - max number of file locks the user can hold +# - sigpending - max number of pending signals +# - msgqueue - max memory used by POSIX message queues (bytes) +# - nice - max nice priority allowed to raise to values: [-20, 19] +# - rtprio - max realtime priority +# - chroot - change root to directory (Debian-specific) +# +# +# + +#* soft core 0 +#root hard core 100000 +#* hard rss 10000 +#@student hard nproc 20 +#@faculty soft nproc 20 +#@faculty hard nproc 50 +#ftp hard nproc 0 +#ftp - chroot /ftp +#@student - maxlogins 4 + +# End of file + +@ctfuser hard as 512000 +@ctfuser hard nproc 25 +@ctfuser hard maxlogins 4 diff --git a/shell/include/etc/sudoers b/shell/include/etc/sudoers new file mode 100644 index 0000000..194a538 --- /dev/null +++ b/shell/include/etc/sudoers @@ -0,0 +1,31 @@ +# +# This file MUST be edited with the 'visudo' command as root. +# +# Please consider adding local content in /etc/sudoers.d/ instead of +# directly modifying this file. +# +# See the man page for details on how to write a sudoers file. +# +Defaults env_reset +Defaults mail_badpass +Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin" + +# Host alias specification + +# User alias specification + +# Cmnd alias specification + +# User privilege specification +root ALL=(ALL:ALL) ALL +ctfadmin ALL=(ALL) NOPASSWD:ALL + +# Members of the admin group may gain root privileges +# admin ALL=(ALL) ALL + +# Allow members of group sudo to execute any command +# sudo ALL=(ALL) NOPASSWD:ALL + +# See sudoers(5) for more information on "#include" directives: + +#includedir /etc/sudoers.d \ No newline at end of file diff --git a/shell/setup.sh b/shell/setup.sh new file mode 100755 index 0000000..1bf9668 --- /dev/null +++ b/shell/setup.sh @@ -0,0 +1,30 @@ +FILE=`basename $0` +PROJECT=$(realpath `dirname FILE`) +INCLUDE=$PROJECT/include +source $PROJECT/.env + +#======================================== +echo "Copying configuration files..." +cp $INCLUDE/etc/pam.d/login /etc/pam.d +cp $INCLUDE/etc/sudoers /etc +chmod 440 /etc/sudoers +cp $INCLUDE/etc/adduser.conf /etc +cp $INCLUDE/etc/security/limits.conf /etc/security +cp $INCLUDE/bin/addctfuser /bin/addctfuser + +#======================================== +echo "Creating administrator user..." +groupadd ctfadmin +useradd --gid ctfadmin \ + --groups sudo \ + --home-dir /home/ctfadmin \ + --create-home \ + --shell /bin/addctfuser ctfadmin +echo "ctfadmin:$ADMIN_PASSWORD" | chpasswd + +chown ctfadmin:ctfadmin /bin/addctfuser +chmod 0100 /bin/addctfuser + +#======================================== +echo "Creating ctfuser group..." +groupadd --gid 1337 ctfuser